Agent 状态机设计:别让对话历史替你管理流程
一、Agent 需要显式状态
很多 Agent 项目早期只靠对话历史推进流程。用户说一句,模型猜下一步;工具返回一段文本,模型再猜下一步。Demo 阶段看起来灵活,进入生产后就会出问题:步骤跳过、重复执行、失败后不知道退到哪里。
Agent 不是聊天窗口,它在执行任务。任务就应该有状态机。
我第一次把 Agent 放上线时犯过一个典型错误:计划器拆完步骤后,每个步骤执行完全依赖 prompt 里的"请根据上一步结果决定下一步"。测试时表现不错,但上线两天后出现了一个致命 bug——某用户的文件处理任务卡在第三步,工具调用超时了,Agent 没有重试也没有报告失败,而是直接跳过第三步开始执行第四步。第四步依赖第三步的结果,所以直接报错。用户看到的是"系统错误",日志里只有工具返回那个 "null",我花了两个小时才定位到问题发生的位置。如果当时有显式状态机,"执行工具失败"会触发明确的"重试或降级"状态,而不是"模型自己猜下一步"。
二、先拆任务状态
flowchart TD A[接收任务] --> B[澄清需求] B --> C[生成计划] C --> D[执行工具] D --> E{是否成功} E -->|是| F[汇总结果] E -->|否| G[重试或降级] G --> C状态机的价值,是让系统知道当前处于哪一步、允许哪些动作、失败后能去哪。没有状态机,Prompt 再长也只是软约束。
agent_states: - collecting_requirements - planning - executing - reviewing - completed - failed状态名称不要太玄。工程团队、产品和运维都能看懂,后续排障才方便。
状态数量不是越多越好。见过有团队的状态机有 15 个状态——"等待用户输入""等待工具返回""重试计划中""降级评估中"……流程复杂到维护人员都看不懂。建议控制在 6-8 个核心状态,每个状态对应一个用户可见的阶段。产品侧也更容易理解"任务在规划中"比"任务在 RETRY_EVALUATION_PENDING 状态"强得多。
三、状态转换要可验证
type AgentState string const ( StatePlanning AgentState = "planning" StateExecuting AgentState = "executing" StateReviewing AgentState = "reviewing" ) type Transition struct { From AgentState To AgentState Reason string }每次状态转换都要记录原因。比如从 executing 到 reviewing,是因为工具全部执行成功;从 executing 到 planning,是因为工具失败需要重新规划。状态变化不能只存在模型回答里。
还要限制非法转换。已经 completed 的任务,不应该再执行工具;failed 的任务需要人工恢复或重新创建。系统层拦住非法转换,比指望模型自觉可靠。
实现时建议用一个 allowlist 控制合法转换。比如 collecting_requirements 只能转到 planning 或 failed;planning 只能转到 executing 或 failed(如果计划不可行)。用 map 硬编码这些规则,不要依赖模型判断哪些转换合法。
曾经遇到过一个线上事故:Agent 在 completed 状态后,因为用户追问了一句"能再细化一下吗",模型在对话历史里看到之前的工具调用成功,直接在 prompt 里要求再次调用写入工具,创建了重复的工单。如果有 allowlist,系统会直接拒绝"completed 状态下不允许调用写作工具",拦截这次异常操作。
四、状态机要连接观测和恢复
Agent 出问题时,排障人员需要知道任务卡在哪个状态、最近一次工具调用是什么、失败原因是否可重试。状态机如果只在内存里,一重启就丢,那还不如没有。
agent_state_store: persist_state: true record_last_tool_call: true record_retry_count: true support_resume: true支持恢复也很重要。任务执行到一半服务重启,系统应该能从持久化状态继续,而不是重新执行所有工具。尤其是创建工单、发送通知、写数据库这类有副作用动作,重复执行会制造新事故。
恢复时要重新加载上下文,包括对话历史、工具调用历史、当前状态和剩余预算。但如果对话历史太长,可以压缩后加载,不必全量恢复。关键是不丢失状态,不是不丢失每一段对话文字。
状态机还可以给用户更清晰的反馈。不要只显示"处理中",而是显示"正在检索资料""正在执行工具""正在复核结果"。用户知道系统在做什么,就更愿意等待。
最后,状态机不要设计得过细。每个小动作都变成状态,会让流程难以维护。状态粒度应该和恢复、观测、权限控制有关,能支撑工程动作就够。
状态机还要有版本。Agent 流程升级后,旧任务可能仍停留在旧状态定义里,不能强行套新流程。可以在任务记录里保存state_machine_version,恢复时按对应版本解释状态。
agent_state_versioning: persist_version: true allow_migration: explicit_only reject_unknown_state: true这样做虽然多一层管理,但能避免流程升级把历史任务恢复坏。
版本迁移也要慎重。如果旧状态和新状态差异太大,与其强行迁移不如让旧任务在旧流程里走完,新任务用新流程。强制迁移可能把正常任务变成无法处理的任务。
五、总结
Agent 状态机要显式管理任务阶段、允许动作、状态转换、失败恢复和用户反馈。
对话历史可以提供上下文,但不能替你管理流程。真正可落地的 Agent,需要一套能被代码检查的状态机。生产系统里,硬编码的 allowlist 比 prompt 里的"请严格遵守流程"可靠得多。