第5章 构建“数据分析助手Agent”完整工具箱本章你将学到为Agent添加第三个基础工具write_file实现分析结果的持久化设计一个ToolManager类统一管理工具的注册、查询和执行用Trae生成完整的工具管理模块理解关注点分离的设计原则进行集成测试Agent读取CSV → 执行pandas计算 → 写入分析报告理解“工具箱”为什么比“工具列表”更适合持续扩展本章你将产出一个包含三个基础工具和ToolManager的完整Agent脚本以及一份由Agent自动生成的analysis_report.md“”全部章节**收录在专栏《AI应用工程化实战教程》之【智能体工具使用实战】5.1 从两个工具到完整闭环第4章结束时你的Agent有两只手read_file能读取数据execute_python能执行计算。它终于可以完成第1章那个任务了——读取scores.csv计算高数平均分告诉你结果。但这里有一个尴尬分析完了结果只在终端里。你让Agent分析成绩单。它读了文件用pandas算出了各科平均分、最高分最低分、需要补考的学生名单。然后它把结果输出在对话里。你看着屏幕上的分析觉得很有用想保存下来发给班长——然后你手动复制、粘贴、保存。Agent做了80%的工作最后20%还是落在了你手上。一个真正有用的数据分析助手应该能帮你把分析结果直接保存为文件。你只需要告诉它“分析scores.csv生成报告”然后去文件夹里找analysis_report.md。这才是完整的自动化。我们需要第三个工具write_file。有了它Agent的工作流就形成了完整闭环用户请求 → read_file(数据) → execute_python(计算) → write_file(报告) → 完成5.2 工具3write_file5.2.1 设计思路write_file的功能是接收一个文件路径和一段内容把内容写入文件。听起来简单但它涉及一个重要的安全考量不能让Agent写入任意路径。如果Agent能写入~/.bashrc或C:\Windows\System32\它就可以修改你的系统配置。我们需要在工具实现中加入路径安全校验只允许写入项目目录下的文件或用相对路径禁止写入系统目录禁止覆盖特定的关键文件对于教学场景我们的约束可以适度放宽只允许写入相对路径不能以/或C:\开头的绝对路径且禁止路径中包含..防止目录穿越。5.2.2 用Trae生成 write_file 工具在Trae的AI对话面板中输入在项目>5.2.3 审查生成的代码打开agent.py检查以下要点工具定义检查description中是否说明了路径限制只能相对路径parameters中path和content是否都是必填工具实现检查是否有路径校验逻辑拒绝绝对路径、拒绝..是否创建了目标目录os.makedirs是否用try...except包裹了文件操作是否返回了统一的字符串结果成功或错误信息TOOL_MAP 检查是否新增了write_file: write_file的映射系统提示词检查是否增加了write_file的使用说明5.3 工具多了需要一个管理器现在你的Agent有三个工具read_file、execute_python、write_file。它们各自定义在一个大的tools列表里各自实现散落在脚本中各自通过TOOL_MAP字典来映射。三个工具还好。但想想后面的发展Agent可能自建工具工具箱会不断增长。如果每个工具都手动管理agent.py会变得越来越臃肿工具定义、工具实现、工具注册的逻辑会纠缠在一起。我们需要一个ToolManager——一个专门管理工具的类。它的职责很简单注册工具把工具定义和实现函数绑定在一起查询工具列表给Agent用的工具定义JSON数组执行工具根据名称调用对应的实现函数列出所有工具给人看的工具清单有了 ToolManageragent.py的工具调用循环就不再需要知道具体有哪些工具。它只需要# 之前紧耦合tools[read_file_tool,execute_python_tool,write_file_tool]TOOL_MAP{read_file:read_file,execute_python:execute_python,write_file:write_file}# 之后通过 ToolManager 解耦tool_managerToolManager()tool_manager.register(read_file_tool,read_file_func)tool_manager.register(execute_python_tool,execute_python_func)tool_manager.register(write_file_tool,write_file_func)toolstool_manager.get_tool_definitions()resulttool_manager.execute(func_name,arguments)这是软件工程中经典的关注点分离原则工具调用循环负责“调API、解析tool_calls、循环”ToolManager负责“管工具”。两者互不干扰各自可以独立修改和测试。5.4 用Trae生成 ToolManager 类在Trae的AI对话面板中输入在项目>审查生成的 ToolManager打开tool_manager.py检查register是否同时存储了工具定义和实现函数get_tool_definitions返回的格式是否与DeepSeek API要求的tools参数格式一致execute是否包含了异常捕获execute中函数调用是否使用**arguments解包参数测试代码是否覆盖了所有方法如果发现execute方法没有处理工具不存在的情况要求Trae修正tool_manager.py 的 execute 方法中如果传入的 name 不存在于注册表中应该返回 错误未知工具 [name] 而不是抛出 KeyError。5.5 用 ToolManager 重构 agent.py现在有了 ToolManager我们需要重构agent.py把分散的工具管理逻辑替换为统一的 ToolManager 调用。在Trae对话面板中输入请修改 agent.py使用 tool_manager.ToolManager 来管理工具。 ## 修改要求 1. 在文件顶部导入 ToolManager from tool_manager import ToolManager 2. 初始化 ToolManager tool_manager ToolManager() 3. 注册所有工具 - 将 read_file、execute_python、write_file 三个工具通过 tool_manager.register() 注册 - 每个工具的注册包括工具定义JSON Schema 实现函数 4. 修改工具调用循环 - tools tool_manager.get_tool_definitions() - 在执行工具时使用 tool_manager.execute(func_name, arguments) - 删除旧的 TOOL_MAP 字典 5. 保留所有工具的实现函数和工具定义只是改变它们的注册和管理方式 6. 代码风格保持清晰在修改处加注释说明审查重构后的 agent.py打开重构后的agent.py检查导入和初始化是否导入了ToolManager是否正确初始化了tool_manager对象工具注册三个工具是否都注册了每个register调用是否传入了工具定义和实现函数两个参数工具调用循环tools tool_manager.get_tool_definitions()是否正确替换了原来的硬编码工具列表执行工具的地方是否改为tool_manager.execute(func_name, arguments)旧的TOOL_MAP是否已删除功能完整性所有工具的功能没有变化只是管理方式变了如果Trae在重构时不小心删除了某个工具的实现函数在对话面板中指出来要求恢复。5.6 集成测试完整的数据分析闭环现在你的Agent拥有了完整的工具箱和统一的管理器。是时候进行一次完整的集成测试了。5.6.1 准备测试数据确保scores.csv在项目根目录。我们在第3章已经创建了它。如果你还没有让Trae生成一份请确保 scores.csv 存在于项目根目录包含至少20行模拟学生成绩数据。列学号、姓名、高数、线代、Python、英语。5.6.2 设计测试请求修改agent.py底部if __name__ __main__:中的测试代码为if__name____main__:request 请帮我完成以下数据分析任务 1. 读取 scores.csv 文件 2. 计算以下指标 - 各科平均分、最高分、最低分 - 每门课的不及格人数低于60分 - 总分最高的3名学生 - 总分最低的3名学生 3. 将以上分析结果整理成一份清晰的Markdown报告保存为 analysis_report.md 请依次完成每一步。 run_agent(request)5.6.3 运行测试在Trae终端中执行python agent.py观察终端输出。你期望看到的工具调用顺序是第1轮Agent 调用 read_file(scores.csv) 第2轮Agent 调用 execute_python(import pandas as pd\ndf pd.read_csv(scores.csv)...) → 计算各科平均分、最高分最低分、不及格人数、总分排名 第3轮Agent 调用 write_file(analysis_report.md, 报告内容...) 第4轮Agent 整合结果输出总结打开项目目录你会看到一个analysis_report.md文件。它的内容应该类似# 成绩分析报告 ## 数据概况 - 学生总数20人 - 科目高数、线代、Python、英语 ## 各科统计 | 科目 | 平均分 | 最高分 | 最低分 | 不及格人数 | |------|--------|--------|--------|------------| | 高数 | 72.3 | 95 | 43 | 3 | | 线代 | 74.1 | 90 | 55 | 2 | | Python | 77.8 | 95 | 58 | 1 | | 英语 | 78.5 | 93 | 65 | 0 | ## 总分排名 Top 3 1. 王五 - 366分 2. 郑十 - 364分 3. 周八 - 356分 ## 总分排名 Bottom 3 ...这是你的Agent第一次完成了一个完整的任务闭环读数据 → 算结果 → 写报告。不是你手动执行的是Agent自己判断每一步该用什么工具、传什么参数、如何处理中间结果最终生成了一份可以直接交付的报告。5.7 理解“工具箱”的设计价值现在停下来回顾一下你刚刚构建的系统。它和你在第2章写的minimal_agent.py有什么本质不同minimal_agent.py第2章agent.py本章工具数量1个calculator3个read_file, execute_python, write_file工具管理手动列表 手动映射字典ToolManager 统一管理新增工具成本要改四处工具定义、实现函数、TOOL_MAP、tools列表只需一处tool_manager.register(...)安全性无沙盒execute_python 受沙盒保护闭环能力只能计算并输出在对话里读取→计算→持久化全流程自动化ToolManager 的价值在三个工具时还不明显。但想象下一章的场景Agent自己创建了新工具需要动态注册到工具箱里。如果没有 ToolManager你需要手动修改agent.py的代码。有了 ToolManagerAgent只需要调用tool_manager.register(new_tool_def, new_tool_func)。这就是设计模式的力量——它不是为了解决今天的问题而是为了让明天的扩展不需要推倒重来。5.8 首次Git提交本章节点本章完成了Agent完整工具箱的构建。这是第二部的一个里程碑节点应该做一次Git提交。在Trae终端中执行gitinit# 如果还没有初始化gitaddagent.py sandbox.py test_sandbox.py tool_manager.py scores.csv analysis_report.md .env .gitignoregitcommit-mv1.0: 数据分析助手Agent完整工具箱 — read_file execute_python(沙盒) write_file ToolManager用git log确认提交成功。5.9 本章小结三个工具形成了完整闭环read_file读取数据 →execute_python执行计算 →write_file保存结果。Agent从“能分析”变成了“能交付”。write_file需要路径安全校验拒绝绝对路径和目录穿越防止Agent写入系统关键位置。ToolManager 统一管理工具注册、查询、执行、列表。关注点分离——工具调用循环不关心有哪些工具ToolManager 不关心API怎么调用。集成测试验证了闭环Agent成功完成了一次从读取CSV到生成Markdown报告的完整任务。设计模式为扩展铺路新增工具只需要register不需要改动循环逻辑。这为第7章“Agent自建工具”打好了地基。下一章我们将面对第二部最关键的工程挑战如何评测一个工具增强型Agent它的行为不只是“输出一段文本”还包含“选择工具→传参数→理解返回结果”。我们将扩展第一部的评测框架新增工具调用审计维度。课后练习运行集成测试观察终端日志中每一轮API调用的细节。Agent在哪个步骤调用了哪个工具参数是什么它有没有在不需要的时候也调用工具比如在最后总结时还在调工具打开analysis_report.md检查报告的质量。有没有数据错误格式是否清晰如果报告质量不理想你觉得是哪个环节的问题——是Agent没有准确理解数据还是生成报告时没有组织好结构修改write_file的路径校验逻辑增加一个功能如果目标文件已经存在不覆盖而是返回错误信息“文件已存在请使用不同的文件名”。测试Agent在这种情况下会如何反应。进阶在ToolManager中增加一个unregister(name)方法允许移除一个已注册的工具。然后写一段测试注册三个工具移除其中一个确认get_tool_definitions只返回两个。