如何设计一个可观察、可恢复的 Agent 系统
Agent 最容易做出 Demo,也最容易在生产里失控。
Demo 只需要让它完成一次任务。生产系统需要回答更难的问题:它现在执行到哪里了?为什么调用这个工具?失败在哪一步?能不能从失败点继续?用户能不能取消?管理员能不能审计?下次改 Prompt 会不会让它更差?
可观察和可恢复,是 Agent 从玩具走向系统的分界线。
设计目标
一个生产级 Agent 系统至少应该满足六个目标。
第一,过程透明。系统能展示每一步计划、工具调用、观察结果、判断和错误。
第二,状态持久。任务执行不依赖单个 HTTP 请求或内存对象,Worker 重启后也能恢复。
第三,边界明确。工具调用经过权限、schema、风险等级、超时和审批控制。
第四,失败可恢复。失败后可以重试当前步骤、改用 fallback、请求用户补充信息、转人工或安全终止。
第五,成本可控。每个 run 有最大步数、最大耗时、最大 token、最大工具调用次数。
第六,可评估。系统不仅评估最终答案,还评估计划质量、工具选择、参数正确性和恢复能力。
这六个目标决定了 Agent 的架构不能只是一段循环代码。
总体架构
一个可观察、可恢复的 Agent 系统可以拆成九个核心模块。
这个架构的重点是 Orchestrator 和 State Store。
Orchestrator 不只是 while loop。它应该像一个任务状态机,明确每个 run 可以从什么状态进入什么状态。
State Store 也不只是保存最终回答。它要保存 Agent 的每个 step、tool call、observation、error、artifact 和事件。
第一步:定义 Run 和 Step
Agent 的顶层单位应该是 run。
Run 表示一次用户目标的执行过程。它有状态、预算、创建者、配置版本和最终结果。
Step 表示一次可恢复的执行单元。它可以是一次规划、一次工具调用、一次验证、一次生成,也可以是一次用户确认。
这里最重要的是:Step 是恢复边界。
如果系统无法把失败定位到某个 step,就很难实现“从失败处重试”。所以不要只保存一串聊天消息,要保存结构化步骤。
第二步:设计事件流
前端需要实时看到 Agent 的执行过程。最常见的方式是 SSE 或 WebSocket。
事件不应该只是文本 token。Agent 事件应该描述状态变化。
有了事件流,前端可以构建一个清晰的执行时间线,而不是把所有内容塞进聊天气泡。
UI 至少应该提供:
- 当前状态。
- 步骤列表。
- 每步耗时。
- 工具名称和摘要。
- 失败原因。
- 重试入口。
- 取消入口。
- 人工确认入口。
- 最终结果和证据来源。
对用户来说,Agent 的可信度很大程度来自“我看得懂它在做什么”。
第三步:让工具边界可审计
Agent 的风险主要来自工具。
一个工具定义不应该只是函数名和描述。它还应该包含输入输出 schema、权限、风险等级、超时、重试策略、是否幂等、是否需要审批。
工具执行时要记录完整审计信息:
如果工具会产生真实外部影响,例如发邮件、修改数据库、创建订单,默认应该需要人工确认或 dry run。
Agent 可以建议行动,但执行权必须经过工具层控制。
第四步:设计状态机
可恢复 Agent 必须有明确状态机。
状态机带来几个好处。
第一,前端能稳定渲染不同状态。waiting_approval 显示确认面板,waiting_input 显示补充表单,failed 显示恢复操作。
第二,后端能拒绝非法操作。例如 completed 的 run 不能继续追加工具调用,cancelled 的 run 不能被 Worker 继续执行。
第三,恢复逻辑更清楚。只有 recoverable error 才允许 retry,只有 approval step 才允许 approve。
第五步:实现恢复策略
恢复不是一句“让模型再试一次”。
不同失败要有不同策略:
恢复策略应该由应用层决定,模型可以参与建议,但不能完全自主。
一个 recover step 可以记录:
恢复动作本身也应该进入日志。否则你只知道任务最后成功了,却不知道它曾经失败过两次。
第六步:加入验证器
Verifier 是 Agent 的刹车。
它要回答几个问题:
- 当前结果是否满足用户目标?
- 是否有足够证据支持结论?
- 工具调用是否成功且结果可信?
- 输出格式是否符合 UI 或 API 需要?
- 是否需要用户确认?
- 是否应该继续执行,还是停止?
验证器可以分层:
- 规则验证:schema、必填字段、引用 ID、权限、最大步骤。
- 事实验证:结论是否有证据来源。
- 目标验证:是否覆盖 success criteria。
- 风险验证:是否涉及写操作、敏感数据或外部影响。
- 模型评审:开放式任务由 LLM-as-judge 辅助判断。
不要把验证完全交给同一个 Agent 自评。可以使用独立 Prompt、独立模型或规则系统,减少“自己给自己打分”的偏差。
第七步:为前端设计执行视图
Agent 前端的核心不是聊天气泡,而是执行视图。
一个实用布局可以包含四块:
- 顶部任务状态:标题、运行状态、耗时、取消按钮。
- 左侧步骤时间线:每一步状态、工具、耗时、失败标记。
- 中间详情面板:当前步骤的输入、输出、证据、错误。
- 底部行动区:确认、重试、补充信息、接受结果。
不要把所有内部细节暴露给普通用户。前端可以做分层:
这能兼顾信任感和可用性。普通用户不需要看完整 JSON,但开发者必须能复盘。
第八步:建立观察指标
Agent 的监控不能只看成功率。
建议至少记录:
- task success rate。
- average steps per run。
- tool success rate。
- tool parameter validation failure rate。
- recovery success rate。
- approval rate。
- cancellation rate。
- timeout rate。
- average cost per run。
- p50 / p95 duration。
- verifier failure categories。
- user satisfaction 或人工反馈。
这些指标能回答不同问题。
如果 average steps per run 持续升高,可能是 planning drift。
如果 tool parameter validation failure rate 升高,可能是 Prompt 或 schema 描述不清。
如果 recovery success rate 很低,说明失败处理只是形式存在。
如果 cancellation rate 很高,可能是前端反馈不足或执行太慢。
系统设计
把上述模块组合起来,可以得到一个简化执行流程:
这条链路里,每个箭头都应该能追踪。生产问题排查时,你需要知道是哪一次模型调用、哪个工具参数、哪个验证器判断导致了结果。
可靠性边界
可观察、可恢复不等于 Agent 永远正确。它只是把风险显性化,并提供控制手段。
Agent 的边界应该写进系统,而不是只写进 Prompt:
- 最大步骤数。
- 最大运行时长。
- 最大成本。
- 允许工具列表。
- 每个工具的风险等级。
- 写操作审批。
- 外部内容隔离。
- 敏感字段白名单。
- 连续失败上限。
- 可恢复错误类型。
- 最终结果验证规则。
尤其要注意:外部内容不能变成系统指令。网页、文档、用户输入、工具返回都应该被标记为不可信内容。它们可以作为资料,但不能覆盖系统策略。
常见坑
只有日志,没有状态
日志能帮助排查,但不能支撑恢复。恢复需要结构化状态:run、step、tool call、observation、error、artifact。
如果你只能在日志里搜索某个错误字符串,就说明系统还没有真正可恢复。
只展示最终答案
Agent 执行时间越长,用户越需要过程反馈。只展示最终答案会让用户觉得系统卡住、不可信,也会让失败体验很差。
前端至少要展示当前步骤和可取消入口。
所有失败都重试
重试不是万能药。权限失败、用户输入缺失、目标不合法、成本超限,都不应该盲目重试。
把失败类型分清楚,比增加重试次数更重要。
让模型绕过工具层
模型不应该直接决定真实副作用。它可以提出“建议发送邮件”,但工具层必须检查权限、审批、收件人、内容和审计要求。
工具层是 Agent 系统的安全边界。
没有版本概念
Agent 行为受 Prompt、模型、工具描述、schema、验证器和恢复策略共同影响。
如果不记录版本,线上问题出现后很难复现。每个 run 都应该记录关键配置版本。
小结
可观察、可恢复的 Agent 系统,本质是把“模型自主执行”包进工程边界。
你需要用 run 和 step 定义状态,用事件流连接前端,用工具层控制权限和副作用,用验证器判断是否继续,用恢复策略处理失败,用指标和 Eval 持续改进。
Agent 的价值来自自主性,但生产可信度来自边界。只有当用户和工程团队都能看见过程、理解失败、恢复任务时,Agent 才有资格承担真实工作。