
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 异步回调),但逻辑结构完全一致:
chat_completion(messages, tools) | call_apihttp_post_async | |
urlopen() | poll_curl(500ms 间隔) | |
data["choices"][0] | json::parsechoices | |
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 个高层工具(summary、create_material、export_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 的架构可以作为一个参考起点:
先写 Harness:让你的软件能接收外部命令并返回结果。HyperSeek 用的是 TCP Socket + JSON,简单可靠,任何语言都能对接。
再写 LLM Loop:60 行
for循环,不做任何抽象。等你真正遇到了需要抽象的问题(比如多模型切换、上下文管理、错误重试),再考虑要不要引入框架。把安全做在 Loop 里面:不要相信 LLM 输出的任何命令。白名单、破坏性确认、操作前备份——这些在 Harness 的执行层做,而不是在 LLM 的 prompt 里"建议"。
给 LLM 好用的工具:裸 API 命令对 LLM 不友好。花时间封装一层语义化的高层工具(HyperSeek 的 13 个
::hmtools),投入产出比极高——LLM 的调用成功率会显著提升。适配比替换更重要:不要因为本地模型不支持 tool calling 就放弃它。HM_CMD 文本协议证明,有时候一个简单的回退方案比换模型更实际。
结语
回到最开始那个公式:
Agent = LLM Loop + Harness
HyperSeek 用 60 行 Python 和一套异步 Tcl 回调,在 HyperMesh 这个 40 年历史的 CAE 软件里,实现了从"中文指令"到"模型操作"的完整闭环。没有魔法,没有框架,只是基本的Agent逻辑。
下次有人跟你聊 Agent 的时候,你可以问他一句话:
"你的 Loop 是怎么写的?"
最后的最后,欢迎大家关注HyperSeek公众号,并尝试体验HyperSeek插件。
使用方法和下载链接见HyperSeek - 在一个运行在HyperMesh中的AI Agent
夜雨聆风