【智能体工具使用实战07】让Agent给自己造工具

第7章 让Agent给自己造工具

本章你将学到

  • 让Agent在发现能力缺口时,自动生成新的工具代码
  • Agent调用自己的 write_file 工具保存代码,然后注册到 ToolManager
  • 理解"Agent自我扩展"的完整流程和安全边界
  • 亲眼见证Agent从"工具使用者"变成"能力扩展者"

本章你将产出 :一个具备自建工具能力的Agent,以及至少一个由Agent自己创建并成功调用的新工具

全部章节 :收录在专栏《AI应用工程化实战教程》之【智能体工具使用实战】


7.1 一个尴尬的时刻

第5章结束时,你的Agent完成了数据分析闭环:读文件 → 执行计算 → 写报告。第6章你给它搭建了评测体系,知道它哪里做得好、哪里容易出错。

现在设想这样一个场景。

你帮老师处理一份学生成绩数据。除了常规的统计------平均分、最高分、排名------老师还提了一个特别的要求:"帮我找出成绩波动最大的学生,也就是四科成绩标准差最大的那个。"

你把任务交给Agent:

复制代码
请读取 scores.csv,找出四科成绩波动最大的学生(标准差最大),生成一份简短报告保存为 volatility_report.md。

Agent开始工作。它调用了 read_file,拿到了数据。然后它调用 execute_python,在代码里写了:

python 复制代码
import pandas as pd
df = pd.read_csv('scores.csv')
# 计算每个学生的标准差
df['std'] = df[['高数', '线代', 'Python', '英语']].std(axis=1)
# 找到标准差最大的学生
max_std_row = df.loc[df['std'].idxmax()]
print(f"成绩波动最大的学生:{max_std_row['姓名']},标准差:{max_std_row['std']:.2f}")

沙盒执行成功。Agent拿到了结果,然后调用 write_file 生成了报告。

一切顺利。但这里有一个值得注意的细节:Agent在 execute_python 中写的代码,是一次性的。 它写完、执行完、沙盒销毁临时文件,这段代码就消失了。下次你再让它做类似的任务(比如"找出另一个班级成绩波动最大的学生"),它需要重新生成一遍这段代码。

如果Agent能把这段"计算波动率"的逻辑持久化为一个工具,情况就不同了。第一次遇到需求时它创建工具,以后每次需要时直接调用。工具箱会随着使用不断增长,Agent的能力边界也会随之扩展。

这就是本章要做的事:让Agent给自己造工具。


7.2 Agent自我扩展的完整流程

回顾一下你的项目架构。Agent有三层能力:

层次 当前状态 由谁管理
工具调用循环 agent.py 中的 run_agent 函数 固定逻辑,不需要改动
工具管理器 tool_manager.py 中的 ToolManager 支持动态注册新工具(register 方法)
工具箱 三个预置工具 + 可能的自建工具 初始有三个,可以不断增加

关键洞察是:ToolManager的 register 方法可以在运行时被调用。 不需要修改 agent.py 的源代码,不需要重启程序。只要Agent能生成正确的工具代码,它就可以通过以下流程把它变成工具箱的永久成员:

复制代码
Agent 发现能力缺口
    │
    ▼
Agent 生成新工具的 Python 代码
    │
    ▼
Agent 调用 write_file 保存代码到 tools/ 目录
    │
    ▼
Agent 在自己的运行环境中 import 新代码
    │
    ▼
Agent 调用 tool_manager.register(工具定义, 实现函数)
    │
    ▼
新工具永久加入工具箱,后续任务可以直接使用

这六个步骤中,前三步由Agent通过调用自身工具完成(生成代码是它的推理能力,保存文件是调用 write_file),后两步需要 agent.py 提供一个支持动态注册的接口。


7.3 设计"造工具"的触发机制

Agent不会无缘无故开始造工具。它需要知道自己有这个能力,以及什么时候应该用。

我们在系统提示词中增加一段"造工具"指令。在Trae对话面板中输入:

复制代码
请修改 agent.py 的系统提示词,在工具使用说明之后增加以下内容:

## 自建工具能力

当现有工具无法直接满足用户需求时,你可以创建新的工具来扩展自己的能力。

### 何时创建工具
- 某个计算逻辑需要反复使用(如"计算波动率""计算同比增长率")
- 现有工具需要组合多次才能完成,而封装为一个工具更高效
- 用户明确要求"以后这种任务都帮我自动处理"

