AI Agent 项目学习笔记(十):文件操作、终端执行与 PDF 生成工具

1. 本期目标

上一篇文章分析了 ai_agent 项目中的三个联网工具:

复制代码
WebSearchTool
WebScrapingTool
ResourceDownloadTool

它们主要解决的是:

复制代码
智能体如何从外部网络获取信息?

这一期继续分析工具模块中的另一类能力:

复制代码
本地执行与结果交付能力

重点分析三个工具:

复制代码
FileOperationTool
TerminalOperationTool
PDFGenerationTool

这三个工具让智能体不只是"查资料"和"回答问题",还可以进一步完成:

复制代码
读写本地文件
执行终端命令
生成 PDF 文件

Spring AI 官方文档中也说明,Tool Calling 主要用于两类场景:一类是信息检索,另一类是执行动作;模型只负责请求工具调用并提供参数,真实工具执行由应用程序完成。这个项目中的文件读写、终端命令和 PDF 生成,正属于"执行动作"这一类工具能力。(Home)


2. 为什么要分析这三个工具?

前面讲的联网工具主要用于:

复制代码
找信息
读网页
下载资源

而本期的三个工具主要用于:

复制代码
处理本地数据
执行本地操作
生成最终交付物

可以这样理解:

复制代码
FileOperationTool:
让智能体能把内容保存下来,或者读取已有文件。

TerminalOperationTool:
让智能体能调用系统命令,完成更底层的本地操作。

PDFGenerationTool:
让智能体能把最终内容整理成 PDF 文件,形成可交付结果。

三者结合起来,就形成了一条从"生成内容"到"保存内容"再到"交付内容"的链路:

复制代码
模型生成文本
    ↓
写入本地文件
    ↓
必要时执行命令处理
    ↓
生成 PDF 报告
    ↓
返回文件路径

这也是 Agent 从"聊天机器人"走向"任务执行助手"的关键一步。


3. 这三个工具在项目中的注册位置

这三个工具和其他工具一样,都在 ToolRegistration 中统一注册。

ToolRegistration 会创建 FileOperationToolTerminalOperationToolPDFGenerationTool 等工具对象,并通过 ToolCallbacks.from(...) 转换成 ToolCallback[],最后提供给 LoveApp 使用。也就是说,模型能够调用这些工具,是因为它们被统一放入了 allTools 这个工具列表中。(GitHub)

整体关系可以写成:

复制代码
FileOperationTool
TerminalOperationTool
PDFGenerationTool
        ↓
ToolRegistration.allTools()
        ↓
ToolCallback[]
        ↓
LoveApp.doChatWithTools()
        ↓
模型可选择调用这些工具

所以,工具类本身只是定义能力;真正让模型看到这些能力的是 ToolRegistration


4. 文件保存根目录:FileConstant

在分析具体工具之前,需要先看一个基础常量:

复制代码
public interface FileConstant {
    String FILE_SAVE_DIR = System.getProperty("user.dir") + "/tmp";
}

项目把所有工具生成或保存的文件统一放到项目根目录下的 tmp 目录中。FileOperationTool 会使用 tmp/fileResourceDownloadTool 会使用 tmp/downloadPDFGenerationTool 会使用 tmp/pdf。这种设计比把文件散落在项目根目录更清晰,也方便后续统一清理临时文件。(GitHub)

可以理解为:

复制代码
项目根目录
    ↓
tmp
    ├─ file
    ├─ download
    └─ pdf

本期重点关注:

复制代码
tmp/file
tmp/pdf

以及终端命令执行工具。


5. FileOperationTool:文件操作工具

FileOperationTool 提供两个核心能力:

复制代码
readFile
writeFile

它内部定义的文件目录是:

复制代码
private final String FILE_DIR = FileConstant.FILE_SAVE_DIR + "/file";

也就是说,所有通过该工具读写的普通文本文件,默认都位于:

复制代码
项目根目录/tmp/file

源码中可以看到,readFile() 使用 Hutool 的 FileUtil.readUtf8String(filePath) 读取 UTF-8 文本,writeFile() 会先创建目录,再使用 FileUtil.writeUtf8String(content, filePath) 写入内容。(GitHub)


6. readFile:读取文件内容

readFile() 的方法定义是:

复制代码
@Tool(description = "Read content from a file")
public String readFile(
        @ToolParam(description = "Name of the file to read") String fileName)

它的输入是:

复制代码
fileName:要读取的文件名

内部流程可以理解为:

复制代码
接收 fileName
    ↓
拼接路径:tmp/file/fileName
    ↓
