把智能体约束落实到工具调用检查
将可由脚本判断的规则从提示词移到工具执行入口,违规时拒绝调用,并明确说明原因和下一步合法操作。文章结合多个 Agent 框架,讨论先读后改、路径限制和预算检查等落地方式。
AI 深度解读
Agent 反复调用失败的工具,未必是系统提示词写得不够严。作者提出的办法是:把脚本能检查的规则搬到工具调用处,执行前拦截,再明确告诉模型下一步该怎么做。
文章用 OpenHands SDK、OpenCode、Hermes Agent、DeepSeek harness 和 Strands 的实现举例。它值得读的地方,是给 AI 编程工具的约束划了一条实用边界:哪些交给代码执行,哪些继续留给模型判断。
为什么把规则放到工具调用处
作者的解释是,提示词里的规则读过之后,可能在不断增长的对话中失去影响;工具拒绝却出现在犯错当下,针对具体动作,而且靠近上下文末尾。模型不必一直记住规则,只需根据眼前的反馈纠正行动。
这里的 harness,可以理解为包在模型外面的执行层,负责调用工具、管理权限、记录状态和控制运行过程。
文章把这套纠偏过程分成四步:
- 检测调用是否违反可由脚本判定的规则。
- 拒绝执行,并返回标记为错误的工具结果。
- 解释失败原因,指出允许的下一步。
- 让模型读取反馈后重试。
拦截只完成了一半,另一半是告诉模型怎么继续。
只有一句「denied」,模型还得猜原因,可能只是微调参数后再次调用。DeepSeek 就遇到过类似问题:某些工具只能从代码中调用,如果只返回「unknown tool」,模型会判断部署出了故障。更有效的反馈是说明:只有 run_code 能直接调用,请在 run_code 程序内部调用目标工具。
先把这四类规则变成检查
第一类是「先读再改」。执行层维护文件读取记录,拒绝修改未读取或读取后已经变化的文件。DeepSeek 的 fs-observation-policy 会提示编辑前必须先读取文件。它约束的是工具动作是否具备必要上下文。
第二类是重复调用。按工具名和参数统计次数:DeepSeek 的 repeat-tool-reminder 在第 3、5、8 次提醒不要原样重试;Hermes 的 tool_guardrails.py 在第 5 次阻断,要求改变策略或解释阻塞原因。两者的阈值和处理方式并不相同。
第三类是步骤预算。OpenCode 在预算耗尽时,将 toolChoice: "none",追加收尾消息,并拒绝仍然到达的工具调用,最终让运行以回答结束,对应实现位于 llm.ts。
第四类是危险命令和路径。OpenCode 的 shell.ts 会解析命令,把每条命令和每个目标目录分别作为权限检查对象。Hermes 的 approval_floors.py 则保留任何模式都不能绕过的阻止清单,包括 --yolo。
文章还列出了一些更具体的反馈:OpenCode 的 plan.ts 限制 edit 只能修改 Plan 目录内的文件;Strands 的 subagent.py 在委派深度达到上限时,要求当前 Agent 自己完成任务;Hermes 的 approval.py 会明确要求停止重试、停止改写命令,等待用户。
这些反馈共同解决一个问题:拒绝之后,任务仍然有明确去向。
能由执行层完成的事,就少让模型绕路
作者用读取 PDF 举了一个容易理解的例子。
模型执行 cat paper.pdf,或者用 shell 单行命令把 PDF 当文本输出时,工具可以识别 .pdf 目标,拒绝调用,并要求改用 parse_pdf。
另一种处理是直接接管:文件读取工具发现 PDF 后,自动解析,保存成 paper.md,再回复模型去读取这个文件。
作者更偏向后一种,只要执行层具备完成工作的能力,就把这一步交给代码。这里减少的不只是一次错误调用,也包括模型重新选择工具、组织参数的过程。
已有工具不方便修改,还可以用钩子(Hooks)接入检查:
PreToolUse可以带理由拒绝调用;DeepSeek 会把拒绝转成模型能读取的工具反馈。- OpenHands 沿用 Claude Code 的约定,钩子脚本以退出码
2结束时阻断调用,对应hooks/types.py和hooks/manager.py。 - Hermes 的
shell_hooks.py支持通过 JSON 阻断、改写输入或附加说明。 Stop钩子还能拒绝结束;DeepSeek 和 OpenHands 会把原因反馈给模型,让任务继续。
纠偏也可以很轻量。例如 OpenCode 的 read.ts 会为不存在的文件提供候选;Hermes 的终端守卫会要求去掉包装层,改用 terminal(…, background=true)。
我的判断
我认同作者对职责的划分:环境能禁止的行为,由环境限制;脚本能判定的规则,在工具调用处执行;搜索什么、怎么写、何时询问这类判断,留给提示词。
相较于反复追加「never」「always」,调用前检查可以直接阻止一次违规动作,反馈也更具体。但文章没有提供成功率、耗时或 token 消耗的对照数据。「比提示词更有效」在这里主要是机制解释和工程经验,不能据此推算所有团队的收益。
实现也有前提。先读再改需要可靠的文件状态记录;重复调用需要区分无效重试与合理轮询;PDF 自动转换需要解析能力。原文没有交代这些边界如何处理。
作者称 OpenCode、Hermes 和 OpenHands SDK 在提示词之外各有超过 20k 行代码,用来说明执行层的重要性。这个数字不能作为质量证明,小团队也不必照着搭一套完整框架。
能拿来做什么
- 给已有 AI 编程工具挑一条能机械判定的规则,先实现检查、错误原因和合法下一步,再删除提示词中对应的重复要求。
- 在文件编辑工具中记录读取状态,修改前检查文件是否读过、是否发生变化,让前端和 Node 项目的改动基于当前内容。
- 对完全相同的工具名和参数统计重试次数,达到阈值后要求换策略或报告阻塞原因,并为合理轮询单独设计规则。
- 如果 Agent 经常走错文件读取路径,可以先通过钩子提示正确工具;已有稳定解析器时,再由执行层自动转换并返回结果路径。