### 如何创建工具
1. 写一个 Python 函数,包含:
   - 清晰的函数名(calc_开头、check_开头、convert_开头)
   - 完整的 docstring 说明功能、参数、返回值
   - 类型注解
   - 异常处理
2. 调用 write_file,将函数代码保存到 tools/ 目录下(文件名与函数名相同,如 tools/calc_volatility.py)
3. 告诉用户你创建了新工具,说明它的名称和功能

### 约束
- 新工具只能使用沙盒允许的模块(pandas, numpy, math, statistics, json, csv, collections, itertools, datetime, re, string, decimal, fractions)
- 新工具的函数签名必须简单明确
- 不要创建与现有工具功能重复的工具
- 单次会话最多创建3个新工具
- 创建工具前,简要向用户说明你打算创建什么工具、为什么需要它
修改 run_agent 函数以支持动态注册

有了"造工具"的指令,Agent理论上会尝试调用 write_file 来保存工具代码。但这还不够------保存完代码后,Agent需要能真正把这个新工具注册到 ToolManager 中。

目前 run_agent 函数的流程是:初始化时注册三个预置工具,然后进入工具调用循环。ToolManager是在函数外部初始化的(或者函数内部初始化但无法被Agent的工具调用触及)。

我们需要增加一个特殊的工具:register_tool。它不是给用户任务用的,而是给Agent自己用的------当Agent写好了工具代码并保存后,它调用 register_tool 来动态注册。

在Trae对话面板中输入:

复制代码
请修改 agent.py,增加一个特殊工具 register_tool。

## 工具定义
- 名称:register_tool
- 用途描述:动态注册一个新的工具到工具箱中。当你创建了一个新工具的Python文件后,调用此工具将其注册。注册后,该工具将在后续任务中可用。
- 参数:
  - tool_name(必填,字符串):工具名称,应与Python文件名(不含.py)一致,如 "calc_volatility"
  - tool_file_path(必填,字符串):工具代码文件路径,如 "tools/calc_volatility.py"
  - tool_description(必填,字符串):工具的功能描述,用于让AI理解何时使用该工具
  - function_name(必填,字符串):文件中要注册的函数名
  - parameters_schema(必填,字符串):参数的JSON Schema描述(简化版,只描述参数名、类型、说明)

## 工具实现
- 使用 importlib 动态导入模块:
  import importlib.util
  spec = importlib.util.spec_from_file_location(tool_name, tool_file_path)
  module = importlib.util.module_from_spec(spec)
  spec.loader.exec_module(module)
  func = getattr(module, function_name)
- 构建工具定义字典(type: "function", function: {name, description, parameters})
- 调用 tool_manager.register(tool_def, func) 注册
- 返回:成功时返回 "工具 [tool_name] 注册成功";失败时返回错误信息

## 安全约束
- tool_file_path 必须以 "tools/" 开头(只允许从 tools 目录加载)
- 注册前打印工具代码供人工审查(在日志中输出)
- 不覆盖已存在的同名工具

## 注意
register_tool 本身也需要加入 tools 列表中(它也是工具),但不需要加入初始的三个工具中------
在创建 ToolManager 之后、进入循环之前,单独注册它。
审查 register_tool 的实现

打开 agent.py,检查以下要点:

工具定义检查

  • register_tool 的描述中是否清晰说明了它的用途和约束?
  • parameters_schema 参数是否说明了格式要求?

工具实现检查

  • 是否使用了 importlib.util 进行动态导入?
  • 是否有路径安全检查(必须 tools/ 开头)?
  • 是否构建了正确的工具定义字典(符合DeepSeek API格式)?
  • 是否调用了 tool_manager.register
  • 是否处理了各种异常(文件不存在、函数名错误、重复注册)?

安全约束检查

  • 注册前是否打印了工具代码?
  • 是否阻止了 tools/ 以外的路径?

7.5 准备 tools 目录

在项目根目录下创建一个 tools/ 文件夹。这是Agent自建工具的存放位置。

在Trae终端中:

bash 复制代码
mkdir tools

tools/ 目录下创建一个 __init__.py 文件(空文件即可),让它成为一个Python包:

bash 复制代码
touch tools/__init__.py

7.6 实例演示:Agent给自己造一个"波动率计算"工具

现在所有基础设施就绪。我们来实际运行一次,看看Agent如何给自己造工具。

