agent-tool-design
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAgent Tool Design
Agent工具设计
The Agent Tool Contract — 5 principles for designing tools that agents call reliably.
Agent工具契约——设计可被Agent可靠调用的工具的5条原则。
The 5 Principles
5条原则
Principle 1: Predictable Signature
原则1:可预测的签名
Tools must have typed, named parameters with clear required/optional distinction. No positional ambiguity.
Good:
javascript
// Clear, named, typed
function searchCode({ query, limit = 20, type = 'semantic' }) { ... }Bad:
javascript
// Positional, ambiguous
function searchCode(q, n, t) { ... }工具必须具备带类型的命名参数,明确区分必填/可选,不存在位置歧义。
正面示例:
javascript
// Clear, named, typed
function searchCode({ query, limit = 20, type = 'semantic' }) { ... }反面示例:
javascript
// Positional, ambiguous
function searchCode(q, n, t) { ... }Principle 2: Rich Errors
原则2:丰富的错误信息
Errors must include: error code (machine-readable), message (human-readable), context (debugging data).
Good:
javascript
throw {
code: 'FILE_NOT_FOUND',
message: `File not found: ${path}`,
context: { path, cwd: process.cwd() },
};Bad:
javascript
throw new Error('not found'); // No context for agent to act on错误必须包含:错误码(机器可读)、消息(人类可读)、上下文(调试数据)。
正面示例:
javascript
throw {
code: 'FILE_NOT_FOUND',
message: `File not found: ${path}`,
context: { path, cwd: process.cwd() },
};反面示例:
javascript
throw new Error('not found'); // No context for agent to act onPrinciple 3: Token-Efficient Output
原则3:Token高效的输出
Tools return structured minimal data. No prose explanations, no redundant wrapping, no verbose status messages. Agents format output themselves.
Good:
javascript
return { files: ['a.js', 'b.js'], total: 2 };Bad:
javascript
return { status: 'success', message: 'Found 2 files successfully', data: { files: [...], metadata: {...} } };Rule of thumb: If the output contains prose an agent would re-read to extract facts, it's too verbose.
工具返回结构化的最小数据,不要散文式解释、冗余封装、冗长的状态消息,Agent会自行格式化输出。
正面示例:
javascript
return { files: ['a.js', 'b.js'], total: 2 };反面示例:
javascript
return { status: 'success', message: 'Found 2 files successfully', data: { files: [...], metadata: {...} } };经验法则: 如果输出中包含Agent需要反复读取才能提取事实的散文式内容,说明输出过于冗长。
Principle 4: Idempotency
原则4:幂等性
Tools must be safe to retry. Running a tool twice should produce the same result as running it once.
Good:
javascript
// Upsert instead of insert
db.upsert({ id, ...data });
// mkdir -p instead of mkdir
fs.mkdirSync(path, { recursive: true });Bad:
javascript
// Fails on retry
db.insert({ id, ...data }); // duplicate key error
fs.mkdirSync(path); // EEXIST error工具必须可安全重试,运行两次产生的结果应该和运行一次完全一致。
正面示例:
javascript
// Upsert instead of insert
db.upsert({ id, ...data });
// mkdir -p instead of mkdir
fs.mkdirSync(path, { recursive: true });反面示例:
javascript
// Fails on retry
db.insert({ id, ...data }); // duplicate key error
fs.mkdirSync(path); // EEXIST errorPrinciple 5: Graceful Degradation
原则5:优雅降级
Partial success > hard failure. Return what succeeded with a clear indication of what didn't.
Good:
javascript
return {
succeeded: ['file1.js', 'file2.js'],
failed: [{ file: 'file3.js', reason: 'PERMISSION_DENIED' }],
partial: true,
};Bad:
javascript
// One file fails -> entire batch throws
throw new Error('Failed to process file3.js');部分成功优于完全失败,返回已成功执行的内容,同时清晰标记未成功的部分。
正面示例:
javascript
return {
succeeded: ['file1.js', 'file2.js'],
failed: [{ file: 'file3.js', reason: 'PERMISSION_DENIED' }],
partial: true,
};反面示例:
javascript
// One file fails -> entire batch throws
throw new Error('Failed to process file3.js');Anti-Pattern Table
反模式表格
| Anti-Pattern | Problem | Fix |
|---|---|---|
| Verbose status wrapping | Wastes tokens; agent re-parses to extract data | Return data directly |
| Positional args | Ambiguous; breaks on refactor | Named params with types |
| Swallowed exceptions | Agent thinks success; work is lost | Always surface errors explicitly |
| Non-idempotent mutations | Retry causes duplicate data or errors | Upsert semantics; check-then-set |
| Hard failures on partial input | One bad item breaks entire batch | Return partial results |
| Side-effect-heavy reads | Read tools that trigger writes confuse agents | Separate reads from writes |
| String error messages only | Agent can't programmatically handle errors | Include machine-readable error codes |
| Untyped return shape | Agent can't reliably destructure output | Document and enforce return schema |
| 反模式 | 问题 | 修复方案 |
|---|---|---|
| 冗长的状态封装 | 浪费Token;Agent需要重新解析才能提取数据 | 直接返回数据 |
| 位置参数 | 歧义性高;重构时容易失效 | 使用带类型的命名参数 |
| 吞掉异常 | Agent会误以为操作成功,工作成果丢失 | 始终显式抛出错误 |
| 非幂等的变更操作 | 重试会导致重复数据或错误 | 使用Upsert语义;先检查再设置 |
| 部分输入错误直接完全失败 | 一个错误项导致整个批次处理失败 | 返回部分结果 |
| 带副作用的读操作 | 会触发写入的读工具会让Agent产生混淆 | 读写逻辑分离 |
| 仅返回字符串错误消息 | Agent无法通过程序逻辑处理错误 | 包含机器可读的错误码 |
| 无类型的返回结构 | Agent无法可靠地解构输出 | 文档化并强制要求返回schema |
Review Checklist
审核检查清单
Before shipping any tool:
[ ] Parameters are named (not positional)
[ ] Required vs optional params are explicit
[ ] All error paths return { code, message, context }
[ ] Output contains no prose — only structured data
[ ] Tool is idempotent (safe to retry)
[ ] Partial failure returns partial results, not throws
[ ] Return shape is documented in JSDoc or TypeScript types
[ ] Token budget for output estimated (< 500 tokens for standard tools)发布任何工具前,请确认:
[ ] 参数为命名形式(非位置参数)
[ ] 明确区分必填和可选参数
[ ] 所有错误路径都返回 { code, message, context }
[ ] 输出仅包含结构化数据,无散文式内容
[ ] 工具具备幂等性(可安全重试)
[ ] 部分失败时返回部分结果,而非抛出异常
[ ] 返回结构已在JSDoc或TypeScript类型中说明
[ ] 预估输出的Token预算(标准工具输出低于500 Token)Iron Laws
铁律
- ALWAYS use named parameters — never positional arguments in tool signatures; positional args break on refactor and create ambiguity for agents.
- ALWAYS include machine-readable error codes — never surface plain string errors only; agents need to handle errors programmatically.
{ code, message, context } - NEVER mix reads and writes in the same tool — read tools that trigger side effects confuse agents and prevent safe retries.
- ALWAYS design for idempotency — retry must produce the same result as the first call; use upsert semantics and patterns.
mkdir -p - ALWAYS return partial results on partial failure — never let one failing item abort the entire batch; return .
{ succeeded, failed, partial: true }
- 始终使用命名参数 —— 工具签名中绝对不要使用位置参数;位置参数在重构时会失效,还会给Agent带来歧义。
- 始终包含机器可读的错误码 —— 绝对不要仅返回纯字符串错误;Agent需要 结构才能通过程序逻辑处理错误。
{ code, message, context } - 绝对不要在同一个工具中混合读写逻辑 —— 会触发副作用的读工具会让Agent产生混淆,还会导致无法安全重试。
- 始终按照幂等性设计 —— 重试产生的结果必须和首次调用完全一致;使用Upsert语义和 这类模式。
mkdir -p - 部分失败时始终返回部分结果 —— 绝对不要让一个失败项终止整个批次的处理;返回 结构。
{ succeeded, failed, partial: true }
Integration
集成
- Used by: skill when designing new tools
tool-creator - Reviewed by: agent during tool PRs
code-reviewer - Pairs with: skill (consuming external tools)
dynamic-api-integration - Complements: skill (evaluating tool output quality)
agent-evaluation
- 使用者:设计新工具时的 skill
tool-creator - 审核者:工具PR审核阶段的 agent
code-reviewer - 搭配使用:skill(消费外部工具)
dynamic-api-integration - 互补:skill(评估工具输出质量)
agent-evaluation
Memory Protocol (MANDATORY)
内存协议(强制要求)
Before starting:
Read
.claude/context/memory/learnings.mdAfter completing:
- New pattern ->
.claude/context/memory/learnings.md - Issue found ->
.claude/context/memory/issues.md - Decision made ->
.claude/context/memory/decisions.md
ASSUME INTERRUPTION: If it's not in memory, it didn't happen.
开始前:
阅读
.claude/context/memory/learnings.md完成后:
- 新模式 -> 写入
.claude/context/memory/learnings.md - 发现问题 -> 写入
.claude/context/memory/issues.md - 做出决策 -> 写入
.claude/context/memory/decisions.md
假设会发生中断:如果内容没有记录在内存中,就等于没有发生过。