Install
$ agentstack add skill-yinqd3-workbuddy-skills-tool-call-repair ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Tool Call Repair — 工具调用输入修复管线
概述
当 LLM 输出工具调用参数时,频繁出现四种可穷举的结构性错误。不是模型能力问题,是合约设计问题。此 skill 提供一套 validate-then-repair 管线,在 Zod/JSON Schema 校验失败时自动修复可恢复的输入噪声,并记录修复率遥测数据。
核心理念
> 严格的 Schema 是一种有代价的选择——它过滤了噪声,也过滤了任何没有背下这个特定 JSON 合约的模型产生的可恢复噪声。
validate-then-repair > preprocess-then-validate:让校验器先报错,Schema 本身就是"哪里会坏"的先验,修复预算只花在校验器实际不同意的精确路径上。
工作流决策树
LLM 输出 tool call 参数
│
▼
按原样解析 (parse as-is)
│
┌────┴────┐
▼ ▼
成功 失败
│ │
▼ ▼
直接执行 遍历 Zod issue list
│
├─ 形状问题 ──→ 四项修复(按顺序尝试)
├─ 链接泄漏 ──→ Markdown 链接解包
└─ 关系问题 ──→ 扩展语义 + 默认值
│
▼
再次解析
┌───┴───┐
▼ ▼
成功 失败
│ │
▼ ▼
记录 repaired 记录 invalid
执行工具 返回模型可读的重试消息
1. 四项通用形状修复
这是覆盖 ~90% 开源模型工具调用失败的模式集合。四项修复共享一个结构:输入 JSON、Zod 报出的 issue 路径、尝试修复 → 返回修复后 JSON 或 null。
修复 1: 剥离 null 值
模型常对可选字段发送 null 而非省略。
# 修复前: {"limit": null, "query": "hello"}
# 修复后: {"query": "hello"}
修复 2: 解析字符串化的 JSON 数组
模型把数组序列化为 JSON 字符串。
# 修复前: {"filePath": "[\"a\",\"b\"]"}
# 修复后: {"filePath": ["a", "b"]}
修复 3: 解包多余的外层对象包装
单参数被包在 {} 里。
# 修复前: {"args": {"query": "hello"}},但 schema 期望 args 是 string
# 修复后: {"args": "hello"}
修复 4: 裸值包装为单元素数组
# 修复前: {"paths": "foo"}
# 修复后: {"paths": ["foo"]}
⚠️ 执行顺序(关键)
修复 2 必须在修复 4 之前运行。否则:
输入: {"paths": "[\"a\",\"b\"]"}
先执行修复4 → {"paths": ["[\"a\",\"b\"]"]} ← 错误!
先执行修复2 → {"paths": ["a", "b"]} ← 正确
使用方式
调用 scripts/repair.py:
python scripts/repair.py \
--input '{"filePath":"[\"a\",\"b\"]"}' \
--schema-path schema.json \
--tool-name writeFile
输出 JSON:
{
"repaired": true,
"output": {"filePath": ["a", "b"]},
"repairs_applied": ["json_array_parse"],
"telemetry": "tool_input_repaired:writeFile"
}
2. Markdown 链接泄漏修复
问题
训练于对话输出的模型,在工具调用中泄漏了自动链接行为:
filePath: "/Users/x/proj/[notes.md](http://notes.md)"
工具试图创建字面名为 [notes.md](http://notes.md) 的文件。
修复
两条正则,仅在链接文本等于无协议前缀的 URL 时解包。合法 Markdown(如 [click](https://x.com))不受影响。
import re
def unwrap_autolink(value: str) -> str:
"""只在退化情况下解包 Markdown 自动链接。"""
match = re.match(r'^\[(.+?)\]\((\1)\)$', value)
if match:
unwrapped = match.group(1)
# 确保解包后是合法路径(非完整 URL)
if not re.match(r'^https?://', unwrapped):
return unwrapped
return value
Schema 层面的预防
使用语义化类型别名替代裸 z.string():
// 不要用
const filePath = z.string()
// 用这个——给模型更强的上下文信号
const filePath = pathString() // 语义暗示:这是给 fopen 的,不是进聊天气泡的
3. 关系不变量处理
问题
形状层面各自合法的字段,组合起来违反了关系不变量。例如 read_file 要求 offset 和 limit 必须同时出现。
模型发 { absolutePath, limit: 30 } → Zod 报错。
解法:扩展语义 + 透明默认值
在函数内部教它理解模型的意图,而非拒绝:
def read_file(absolutePath: str, offset: int | None = None, limit: int | None = None):
# 修复关系不变量
if limit is not None and offset is None:
offset = 0
repaired = True
elif offset is not None and limit is None:
limit = 2000 # 匹配常见 read tool 的默认值
repaired = True
result = _do_read(absolutePath, offset, limit)
if repaired:
# 透明化决策——不用 Error: 前缀,终端不标红
result["_note"] = (
f"Note: {'offset' if offset == 0 else 'limit'} was not provided; "
f"defaulted to {offset if offset == 0 else limit}. "
"To adjust, retry with both offset and limit."
)
return result
原则
- 能修就修 → 形状问题
- 修不了就扩展语义 → 关系不变量
- 无论哪种都把选择透明化 → 结果中告知模型
4. 遥测集成
修复管线天然产出按 (model, tool) 维度的修复率数据。在工具执行层记录:
# 成功修复
metrics.increment(f"tool_input_repaired:{tool_name}", tags={"model": model_id})
# 修复失败
metrics.increment(f"tool_input_invalid:{tool_name}", tags={"model": model_id})
这些数据可以:
- 在用户察觉之前发现某个模型在特定合约上退化
- 指导 Schema 设计优化
- 帮助决策是否需要针对特定模型增加更多修复规则
修复优先级总结
| 优先级 | 修复类型 | 适用条件 | 策略 | |--------|---------|---------|------| | P0 | 四项形状修复 | Zod 报出 type/key/container 错误 | 按固定顺序尝试 | | P1 | Markdown 链接解包 | 路径字段匹配 [text](text) 模式 | 正则解包 | | P2 | 关系不变量默认值 | 字段各自合法但组合违反约束 | 扩展语义 + 透明标注 |
5. 集成方式(推特作者的实际架构)
修复层是工具执行器内部的薄中间件,不是独立预处理步骤。核心代码只有 safeParseWithRepair 一个函数,嵌在 execute() 方法里。
TypeScript 实现(推荐)
直接使用 scripts/repair.ts 中的 ToolExecutor 类:
import { ToolExecutor, pathString } from "./repair";
import { z } from "zod";
const executor = new ToolExecutor({ debug: true });
// 注册工具——Schema + 关系不变量默认值
executor.register({
name: "writeFile",
schema: z.object({
filePath: pathString(), // 语义类型,防 autolink 泄漏
content: z.string(),
}),
handler: async (input) => {
// 实际写文件逻辑
return { ok: true };
},
});
executor.register({
name: "readFile",
schema: z.object({
absolutePath: pathString(),
offset: z.number().optional(),
limit: z.number().optional(),
}),
relationalDefaults: {
offset: 0, // model 忘了 offset → 自动补
limit: 2000, // model 忘了 limit → 自动补
},
handler: async (input) => {
return { content: "...", _note: "..." };
},
});
// 调用——修复管线在 execute() 内部自动运行
const result = await executor.execute("readFile", {
absolutePath: "/x/file.txt",
limit: 30, // model 漏了 offset,executor 自动补 offset:0
});
集成要点
| 关注点 | 做法 | |--------|------| | 集成位置 | execute() 方法内,safeParse 失败之后 | | 快速通道 | 合法输入直接执行,修复预算为零 | | 修复层 | applyRepairs() 递归遍历 + 按顺序尝试四项修复 | | 关系不变量 | relationalDefaults 配置,在 schema 校验通过后补字段 | | 遥测 | metrics.increment() 按 tool name 计数 | | 错误返回 | 不用 Error: 前缀,终端不标红;附加 _note 告知模型 |
与直接使用 safeParseWithRepair 的区别
如果不想用 ToolExecutor 类,也可以直接调用核心函数:
import { safeParseWithRepair, extractHints } from "./repair";
const schema = z.object({ filePath: pathString(), content: z.string() });
const result = safeParseWithRepair(rawArgs, {
toolName: "writeFile",
schema,
hints: extractHints(schema),
metrics: myMetrics,
});
if (result.error) {
return { error: result.error }; // model-readable retry message
}
// result.data is the validated & repaired input
参考资源
scripts/repair.ts— TypeScript 实现(推荐),含ToolExecutor类 +safeParseWithRepair+pathString()语义类型scripts/repair.py— Python CLI 实现,适合脚本/管道场景references/failure_catalog.md— 各模型的典型失败案例及对应修复
已集成项目
- CodeBuddy/WorkBuddy — 通过
PreToolUsehook 注入修复管线,所有工具调用自动经过修复层 - Hook 脚本:
~/.workbuddy/hooks/tool-call-repair-hook.py - 配置:
~/.workbuddy/settings.json(hooks.PreToolUse,matcher:*) - 修复范围:stripnulls / jsonparse / unwrapobject / autolink(不含 wraparray,hook 无 Schema 信息)
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: yinqd3
- Source: yinqd3/workbuddy-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.