7.6.1 发起任务

修改 agent.py 底部的测试入口:

python 复制代码
if __name__ == "__main__":
    request = """
我需要你帮我分析 scores.csv 中的数据。具体任务是:

找出四科成绩(高数、线代、Python、英语)波动最大的学生,也就是标准差最大的那个。

但注意:我以后可能还会反复让你做类似的计算(不同班级、不同科目)。如果现有工具无法直接完成,你可以考虑创建一个可复用的工具。

请完成分析,并将结果保存到 volatility_report.md。
"""
    output, log = run_agent(request)
    print("\n" + "=" * 60)
    print("执行日志:")
    print(log)
7.6.2 运行并观察

在Trae终端中:

bash 复制代码
python agent.py

观察终端日志。你期望看到的执行轨迹可能是这样的:

复制代码
[用户请求] 我需要你帮我分析 scores.csv...

[第1轮] 模型调用工具: read_file({"path": "scores.csv"})
[工具返回] 学号,姓名,高数,线代,Python,英语...

[第2轮] 模型调用工具: execute_python({"code": "import pandas..."})
[工具返回] 成绩波动最大的学生:孙七,标准差:12.36

[第3轮] 模型:我已完成分析。但我注意到"计算成绩波动"是一个可复用的需求。
        我建议创建一个 calc_volatility 工具,以后可以直接调用。
        现在我先完成当前任务。

[第3轮] 模型调用工具: write_file({"path": "volatility_report.md", ...})
[工具返回] 成功写入文件

[第4轮] 模型:报告已保存。接下来我为"计算成绩波动"创建一个可复用工具。

[第4轮] 模型调用工具: write_file({"path": "tools/calc_volatility.py", "content": "def calc_volatility(...)..."})
[工具返回] 成功写入文件

[第5轮] 模型调用工具: register_tool({"tool_name": "calc_volatility", ...})
[工具返回] 工具 calc_volatility 注册成功

[第6轮] 模型最终回答: 分析完成!波动最大的学生是孙七(标准差12.36)。
         报告已保存到 volatility_report.md。
         我还为你创建了一个 calc_volatility 工具,以后你可以直接让我用它来分析任何成绩数据。

如果Agent没有主动创建工具,而是直接完成了任务就结束------这很正常。不是每次任务都需要造工具。你可以明确要求它:

复制代码
在分析完成后,请为"计算成绩波动"这个功能创建一个可复用的工具。
7.6.3 验证新工具是否真的被注册了

修改测试入口,在任务完成后列出所有工具:

python 复制代码
if __name__ == "__main__":
    request = """..."""
    output, log = run_agent(request)
    
    print("\n" + "=" * 60)
    print("当前工具箱:")
    for name in tool_manager.list_tools():
        print(f"  - {name}")

再次运行。你应该看到 calc_volatility 出现在工具列表中。

7.6.4 测试新工具是否真的能用

再写一段测试,直接使用新工具:

python 复制代码
if __name__ == "__main__":
    # 第一次任务:创建工具(如果还没创建)
    request1 = "请为计算成绩波动创建一个可复用的工具 calc_volatility"
    run_agent(request1)
    
    # 第二次任务:使用新工具
    request2 = """
    请读取 scores.csv,使用 calc_volatility 工具计算每个学生的成绩波动,
    然后调用 write_file 将结果保存为 all_volatility.csv。
    """
    output, log = run_agent(request2)
    print(output)

如果一切正常,Agent在第二次任务中会直接调用 calc_volatility 工具,而不是重新写一段 execute_python 的代码。它用上了自己造的工具。


7.7 工具箱的持续生长

一旦Agent有了自建工具的能力,一个有趣的长期效应就出现了:工具箱会随着使用不断生长。

你可以想象这样一个演化路径:

  • 第1次任务 :Agent创建了 calc_volatility
  • 第3次任务 :Agent发现需要做数据归一化,创建了 normalize_data
  • 第5次任务 :Agent发现每次都要读取CSV然后做描述性统计,创建了 quick_summary
  • 第10次任务:Agent已经积累了6个自建工具,新任务中80%的工具调用都是自建工具

这就是"AI自我扩展"的雏形。Agent不再是一个固定功能的程序,而是一个能力可以随时间积累的系统

当然,在教学中,我们的规模不大。但这个概念本身,是本科生理解"AI Agent的长期价值"的关键。


