M11 · 动态工具权限与阶段状态机#
简历 Bullet Point: 四阶段状态机(PLANNING→SEARCHING→COMPARING→CONCLUDING)动态收缩可用工具子集,执行层哨兵拦截越权调用而非摘工具——保住 prompt cache 前缀稳定;终结工具豁免阶段门解决”漂移检测要求收尾但阶段门拦住终结工具”的互矛盾
开场钩子#
第一轮端到端测试,模型第二轮直接调 shopping_summary——手上一条候选都没有,吐出空清单。它跳过了 planner 和 item_search,直接进了收尾。原因很简单:13 个工具摆在面前,模型觉得”用户说要推荐,我直接推荐不就行了”。
解决方案不是在代码里焊死调用顺序(那就成了 Workflow),而是做阶段状态机——每个阶段只允许调对应工具子集。
一、模块运作流程#
1.1 四阶段#
| 阶段 | 允许的工具 | 转移条件 |
|---|---|---|
| PLANNING | planner, category_insight, web_search, ask_user | planner 调用完成 |
| SEARCHING | item_search, dispatch_tool, web_search | 候选数量足够 |
| COMPARING | price_compare, shipping_calc, item_picker | item_picker 返回 |
| CONCLUDING | shopping_summary, chat_fallback | 终结工具调用 |
终结工具(shopping_summary / chat_fallback)豁免阶段门——任何阶段都可以调用。
1.2 拦截而非摘除#
关键设计选择:不从模型可见的工具表里移除被禁工具。摘工具改 system prompt → 破坏 prompt cache 前缀匹配。执行层 phase_check Hook 在 pre_tool_call 检查阶段权限,越权返回 sentinel 消息。
代价:模型白花一次 tool_call 解码(~50-100 token)。收益:保住 cache 命中率(miss 一次 ~11000 token)。
1.3 与漂移检测的互矛盾#
漂移检测触发”连续严重”→ 强制收尾 → 授权阶段机到 CONCLUDING → 模型调 shopping_summary。如果终结工具受阶段门约束,在 PLANNING 阶段漂移检测要求收尾但阶段门又拦住 shopping_summary → 死锁。
解法:终结工具豁免阶段门。任何时候 Agent 想收尾都不应该被拦。
1.4 回退机制#
ItemPicker 返回空 → 回退到 SEARCHING(候选不够,需要再搜)。只有这一个回退条件,其他阶段转移都是单向的。
二、面试问答#
Q1: statewright 说”缩小工具空间效果更好”,你怎么看?#
我的哨兵实现了等效效果——模型调 item_search 被拦后下一轮不再调,选择空间被等效缩小。区别只在”看不到”(摘工具)vs”调了被拦”(哨兵)。做过 A/B 对比:摘工具版延迟更低(65.3s vs 79.3s),但 cache 命中率不可预测。长对话场景选保 cache。
Q2: 为什么终结工具豁免阶段门?#
终结工具的作用是”Agent 想收尾了”。如果收尾被拦,Agent 就只能继续跑——可能已经跑偏了、预算已经快用完了。任何时候 Agent 想收尾都不应该被拦。
Q3: 阶段状态机会不会太严格?#
有一个”补搜闸”场景:item_picker 返回空(没有候选满足条件),需要回到 SEARCHING 补搜。如果只有单向转移,Agent 就卡在 COMPARING 阶段出不来。所以保留了这一个回退条件。
三、诚实边界#
| 维度 | 做了 | 没做 |
|---|---|---|
| 阶段控制 | 4 阶段 + 执行层哨兵 | 动态调整阶段工具子集 |
| 终结豁免 | 任何阶段可收尾 | — |
| A/B 对比 | 摘工具 vs 哨兵 | 大规模线上实验 |