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

相关推荐
qzhqbb2 小时前
Web 服务器(Nginx、Apache)
服务器·前端·nginx
天若有情6732 小时前
前端进阶必看:吃透这些高阶知识,告别CRUD,迈向高级前端工程师
前端·状态模式
tobebetter95272 小时前
WSL2 + Windows + remote Chrome CDP openclaw 浏览器自动化
chrome·windows·自动化
coderYYY3 小时前
git push报错Authentication failed for ‘xxx’也不会弹要求输入用户名密码的最终解决方法
前端·git·gitee·github
l1t3 小时前
QWen 3.5plus总结的总结基准测试结果的正确方法
前端·数据库
kyriewen113 小时前
为什么我的代码在测试环境跑得好好的,一到用户电脑就崩?原来凶手躲在地址栏旁边
开发语言·前端·javascript·chrome·ecmascript·html5
小北方城市网4 小时前
JavaScript 实战 —— 实现一个简易的 TodoList(适合前端入门 / 进阶)
开发语言·前端·javascript
是上好佳佳佳呀4 小时前
【前端(二)】CSS 知识梳理:从编写位置到选择器优先级
前端·css
倾颜4 小时前
我是怎么把单 Tool Calling 升级成多 Tool Runtime 的
前端·后端·langchain