读取 UTF-8 文本内容
    ↓
返回文件内容

如果读取失败,它会返回:

复制代码
Error reading file: 具体错误信息

这个工具适合让智能体读取之前保存的文本内容,例如计划、摘要、报告草稿等。(GitHub)


7. writeFile:写入文件内容

writeFile() 的方法定义是:

复制代码
@Tool(description = "Write content to a file")
public String writeFile(String fileName, String content)

它有两个输入:

复制代码
fileName:要写入的文件名
content:要写入的内容

内部流程是:

复制代码
接收 fileName 和 content
    ↓
拼接路径:tmp/file/fileName
    ↓
创建 tmp/file 目录
    ↓
以 UTF-8 形式写入文本
    ↓
返回保存路径

写入成功后,工具会返回:

复制代码
File written successfully to: 文件路径

这意味着模型可以先生成一段内容,再调用该工具把内容保存成本地文件。(GitHub)


8. FileOperationTool 的使用场景

这个工具适合处理以下任务:

复制代码
保存用户计划
保存模型生成的建议
保存中间分析结果
读取之前保存的草稿
继续修改某个文件内容

例如用户说:

复制代码
帮我把刚才的约会计划保存成 plan.txt。

模型可以调用:

复制代码
writeFile(fileName = "plan.txt", content = "具体计划内容")

之后用户再说:

复制代码
读取 plan.txt,帮我改得更正式一点。

模型可以调用:

复制代码
readFile(fileName = "plan.txt")

然后基于读取内容继续修改。

所以,FileOperationTool 提供的是一种简单的"本地工作区记忆"。


9. FileOperationTool 的价值

多轮对话记忆主要保存的是聊天上下文。

而文件工具保存的是明确的任务产物。

两者区别是:

复制代码
ChatMemory:
保存对话上下文,服务于模型理解前后文。

FileOperationTool:
保存文件内容,服务于任务结果沉淀和后续处理。

例如:

复制代码
ChatMemory 记住:
用户前面说过想要周末约会计划。

FileOperationTool 保存:
一份完整的周末约会计划文本。

这说明文件工具不是简单的辅助功能,而是让 Agent 有了"工作区"的雏形。


10. FileOperationTool 当前可以改进的地方

当前实现比较适合学习,但正式系统中需要注意几个问题。

第一,fileName 直接拼接到路径中:

复制代码
String filePath = FILE_DIR + "/" + fileName;

如果文件名包含 ../ 这类路径片段,就可能带来路径穿越风险。后续应该限制文件名只允许字母、数字、下划线、短横线和安全扩展名,并检查最终路径仍然位于 tmp/file 目录内。(GitHub)

第二,当前没有文件大小限制。模型如果写入很长内容,可能导致磁盘占用增长。后续可以限制单个文件最大大小,例如 1 MB 或 5 MB。

第三,当前写入会覆盖已有文件。正式系统中可以增加:

复制代码
是否允许覆盖
自动生成版本号
写入前备份
返回文件大小

这样更安全,也更适合真实任务场景。


11. TerminalOperationTool:终端执行工具

TerminalOperationTool 是项目中风险最高、能力也最强的工具。

它提供的方法是:

复制代码
@Tool(description = "Execute a command in the terminal")
public String executeTerminalCommand(String command)

输入是:

复制代码
command:要执行的终端命令

源码中,它使用:

复制代码
new ProcessBuilder("cmd.exe", "/c", command)

启动系统命令。这说明当前实现更偏向 Windows 环境,因为它使用的是 cmd.exe /c。工具会读取命令标准输出,并在进程退出码非 0 时追加失败退出码信息。(GitHub)


12. TerminalOperationTool 的执行流程

这个工具的执行流程可以写成:

复制代码
接收 command
    ↓
构造 ProcessBuilder("cmd.exe", "/c", command)
    ↓
启动进程
    ↓
读取标准输出
    ↓
等待命令结束
    ↓
如果 exitCode != 0,追加错误码
    ↓
返回命令输出

例如用户让智能体查看当前目录,模型可能调用:

复制代码
executeTerminalCommand(command = "dir")

工具执行后返回命令输出。

这类工具让 Agent 具备了真实操作本地环境的能力,但也意味着需要非常严格的安全控制。


13. TerminalOperationTool 的适用场景

学习阶段,它可以用于演示:

复制代码
查看目录
运行简单命令
检查文件是否存在
执行脚本
调用本地程序

例如:

复制代码
查看 tmp 目录下有哪些文件。

模型可能调用:

复制代码
dir tmp

再例如:

复制代码
帮我检查生成的 PDF 文件是否存在。

