AGENT ENGINEERING PLAYBOOK 18 / 30
打开全书目录

第一部分:Agent 基础

  1. 00 从 LLM 到 Agent:先把基本概念说清楚
  2. 01 Skill、Tool 与 Environment:方法、能力和外部世界
  3. 02 Workflow 与 Agent:谁来决定下一步
  4. 03 Agent Loop:一个 while 循环如何变成执行系统

第二部分:Context、State 与 Memory

  1. 04 Context Assembly:Agent 这一轮看见什么
  2. 05 Run State:Agent 现在做到哪里
  3. 06 Plan、Todo 与 Task Graph
  4. 07 Workspace 与 Sandbox:执行环境也是状态
  5. 08 Memory Architecture:Session、User、Agent 与 Long-term Memory

第三部分:Tool、Skill 与控制面

  1. 09 Tool Protocol:动作空间的稳定接口
  2. 10 Skill:可执行知识的工程结构
  3. 11 Router 与能力发现
  4. 12 Identity、Permission、Guardrail 与 Human-in-the-Loop
  5. 13 副作用、幂等、事务与补偿

第四部分:Agent Runtime 与执行协议

  1. 14 Framework、Runtime 与 Protocol
  2. 15 Agent Definition、Thread、Run、Attempt 与 Step
  3. 16 Event、Artifact、Checkpoint 与 Trace
  4. 17 Agent 任务生命周期
  5. 18 Interrupt、Resume、Cancel 与 Retry

第五部分:Observability、Evaluation 与优化

  1. 19 Trace、Span 与 Event:看见完整执行轨迹
  2. 20 结果、过程与轨迹评估
  3. 21 Dataset、Bad Case、Replay 与 Regression
  4. 22 Prompt、Skill、Model 与 Agent Version
  5. 23 质量、成本、安全与效率指标
  6. 24 从线上轨迹到优化闭环

第六部分:Multi-Agent

  1. 25 什么时候应该拆成多个 Agent
  2. 26 Orchestrator、Worker、Reviewer 与 Peer
  3. 27 Delegation Contract、Task Tree 与消息协议
  4. 28 Blackboard、共享状态与并发控制
  5. 29 结果合并、冲突仲裁与最终责任
第十八章 / 第四部分:Agent Runtime 与执行协议

Agent 任务生命周期

设计 Run 从提交、排队、运行到等待、取消、失败和完成的完整状态机。

更新于 2026/8/12

Agent 任务不是一个更慢的 HTTP 请求。它可能排队数分钟,等待用户补充文件,等待系统授权,或者在外部构建结束后继续。若 Runtime 只有 runningdone,所有这些情况都会被误判成卡死或失败。

生命周期的价值,是让每种暂停和结束都拥有明确语义、负责人和合法下一步。

一套可工作的 Run 状态机

Agent Run 生命周期状态机

状态含义谁能推动下一步
SUBMITTED请求已被接受,尚未进入调度Runtime
QUEUED已满足基本校验,等待执行资源Scheduler
RUNNINGWorker 正在推进 RunWorker / Runtime
INPUT_REQUIRED缺少用户提供的信息或选择用户
AUTH_REQUIRED动作需要额外授权用户 / 审批系统
WAITING等待已知外部事件或时间条件事件源 / Timer
CANCEL_REQUESTED已接受取消请求,正在停止Worker / Runtime
COMPLETED目标通过完成验证终态
FAILED确定性错误使本次 Run 无法继续终态
TIMED_OUT超过 Run 的时间边界终态
CANCELED取消已经完成,结果不再被接受终态

Run Status 应该是对外稳定语义。内部可以有更细的 Worker 状态,但不能要求调用者理解 planner_node_4 才知道任务是否仍需等待。

从提交到真正运行

SUBMITTED 表示 Runtime 已经接收并持久化请求。此时可以完成幂等去重、Schema 校验、配额检查和 Agent Version 解析。

QUEUED 表示任务可以执行,只是在等待资源。将两者分开后,系统能区分“请求还在校验”与“调度拥堵”。如果创建接口同步完成这些工作,也可以快速从 SUBMITTED 转入 QUEUED,但事件历史仍保留两个语义。

Worker 获取带租约的 Attempt 后,Run 才进入 RUNNING。租约到期不等于 Run 失败;Scheduler 可以创建新 Attempt 恢复执行。

INPUT_REQUIRED 不是失败

Agent 发现缺少复现账号时,任务没有失败。它已经成功识别阻塞条件,并把下一步责任交给用户。

一个完整的输入请求应该包括:

代码块说明|Playbook 参考 Schema: 这段 YAML 把“INPUTREQUIRED 不是失败”落成可检查的结构化记录,主要字段包括 required_input。除非正文另有说明,字段名和示例值用于解释设计语义,不是某个外部协议的标准格式。

required_input:
  request_id: req_09
  type: structured_input
  prompt: 提供一个能复现登录失败的测试账号
  schema:
    account_id: string
    environment: [staging, test]
  reason: 当前日志已脱敏,无法关联失败用户
  expires_at: 2026-08-12T15:36:10+08:00

提交输入时必须携带 request_id。如果 Run 已经因其他输入恢复,迟到答案不能被静默应用到新的等待点。

AUTH_REQUIRED 与它相似,但责任和审计不同。前者缺少信息,后者需要扩大动作权限。授权响应应该绑定具体动作、资源、范围和有效期,不能把“允许这一次修改配置”解释成永久权限。

