Learn Claude Code Agent 开发 | 2、插拔式工具系统:扩展功能不修改核心循环

Learn Claude Code Agent 开发 | 2、插拔式工具系统:扩展功能不修改核心循环

整体概述

多工具分发核心实现是基础智能体循环的直接扩展,核心思想就是:

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

这是 智能体工具分发机制 的核心实现,体现了 Harness 层最重要的设计原则:扩展工具不修改核心循环


解决的核心问题

单bash工具存在很多缺陷:

  1. cat 截断不可预测,sed 遇到特殊字符容易崩溃
  2. 每次 bash 调用都是不受约束的安全面,容易产生越权操作
  3. 扩展工具需要修改核心循环代码,违反开闭原则

核心设计思想

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

核心创新:用**分发字典(dispatch map)**替代硬编码的工具调用,实现工具的插拔式扩展。


逐段解析

1. 环境初始化

python 复制代码
WORKDIR = Path.cwd()
client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))
MODEL = os.environ["MODEL_ID"]

SYSTEM = f"You are a coding agent at {WORKDIR}. Use tools to solve tasks. Act, don't explain."
  • 新增 WORKDIR 常量作为工作区根目录,是路径沙箱的基础
  • 系统提示从 "Use bash" 改成 "Use tools",明确支持多工具

2. 路径安全沙箱 ⭐ 核心安全机制

python 复制代码
def safe_path(p: str) -> Path:
    path = (WORKDIR / p).resolve()
    if not path.is_relative_to(WORKDIR):
        raise ValueError(f"Path escapes workspace: {p}")
    return path

功能:防止智能体访问工作目录之外的文件

  • 将输入路径解析为绝对路径
  • 校验路径是否在 WORKDIR 之下
  • 如果路径逃逸(比如 ../etc/passwd)直接抛出错误
  • 所有文件操作工具都会先调用这个函数做安全校验

3. 工具实现

提供了4个工具,每个工具都是独立的处理函数:

(1)bash 工具

和基础实现基本一致,只是将 cwd 固定为 WORKDIR,提升安全性。

(2)read_file 工具
python 复制代码
def run_read(path: str, limit: int = None) -> str:
    try:
        text = safe_path(path).read_text()
        lines = text.splitlines()
        if limit and limit < len(lines):
            lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
        return "\n".join(lines)[:50000]
    except Exception as e:
        return f"Error: {e}"

功能:安全读取文件内容

  • 支持 limit 参数限制读取行数,避免大文件撑爆上下文
  • 超过行数限制时会提示剩余行数,更友好
  • 统一异常处理,错误信息直接返回给模型
(3)write_file 工具
python 复制代码
def run_write(path: str, content: str) -> str:
    try:
        fp = safe_path(path)
        fp.parent.mkdir(parents=True, exist_ok=True)
        fp.write_text(content)
        return f"Wrote {len(content)} bytes to {path}"
    except Exception as e:
        return f"Error: {e}"

功能:写入文件

  • 自动创建不存在的父目录
  • 返回写入结果给模型确认
(4)edit_file 工具
python 复制代码
def run_edit(path: str, old_text: str, new_text: str) -> str:
    try:
        fp = safe_path(path)
        content = fp.read_text()
        if old_text not in content:
            return f"Error: Text not found in {path}"
        fp.write_text(content.replace(old_text, new_text, 1))
        return f"Edited {path}"
    except Exception as e:
        return f"Error: {e}"

功能:精确替换文件内容

  • 只替换第一次出现的匹配文本,避免误修改
  • 匹配不到内容时明确返回错误,模型可以调整匹配文本重试

4. 工具分发字典 ⭐ 核心扩展机制

python 复制代码
TOOL_HANDLERS = {
    "bash":       lambda **kw: run_bash(kw["command"]),
    "read_file":  lambda **kw: run_read(kw["path"], kw.get("limit")),
    "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
    "edit_file":  lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
}
  • 键是工具名,值是对应的处理函数
  • 用 lambda 统一参数传递,适配不同工具的参数差异
  • 加新工具只需要在这里加一行映射,不需要修改其他代码

5. 工具定义数组

