Learn Claude Code

Python 复刻agents/s02_tool_use.py

你跟 Claude Code 说一件事,它自己挑该用哪个工具——读文件用 Read、跑 Stata 用 mcp__stata-mcp__stata_do、跨文件搜词用 Grep、写审计报告用 Write。代码这边只是按工具名字找到对应的执行函数,调用一下,把结果回传。挑哪个工具完全是模型的判断。


2.1 工具是什么

工具是一份给模型看的清单。清单上每一条工具都有三样字段:名字、用途描述、参数格式。名字比如 Read 或 Grep。用途描述一句话写明这个工具能做什么。参数格式说明调用时该传什么。这份清单随每次模型调用一起发给模型,模型由此知道当前任务可以用哪些工具。

代码这边维护一份与清单对应的对照表:工具名字 → 执行函数。模型说"调用 Read,文件路径是 02_变量字典/测算方法说明.md",代码查对照表找到 Read 对应的函数,按文件路径参数运行,把读出来的内容作为结果回传给模型。

整个调度不做任何"该用什么工具"的判断。判断完全在模型那一头,依赖工具清单里"用途描述"那一句写得清不清楚。

2.2 一次工具调用的完整过程

模型每轮发回的响应里可能含工具调用请求。请求里有三个字段:调用的工具名、一个用来配对结果的标识号、一份参数对象。参数按工具规定的格式填好。

代码侧处理这次调用就是一行:在对照表里按工具名找到执行函数,把参数解包传进去,运行,得到一个字符串结果。执行函数内部读文件、调 stata-mcp、跑 Grep、调外部 API 都可以,对调度系统的唯一约束是返回字符串。

结果回到对话历史时带上原请求的标识号,让模型在下一轮看到结果时知道这是它哪次调用的回报。模型在下一轮根据结果决定下一步动作——继续调下一个工具、向你回报、或者结束本轮。

新工具进来的代价是一行对照表加一个执行函数。工具清单里"用途描述"写好之后,模型在合适的场景自己学会调用,循环本身不用改一行。

2.3 一次真实运行里模型挑了哪些工具

下面这段记录的是上面那次"PC 字段一致性核对"实际跑出来的工具序列。同一个任务里 Claude Code 自己挑了四类不同工具,按顺序串起来,没人事先指定该用哪个。

模型决定调用工具参数工具结果摘要
1先看口径定义Read02_变量字典/测算方法说明.mdPC(A2 主)= 稳定型股权 2(持股标准差 ln(1+x) 软压缩)+ 关系型债权 2(银行长贷 /(银行长贷+应付债券+应付票据)),z-score 标准化后相加
2看数据集里实际怎么落的字段mcp__stata-mcp__get_data_infodata_path=04_中间数据/main_panel.dta含字段 PC、PC_A1、PC_B2、PC_C、SD_A2_ln、Bank、z_SD_A2、z_Bank;PC 的 label 是"耐心资本(A2 主口径,z(SD_股权)+z(Bank_债权))"
3核 do4 拼面板时的构造代码Read.statamcp/stata-mcp-dofile/do4_panel_assemble.do第 85-87 行:egen z_SD_A2 = std(SD_A2_ln)、egen z_Bank = std(Bank)、gen PC = z_SD_A2 + z_Bank。与 02 字典一致
4核 do6 基准回归用的 PC 名字Greppattern="reghdfe.*PC " path=.statamcp/stata-mcp-dofile/do6_baseline.do7 处命中:第 54 行 PC、第 60 行 PC_A1、第 66 行 PC_C、第 89 行 PC(三分项 LHS=esg_score)、第 94/99/104 行(E/S/G 三分项)
5核 do10 稳健性用的口径替换Greppattern="reghdfe.*PC" path=.statamcp/stata-mcp-dofile/do10_robust.doA2/A1/B2/C/仅股权/仅 Bank/仅 Rdebt 七套口径都在;命名一致
6写核对报告Writepath=06_结果输出/pc_consistency_check.md报告含口径定义、字段名映射、构造代码摘录、回归引用位置;判定"02 字典 ↔ do4 构造 ↔ do6/do10 引用三方一致"

四类工具完成了一次跨字典、数据集、do-file、报告的核对:Read 读 markdown 与 do-file 文本,stata-mcp 工具读 .dta 元数据,Grep 在跨文件正则定位,Write 落核对报告。代码侧的调度逻辑没有任何"该用 Read 还是 stata-mcp"的 if-else——模型每轮看到当前对话历史,自己决定该挑哪个工具继续推进。

2.4 为什么这样设计

代码层无法穷举"什么场景用什么工具"的组合。"核字段定义就用 Read"这条规则容易写,"核字段定义但还要去 Stata 看实际 label、再到 .do 里 Grep 看代码用法、最后写报告"这条组合规则就开始难。组合的数量呈指数增长,写不完。模型在训练时见过千万种类似的"看见这种场景该用哪几个工具组合"的例子,泛化判断是它的强项。

调度逻辑保持一行,让 Claude Code 的扩展成本极低。装一个 skill 新增的工具自动进入对照表,模型自动学会调它。任何外部能力都能通过这一行接入。实证项目里你装 stata-mcp 工具读 .dta、装 PDF 抽取工具读 01_文献/ 下面 20 篇耐心资本论文的 PDF、装 docx 工具读 07_论文写作/01_主表/PC_ESG_主表汇总.docx,调度代码一行不改。

2.5 容易踩的坑

模型有时会调一个清单上没有的工具名。这通常是用户的指令暗示某个工具但 Claude Code 实际未装。处理方式是调度入口检测到"对照表里没这个工具名"时返回一个明确的错误信息,让模型在下一轮重新选。

模型偶尔会传错参数——比如 stata-mcp 要求 data_path 是绝对路径但模型传了相对路径,或者 Grep 漏了 pattern 字段。执行函数入口需要做参数校验并返回明确错误,让模型在下一轮修正。上面那次第 2 轮调用 stata-mcp 时模型先试过 data_path="main_panel.dta"(相对路径),stata-mcp 返回路径不存在,模型在第 3 轮改成绝对路径才成功——这种自纠错完全在模型那一头,调度代码不参与。

工具用途描述写得模糊时,模型可能在错误的场景调用错误的工具。用途描述是模型做触发判断的唯一依据,描述质量直接决定调度准确性。装一个 skill 时如果它的用途描述含糊,效果会很差。

2.6 知识地图

关键词含义容易误会的点
工具清单名字、用途、参数格式的列表模型只看得到清单里列的,未列的工具它根本不会调
工具名模型在调用时指定执行哪个工具的字符串由模型显式写出;代码按字段查对照表,不靠上下文猜
参数格式每个工具规定的输入结构决定模型生成参数时填哪些字段;松散的格式容易让模型传错参数
工具对照表代码侧"名字 → 执行函数"的查找结构没有 if-else 决定执行什么,纯查表