模型为何调用文档里明确禁止的工具?一次 Agent Skill 健康度整改实录
导读:这篇文章用两起真实整改案例回答"模型为何调用文档里明确禁止的工具":对话下单 Skill 健康度仅 38/100、中断率 79.1%,trace 显示未声明乃至被禁用的工具被大量调用(子 agent 占墙钟 62.8%、被明令禁止的检索工具被调 53 次)。核心结论是单一负面禁令不足以约束模型,须改为正向声明"仅允许以下工具"并配合平台侧真移除;案例二补充了"被路由到错误技能时文档还能做什么"。适合治理 Agent Skill 健康度、做 trace 实证归因的工程师阅读。 分类:AI评测 | 标签:Agent、Skill、健康度、实战案例
阅读提示:本文为内部整改案例的脱敏改写。文中所有健康度分数、中断率、耗时、token 数、工具调用次数等量化指标均保留数量级并做了脱敏示意,不代表任何线上真实数值;内部系统名、工具名、域名、业务字段均已用通用说法替代。
从两个"红牌"健康度报告说起
在自研 Agent 平台上做 Skill 治理时,我反复遇到同一种典型场景:模型明明配有详细的 SKILL.md 文档,行为却完全不受文档约束——它会调用文档里没有声明、甚至明确禁止的工具,把一次本应几秒完成的对话,拖到用户放弃等待。
本文用两起真实整改案例完整复盘这件事。案例一回答"模型为何无视文档禁令",案例二补充一个容易被忽视的视角:"当模型被路由到了错误的地方,文档还能做什么"。
案例一:对话下单 Skill,健康度 38/100
1. 结论先行:用户等不到结果
健康度 38/100,判定为不健康。核心矛盾一句话——用户等不到结果:中断率 79.1%(197/249),静默 p95 17.3s,latency p95 84.3s。而结构性慢因是嵌套子 agent:调用 57 次,墙钟 2310.62s,占 trace 的 62.8%。
关键指标一览(建议验收值):
| 指标 | 当前值 | 目标 |
|---|---|---|
| 中断率 | 79.1%(197/249) | <50% |
| latency p95 | 84.3s(p50 46.74s) | <60s |
| max_silence p95 | 17.33s | <3s |
| LLM 次数 mean | 6.86(p95 13.05) | ≈4 |
| 嵌套子 agent 墙钟占比 | 62.8%(57 次 / 2310.62s) | <30% |
| 工具次数 p95 | 23.05(合计 914) | ≈6 |
| 版本号上报 | 缺失(无法对比上版) | 每次发布必带 |
健康度四维拆解(n=257,近 5 自然日):
| 维度 | 权重 | 得分 | 扣分原因 |
|---|---|---|---|
| 可靠性 | 40% | 0.21 | 中断率 79.1% |
| 时延 | 25% | 0.10 | p95 84.3s;静默 p95 17.3s |
| 工具健康 | 20% | 1.00 | 工具错误率 0.0% |
| 循环健康 | 15% | 0.50 | LLM 轮次 p95 13.1 |
几个值得注意的数字:平均用户感知耗时 48.43s(名义上达标 ≤50s,但 79% 的用户根本没等到结果);中断分布为 hard_cancel 175 / client_abort 22 / ok 52;真实错误率 0.0%——链路没有错,就是慢到用户放弃。
耗时结构说明慢在哪里:
| 环节 | 独占耗时 | 占比 | 说明 |
|---|---|---|---|
| 模型生成 / 思考 | 3363.74s(42.05s/trace) | 78.4% | 含嵌套子 agent 内部影子 |
| 异常 span | 790.06s(9.88s/trace) | 18.4% | 0.86 次 / trace |
| 工具执行 | 129.51s(1.62s/trace) | 3.0% | 非主要慢因 |
| 知识读取 | 6.61s(0.08s/trace) | 0.2% | 8.22 次 / trace,次数高耗时低 |
| 嵌套子 agent(inclusive) | 2310.62s | 62.8% | 结构性慢因,优先排查 |
2. trace 实证:模型大量调用文档未声明、甚至明确禁止的工具
看报告只能知道"慢",不知道"为什么慢"。下一步把线上 trace 的 Top 工具与 SKILL.md(v0.3.28)的声明逐项核对:
| 工具(名称已中性化) | 线上次数 | 工具耗时(占比) | SKILL.md 口径 |
|---|---|---|---|
| 知识读取工具(skill_view) | 658 | 6.61s(4.9%) | 声明:包内文档读取方式 |
| 订单接口查询工具(HTTP GET) | 109 | 35.95s(26.4%) | 声明:主 API 工具 ✓ |
| 子任务委派(sub-agent) | 57 | 墙钟 2310.62s(62.8%) | 未声明 |
| 内部知识检索工具 | 53 | 62.3s(45.8%) | 明确禁止 |
| 命令执行工具 | 19 | 26.29s(19.3%) | 未声明 |
| 网页搜索工具 | 6 | 0.95s(0.7%) | 未声明 |
| 第三方接口调用 / 订单接口提交(HTTP POST) | 6 / 3 | 1.96s / 1.53s | POST 已声明 ✓ |
核对结论非常讽刺:模型在运行时大量调用文档未声明、甚至明确禁止的工具。其中子任务委派恰好就是那个占墙钟 62.8% 的结构性慢因的直接来源;被明令禁止的内部知识检索工具也在偷偷用。
由此得到第一个方法论结论,也是本文标题的答案:
单一负面禁令不足以约束模型。 在文档里写"禁止 X、禁止 Y"并不会让模型听话;必须改为正向声明"仅允许使用以下工具",再配合平台侧把这些工具从模型可见的工具列表里真正移除,约束才能闭环。
3. 问题归因与 Proxy 下沉信号:4/4 命中
按问题类型归因:
| 类型 | 线上证据 | 处理方向 |
|---|---|---|
| 对话理解失败 | 错误率 0.0%,无意图/槽位失败记录 | 优化 skill(低优先) |
| 接口拼装失败 | 错误率 0.0% | 无(未命中) |
| 状态判断失败 | 多轮规划/校验空转:LLM mean 6.86、子任务 57 次、异常 span 0.86 次/trace | 收敛轮次与子 agent(P0) |
| 系统能力失败 | 异常 span 占 18.4% 但真实错误率 0% | 定位重试/超时/日志噪音(P1) |
同时按"Proxy 下沉"方法论打了 4 个信号,4/4 命中:
| 信号 | 证据 |
|---|---|
| LLM 独占 ≥70% 且 p95 轮次高 | 78.4%(含嵌套影子)· p95 13.05 |
| 知识读取次数多但耗时低 | 8.22 次/trace · 0.08s → 材料应前置缓存或 Proxy prepare |
| 单工具占工具耗时大头 | 内部知识检索 45.8% + 命令执行 19.3% → 查询编排应下沉进 Proxy |
| 错误率低、感知仍慢 | 0.0% vs p95 84.3s → 链路过长 |
方法论上满 3 条信号就应评估把编排逻辑下沉到 Proxy;本轮先做文档级收敛、用数据验证"子 agent 空转"假设,再决定是否动用后端资源。
4. 工作包 W1–W7 与里程碑 M0–M5
围绕交付物拆了 7 个工作包:
| 编号 | 工作包 | 优先级 | 归属 | 完成定义 |
|---|---|---|---|---|
| W6 | 诊断抽样:验证子 agent 与异常来源 | P0 | 分析 | 诊断结论文档(假设定案) |
| W7 | 平台能力验证:流式/进度消息/子 agent 限深/工具移除 | P0 | 平台依赖 | 能力矩阵(决定 W3 与目标值) |
| W1 | 工具边界收敛(正向声明 + 平台白名单) | P0 | skill | 全包 grep 零残留 + 白名单确认 |
| W2 | 子 agent 与轮次收敛(限深/退出/共享背景前置) | P0 | skill | SKILL.md 轮次约束段 + 主流程 5–8 步 |
| W3 | 占位话术与静默治理 | P0 | skill | 进度话术段落地 |
| W4 | 异常与工具调用收敛(退避上限/降级直答/合并查询/读取缓存) | P1 | skill | references 裁剪 + 错误兜底规则 |
| W5 | 版本上报与发布纪律(zip 校验/RELEASE-NOTES/版本号上报) | P0 | 工程 | 技能包 + 校验报告 + 版本号可见 |
关键工作项的要点:
- W1 工具边界:SKILL.md 加正向铁律——「仅允许使用声明工具(订单接口查询/提交、地址解析、追问用户、知识读取),禁止子任务委派 / 网页搜索 / 命令执行 / 内部知识检索」,沿用复盘结论"负面禁令不足";平台侧确认可否从模型工具列表移除上述违规工具。
- W2 子 agent 与轮次:子 agent 最多 3 轮、无新信息立即返回、单次工具可完成的子任务不起子 agent;商品库/规则类共享背景一次性写入(不每轮重读);多轮追问合并为一次结构化收集;删除"规划—校验—再规划"重复段。
- W3 占位话术:任务执行期间推送"正在查询数据 / 核对信息"类进度话术;禁止 3s 以上无输出(平台不支持中间消息时降级为"首条即成品"保证,依赖 W7)。
- W4 异常与工具收敛:错误分支退避重试上限(约 2 次),失败一次即降级直答、不触发新一轮 LLM 重规划;查询类工具按维度合并;已读材料缓存、同一路径禁重复读取。
- W5 发布纪律:zip 级断言脚本(根目录 SKILL.md、版本号、禁用词、连续重复行);确认版本号上报链路(当前缺失);RELEASE-NOTES 保持 3 版上限。
里程碑(带退出条件):
| 里程碑 | 内容 | 退出条件 |
|---|---|---|
| M0 | 方案确认 | 用户确认范围 / 验收 / 决策点 |
| M1 | 诊断与能力验证(W6 + W7) | 子 agent 行为结论 + 平台能力矩阵齐备 |
| M2 | 文档改造(W1–W4) | 全包核对零残留 + 静态扫描无新增提示 |
| M3 | 打包发布(W5) | zip 校验通过 + 版本号上报可见 |
| M4 | 上线观察 5 自然日(同口径取数) | 验收表全项达标;未达标回看方案 V2 |
| M5(可选) | Proxy 下沉评估(B 阶段) | D4 确认后另行立项,以原型验收为准 |
关键路径为 M0 → M1 → M2 → M3 → M4,其中有一条必须强调的纪律:
M1 是一道门禁:未验证假设,就不改文档。 W6 诊断抽样没完成、平台能力矩阵没拿到之前,不允许进入 M2 的文档改造——否则就是在未经验证的假设上改文档,方向错了全部返工。
支撑 M1 的是三条待验证假设:H1 子 agent 空转(57 次调用多为多轮规划/重复探索);H2 长静默劝退(p95 17.3s 是 hard_cancel 175 次的主因);H3 读取重复(8.22 次/trace 含同路径重复读取)。每一条都有明确的验证方法(查慢 trace、话术上线前后对比、grep + trace 采样)。
5. 决策点与部分砍项
方案里埋了 5 个决策点 D1–D5,用户确认结果如下:
| 决策点 | 建议(推荐前置) | 确认结果 | 影响 |
|---|---|---|---|
| D1 范围 | P0(W1–W3 + W5–W7)先行 | 只做 P0 先行 | W2 / W3 / W5 入本轮;W4(P1)不做 |
| D2 工具收敛 | 未声明工具全禁 | 不动(怕影响业务) | W1 取消:工具清单与白名单零改动 |
| D3 验收目标值 | 按报告 AI 建议 | 上线后跑数据看健康报告 | 验收判定延后至 M4 数据 |
| D4 Proxy 下沉 | A 阶段验证后评估 | 不做 | M5 关闭 |
| D5 版本上报 | 必做 | 要加版本号 | 入 W5 执行 |
注意 D2 这个"砍项":工具收敛本来是消除 62.8% 慢因的最大杠杆,却因业务顾虑被砍掉,W1 随之取消。这种"证据充分但决策层担心业务风险"的博弈在真实整改里非常常见——文档治理的边界就是把能改的改好、把不能改的写进风险清单,等数据说话。
交付边界趁早说清:只管优化 skill + 打 zip,不部署、不发布、不管理验收。配套的治理规则:本方案为基线,范围/验收/目标值变更须回看并升版(V1 → V2),不静默改基线;取数同口径(观测平台近 5 自然日、全量墙钟),发布后 5 日判定 M4。
风险与备选(精简版):
| 风险 | 预警信号 | 响应动作 / 备选 |
|---|---|---|
| R1 平台不支持流式/进度消息/工具移除 | 能力矩阵出现缺口 | 占位话术降级,静默目标修订为 ≤5s 重新验收;工具移除不可行则退化为"仅限次数" |
| R2 工具收敛伤业务 | 业务走查断链或错误率上升 | 保留必要工具仅限次数/范围,灰度发布观察 |
| R3 client_abort 不可全归因时长 | 指标达标后中断率仍 >50% | 超长任务拆异步通知,或按 hard_cancel 占比修订验收口径 |
| R4 文档改动破坏正确性 | 回归走查失败 / 错误率上升 | 灰度 + 回滚至基线包 |
| R5 未验证假设即改造 | M1 未完成直接进入 M2 | M1 门禁强制,诊断结论先行 |
6. 执行记录与 zip 级校验
本轮实际落地(版本 v0.3.29):
- W2 + W3 合并落地:SKILL.md 新增强制规则 17「读取纪律:每会话每文件只读一次,禁止重复读同一文件」、规则 18「一次问全 · 单步完成 · 不空转」;未引入进度占位话术——与既有"零过程句 · 首条必成品"实锤纪律冲突,避免业务风险。
- W5:版本 0.3.28 → 0.3.29(SKILL.md frontmatter + RELEASE-NOTES,最近更新保持 3 版上限)。
- W1、W4、W6、W7 未做:分别因 D2 决策、P1 范围、用户直接确认范围、平台/部署不在职责。
- references 30 个文件与全部业务规则、接口、兜底链零改动(约束:不碰业务)。
打包与校验(zip 级断言脚本):
| 项 | 结果 |
|---|---|
| 产物 | dist/order-skill-prod-v0.3.29.zip |
| 大小 / 文件数 | 90.3 KB(原 87.5 KB)· 35 项 |
| 结构 | 与线上包 namelist 逐项一致,仅替换 SKILL.md 与 RELEASE-NOTES |
| zip 级校验 | SKILL.md version 0.3.29 ✓ · RELEASE-NOTES version 0.3.29 ✓ · 规则 17/18 在包内 ✓ · 最近更新 3 版上限 ✓ |
里程碑状态:M0 ✅ · M1 ⏭ 跳过(用户直接确认范围)· M2 ◐ 部分完成 · M3 ✅ · M4 ⏳ 待上线后 5 日数据。
上线后重点对比:LLM 次数 mean(6.86 → ?)、知识读取次数(8.22 → ?)、中断率(79.1% → ?)、latency p95(84.3s → ?),并验证版本号是否真的上报成功;若指标未达标,复审 D2 工具收敛(唯一大杠杆)。
(此后还有一轮 v0.3.30:处理第二份扫描反馈——版本历史归档到独立文件、示例注释补充"线上域同样适用"说明,zip 校验同样通过。细节不展开。)
案例二:订单查询 Skill,健康度 53/100
1. 根因:6 条慢 trace 里 5 条是"被路由错地方"的任务
第二个案例的根因不是文档质量,而是误路由。健康度 53/100,6 条慢 trace 采样中 5 条都是域外任务——日报统计、履约登记、保险引导等。模型被绑定在当前技能后,越界读取包外文件、猜参数反复锤接口、上下文膨胀到 148k–169k tokens,单轮生成从 5s 涨到 23s。
初始健康指标(事实):
| 指标 | 当前值 | 结论 |
|---|---|---|
| 平均耗时 | 88.03s(基线 50s,+76.1%) | 未达标 |
| 时延 p95 / p99 | 254.9s / 327.8s(n=32) | ≥180s FAIL,时延维度 0 分 |
| 中断率 | 21.9%(7/32,hard_cancel 4 + client_abort 3) | 扣可靠性 |
| LLM 次数 p95 / mean | 18.15 / 10.0 | 循环健康度 0.13 |
| 知识读取平均 | 5.28 次/trace(95 次) | 偏高 |
| 模型生成 / 思考独占 | 89.6%(1148.34s / 18 trace) | 严重拥堵,唯一大头 |
| 工具错误率 | 1.4%(3/217) | 正常 |
| 版本上报 | 未上报版本号 | 无法与上一版对比 |
慢 trace 画像(trace id 已省略,诉求与归属已泛化):
| 样例 | 时延 | LLM 轮 | 用户诉求(脱敏) | 真正归属 |
|---|---|---|---|---|
| 慢样例 A | 308.8s | 16 | 统计本人名下当日已履约/待履约/发起签单 | 报表统计域 |
| 慢样例 B | 210.8s | 9 | 再次统计同上 | 报表统计域 |
| 慢样例 C | 90.3s | 14 | 线索履约登记操作 | 履约操作域 |
| 慢样例 D | 34.4s | 7 | 单条履约登记 | 履约操作域 |
| 慢样例 E | 46.6s | 4 | 保险引导(附截图) | 其他业务域 |
| 快样例 F | 19.3s | 4 | 非本域订单查询 | 非本域订单(正确拒绝) |
6 条中仅 1 条在域内范围边缘;而域内正例(订单列表/详情/换单链)在采样中没有一条慢 trace——慢的全部是"不属于我"的任务。
归纳出的共性失败模式:
- 误判滞留:模型首轮 reasoning 已指出"这应该由报表统计类技能处理",但被绑定在订单查询子技能无法切换,只能在本技能内硬解。
- 越界抓取:读取其他技能包目录下的 SKILL.md、INSTRUCTIONS.md 等包外文件。
- 猜参锤接口:同一参数试 3–4 个候选值、参数在 body/query 间试错、body 误传类型;同一接口重复调用(32 条 trace 内订单接口提交类调用合计 49 次)。
- 上下文膨胀:单条 trace 输入 token 从 28k 膨胀到 148k–169k,每轮生成 5s → 23s。
- 用户取消:等待 3–5 分钟无有效输出 → client_abort / hard_cancel(中断率 21.9%)。
2. 方案 A / B / A+ / 不动的决策
摆在面前的是四个选项(决策时点 2026-08-31):
| 选项 | 改动范围 | 成本 / 风险 | 预期收益 |
|---|---|---|---|
| A · 边界治理全套(推荐) | 范围小节 + 行为准则 4 条 + 双 description 收紧 + RELEASE-NOTES + 版本 2.2.0 | 纯文档改动,不碰接口与流程;description 改动对平台召回影响小(保留原触发词) | 误路由任务 1–2 轮内转出;LLM p95 降至 8–10;时延 p95 降 40%+ |
| B · 仅范围声明 | 只加范围小节与 escalate 出口 | 最低 | 能减滞留轮次,但模型仍被路由进来后才转出,版本无法对比 |
| A+ · 全套 + 单链读取标注 | A + 换单链文档标注"按需读一次、禁重复读" | 低 | 进一步降低域内读取 |
| 保持不动 | 无 | 无 | 维持 53/100,慢 trace 与中断持续 |
最终选 A:范围声明 + escalate 出口、4 条行为准则、双 description 收紧、补版本上报,版本 2.1.0 → 2.2.0。
四条行为准则(复用价值很高):
- 禁读包外文件:禁止读取本技能包目录以外的文件;需要其他技能能力时 escalate,不得自行读取其文档。
- 参数不猜:同一查询最多尝试 2 种候选参数组合,仍无结果或返回异常即停止并向用户说明口径;禁止连续换参重复调用。
- 每会话每文件只读一次:已读文件禁止重复读取。
- 一次规划、集中执行:功能链(如换单 12 步)开始前一次性读完所需文档,执行中不再读文档;接口返回后直接构造下一步请求。
范围小节的核心是双向声明:写清楚"属于什么"(已有订单/线索 ID 的单笔查询与操作),更要写清楚"不属于什么——立即 escalate,禁止用本技能接口硬查"(统计/日报/业绩汇总、履约写操作、投保/保险/签约引导、工资发放查询、任何汇总/统计/报表类诉求),并规定 escalate 的 reason 必须写明"应转哪个域 + 用户诉求一句话"。
双 description 收紧:SKILL.md frontmatter 与 skill.json 的 description 均追加范围排除句(保留全部原触发词),降低误路由概率。补版本上报:新增 RELEASE-NOTES(name/version/generated,与平台通用格式一致),修复健康报告"未上报版本号"。
风险与未知,照实写:
- escalate 依赖平台再分流:其中一条 trace 显示 escalate 后中枢仍把任务委派回本技能——文档只能减少滞留轮次,根治误路由需要平台侧技能路由优化(另行反馈平台)。
- 域内基线未知:本次 32 条 trace 中域内任务占比低,建议上线后对比下一轮健康报告 p50。
- description 修改可能轻微影响关键词召回,已采用"保留原触发词 + 追加排除句"策略。
- client_abort 与平台流式能力相关,长静默根治需平台开启流式输出与占位推送。
3. 09-07 验证:两条兑现、一条未兑现,诚实归因
同口径重跑(近 5 自然日、同等采样规模),已核实线上包版本号较基线推进、v2.2.0 已部署生效。目标兑现核对:
| 指标 | 基线 08-31 | 本轮 09-07 | 结论 |
|---|---|---|---|
| 明细覆盖率 | 52% | 98% | ✅ 巨型 trace 29→1,上下文膨胀显著缓解 |
| 中断率 | 12.7% | 5.7% | ✅ 目标达成 |
| 时延平均 / p95 | 93.0 / 258.8s | 114.0 / 316.8s | ⚠️ 未兑现 |
| LLM 次数 p95 | 18 | 22.1 | ⚠️ 上升(含统计口径变化:覆盖率提升后最胖 trace 被计入) |
两条兑现、一条没兑现,我的建议是如实写:
- 方案核心目标(误路由快速转出、降上下文膨胀、降滞留轮次)已获实证,明细覆盖率与中断率是直接证据。
- 时延未兑现,主要归因全局模型因素——同期四个技能的单轮生成 p95 同步翻倍(约 19s→40s),不是文档改动能抵消的;待模型负载恢复后重测。
- 下一轮重点看 LLM 次数 p95 是否回落、时延是否随全局恢复;对比时同时记录单轮生成耗时基线。(注:验证时按同口径对基线重新取数,其数值与最初抽样报告略有差异属正常。)
复盘:三条可复用的经验
- 负面禁令不足,需要正向声明。 两个案例反复印证同一件事:文档里写"禁止 X"几乎没用,要写"仅允许以下工具 / 范围",并尽可能在平台侧把工具从模型可见列表真正移除。案例一里明令禁止的检索工具被偷偷调用 53 次,就是最直接的证据。
- 健康度数字只是入口,trace 实证才是证据。 38 分和 53 分都只是红牌;真正指导整改的是"工具 × 声明对照表"和"慢 trace 画像"——前者抓出越权调用,后者抓出误路由。没有 trace 之前,一切归因都是猜测。
- M1 门禁:未验证假设不改文档。 "子 agent 空转""静默劝退""重复读取"这些假设被 trace 验证之前,不要动文档。先诊断、再验证、后改造——改错方向的返工成本,远高于诊断成本。
如果你也在治理 Agent Skill 的健康度,希望这两份实录能帮你少踩一个坑:先向 trace 要证据,再让文档说人话。