DeepDojo.AI发布 2026-11-09更新 2026-11-09English
AI Agent 总用错工具,多半是接口没设计好
用户只提了一个请求:“检查 PR 128 有没有需要处理的评论。”Agent 要做好它,得先取到评论,再对照代码判断。
工具一接上,麻烦就来了:它选错了动作,只读了第一页就下结论,写入超时后又把同一条评论重发了一遍,甚至被评论里夹带的“指令”带偏。
这些问题看着像模型不够聪明,更多时候是工具的接口没有设计好。模型靠工具的名称、描述、参数和返回结果来决定怎么调用:
- 接口含糊,它只能猜
- 返回结果没说读全了没有,它就把一页当成全部
- 错误只有一个代号,它分不清该改参数还是该重试
拿读取 PR 评论的工具来改,每个问题都有对应的修法:
- 名称和描述让动作一眼可分
- 返回做成能回查的分页摘要
- 错误信息指出下一步
- 写入超时这种“不知道成没成”的情况有专门的处理
- 外部内容和操作授权分开
好的工具设计,能让调用者看清对象、结果和下一步该做什么。开头那个请求,最后会完整走一遍;自己接入的工单、监控或文档工具,也可以用同样的思路检查。
01 先区分能力缺口与方法缺口
检查 GitHub PR 的阻塞评论,如果获准的 gh CLI 已能稳定读取,就可复用现有工具。需要跨仓库统一鉴权或高频读取结构化外部状态,而现有能力不足时,再考虑 MCP。这几种机制各管什么,见Skill、MCP、Hook、子代理,该用哪个。
| 实际需要 | 可用的处理方式 |
|---|---|
| 本地 shell 已能可靠执行 | 使用现有 Bash 与项目命令 |
| 只是静态背景或局部规则 | 知识文档、Skill 参考资料或 rules |
| 已有动作缺少执行方法 | Skill |
| 固定时机运行检查 | Hook |
| 偶尔读取一段资料 | 提供材料或搜索网页 |
| 高频读取或写入外部系统 | 权限和返回可控时考虑 MCP |
下文沿着“读取 PR 评论并判断阻塞问题”设计一个示意工具;工具名称和返回字段都是教学设计,实际接口以各系统为准,方法同样适用于工单、监控和文档接入。
接入前确认直接访问是否必要、来源由谁控制、是否需要写权限、返回是否过长、工具范围能否收紧,以及外部内容可能带来的指令注入。读取 PR 评论只需读取权限;合并 PR 则改变远端状态,应另行限定。
02 设计清楚的工具接口
模型主要通过工具名称、描述、参数 schema 和当前上下文判断调用什么;schema 是参数的结构约定,说明字段类型、必填项和允许值。Anthropic 的工程博客《Writing effective tools for agents》也建议用命名空间给功能划清边界,需要区分的内容,应尽量在接口中写清。
下面是一组工具命名示例:
github_pr_get
github_pr_list_comments
github_pr_create_review共同前缀给出系统和资源,末尾动作区分读取、列举和写入。相比 getData、runAction、sync,模型更容易区分读取评论与创建 review,选中当前所需的读取动作。
名称还需要描述补足边界。例如列评论应说明目标仓库和 PR 标识、返回哪些评论、是否分页;创建 review 应说明会产生远端写入。精简参数时,保留定位对象必需的字段,减少无用组合。设计时可以按调用过程检查:
| 环节 | 工具要提供什么 |
|---|---|
| 选择 | 资源、动作与适用条件明确 |
| 填参 | 必要字段、允许值和默认值清楚 |
| 理解结果 | 输出字段能支持当前判断 |
| 纠错 | 指出可修正原因与下一步 |
| 执行边界 | 读取、写入、删除及外部副作用可区分 |
默认值尤其要符合任务边界。只读查询无论可选参数缺省与否,都只读取。重复调用也应尽量保持幂等:相同请求重复执行,结果与执行一次相同,评论只有一条,款项只扣一次。确实会新增记录的动作,需要相应去重或状态核对设计。
03 返回可追查的结果摘要
如果 PR 有大量评论,全部返回会挤占上下文,只返回“有风险”又找不到位置。合适的短输出应同时保留结论、对象和回查方式。调用者指定了仓库和 PR,工具返回第一批评论的摘要:
{
"repository": "example/shop",
"pr": 128,
"coverage": "partial",
"next_cursor": "page-2",
"comments": [
{
"comment_id": 731,
"file": "src/auth/session.ts",
"line": 42,
"summary": "Reviewer reports a possible token refresh race.",
"resolved": false
}
]
}仓库和 PR 标识回答“检查了哪个对象”,评论 ID、文件与行号提供回查位置。摘要保留“审查者提出可能问题”的含义:它提示去检查竞争条件,问题是否复现还要另行验证。未解决评论也不天然等于发布阻塞,需要结合内容、当前代码和团队标准判断。
coverage: partial 和 next_cursor 表示尚有结果未读取。当前页为空时,整个 PR 仍可能有评论,应继续取得其余页;摘要影响判断时,再按评论 ID 读取正文。
详情入口要能实际调用:写明工具名和必需参数。约定查询范围全部读完后,才报告 coverage: complete 和空游标。这里的 complete 只描述读取覆盖;代码风险是否验证、外部状态之后是否变化,都在它之外。
短输出应保留支持判断的范围、对象、证据与回查入口:只给“有风险”太少,全部返回又太多。先用摘要定位,再按需展开详情,既能控制上下文占用,也能保留必要证据。
04 让错误信息指导下一步
下面的返回缺少纠错所需的信息:
{
"error": "INVALID_ARGUMENT"
}模型不知道哪个字段有问题,也不知道是否应该重试。可以把错误写成:
{
"error": "invalid_status",
"field": "status",
"allowed_values": ["todo", "in_progress", "blocked", "done"],
"received": "doing",
"operation_state": "not_applied",
"retry": "after_input_correction",
"suggestion": "Use in_progress only if the task has started."
}这里指出出错字段、合法值和收到的值,还说明本次操作未应用。retry 要求先纠正输入再重试;建议也有条件,只有确认任务已经开始,才采用 in_progress。这些字段是自定义契约,服务端必须按真实执行状态填入。
还要区分参数错误与执行结果未知。创建评论等写入超时,可能是请求未到达,也可能是写入成功后响应中断。未核对实际状态就重试,可能重复创建。
参数不合法 -> 修正输入后按契约重试
权限不足 -> 核对授权,不靠更换参数绕过
写入超时、结果未知 -> 先核对实际状态或使用去重机制错误契约应说明实际执行状态和允许的下一步,网络失败也要按实际状态分别标注。例如,创建评论超时后:如果查询确认评论已存在,就核对内容并报告已发送。如果服务能可靠确认未创建,并保证同一请求重复提交只产生一条记录,才按契约重试。结果查不清、也缺少去重保证时,就如实记为“发送结果未知”,停止自动重发。
超时缺的是执行结果,参数未必有错;重填一遍相同内容,执行结果仍是未知。