模型可能调用:

复制代码
dir tmp\pdf

从能力上看,它让智能体从"只能调用特定 Java 工具"扩展为"可以调用系统命令"。这是一种更通用的执行能力。


14. TerminalOperationTool 的高风险点

这个工具必须重点讨论安全问题。

因为当前实现把模型给出的 command 直接交给:

复制代码
cmd.exe /c

执行。也就是说,如果缺少限制,模型可能触发删除文件、访问敏感目录、执行恶意命令等危险操作。(GitHub)

正式系统中不能直接暴露无限制终端工具,至少需要增加:

复制代码
命令白名单
危险命令黑名单
工作目录限制
执行超时
输出长度限制
低权限运行
沙箱隔离
人工确认机制
工具调用审计日志

比较安全的思路是:

复制代码
不让模型直接执行任意 command,
而是提供受控命令工具。

例如,不提供:

复制代码
executeTerminalCommand(command)

而是提供更窄的工具:

复制代码
listFiles(directory)
checkFileExists(fileName)
runPredefinedScript(scriptName)

这样可以减少模型执行任意命令的风险。


15. TerminalOperationTool 的跨平台问题

当前工具写死了:

复制代码
cmd.exe /c

这适合 Windows,但在 Linux 或 macOS 环境下不能直接运行。(GitHub)

如果后续要做跨平台支持,可以根据操作系统判断:

复制代码
Windows:
cmd.exe /c command

Linux / macOS:
/bin/sh -c command

不过,仅仅适配跨平台还不够。终端执行本身是高风险能力,跨平台支持应该和安全控制一起设计。


16. PDFGenerationTool:PDF 生成工具

PDFGenerationTool 用于把模型生成的内容写入 PDF 文件。

它的方法定义是:

复制代码
@Tool(description = "Generate a PDF file with given content", returnDirect = false)
public String generatePDF(String fileName, String content)

它接收两个参数:

复制代码
fileName:生成的 PDF 文件名
content:写入 PDF 的内容

源码中,它会把 PDF 保存到:

复制代码
FileConstant.FILE_SAVE_DIR + "/pdf"

也就是:

复制代码
项目根目录/tmp/pdf

工具内部使用 iText 创建 PdfWriterPdfDocumentDocument,并用 STSongStd-LightUniGB-UCS2-H 创建中文字体,最后把 content 放进一个 Paragraph 写入 PDF。(GitHub)


17. PDFGenerationTool 的执行流程

可以把 PDF 生成流程理解为:

复制代码
接收 fileName 和 content
    ↓
拼接路径:tmp/pdf/fileName
    ↓
创建 tmp/pdf 目录
    ↓
创建 PdfWriter
    ↓
创建 PdfDocument
    ↓
创建 Document
    ↓
设置中文字体
    ↓
把 content 放入 Paragraph
    ↓
写入 PDF
    ↓
返回 PDF 文件路径

生成成功后,工具返回:

复制代码
PDF generated successfully to: 文件路径

这让 Agent 具备了生成可交付文件的能力。(GitHub)


18. returnDirect = false 的理解

PDFGenerationTool@Tool 注解中设置了:

复制代码
returnDirect = false

这表示工具结果不会直接作为最终回复返回给用户,而是会先返回给模型,让模型再组织最终回答。源码中该注解写在 generatePDF() 方法上。(GitHub)

可以理解为:

复制代码
工具返回:
PDF generated successfully to: xxx

模型最终回答:
我已经为你生成了 PDF 文件,保存路径是 xxx。

这样用户看到的回答会更自然,而不是直接看到工具原始返回值。


19. PDFGenerationTool 的使用场景

这个工具适合把模型生成的最终内容整理成正式文件。

例如:

复制代码
生成恋爱沟通建议报告
生成约会计划 PDF
生成咨询总结 PDF
生成用户问题分析报告
生成行动清单 PDF

用户可以说:

复制代码
把刚才的建议整理成一份 PDF。

模型就可以调用:

复制代码
generatePDF(
    fileName = "love_advice.pdf",
    content = "整理后的建议内容"
)

工具执行后返回 PDF 保存路径,模型再把路径告诉用户。


20. PDFGenerationTool 当前实现的优点

当前实现有三个优点。

第一,目录统一。PDF 文件统一保存到 tmp/pdf,与 FileConstant.FILE_SAVE_DIR 保持一致。(GitHub)

第二,支持中文。源码中使用了 STSongStd-LightUniGB-UCS2-H 创建字体,这说明作者考虑到了中文内容写入 PDF 的问题。(GitHub)