WAITING 表示系统知道自己在等什么

等待构建完成、等待某个时间窗口或等待 Webhook,都属于 WAITING。Runtime 应保存明确等待条件:

代码块说明|Playbook 参考 Schema: 这段 YAML 把“WAITING 表示系统知道自己在等什么”落成可检查的结构化记录,主要字段包括 wait_condition。除非正文另有说明,字段名和示例值用于解释设计语义,不是某个外部协议的标准格式。

wait_condition:
  type: external_event
  event: build.finished
  correlation_id: build_782
  wake_at: null
  timeout_at: 2026-08-11T16:20:00+08:00

如果系统不知道下一步由谁推动,也没有可观察的恢复条件,那不是 WAITING,而是设计缺失。可以将其诊断为阻塞原因,最终进入 FAILED 或升级人工处理。

BLOCKED 是否作为独立状态取决于产品需要。若使用它,必须定义解除者和恢复条件。不能把所有无法解释的停滞都放进 BLOCKED

请求取消不等于已经取消

用户点击取消时,Worker 可能正在调用外部 Tool。Runtime 首先进入 CANCEL_REQUESTED,发布取消信号,并阻止创建新的普通 Step。

只有完成以下处理后,Run 才进入 CANCELED

  • Worker 已停止推进,或租约已失效。
  • 未开始的 Tool 调用已经撤销。
  • 在途结果即使迟到也会被版本栅栏拒绝。
  • 已经发生的副作用被记录并对用户可见。

外部系统未必支持撤销。取消一个已发送的邮件不会让收件箱里的邮件消失。因此 CANCELED 表示 Runtime 不再继续目标,不表示现实世界回到了 Run 开始前。

COMPLETED 必须经过验证

模型产生最终回答时,Run 仍不应该直接完成。它只是提出完成申请。Runtime 应运行与目标相匹配的验证:测试、Schema 校验、真实状态查询或人工验收。

代码块说明|层级示意: 这段文本图按缩进和树枝符号阅读,用来展开“COMPLETED 必须经过验证”中的父子关系与归属边界。它描述的是逻辑结构,不要求数据库或服务按同样层级拆分。

RUNNING
  └── 模型请求完成
        ├── 验证通过 → COMPLETED
        └── 验证失败 → 记录证据并回到 RUNNING

COMPLETED 最好保存完成原因、主要 Artifact 和证据引用。这样上层系统不需要解析最终 Response 才能判断目标是否满足。

FAILED、TIMED_OUT 与等待超时

FAILED 表示本次 Run 在当前目标、输入和权限下无法继续,并且有明确错误分类。例如 Tool Schema 不兼容、必须资源不存在、预算已经耗尽。

TIMED_OUT 表示达到 Run 的整体截止时间。它与单次 Tool Timeout 不同:Tool 超时可以重试或换策略,Run 超时则是生命周期终态。

等待也可以有局部超时。等待构建 20 分钟未收到事件,Runtime 可以先从 WAITING 恢复,让 Agent 决定重新查询、延长等待或失败;不必自动把整个 Run 标记为 TIMED_OUT。

状态转换需要条件和副作用

状态机不是一张展示图,而是一组可以检查的规则:

代码块说明|伪代码: 这段 Python 以 transition() 为主要入口说明“状态转换需要条件和副作用”怎样运行。它省略了持久化、并发、权限和完整错误处理,用于解释决策顺序,不是可直接部署的完整实现。

def transition(run_id, expected_revision, target, cause):
    state = load_for_update(run_id)

    assert state.revision == expected_revision
    assert target in ALLOWED_TRANSITIONS[state.status]
    assert guard_passed(state, target, cause)

    save_state_and_event(
        state.with_status(target),
        event=f"run.{target.lower()}",
        cause=cause,
    )

例如 INPUT_REQUIRED → RUNNING 的 Guard 应验证 request_id、输入 Schema 和有效期;RUNNING → COMPLETED 应验证完成证据;任何非终态转入 CANCELED 都要检查取消代次。

同一个 Thread 是否允许并发 Run

Thread 共享消息和 Artifact,并发策略必须显式选择。

策略行为适合场景风险
Reject已有活动 Run 时拒绝新 Run强一致任务用户需要等待
Queue新 Run 排在旧 Run 之后连续编辑、客服会话后续输入可能过时
Cancel-and-replace取消旧 Run 后启动新 Run搜索、实时建议旧副作用不能自动撤回
Fork从某个 Thread Revision 创建分支方案探索、独立研究需要后续合并
Allow with isolation并发运行,各自绑定快照相互独立的任务仍可能争用外部资源

默认串行通常最安全,但不是唯一答案。即使允许并发,每个 Run 也应绑定创建时的 thread_revision。合并结果前检查 Thread 是否已经变化,避免旧 Run 覆盖新输入。

本章检查

  • Run 是否区分提交、排队、运行、等待和不同终态。
  • INPUT_REQUIRED 与 AUTH_REQUIRED 是否携带可验证的请求身份和恢复条件。
  • WAITING 是否明确等待事件、关联 ID 和超时策略。
  • CANCEL_REQUESTED 是否先于 CANCELED,并处理在途结果。
  • COMPLETED 是否由独立证据驱动。
  • Tool Timeout、等待超时和 Run Timeout 是否分开处理。
  • 每个状态转换是否拥有合法来源、Guard、Event 和原子更新。
  • Thread 并发 Run 策略是否显式,并对旧快照写入进行检查。