乐于分享
好东西不私藏

HyperSeek V1逻辑分析,说明 AI Agent 到底是怎么"干活"的

HyperSeek V1逻辑分析,说明 AI Agent 到底是怎么"干活"的

Agent 的本质:一个 while 循环 + 一根数据线

用 HyperSeek V1的简单架构,展示 AI Agent 的基本逻辑


引言:Agent 听起来很玄,其实很简单

2025 年以来,"AI Agent"成了最火的词。各种框架层出不穷:LangChain、AutoGen、CrewAI……每个都告诉你 Agent 能做"自主决策""多步推理""工具调用"。

但抛开所有营销话术,Agent 的核心就两样东西

  • LLM Loop
    一个 while 循环,反复调用大模型,直到任务完成。
  • Harness
    一套"插头",把 LLM 输出的指令接到真实的执行环境里。

就这么简单。没有魔法,没有"涌现的自主意识"。

本文以 HyperSeek(个运行在 HyperMesh 有限元前处理软件里的 AI 助手)——为例,逐行拆解这个公式到底是怎么工作的。


一、先看全貌:HyperSeek 的两条命

HyperSeek 是一个把大模型接进 Altair HyperMesh 的 Agent。你可以用中文告诉它"统计当前模型的节点数量""导出 OptiStruct 求解文件""删除所有叫 panel_old 的组件",它会自己查文档、执行命令、把结果告诉你。

它提供了两条完全独立的控制路径

有意思的是:两条路径用了两种完全不同的语言实现同一个 LLM Loop 模式——路径 1 是 Tcl 写的,路径 2 是 Python 写的。这恰恰说明,LLM Loop 是一个语言无关的设计模式,不是某个框架的专属能力。


二、核心:LLM Loop 到底长什么样?

2.1 最简版本:Python 实现的 LLM Loop

HyperSeek 的外部 Harness(agent_llm.py)包含一个教科书级别的 LLM Loop。我把关键代码简化后贴在下面:

defrun_agent(prompt):# 准备工具定义(告诉 LLM 它能调用什么)    tools = [HM_TOOL, RAG_TOOL]  # hm_command + rag_search# 初始化消息列表(system prompt + 用户任务)    messages = [        {"role""system""content": SYSTEM_PROMPT},        {"role""user""content": prompt},    ]# ═══════════════════════════════════════════# 这就是 LLM Loop —— 一个 for 循环# ═══════════════════════════════════════════for _ inrange(60):  # 硬上限,防止死循环# 第 1 步:调用 LLM        data = chat_completion(messages, tools=tools)# 第 2 步:取出 LLM 的回复        choice = data["choices"][0]        message = choice.get("message", {})        messages.append(message)  # 加入对话历史        finish_reason = choice.get("finish_reason""")# 第 3 步:如果 LLM 输出了文字,打印给用户看        content = message.get("content"or""if content.strip():print(f"\n{content}")# 第 4 步:如果 LLM 说"我完成了",退出循环if finish_reason != "tool_calls":return# ← 任务结束# 第 5 步:否则,LLM 一定想调用工具        tool_calls = message.get("tool_calls"or []for tool_call in tool_calls:            name = tool_call["function"]["name"]            args  = json.loads(tool_call["function"]["arguments"])# 第 6 步:执行工具,拿到结果if name == "hm_command":                output = bridge.run(args["command"])   # 发给 HyperMesh 执行elif name == "rag_search":                output = rag.query(args["query"])      # 查本地知识库print(f"< {output[:1500]}")# 第 7 步:把工具结果塞回消息列表            messages.append({"role""tool","tool_call_id": tool_call["id"],"content": output            })# 第 8 步:回到第 1 步,继续循环 —— LLM 会看到工具结果,#         决定下一步是继续调工具,还是输出最终答案

这就是全部。 60 行代码,没有框架,没有抽象层,urllib 发 HTTP 请求,标准库 json 解析响应。但它确实是一个能自主完成多步仿真建模任务的 Agent。

2.2 异步版本:Tcl 实现的 LLM Loop

