开发指南:实现可生产的 Agent Loop Runtime
本篇对应你在 Loop Engineering 子站 「从实验到系统」的工程部分;实现时请同步对照 Hermes 开发指南 的工具链与 DSH 开发指南 的 Session/插件边界。
导读
快速开始解决了「能跑的 ReAct」。开发指南解决五件事:
- 六类循环的 实现契约(接口、状态、停止)。
- 失败模式库(可检索、可告警)。
- 与 Harness 的 清晰分层(谁熔断、谁审批)。
- 并发、重试、checkpoint 的工程选择。
- 如何把 Loop 做成可测、可灰度的模块。
一、Loop Runtime 的推荐模块边界
| 模块 | 输入 | 输出 | 单测重点 |
|---|---|---|---|
TopologySelector | 任务卡元数据 | 拓扑枚举 | 规则表 |
StepEngine | 状态 + 事件 | 新状态 | 迁移完备 |
BudgetAccountant | usage 事件 | 是否耗尽 | 边界值 |
StopJudge | 状态快照 | reason | 优先级 |
ObservationPipeline | 原始 tool_result | 压缩文本 + 指纹 | 截断不丢关键错误 |
WriteBackGate | 候选记忆 | 允许/拒绝 | 过滤策略 |
Tracer | 事件 | 持久化记录 | 字段必填 |
原则:Prompt 可以脏,状态机必须干净。把「下一状态」从「模型说了啥」里剥离出来。
二、ReAct / Tool Loop 深化
2.1 消息协议冻结
选定一种并全局唯一:
| 协议 | 适用 | 解析风险 |
|---|---|---|
OpenAI tool_calls | 多数云 API | 低 |
Hermes <tool_call> | 本地对齐模型 | 中(需修复 JSON) |
| Claude tool_use | Anthropic | 低 |
Loop 层只吃 规范化后的 Action{name,args};解析失败算 fatal 或「要求模型重发」专用步(计入预算)。
2.2 并行工具调用
当模型一次返回多个 tool_calls:
| 策略 | 行为 | 风险 |
|---|---|---|
| 串行 | 按数组顺序 | 慢但简单 |
| 受限并行 | 只读工具并行,写工具串行 | 推荐默认 |
| 全并行 | 同时打 | 写冲突、竞态 |
2.3 重试策略(工具层 vs 循环层)
- 工具瞬时失败(429、超时):Harness 内短重试(1–2 次),指数退避。
- 逻辑失败(404、断言失败):不要盲重试;交给 Loop 改参或 Replan。
- 禁止:对同一指纹无限「再试一次」。
三、Plan–Execute 实现要点
3.1 计划对象
{
"plan_id": "p3",
"version": 2,
"steps": [
{"id": "s1", "goal": "定位失败测试", "tools_allow": ["read_file", "run_pytest"], "done_when": "pytest_output_contains_FAILED"},
{"id": "s2", "goal": "最小补丁", "tools_allow": ["read_file", "edit_file"], "done_when": "diff_non_empty"}
]
}
状态字段:current_step_id、plan_version、replan_count、step_attempt。
3.2 Replan 触发器
| 触发 | 条件 | 动作 |
|---|---|---|
| 签名停滞 | 同 error_signature ≥ N | 升 plan_version |
| 步骤超时 | 单步 steps 超限 | 标记 blocked → replan |
| 用户改目标 | 外部事件 | 强制 replan |
| 工具权限拒绝 | Harness 403 | 可能直接 fatal |
振荡检测:若 plan_version 在 A↔B 间来回,直接失败并请求人审。
四、Reflection 与 Evaluator–Optimizer
4.1 Reflection 契约
反思模型输出必须 schema 化:
{
"verdict": "revise",
"issues": [{"severity": "factual", "detail": "API 名 称未在原文出现", "fix": "删除或补引用"}],
"must_fix": true
}
verdict=pass 才允许成功停止;revise 则把 issues 注入下一轮。max_reflect_rounds 默认 1–2。
4.2 Evaluator–Optimizer 双进程思维
即使跑在同一机器,也要 逻辑隔离:
| 角色 | 权限 | 输入 | 输出 |
|---|---|---|---|
| Optimizer | 可写产物 | 上轮分数+评语 | 新候选 |
| Evaluator | 只读产物 + 只读测试 | 候选 | score + rationale |
刷分检测:score 上升但关键业务指标不变 → 标记 metric_gaming 失败。
4.3 何时用规则评测 vs LLM Judge
| 目标类型 | 推荐传感器 |
|---|---|
| 编译/测试/格式 | 确定性脚本 |
| 文风、温和毒性 | LLM Judge + 抽样人工 |
| 安全性 | 规则 + 专用分类器,慎用被优化器可见的弱 Judge |
五、多 Agent 编排环
5.1 编排器职责
编排器(Orchestrator)不是「另一个爱聊天的 Agent」,而是 带策略的调度器:
- 维护全局目标与预算池(给子 Agent 配额)。
- 定义 handoff schema。
- 检测传球环(A→B→A)。
- 决定并行还是流水线。
5.2 Handoff 载荷
{
"from": "researcher",
"to": "writer",
"artifact_refs": ["notes.md"],
"claims": ["框架X用显式 max_iterations"],
"budget_left": {"steps": 6, "tokens": 20},
"do_not": ["重新全网搜索已完成主题"]
}
缺少 artifact_refs 的 handoff 应拒绝——防止「口头交接」。
5.3 拓扑选择
| 模式 | 结构 | 适用 |
|---|---|---|
| 主管–工人 | 星型 | 任务分解清晰 |
| 流水线 | 链式 | 调研→写→审 |
| 黑板 | 共享存储 | 多专家填同一工件 |
| 自由讨论 | 全连接 | 默认禁止上生产 |
六、Memory Write-back 工程
6.1 写回管道
过滤规则示例:拒绝无来源事实;拒绝密钥/PII;拒绝与当前用户指令冲突的「偏好」。
6.2 读入策略
- 标注
memory_id、created_at、confidence。 - 过期记忆降权或隔离区。
- 冲突时:当前用户指令 > 高置信记忆 > 低置信记忆。
七、失败模式库(开发期必读)
| ID | 名称 | 症状 | 缓解 |
|---|---|---|---|
| L01 | 空转续聊 | 无 tool 且无进展 | 空回合计数熔断 |
| L02 | 指纹死环 | 同参工具反复 | fingerprint |
| L03 | 计划振荡 | plan A/B 跳 | replan 图检测 |
| L04 | 上下文中毒 | 长 obs 后胡言 | 压缩+关键摘要 |
| L05 | 假完成 | 自称 done | 外部验收 |
| L06 | 评测被俘 | 改测试/讨好 judge | 权限隔离 |
| L07 | 预算单维 | 步数未到 token 爆 | 多维预算 |
| L08 | 传球环 | 多 Agent 互踢 | handoff TTL |
| L09 | 写回污染 | 错记忆复用 | 门禁+回滚 |
| L10 | 重试放大 | 429 风暴 | 抖动退避+全局限流 |
| L11 | 影子成功 | 测过但未部署真环境 | 环境标记 |
| L12 | 人审饿死 | 审批队列堵死环 | 超时降级策略 |
把 ID 打进 Trace stop.reason 与监控标签,值班才能「按编号」处理。
八、Checkpoint 与恢复
长任务必须支持从 step K 恢复:
- 每步结束序列化:
messages摘要、plan、budget剩余、artifacts。 - 恢复时校验工具版本与权限版本(Harness bundle hash)。
- 不允许静默跳过已执行的有副作用步骤——需幂等键。
九、可测性:哪些要单测、哪些要评测集
| 层级 | 方法 | 例子 |
|---|---|---|
| 纯函数 | 单元测试 | Budget、StopJudge、fingerprint |
| 契约 | 合约测试 | handoff schema、plan schema |
| 循环行为 | 仿真环境 | mock 工具返回固定序列 |
| 质量 | 黄金任务集 | 20 个任务的成功率/花费 |
| 安全 | 红队 | 诱导改测试、越权路径 |
仿真环境比「真打 API」更适合回归:把 tool 做成有限状态机,断言「必在 ≤N 步停止」且「不出现 L02」。
十、性能与成本工程
| 杠杆 | 做法 | 注意 |
|---|---|---|
| 模型分级 | 规划用强模型,执行用小模型 | 接口一致 |
| 缓存 | 同 query 搜索缓存 | 设 TTL |
| 早停 | 分数平台期退出 | 防假早停 |
| 批并行 | 只读调研并行 | 写串行 |
| 摘要 | 每 K 步折叠历史 | 保留目标与约束 |
成本看板字段:cost_per_success、steps_p50/p95、stop_reason 分布。只看成功率会鼓励「砸预算」。
十一、与框架的关系(选型表)
| 方案 | 优势 | 风险 | 建议 |
|---|---|---|---|
| 自研 200 行 Runtime | 清晰、可教 | 缺生态 | 学习与中小生产首选 |
| LangGraph 等状态图 | 可视化、持久化 | 抽象泄漏 | 图复杂时用 |
| 厂商 Agents SDK | 集成快 | 停止语义不透明 | 外包时仍自建验收 |
| 纯 Prompt 链 | 快 | 不可控 | 仅原型 |
无论选啥,StopJudge 与 Budget 尽量留在你自己的模块,不要完全交给黑盒。
十二、开发检查单(PR 门禁)
- 新增拓扑有状态图与失败模式 ID
- Stop 优先级单测覆盖
- Trace schema 有版本号
- 写工具与评测权限分离
- 有 mock 仿真回归
- 文档更新任务卡字段
- 默认预算在配置而非硬编码魔法数
- 与 Harness bundle 版本联调记录
十三、工作实例:文档站点「自动更新 changelog」Agent
目标:根据 git diff 生成 changelog 条目并通过 lint。
拓扑:Plan–Execute + 规则 Evaluator(markdown lint)+ 单次 Reflection。
预算:steps=15、tokens=60k、shell=10。
停止:lint 通过且 diff 仅触及 changelog;若模型改其它文件 → fatal。
步骤拆解:
git diff→ 压缩为文件列表。- 计划:分组变更 → 起草 → lint → 提交预览。
- 内环禁止
git push(Harness 拒绝)。 - Trace 挂到 CI artifact。
这个实例刻意「无聊」——生产 Loop 的价值往往在无聊任务的稳定性,不在 Demo 炫技。
十四、 日志与隐私
Trace 默认 脱敏:API Key、邮箱、Cookie、用户隐私字段。提供 debug=true 仅在本地打开全量。写回记忆前再跑一次脱敏。违规写盘应在 Harness 层拦截并记 L09。
开发期用合成数据跑仿真,避免把真实客户对话当测试夹具长期滞留。
完成开发指南后,用 GitHub 项目导读 去真实仓库里「找 Loop」,再用 最佳实践 对照生产。