第三,实现简单。它只把一段内容写成一个段落,适合快速验证"模型生成内容 → 工具生成 PDF"的完整链路。


21. PDFGenerationTool 当前可以改进的地方

当前工具只写入一个普通段落,生成的 PDF 比较基础。

后续可以改进为:

复制代码
支持标题
支持小标题
支持列表
支持分页
支持表格
支持页眉页脚
支持生成时间
支持用户名称
支持报告编号

此外,fileName 同样需要安全校验,避免路径穿越和非法扩展名。因为当前工具也是直接把 fileName 拼接到 filePath 中。(GitHub)

更稳妥的做法是:

复制代码
只允许 .pdf 扩展名
自动补全 .pdf 后缀
清理特殊字符
确保最终路径位于 tmp/pdf 目录下

22. 三个工具如何组合成完整任务链?

这三个工具单独看都很简单,但组合起来就能完成更完整的任务。

例如用户提出:

复制代码
帮我生成一份恋爱沟通建议报告,并保存成 PDF。

可能的工具链是:

复制代码
第一步:模型生成报告正文

第二步:FileOperationTool.writeFile
把报告草稿保存成 txt 文件

第三步:PDFGenerationTool.generatePDF
把报告正文生成 PDF

第四步:TerminalOperationTool.executeTerminalCommand
检查 tmp/pdf 目录下文件是否生成成功

第五步:模型返回最终说明
告诉用户 PDF 文件保存路径

也就是说:

复制代码
FileOperationTool 负责保存中间文本
PDFGenerationTool 负责生成交付文件
TerminalOperationTool 负责检查或执行辅助命令

这就是任务型 Agent 的基本执行链。


23. 和前一期联网工具的关系

前一期讲的三个联网工具是:

复制代码
WebSearchTool
WebScrapingTool
ResourceDownloadTool

本期三个本地工具是:

复制代码
FileOperationTool
TerminalOperationTool
PDFGenerationTool

它们可以组成更长的任务链:

复制代码
WebSearchTool 搜索资料
    ↓
WebScrapingTool 抓取网页内容
    ↓
模型总结和改写
    ↓
FileOperationTool 保存草稿
    ↓
PDFGenerationTool 生成 PDF
    ↓
TerminalOperationTool 检查文件

所以,联网工具偏向"获取资料",本地工具偏向"处理资料和生成结果"。


24. 这三个工具和 ChatMemory 的区别

这三个工具也容易和 ChatMemory 混淆。

它们的区别是:

复制代码
ChatMemory:
保存对话上下文,让模型理解前后文。

FileOperationTool:
保存明确的文件内容,让任务产物可以落盘。

PDFGenerationTool:
生成正式文件,让结果可以交付。

TerminalOperationTool:
调用系统命令,让 Agent 能执行更底层的操作。

例如:

复制代码
ChatMemory 让模型知道:
用户之前想要一份约会计划。

FileOperationTool 让系统保存:
plan.txt

PDFGenerationTool 让系统生成:
plan.pdf

TerminalOperationTool 让系统检查:
plan.pdf 是否存在

所以,它们不是替代关系,而是处在不同层次。


25. 当前实现的整体优点

25.1 能力闭环比较完整

这三个工具补齐了 Agent 的本地处理能力:

复制代码
读文件
写文件
执行命令
生成 PDF

配合前面的联网工具,项目已经具备了从资料获取到结果交付的基本闭环。


25.2 工具设计简单直观

每个工具类都围绕一个明确职责展开:

复制代码
FileOperationTool:
文件读写

TerminalOperationTool:
终端执行

PDFGenerationTool:
PDF 生成

这对学习 Tool Calling 很友好。


25.3 和项目统一临时目录结合

文件和 PDF 都放在 FileConstant.FILE_SAVE_DIR 下,也就是项目根目录的 tmp 目录中。这样路径比较集中,后续清理和管理都更方便。(GitHub)


26. 当前实现最需要注意的问题

这一期最需要强调的是安全边界。

文件写入、终端执行、PDF 生成都属于"会改变本地环境"的工具。

尤其是终端执行工具,当前实现直接执行模型给出的命令。这个能力非常强,但正式系统中必须加限制。(GitHub)

建议按照风险等级管理工具:

复制代码
低风险:
PDF 生成
普通文件读取

中风险:
文件写入
资源下载

高风险:
终端执行

高风险工具最好不要默认暴露给模型,而是:

复制代码
需要用户确认
需要权限校验
需要沙箱隔离
需要审计日志
需要限制命令范围

27. 更合理的工具分组设计