7.8 安全边界:不要让Agent的自我扩展失控

工具自建能力给了Agent很大的自主权。必须再次强调安全边界。

第一,所有新工具仍在沙盒内。 Agent自建的工具,内部逻辑如果涉及代码执行,仍然需要通过 execute_python,仍然受沙盒的模块白名单和超时限制。自建工具不能绕过沙盒。

第二,注册前必须打印代码。 register_tool 的实现中有一个关键步骤:在真正注册之前,把工具代码打印到终端日志中。这是给人看的------你可以随时按 Ctrl+C 中断执行。

第三,路径限制。 register_tool 只允许加载 tools/ 目录下的文件。Agent不能通过路径穿越加载系统文件。

第四,数量限制。 系统提示词中明确写了"单次会话最多创建3个新工具"。这防止了Agent陷入"疯狂造工具"的死循环。

第五,不覆盖已有工具。 register_tool 的代码检查工具是否已存在。Agent不能替换 read_fileexecute_python 这些基础工具。


7.9 本章小结

  • Agent从"工具使用者"跃迁到"能力扩展者"。它不再受限于你预先提供的工具集。
  • 自我扩展的完整流程 :发现缺口 → 生成代码 → 写文件 → 动态导入 → 注册 → 调用。ToolManager的 register 方法支持运行时注册是关键。
  • register_tool 是元工具------它不直接完成任务,而是扩展完成任务的能力。
  • 安全边界依然在:路径限制、数量限制、代码打印审查、沙盒约束。Agent的能力扩展是有护栏的。
  • 工具箱会持续生长 。每个自建工具都留在 tools/ 目录下,下次启动Agent时可以被重新加载。

到现在为止,你的Agent已经拥有了三个预置工具,以及至少一个由它自己创建的工具。它读取数据、执行计算、保存报告、扩展自身------一个真正能"干活"的AI Agent的骨架已经完整。

下一章,你将运用第二部全部所学,完成一个结业实战项目:代码仓库健康度分析Agent。它将扫描项目文件夹,分析代码质量,生成报告------并且在这个过程中可能还会自己创建新工具。


课后练习

  1. 运行7.6节的演示,确认Agent成功创建了 calc_volatility 工具。打开 tools/calc_volatility.py,阅读Agent生成的代码。它的代码质量如何?有没有遵循你的指令(类型注解、docstring、异常处理)?
  2. 要求Agent创建第二个工具:check_pass_fail------输入一个学生的各科成绩,返回该学生是否需要补考(任何一科低于60分即为需要补考)。观察Agent是否能基于第一个工具的经验,更高效地完成创建。
  3. 关掉终端,重新启动Python,重新运行 agent.py。Agent还能调用之前创建的工具吗?如果不能,你觉得需要在代码中增加什么功能(提示:在初始化时扫描 tools/ 目录)?
  4. (进阶)修改 register_tool 的实现,增加一个功能:注册新工具时,自动将工具信息追加到一个 tools/registry.json 文件中。下次Agent启动时,从 registry.json 中读取所有自建工具并自动注册。

相关推荐
冰^1 小时前
AI CC Switch 解决了什么?
人工智能·gpt·网络协议·chatgpt·github·aigc
Omics Pro1 小时前
中医临床决策5款大语言模型,谁主沉浮?
数据库·人工智能·机器学习·语言模型·自然语言处理·chatgpt
有来有去95271 小时前
【训推框架】Vime-大规模 LLM/VLM 强化学习训练框架
人工智能·深度学习·语言模型·gpu算力·vllm
卡卡西Sensei1 小时前
2026华为HDC AI 编程核心成果总结
人工智能·华为·hdc
2401_885665191 小时前
从零搭建CNN到迁移学习:以食物分类为例深入理解PyTorch图像分类实战
人工智能·pytorch·深度学习·分类·cnn·迁移学习
wen_zhufeng1 小时前
AudioX\-Turbo:面向通用音频生成的高效多模态统一框架
人工智能·算法·音视频
悟空码字1 小时前
把 Claude Code 变成你的架构顾问:如何用“隐式重构模式”自动消除代码坏味道
ai·大模型·agent·智能体·claude code
IT新视界1 小时前
星环科技发布XClaw:全能桌面智能体,开启轻量安全的AI助手新时代
人工智能·科技·安全
knight_9___1 小时前
AI Agent 是什么?
人工智能·python·agent·rag·mcp