一开始我以为,做一个 AI Agent,核心是把模型接起来。
接一个大模型,注册几个工具(tools),再写一点规划逻辑,让模型一步步决定调用哪个工具,一个最小可用的 demo 很快就能跑起来。
真正开始往下做之后,我才慢慢意识到:让它"跑起来"并不难,难的是让它在复杂场景下稳定地跑下去。
比如:
- 任务执行到一半,需要人工审批
- 服务重启了,任务还能不能继续
- 多个步骤之间,结果怎么稳定传递
- 明明文件已经生成了,系统怎么判断它真的完成了
- 如果某一步做错了,能不能回到正确的位置继续修
到这一步,它就不太像一个"会调用工具的 LLM"了,而更像一个需要状态管理、恢复能力和执行约束的运行时系统(runtime)。
这篇文章不是教程,也不是最佳实践总结。更像是一份阶段性复盘:记录一下我在做这个项目过程中踩过的坑,以及一些后来慢慢形成的架构理解。
我在做的是什么
我在做的是 AvatarOS,一个本地优先的 AI Agent Runtime。
它不是一个单纯"能聊天"的 Agent,而是更偏向执行层:用户用自然语言描述目标,系统去做任务拆解、步骤执行、工具调用、产物管理,并尽量把执行过程做得更可控一些。
目前项目还很早期,也还有不少问题没完全解决。但做的过程中,我越来越明显地感受到一件事:
Agent 真正难的部分,往往不是"怎么让模型回答得更像人",而是"怎么让系统把事情稳定做完"。
1. 审批等待这件事,表面简单,实际上很容易做错
最开始做人工审批的时候,我用了一个很直觉的方案:在执行流里直接等待用户批准。
思路很自然,因为从代码视角看,这就是"执行到这里先停一下,等用户点按钮,再继续往下走"。
但这个方案的问题很快就暴露出来了:只要服务重启,等待中的状态就没了。
它暴露出的根本问题是:
我当时把"审批等待"当成了一段内存中的阻塞过程,而不是一个需要持久化的任务状态。
后来慢慢才想清楚,更合理的模型其实应该是:
- 遇到审批点时,保存 checkpoint
- 把任务状态标记成
waiting_input - 释放原来的执行流
- 等用户操作后,再从 checkpoint 恢复
也就是说,审批不应该是"卡住线程等一等",而应该是"把任务切换到一个可恢复状态"。
这个认知转变对我影响挺大的。因为从这一步开始,很多问题就不再是异步编程层面的细节,而变成了任务生命周期管理的问题。
2. 多步骤任务不是一段长对话,而是一个有结构的执行过程
一开始很容易把 Agent 理解成一个对话循环:
text
用户输入 → LLM 判断下一步 → 调工具 → 观察结果 → 再决定下一步
这个模型在简单任务上完全够用。但任务一复杂,就会暴露很多问题。
比如:
- 第 2 步依赖第 1 步的输出
- 第 4 步必须等第 3 步完成
- 某个中间结果格式错了,后面全都会受影响
- 某一步失败后,不应该整条链路从头再来
这时候你会发现,任务本质上不是"多轮对话",而是一个有依赖关系的执行过程。
后来我越来越倾向于把它建模成一个 DAG:
- 节点是 step
- 边是依赖关系
- 节点有自己的执行状态
这样做的好处不是"更学术",而是更容易把问题说清楚:
- 哪一步依赖哪一步
- 哪一步完成了
- 哪一步失败了
- 哪一步可以重试
- 哪一步不应该被过早调度
这类结构化表达,在复杂任务里比"继续和模型聊下去"稳定得多。
3. "什么时候算完成"比我最初想的难很多
这个问题我一开始其实低估了。
因为在最初的直觉里,任务完成似乎就是:模型觉得做完了,于是输出 FINISH。
但实际跑起来以后会发现,完成判定其实很容易两头出问题:
一种情况是过早结束 。
比如任务还没真正满足目标,但模型觉得"差不多了"。
另一种情况是迟迟不结束 。
比如文件已经生成了,结果系统还是继续读、继续查、继续验证,像是在反复确认一个已经成立的事实。
后来我慢慢觉得,完成这件事不能只依赖模型的主观判断,而应该尽量引入一些更明确的依据,比如:
- 文件是否真的存在
- 输出格式是否可解析
- 表格里是否真的有数据
- 某个关键字段是否包含了预期内容
也就是说,完成判定最好是一部分由 LLM 理解,一部分由程序验证。
仅靠其中一边都不太稳。
4. 多 Agent 这件事,难点可能不在"多",而在"怎么收束"
这是我后面感受比较深的一点。
刚开始接触多 Agent 的时候,很容易觉得它的核心是"角色拆分":研究员去查资料,执行者去跑任务,验证者去检查结果,最后再有一个人总结。
但做着做着会发现,真正麻烦的其实不是怎么把角色拆出来,而是:
- 谁对全局目标负责
- 子任务之间冲突怎么办
- 执行顺序谁来管
- 某个角色给出了不完整结果,谁来兜底
- 最后到底由谁判断任务可以结束
如果这些都没有一个明确的"收束点",多 Agent 往往会变成一种"看起来更复杂、实际上更难控"的系统。
所以我后来越来越在意 Supervisor 这样的角色。
它不一定非要很"强",但至少需要有人站在全局视角上做这几件事:
- 保持目标一致
- 管理 handoff
- 决定是否继续
- 决定是否中断或回退
不然的话,多 Agent 很容易只是"多个 prompt 同时工作",而不是一个真正协作的系统。
5. 真正的输出往往不是一句话,而是一堆产物
很多 Agent demo 的结果都是文本,所以很容易让人产生一种错觉:好像最后把话说出来就算完成了。
但真实任务里,结果经常不是一句回复,而是:
- 一个 Markdown 文件
- 一个 Word / Excel / PPT
- 一张图
- 一份中间数据
- 一个可继续迭代的工作区
这时候,系统要处理的就不仅是"文本对不对",还包括:
- 文件放在哪里
- 这个文件属于哪个任务
- 哪个步骤产生了它
- 后续步骤怎么引用它
- 用户修改后能不能继续迭代
所以后来我越来越觉得,workspace / artifact 这一层很重要。
它让任务结果不再只是"一段模型输出",而变成一个可追踪、可引用、可验证的实体。
6. 我现在越来越倾向于把 Agent 看成 Runtime,而不是 Wrapper
回头看前面这些问题,会发现一个共同点:
它们大多不是 prompt 本身的问题,也不完全是模型能力的问题。
更多是执行层的问题,比如:
- 状态怎么保存
- 任务怎么恢复
- 权限怎么约束
- 副作用怎么隔离
- 系统怎么知道自己是不是卡住了
- 什么时候该让人介入
这些问题放在传统软件工程里并不陌生。
只是到了 Agent 这个语境下,很多人会先被"模型会不会思考"吸引,反而低估了执行系统本身的复杂度。
这也是为什么我现在越来越觉得:
Agent 更像是 "LLM + Runtime",而不只是 "LLM + Tools"。
LLM 很重要,它负责理解、规划、决策。
但 Runtime 同样重要,它负责执行、状态、约束和恢复。
如果没有后面这一层,很多看起来能跑的 Agent,到了真实任务里都会变得很脆弱。
目前的一些想法
到现在为止,我不太敢说自己已经得出了什么成熟结论。更准确地说,是有了一些越来越稳定的倾向:
- 长时任务一定需要持久化状态
- 审批流最好天然支持中断与恢复
- 复杂任务最好有显式结构,而不是完全靠对话推进
- 完成判定不能只交给模型
- 多 Agent 需要一个全局收束机制
- 产物管理在真实任务里比我最初想的更关键
这些想法很多也还在项目里继续验证,未必都是最终答案,但至少它们确实来自一些真实的问题。
写在最后
做这个项目最大的收获之一,是我慢慢意识到:
Agent 的难点,可能不只是"让模型会做事",而是"让系统在复杂环境下持续把事情做对"。
这两者看起来很像,但其实是两套完全不同的问题。
前者更像模型能力问题。
后者更接近运行时、状态机、容错和系统设计问题。
而我这半年真正花时间最多的,恰恰是后者。
如果你也在做 Agent Runtime、HITL 工作流、持久化执行、或者类似方向的东西,欢迎交流。
如果这篇文章能让你少踩一个坑,我就已经很满足了。