当前 ToolRegistration 会把所有工具统一放入 allTools 中。(GitHub)

学习阶段这样写很方便,但后续可以分组:

复制代码
basicTools:
FileOperationTool
PDFGenerationTool
TerminateTool

webTools:
WebSearchTool
WebScrapingTool
ResourceDownloadTool

systemTools:
TerminalOperationTool

然后在不同任务中只注入需要的工具。

例如:

复制代码
普通报告生成:
只注入 basicTools

联网资料整理:
注入 basicTools + webTools

本地环境调试:
经过确认后才注入 systemTools

这样比"一次性给模型所有工具"更安全。


28. 我的理解

我认为这一期的核心是理解 Agent 的"落地执行能力"。

前面的 RAG 和联网搜索让智能体可以获得更多信息。

而本期的三个工具让智能体可以把信息变成结果:

复制代码
FileOperationTool:
把内容沉淀为文件。

TerminalOperationTool:
把模型决策转化为系统命令。

PDFGenerationTool:
把文本答案转化为可交付文档。

这说明一个完整 Agent 不应该只停留在"生成回答",而应该逐步具备:

复制代码
获取信息
处理信息
保存信息
生成交付物
执行受控动作

ai_agent 项目已经把这些能力用简单的 Java 工具类串了起来。


29. 本期重点理解

这一期最重要的是理解三个本地工具的分工。

可以总结为五点:

复制代码
第一,FileOperationTool 提供 readFile 和 writeFile,用于读取和写入 tmp/file 目录下的文本文件。
第二,TerminalOperationTool 使用 ProcessBuilder("cmd.exe", "/c", command) 执行终端命令,当前更偏 Windows 环境。
第三,PDFGenerationTool 使用 iText 把内容写入 PDF,并保存到 tmp/pdf 目录下。
第四,FileConstant 统一规定工具文件保存根目录为项目根目录下的 tmp。
第五,文件写入和终端执行属于高风险工具,正式系统中必须加入路径校验、权限控制、沙箱隔离和审计日志。

一句话概括:

复制代码
FileOperationTool 负责保存和读取任务文本,TerminalOperationTool 负责执行本地命令,PDFGenerationTool 负责把模型生成内容转化为可交付 PDF 文件。

30. 本期小结

本期主要分析了 ai_agent 项目中的三个本地执行工具。FileOperationTool 提供 readFile()writeFile() 两个方法,用于读取和写入 tmp/file 目录下的 UTF-8 文本文件;TerminalOperationTool 使用 ProcessBuilder("cmd.exe", "/c", command) 执行终端命令,并返回命令输出;PDFGenerationTool 使用 iText 创建 PDF 文件,将模型生成内容写入 tmp/pdf 目录,并通过内置中文字体支持中文内容。三个工具配合后,可以让智能体完成"生成内容---保存草稿---执行辅助命令---生成 PDF 交付物"的任务链路。

这一期可以用一句话总结:

复制代码
这三个工具让 LoveApp 从"能获取信息、能回答问题",进一步变成"能保存结果、执行本地操作、生成正式文件"的任务型智能体。

下一期可以继续分析:

AI Agent 项目学习笔记(十一):工具调用安全与 TerminateTool 任务终止机制

下一期重点分析 TerminateTool、工具权限分级、终端执行风险、文件路径安全、联网工具 SSRF 风险、工具调用审计,以及如何把当前项目的工具系统改造成更安全的 Agent 执行框架。

相关推荐
nashane1 小时前
HarmonyOS 6学习:动画流畅与截图性能的双重优化实战
学习·华为·harmonyos
一只大袋鼠1 小时前
SpringBoot 初学阶段知识点汇总(一)
spring boot·笔记·后端
霸道流氓气质1 小时前
批量收集多源 URL 并异步转 PDF 打包下载的完整实现(Spring Boot + Feign + 异步任务)
windows·spring boot·pdf
ゆづき1 小时前
AI能否替代小说作家?
人工智能·笔记·学习·其他·生活
_李小白1 小时前
【android opencv学习笔记】Day 20: 形态学滤波的腐蚀与膨胀
笔记·学习
rosemary5121 小时前
推理框架负责人 — 学习路线 (inference-framework-learning-path)
学习
rosemary5121 小时前
AI Infra 后端开发工程师 — 学习路线
人工智能·学习
Hua-Jay1 小时前
OpenCV联合C++/Qt 学习笔记(二十五)----监督学习聚类及K均值聚类
c++·笔记·opencv·学习·计算机视觉·聚类
red_redemption1 小时前
自由学习记录(191)
学习