python 复制代码
TOOLS = [
    {"name": "bash", "description": "Run a shell command.",
     "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
    {"name": "read_file", "description": "Read file contents.",
     "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
    {"name": "write_file", "description": "Write content to file.",
     "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
    {"name": "edit_file", "description": "Replace exact text in file.",
     "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
]
  • 给 LLM 提供的工具描述,遵循 Anthropic 函数调用格式
  • 每个工具包含名称、功能描述、参数结构(JSON Schema)
  • LLM 根据这些描述决定调用什么工具、传递什么参数

6. 核心智能体循环

和基础智能体循环几乎完全一致,只有工具调用部分做了修改:

python 复制代码
def agent_loop(messages: list):
    while True:
        response = client.messages.create(
            model=MODEL, system=SYSTEM, messages=messages,
            tools=TOOLS, max_tokens=8000,
        )
        messages.append({"role": "assistant", "content": response.content})
        if response.stop_reason != "tool_use":
            return
        results = []
        for block in response.content:
            if block.type == "tool_use":
                # 从分发字典取处理函数,替代硬编码调用
                handler = TOOL_HANDLERS.get(block.name)
                output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
                print(f"> {block.name}: {output[:200]}")
                results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
        messages.append({"role": "user", "content": results})

关键变化

  • 去掉了硬编码的 run_bash 调用
  • 改为从 TOOL_HANDLERS 字典中根据工具名查找处理函数
  • 支持未知工具的错误提示,鲁棒性更强
  • 核心循环逻辑完全不变,实现了对扩展开放、对修改关闭的开闭原则

7. 交互式主循环

和基础实现完全一致,仅提示符做了修改。


基础版对比

组件 基础版(s01) 多工具版(s02)
工具数量 1个(仅 bash) 4个(bash, read, write, edit)
工具调用方式 硬编码 bash 调用 TOOL_HANDLERS 分发字典
路径安全 safe_path() 沙箱防护
Agent 循环 固定 完全不变

设计优势

  1. 扩展性极强:加新工具只需要两步:

    • 写工具处理函数
    • TOOL_HANDLERSTOOLS 中注册
    • 核心循环不需要动一行代码
  2. 安全性提升

    • 路径沙箱防止文件越权访问
    • 专用工具比 bash 更可控,减少安全风险
  3. 稳定性更高

    • 核心循环经过验证不需要修改,减少出问题的概率
    • 工具之间独立,修改一个工具不影响其他功能

使用示例

启动后可以测试这些指令:

bash 复制代码
python agents/s02_tool_use.py
s02 >> Read the file requirements.txt
> read_file: [文件内容]
s02 >> Create a file called greet.py with a greet(name) function
> write_file: Wrote 50 bytes to greet.py
s02 >> Edit greet.py to add a docstring to the function
> edit_file: Edited greet.py
s02 >> Read greet.py to verify the edit worked
> read_file: [修改后的文件内容]

核心收获

这个版本建立了 Harness 层最核心的设计范式:

循环永远不变,变化的只有工具和分发规则

后续的所有功能扩展都是在这个基础上扩展更多工具和机制,核心循环始终保持稳定。这种设计思想也贯穿了整个 Claude Code 的架构。

本内容参考开源项目 learn-claude-code

相关推荐
环境工程笔记2 分钟前
浏览器自动化跑成功了,为什么结果还是不对?
前端
东风破_4 分钟前
一文搞懂 JavaScript 变量声明:var、let、const 到底有什么区别?
前端·javascript
问心无愧05137 分钟前
ctf show web入门261
android·前端·笔记
触底反弹8 分钟前
你真的理解 JavaScript 变量提升(Hoisting)吗?从 V8 引擎编译原理深入剖析
前端·面试
蜡台21 分钟前
Vue2 使用 typescript 教程
前端·vue.js·typescript
光影少年33 分钟前
Redux Toolkit 用法、解决原生Redux 冗余问题
开发语言·前端·javascript·react.js·中间件·前端框架·ecmascript
云水一下41 分钟前
JavaScript 从零基础到精通系列:DOM 操作与事件驱动编程
前端·javascript
ZC跨境爬虫1 小时前
跟着 MDN 学CSS day_32:(Web字体深度解析与实践指南)
前端·javascript·css·ui·html
砍材农夫1 小时前
物联网 基于netty核心实战-安全tls
java·开发语言·前端·物联网·安全