更有意思的是窗体内 Agent。HyperMesh 的 Tcl 解释器是单线程事件循环,不能像 Python 那样同步 while 等待 HTTP 响应。所以 LLM Loop 被拆分成了异步回调链

虽然实现方式不同(同步 for vs 异步回调),但逻辑结构完全一致

步骤
Python 版
Tcl 版
发起请求
chat_completion(messages, tools)call_api
 → http_post_async
等待响应
同步阻塞 urlopen()
异步轮询 poll_curl(500ms 间隔)
解析结果
data["choices"][0]json::parse
 → choices
判断终止
finish_reason != "tool_calls"finish_reason ne "tool_calls"
执行工具
bridge.run()
 / rag.query()
execute
 / ::hmtools::call
结果回传
messages.append(tool_result)lappend messages tool_result
继续循环
for
 循环的下一次迭代
call_api callback
 递归

同一个设计模式,两种语言,两套运行时,行为完全一致。


三、LLM Loop 的五个关键设计决策

光有一个循环不够。从 HyperSeek 的实现中,可以看到一个生产级 Agent 在 LLM Loop 上做的五个关键设计:

3.1 硬上限:防止无限循环

for _ inrange(60):  # 最多 60 轮

LLM 可能在工具调用中"鬼打墙"——反复调用同一个工具、每次都拿不到想要的结果、但永远不觉得任务完成了。硬上限是最简单有效的兜底。HyperSeek 设了 60 轮,对于仿真建模任务绰绰有余。

3.2 上下文裁剪:本地模型的生存之道

本地 llama.cpp 模型的上下文窗口很小(默认 2048 tokens)。多轮工具调用会让 messages[] 迅速膨胀,超出窗口上限。所以窗体内 Agent 做了上下文裁剪