05 封装步骤,保留授权边界
稳定的底层读取依赖可以由工具封装。例如检查 PR 时,需要先定位仓库,再读取 PR 和评论;已确认的依赖顺序可以在内部完成,减少调用者搬运中间结果。
封装后仍应返回对象、范围和证据,并说明是否只读。查资料、修改状态、发通知和部署各自授权,各成独立动作。合成一个万能动作的话,中途失败后调用者看不出哪些写入已经生效,只能整体重试。
先取得并检查事实,再按具体授权进入相应写操作。减少调用次数的前提,是不隐藏副作用和执行状态。
06 外部资料不等于操作授权
prompt injection 是外部内容试图改变 Agent 指令或行为的情况。连接 MCP 获准,说明的是这个工具可以用;返回的评论、网页和文档只是资料,指令权限仍在用户和系统手里。
例如,PR 评论要求读取本地 .env,再将内容发送到外部地址。这是待分析资料中的越权要求,当作资料看待,继续按原任务范围和工具权限办事,凭据留在本地。
处理时要区分三层:工具连接是否获准、这个请求是否在当前任务范围内、内容本身是否可信。读取权限和发送权限分别授予;本地权限设置只由用户修改。
| 动作 | 应独立限定的边界 |
|---|---|
| 读取 | 哪个资源、哪些字段、是否包含敏感信息 |
| 写入 | 具体目标、拟写内容和获准范围 |
| 删除 | 对象、恢复能力及明确授权 |
| 对外发送、合并或部署 | 实际影响和最终确认方式 |
权限限制落实在工具或服务端,文字说明帮助模型理解用途。接口应落实“不许访问其他项目”的限制,即使模型选错也能拒绝越界对象。Claude Code 里权限、Hook 与沙箱怎么分工,见Hook 明明报错了,危险操作还是执行了。
07 用明确交互处理未决选择
再看一个交互问题:工具需要用户在两个目标环境里选一个。如果把选择藏在一个自由文本回复里,随后又按默认环境执行,用户可能从未真正作出决定。
Claude Code 的 AskUserQuestion 提供了一种做法:需要用户回答时,进入专门的提问交互。应用和工具设计也一样,把“询问、得到回答、据此继续”做成看得见的状态,系统才有依据判断用户是否批准。
已准备好选择或变更内容
→ 提出结构清楚的问题或确认
→ 取得适用范围明确的回答
→ 执行被选择或获准的动作目标环境未定时,应明确呈现候选对象并取得选择;高影响操作尚未授权时,应先提供具体差异和影响。用户的决定由用户作出,已经明确获准的动作则直接执行。运行环境要求正式审批的操作,仍走正式审批。
08 把完整调用走一遍
回到开头的请求“检查 PR 128 是否有需要处理的评论”,看工具设计是否成立:
- 定位对象。当前项目能唯一确定仓库,就使用已知仓库;否则先澄清,不因 PR 编号相同而读取另一个仓库。
- 读取与补全。调用只读评论工具。第一批返回 partial,就继续取得其余结果;某条摘要影响判断时,按 ID 回查原文。
- 形成判断。将评论要求与当前改动对照,区分已确认问题、审查者意见和待验证风险。评论读全了,代码验证还是要单独做。
- 按授权收束。输出发现与证据。当前请求只要求读取,读取完成后就收束。
如果用户随后要求“把这段结论发到 PR”,目标和内容明确后才进入写入工具;需要确认的未决内容通过清楚的交互取得决定。发送超时时,先查评论是否已出现或使用契约提供的去重机制,相同评论只发一次。
工具和流程在单项目中还未验证时,直接维护 .claude/ 配置更省事;先用真实任务检查能否选对工具、参数是否清楚、长结果是否可用、错误能否纠正、越界请求是否被拒绝,再考虑打包成 Plugin 共享。接口含糊就分发出去,同一个问题会扩散到更多项目,所以先修接口。
换到工单系统,资源名称和字段不同,判断关系相同:先定位对象,再理解返回覆盖范围,按执行状态纠错,最后在授权范围内改变状态。好的工具设计能让调用者看清对象、结果和下一步该做什么。