1879 字
约 6 分钟
9
S22-J05 · Agent 生产循环与卡死防护

适用读者:Demo 里 Agent 跑得很好、一上真实任务就转圈或烧钱的人。
前置:S21-J01(Agent 循环与工具调用)、S22-J04(ReAct 变体与 DAG)、S20-J05(上下文工程)。
覆盖旧文:4619(Agent 实战全指南:教科书 ReAct 到生产环境——循环、卡死防护、流式体验与成本)→ 1 篇 / 3,380 字符。
扩展点:E-S22-021(教科书 3 步 vs 生产 11 步:多出来的 8 步分别防什么)、E-S22-022(四种卡死模式与三类防护策略)、E-S22-023(流式体验与成本的耦合关系)。
下一篇:S22-X02 · MCP 服务器构建与项目登记。


1. 一句话结论

生产 Agent 的工程量不在"想得更聪明",在"停得下来":教科书 3 步(想→做→观察)跑一百次就会出现四种卡死,而每一轮卡死都在真实烧钱。防护策略必须是代码层的硬约束,不能写在提示词里。

出处:A-4619。


2. 教科书 3 步 vs 生产 11 步(E-S22-021)

教科书:Thought → Action → Observation(循环直到给出答案)
生产(单轮):
① 意图识别/路由(是否需要工具)
② 上下文裁剪(预算内选材,见 S20-J05)
③ 工具清单筛选(只给相关工具,避免选择困难)
④ 参数校验(schema 校验 + 业务前置校验)
⑤ 权限与审批(危险操作走人工,见 S24-A01)
⑥ 执行(超时、重试、幂等)
⑦ 结果裁剪(大输出摘要,避免撑爆上下文)
⑧ 状态记录(写 checkpoint,支持断点续跑)
⑨ 终止判定(max_steps / 预算 / 无进展)
⑩ 输出(结构化 + 引用来源)
⑪ 观测(trace、token、耗时、失败分类)

多出来的 8 步都在防同一类问题:不可控。判据:任何"只靠模型自觉"的约束,在 100 轮里一定会被违反一次。

缺哪步 真实后果
缺 ③ 工具 30 个以上时,选错工具率显著上升
缺 ④ 参数类型错/必填缺失 → 工具 500,Agent 重试同一错误
缺 ⑦ 一次 SQL 查询返回 5 MB → 上下文爆,费用涨十倍
缺 ⑧ 中途失败只能从头跑,成本翻倍
缺 ⑨ 无限循环(见 §3)

3. 四种卡死模式与三类防护(E-S22-022)

卡死模式 机制 观测信号
重复调用 同一工具+同一参数反复调用,结果没变化 连续 N 步参数哈希相同
乒乓循环 两个工具互相触发(A 失败→B 修复→A 再失败) 步骤序列出现周期
等待幻觉 模型声称"已提交,等待结果",实际无状态变化 无新 tool 调用,只有文本
长尾工具 某工具耗时极长但无超时,整体挂住 单步耗时 > 阈值

三类防护策略(按成本从低到高):

① 硬上限(必须有)
   max_steps(如 15)、max_tokens、max_wall_clock、max_cost
   任一触顶 → 强制收尾:输出「已完成部分 + 未完成原因 + 建议下一步」

② 无进展检测(防重复与乒乓)
   记录每步 (tool, args_hash, result_hash)
   连续 3 步 tool+args 相同,或 result_hash 连续不变 → 判定无进展
   → 改变策略:换工具 / 缩小任务 / 直接返回现状

③ 结构化终止(防等待幻觉)
   每轮要求模型输出 {"status": "continue|done|blocked", "next": …}
   只有 status=done 才允许给最终答案;blocked 必须有具体阻塞原因

判据:"防护命中率"要能被观测——每次触发防护都打一条带类型的日志(guard=dup_call|no_progress|budget),否则你永远不知道 Agent 是在正常工作还是在打转。

代码骨架(语言无关,伪代码):

for step in range(MAX_STEPS):
    if cost.used > MAX_COST or clock.elapsed() > MAX_WALL:
        return finish(reason="budget", partial=state.summary())

    action = model.next(state)                 # 强制结构化
    if action.status == "done":
        return finish(reason="done", answer=action.answer)
    if action.status == "blocked":
        return finish(reason="blocked", detail=action.blocked_reason)

    key = (action.tool, hash_args(action.args))
    if key in recent_keys[-2:]:                # 重复调用
        state.hint = "上一步与两步前相同,请换策略或结束"
        log_guard("dup_call"); continue

    result = tools.call(action.tool, action.args, timeout=TOOL_TIMEOUT)
    state.observe(hash(result))                # 结果哈希用于无进展检测
    log_step(step, action, result.size)

4. 流式体验与成本的耦合(E-S22-023)

事实 影响
流式(SSE)不减少总 token 只改善感知延迟(首 token 时间)
流式 + 工具调用 = 两段输出 先流式文本,再暂停等工具,再流式续写 → 前端必须处理"分段"
中途取消要能真正停 客户端断开 → 服务端要中断生成(否则继续烧 token)
长输出流式更贵 用户可能只读前 20%,但全部 token 都已计费

判据:"先让它能停,再让它好看"——取消与超时是成本控制;流式是体验优化。顺序反了,你会得到一个很流畅但很贵的循环。


5. 反例:八种"上线就烧钱"

做法 后果
只有 max_steps,没有预算 每步工具很贵时,15 步也能烧穿
把"不要重复调用"写进提示词 100 轮里必违反一次
不裁剪工具输出 上下文爆、费用涨十倍
无 checkpoint 失败重跑,成本翻倍
工具无超时 单步挂住整个请求
取消不中断生成 用户走了,token 还在烧
无 guard 日志 无法区分"高效"与"打转"
失败后自动重试整轮 重复执行副作用(下单、发信)

副作用防护:写操作必须带幂等键(业务 ID + 操作类型),重试只重试幂等操作——这条与 S17-J03(可靠投递)的幂等设计同源。


6. 自测

  1. 生产 11 步比教科书多出的步骤各防什么?
  2. 四种卡死模式与三类防护的对应关系?
  3. 为什么防护必须是代码层约束?
  4. 流式与成本的关系?取消为什么要中断生成?
  5. 写操作重试的安全前提?
答案
  1. 路由/裁剪/工具筛选/参数校验/审批/超时重试/结果裁剪/状态/终止/结构化输出/观测——分别防错选、爆上下文、挂起、重复、不可复盘。
  2. 重复调用与乒乓→无进展检测;等待幻觉→结构化终止;长尾→硬上限+超时。
  3. 模型对否定指令的遵循不可靠,100 轮内会出现违反。
  4. 流式不减 token,只降感知延迟;不中断则继续计费。
  5. 幂等键(业务 ID + 操作类型),且只重试幂等操作。

7. 下一篇

S22-X02 · MCP 服务器构建与项目登记 —— 5 篇 MCP 教程(Python/TypeScript/自家 API/推荐清单/概念对比)的统一七步骨架与三语言对照,以及 DevSupport AI 与 AI 模拟面试官两个项目的登记。


本篇属于系列《S22 · AI 工程落地实战》。旧文来源:A-4619;扩展点 E-S22-021/022/023。MCP 规范与 SDK 版本迭代较快,具体 API 名以官方文档为准(待核)。

S22-J05 · Agent 生产循环与卡死防护
https://www.clxhxhhr.top/posts/5523/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。