proc ::hyperseek::trim_local_messages {} {    # 保留 system prompt + 最近 6 条消息set start [expr {$n - 6}]    # 每条消息内容截断到 800 字符dictset m content [truncate_for_context $content]}

这是一个工程上的实用妥协:丢掉中间轮次的对话细节,只保留最近的上下文,让模型能继续工作。

3.3 工具结果截断:防止单条结果撑爆上下文

HyperMesh 的某些命令(比如列出所有节点)可能返回几万行输出。如果不截断,一条工具结果就能撑爆 LLM 的上下文窗口:

print(f"< {output[:1500]}")  # 只保留前 1500 字符
append_log [string range $output01500"output"

3.4 HM_CMD 回退协议:当模型不支持 Tool Calling 时

本地 GGUF 模型(如 Qwen2.5-Coder-7B)通常不支持标准的 OpenAI tool calling。HyperSeek 设计了一个文本协议回退方案

在 system prompt 中告诉本地模型:

"When the bridge does not expose tool calling, reply with a single line starting with HM_CMD followed by one Tcl command."

然后 handle_response 中增加对 HM_CMD 行的解析:

if {$provider eq "local"} {foreach line [split$content"\n"] {if {[regexp {^\s*HM_CMD\s+(.+)$} $line -> cmd]} {            # 执行命令,结果回传,继续循环set output [::hyperseek::execute $cmd]lappend messages [list role user content "Result:\n$output"]        }    }    call_api ::hyperseek::handle_response  ;# 继续循环}

这本质上是把 Tool Calling 的 JSON 协议降级成了纯文本协议,但 LLM Loop 的结构完全没有变。

3.5 安全闸门:每一条命令都要过安检

这是 HyperSeek 区别于"裸调 API 的 Demo"最关键的地方。LLM 生成的每一条命令,在执行前都要经过:

proc ::hyperseek::execute {script} {    # ① 白名单检查if {![allowed $script]} { error"command not allowed" }    # ② 破坏性命令确认if {$confirm_destructive && [is_destructive $script]} {set ans [tk_messageBox "Allow this destructive command?\n\n$script"]if {$ans ne "yes"} { error"blocked by user" }    }    # ③ 自动应答 HyperMesh 弹窗catch {hm_answernext yes}    # ④ 执行return [uplevel #0$script]}

安全不是在 LLM Loop 外面套一层壳,而是嵌入在 Loop 的每一次工具执行里。 这使得安全策略对所有入口(GUI、CLI、Python、Tcl)一致生效。

五个决策在 Loop 中的位置


四、Harness:把 LLM 的"想法"变成"动作"

如果说 LLM Loop 是大脑,Harness 就是手脚。它负责把 LLM 输出的抽象指令("执行 hm_info currentfile")翻译成真实世界的操作(在 HyperMesh Tcl 解释器里执行这条命令,并把结果字符串返回)。

HyperSeek 的 Harness 有四个层次:

值得注意的是 Layer 2(高层工具层)的设计哲学

裸 Tcl 命令对 LLM 并不友好——参数位置敏感、引号转义复杂、不同命令的返回格式不一致。13 个高层工具(summarycreate_materialexport_optistruct_fem 等)把裸命令封装成 带类型参数的函数调用,让 LLM 只需要生成结构化的 JSON 参数,工具层负责拼装正确的 Tcl 命令串。

这层抽象还有一个好处:新增工具不需要改动 LLM Loop 的任何代码。写一个 proc,在注册表登记,更新元数据,LLM 下次就能调用了。


五、两种模型的适配策略

HyperSeek 支持两种 LLM 后端,它们的适配方式形成了鲜明对比:

关键洞察:LLM Loop 的结构没有因为模型不同而改变。变的是 Harness 层的适配策略——怎么发请求、怎么解析工具调用、怎么管理上下文。这验证了 "LLM Loop + Harness" 的分层设计是正确的:Loop 的逻辑是稳定的,Harness 的适配是灵活的。


六、Agent 的本质:去掉所有框架之后剩下的东西

如果你把 LangChain 的 AgentExecutor、AutoGen 的 ConversableAgent、CrewAI 的 Crew 全部剥开,剩下的骨架和 HyperSeek 的 60 行 Python 一模一样:

这就是 Agent。 剩下的都是工程细节:

  • 你的 Harness 接的是 HyperMesh 还是浏览器还是数据库?——那是工具层的事。
  • 你的 Loop 是同步 for 还是异步回调?——那是运行时的事。
  • 你要不要加安全闸门、上下文裁剪、回退协议?——那是生产化的事。
  • 你的模型是在线 API 还是本地 GGUF?——在 Loop 看来只是 llm.chat() 的实现不同。

Agent 不神秘。它就是一个 while 循环,加上一根能插进真实世界的数据线。


七、给想自己写 Agent 的工程师

如果你也在考虑把 LLM 接入某个专业软件(CAD、CAE、EDA、BIM……),HyperSeek 的架构可以作为一个参考起点:

  1. 先写 Harness:让你的软件能接收外部命令并返回结果。HyperSeek 用的是 TCP Socket + JSON,简单可靠,任何语言都能对接。

  2. 再写 LLM Loop:60 行 for 循环,不做任何抽象。等你真正遇到了需要抽象的问题(比如多模型切换、上下文管理、错误重试),再考虑要不要引入框架。

  3. 把安全做在 Loop 里面:不要相信 LLM 输出的任何命令。白名单、破坏性确认、操作前备份——这些在 Harness 的执行层做,而不是在 LLM 的 prompt 里"建议"。

  4. 给 LLM 好用的工具:裸 API 命令对 LLM 不友好。花时间封装一层语义化的高层工具(HyperSeek 的 13 个 ::hmtools),投入产出比极高——LLM 的调用成功率会显著提升。

  5. 适配比替换更重要:不要因为本地模型不支持 tool calling 就放弃它。HM_CMD 文本协议证明,有时候一个简单的回退方案比换模型更实际。


结语

回到最开始那个公式:

Agent = LLM Loop + Harness

HyperSeek 用 60 行 Python 和一套异步 Tcl 回调,在 HyperMesh 这个 40 年历史的 CAE 软件里,实现了从"中文指令"到"模型操作"的完整闭环。没有魔法,没有框架,只是基本的Agent逻辑。

下次有人跟你聊 Agent 的时候,你可以问他一句话:

"你的 Loop 是怎么写的?"


最后的最后,欢迎大家关注HyperSeek公众号,并尝试体验HyperSeek插件。

使用方法和下载链接见HyperSeek - 在一个运行在HyperMesh中的AI Agent