架构师(JiaGouX)
我们都是架构师!
架构未来,你来不来?
很多 AI Agent 架构图,末端都会画一个方框:输出结果。
用户提出目标,Planner 拆任务,模型推理,Tool 和 MCP 连接外部系统,Memory 留住过程,Reflection 做检查。箭头一路走到最后,交出一段回答、一个文件,或者一次外部操作。
图画到这里,任务看上去结束了。
放到业务系统里,这一格不够。
假设用户交给 Agent 一个任务:
整理三家竞品最近的产品变化,写成一份简报,发到项目 Slack 群里。
Agent 搜索资料,整理内容,生成简报,然后调用 Slack 接口。发送消息时,请求超时。
这时还不能判定成功或失败。消息可能没有发出去,也可能已经发出,只是响应没有回来。
如果 Agent 回复“任务已完成”,用户可能在群里看不到任何东西;如果 Agent 把超时当成失败,马上重发,群里又可能出现两份简报。
模型有了输出,工具也被调用过。但任务是否完成,系统没有证据。
这里少了一个判断:Agent 不只要产出内容,还要知道哪些事实足以支持自己停下来。
完成不止一层
前面几篇文章里,我们拆过 Planner、Tool、MCP、Memory、Harness 和 Handoff。它们分别处理目标理解、动作选择、系统连接和下一轮调度。
把这些能力接起来,仍然不等于“任务完成”。在工程里,完成至少有几层。
OpenAI Agents SDK 的运行循环能说明这个边界:模型产出符合要求的文本,并且不再发起工具调用时,Runner 会把它判断为 final_output,agent loop 结束。
这是一条运行结束条件,不是业务完成条件。
从一些 Coding Agent 的源码实现看,这条边界也很清楚:assistant、tool_result、result、status 和 task_notification 往往是不同消息;Stop、TaskCompleted 这类事件记录的是运行生命周期。框架能记下工具返回了什么、这一轮怎么结束,但不会替业务判断交付物是否已经被正确接收。
final_output 只能说明这次 run 已经收口。它不能回答:简报是不是发到了正确的群,链接有没有权限,内容是否覆盖了指定的三家竞品。
框架没有做错。它只是不知道每个业务认为什么叫交付。完成条件只能由应用自己补上。
反思不是观察
在一些 Agent 架构里,Reflection 放在动作之后:执行、观察、反思,再决定是否继续。这个环节是否有效,取决于 Reflection 拿到了什么。
如果工具只返回一句:
message sent successfully模型最多只能判断“工具说发送成功”。它看不到目标频道里是否真的出现了消息,也不知道消息里的报告链接能不能打开。
让模型再反思一遍,不会让缺失的事实出现。它很可能只是把工具的说法换一种语气复述出来。
所以要看观察。一次外部动作之后,系统有没有重新读取真实状态。
发送消息,要拿到频道和消息 ID,再按这个 ID 回读。写入文件,要检查文件是否存在、可读,必要章节是否齐全。修改配置,要确认运行中的系统已经加载新值。创建订单,要核对订单状态、金额和对象。
这些检查不一定要交给模型。确定性代码更适合做这类事:按消息 ID 查询、比较内容摘要、校验字段、检查状态转换。
kimi-code 的工具说明里有一条规则适合放在这里:一次 Edit 或 Write 成功后,不必为了证明写入已经发生而重读文件;但如果任务依赖某个精确的文件、API 或输出形态,结束前要检查最终外部契约。
放到 Agent 系统里也是同一件事。不必每一步都验证,但任务终点要验证。越靠近终点,判断依据越应该来自可回读证据,而不是模型的自我确认。
终点先写清
如果最后才问“这算完成了吗”,Agent 只能拿已有输出替自己作证。
规划阶段可以直接写下完成条件。它不需要变成一套流程,只要说清三件事:要得到什么结果,结果落在哪里,用什么事实证明。
回到 Slack 简报的例子,完成条件可以写得很直接。
简报不是“生成一段文字”,而是一份包含三家竞品、指定时间范围和来源的文档。最终对象不是 Agent 工作目录里的临时文件,而是项目 Slack 群里的消息。能证明它完成的事实,是稳定文档链接、目标成员可访问、Slack 返回消息 ID,并且按这个 ID 回读时,频道、正文和链接都对得上。
Planner 拆出的任务就会从“搜索、写作、发送”,变成一条能收口的链路:
目标 -> 行动 -> 观察 -> 验证 -> 完成
`-> 继续 / 等待 / 请人处理验证不需要追求绝对完美。查天气,工具返回有效结果就够了;生成草稿,可以检查文件存在和结构完整;涉及付款、权限、发布或删除,证据要具体到可回读、可比对,必要时交给人确认。
失败代价越高,“完成”越不能只靠一句成功返回。
超时先置未知
一些系统只给 Agent 两个选择:成功或者失败。外部动作一多,这个二选一就不够。
还是 Slack 消息的例子。请求超时只说明调用方没有及时收到响应,它不能证明动作没有发生。此时应该进入第三种状态:未知。
未知不是失败。它表示系统缺少证据,不能宣布完成,也不能直接重试。
幂等设计解决的就是这类问题。AWS 在讨论安全重试时用了一个典型场景:创建资源的请求遇到网络超时,调用方无法确定资源是否已经创建;直接重试,可能产生多个资源。
AWS 的做法是让调用方提供唯一请求标识。服务端用这个标识识别同一次意图,并在重试时给出语义一致的结果。Stripe 的幂等键也是同一类思路。
放到 Agent 系统里,这个请求标识可以来自 task_id、步骤 ID 和动作序号。第一次调用和重试使用同一个幂等键;超时之后先按键查询,能找到已生效结果就继续验证,确认没有执行再重试。
如果外部工具不支持幂等键,也没有可靠的查询接口,Agent 就不该装作知道答案。保留 unknown,把现有证据和风险交给人,比假装确定更可信。
可靠的 Agent 除了会重试,还要知道哪些动作不能随便再来一次。
验收不是新 Agent
有的架构图会再放一个 Verifier Agent。但多一个角色,不等于完成判断已经解决。
我对这种做法会谨慎。
如果验收只是检查文件是否存在、查询消息 ID、比对数据库字段,再放一个模型进去,只会多一层不确定性。完成条件清楚时,代码、规则和状态机更直接,也更容易测试。
只有当结果本身需要语义判断时,模型验收才有价值。比如检查一份竞品简报是否遗漏重要产品变化,不同来源有没有互相矛盾,结论是否超出了证据。
这时模型可以参与评估,但它引用的材料、评分口径和最终证据要留下。
要补的是验收能力,不一定是验收 Agent。它可以是 Runtime 里的一段闭环。
这样分工能避开一个误区:让模型执行、自评,再自己宣布通过。
模型可能做对。但出了问题,很难知道错在动作、观察,还是那句过早的“已完成”。
记录要小
一个可用的完成记录不需要复杂。它至少留下任务状态、成功条件、外部结果标识、验证结果,以及下一步要做什么。
{
"status": "completed | failed | unknown | needs_human",
"success_criteria": ["report_created", "message_visible"],
"result_ref": "slack:channel/message_id",
"idempotency_key": "task_id/step_id/action_index",
"verification": "matched",
"evidence": ["report_url", "message_id"],
"next_action": null
}这些字段看起来不如 Planner、Reasoning、Memory 吸引人,却决定了 Agent 的产出能不能进入下一段业务流程。
后续系统不用再解析一大段自然语言来猜任务是否结束。监控可以分清完成、明确失败和结果未知。人工接手时,看到的是已经核对过的事实,而不是一句笼统的错误提示。
如果重画那张 Agent 架构图,我会在“输出结果”下面多留一点位置。
输出只是模型结束表达,工具返回只是一次调用结束。只有当结果被观察、被核对,并且能对应用户最初的目标,这项任务才有资格收口。
一个可以放心交事的 Agent,不该只会说“任务已完成”。证据不足时,它应该停在 unknown。能够恢复的任务继续往下走;完成条件满足后再结束。终点要写清楚,证据要留下来。
往期相关推文
《AI Agent 架构全链路拆解:从 Planner、Tool、MCP 到 Memory 和 Reflection》 《Codex Harness 源码拆解:Agent 控制面如何嵌进业务系统》 《从 Claude Code 到 Codex:Coding Agent 的 Harness》 《四大多 Agent 架构详解(四):Handoff 之后,下一轮到底由谁负责》
参考资料
OpenAI Agents SDK,Running agents(https://openai.github.io/openai-agents-python/running_agents/) Malcolm Featonby,Making retries safe with idempotent APIs(https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/) Stripe,Idempotent requests(https://docs.stripe.com/api/idempotent_requests)
如喜欢本文,请点击右上角,把文章分享到朋友圈
如有想了解学习的技术点,请留言给若飞安排分享
因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享
·END·
相关阅读:
- Loop Engineering,应该赞成还是反对?
- Claude 做方案,Codex 写代码:多模型协作怎么交接才稳
- 架构排熵:Loop Engineering 的持续清理系统
- CLAUDE.md 拆解:Agent 进仓库前的上下文入口
- Harness工程还没唱罢,Environment工程已然登场
- 设计 Self-Harness 架构:会自我改进的Harness
版权申明:内容来源网络,仅供学习研究,版权归原创者所有。如有侵权烦请告知,我们会立即删除并表示歉意。谢谢!
架构师
我们都是架构师!
关注架构师(JiaGouX),添加“星标”
获取每天技术干货,一起成为牛逼架构师
技术群请加若飞:1321113940 进架构师群
投稿、合作、版权等邮箱:[email protected]