Learn Claude Code
s02

工具

工具与执行

One Handler Per Tool

237 LOC4 个工具TypeScriptTool dispatch map
The loop stays the same; new tools register into the dispatch map

s01 > [ s02 ] s03 > s04 > s05 > s06 | s07 > s08 > s09 > s10 > s11 > s12

"加一个工具, 只加一个 handler" -- 循环不用动, 新工具注册进 dispatch map 就行。

Harness 层: 工具分发 -- 扩展模型能触达的边界。

问题

只有 bash 时, 所有操作都走 shell。cat 截断不可预测, sed 遇到特殊字符就崩, 每次 bash 调用都是不受约束的安全面。专用工具 (read_file, write_file) 可以在工具层面做路径沙箱。

关键洞察: 加工具不需要改循环。

解决方案

+--------+      +-------+      +------------------+
|  User  | ---> |  LLM  | ---> | Tool Dispatch    |
| prompt |      |       |      | {                |
+--------+      +---+---+      |   bash: run_bash |
                    ^           |   read: run_read |
                    |           |   write: run_wr  |
                    +-----------+   edit: run_edit |
                    tool_result | }                |
                                +------------------+

The dispatch map is a dict: {tool_name: handler_function}.
One lookup replaces any if/elif chain.

工作原理

  1. 每个工具有一个处理函数。路径沙箱防止逃逸工作区。
function safePath(relativePath: string): string {
  const filePath = resolve(WORKDIR, relativePath);
  const normalizedWorkdir = `${WORKDIR}${process.platform === "win32" ? "\\" : "/"}`;
  if (filePath !== WORKDIR && !filePath.startsWith(normalizedWorkdir)) {
    throw new Error(`Path escapes workspace: ${relativePath}`);
  }
  return filePath;
}
  1. dispatch map 将工具名映射到处理函数。
const TOOL_HANDLERS = {
  bash: (input) => runBash(String(input.command ?? "")),
  read_file: (input) => runRead(String(input.path ?? ""), Number(input.limit ?? 0) || undefined),
  write_file: (input) => runWrite(String(input.path ?? ""), String(input.content ?? "")),
  edit_file: (input) =>
    runEdit(String(input.path ?? ""), String(input.old_text ?? ""), String(input.new_text ?? "")),
};
  1. 循环中按名称查找处理函数。循环体本身与 s01 完全一致。
for (const block of response.content) {
  if (block.type !== "tool_use") continue;

  const handler = TOOL_HANDLERS[block.name as ToolUseName];
  const output = handler
    ? handler(block.input as Record<string, unknown>)
    : `Unknown tool: ${block.name}`;

  results.push({
    type: "tool_result",
    tool_use_id: block.id,
    content: output,
  });
}

加工具 = 加 handler + 加 schema。循环永远不变。

相对 s01 的变更

组件之前 (s01)之后 (s02)
Tools1 (仅 bash)4 (bash, read, write, edit)
Dispatch硬编码 bash 调用TOOL_HANDLERS 字典
路径安全safe_path() 沙箱
Agent loop不变不变

试一试

cd learn-claude-code
cd agents-ts
npm install
npm run s02

试试这些 prompt (英文 prompt 对 LLM 效果更好, 也可以用中文):

  1. Read the file package.json
  2. Create a file called greet.ts with a greet(name: string) function
  3. Edit greet.ts to add a JSDoc comment
  4. Read greet.ts to verify the edit worked