1、文件操作
文件操作工具主要提供 2 大功能:保存文件、读取文件。
特性 | Methods 方式 |
---|---|
定义方式 | 使用 @Tool和 @ToolParam注解标记类方法 |
语法复杂度 | 简单,直观 |
支持的参数类型 | 大多数 Java 类型,包括基本类型、POJO、集合等 |
编写文件操作工具类,通过注解式定义工具,代码如下:
public class FileOperationTool {private final String FILE_DIR = FileConstant.FILE_SAVE_DIR + "/file";@Tool(description = "Read content from a file")public String readFile(@ToolParam(description = "Name of the file to read") String fileName) {String filePath = FILE_DIR + "/" + fileName;try {return FileUtil.readUtf8String(filePath);} catch (Exception e) {return "Error reading file: " + e.getMessage();}}@Tool(description = "Write content to a file")public String writeFile(@ToolParam(description = "Name of the file to write") String fileName,@ToolParam(description = "Content to write to the file") String content) {String filePath = FILE_DIR + "/" + fileName;try {// 创建目录FileUtil.mkdir(FILE_DIR);FileUtil.writeUtf8String(content, filePath);return "File written successfully to: " + filePath;} catch (Exception e) {return "Error writing to file: " + e.getMessage();}}
}
注意:在 AI 调用工具时,一定要思考 健壮性问题 ,如果调用工具失败,要try catch 操作,否则对话就会崩溃。
2、网页抓取
网页抓取工具的作用是根据网址解析到网页的内容。
编写网页抓取工具类
public class WebScrapingTool {@Tool(description = "Scrape the content of a web page")public String scrapeWebPage(@ToolParam(description = "URL of the web page to scrape") String url) {try {Document doc = Jsoup.connect(url).get();return doc.html();} catch (IOException e) {return "Error scraping web page: " + e.getMessage();}}
}
编写单元测试代码:
@SpringBootTest
public class WebScrapingToolTest {@Testpublic void testScrapeWebPage() {WebScrapingTool tool = new WebScrapingTool();String url = "https://blog.csdn.net/cui_hao_nan?spm=1000.2115.3001.5343";String result = tool.scrapeWebPage(url);assertNotNull(result);}
}
debug 结果显示
3、终端操作
终端操作工具的作用是在终端执行命令。
1)可以通过 Java 的 Process API 实现终端命令执行,注意 Windows 和其他操作系统下的实现略有区别)。工具类代码如下:
public class TerminalOperationTool {@Tool(description = "Execute a command in the terminal")public String executeTerminalCommand(@ToolParam(description = "Command to execute in the terminal") String command) {StringBuilder output = new StringBuilder();try {ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", command);
// Process process = Runtime.getRuntime().exec(command);Process process = builder.start();try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {String line;while ((line = reader.readLine()) != null) {output.append(line).append("\n");}}int exitCode = process.waitFor();if (exitCode != 0) {output.append("Command execution failed with exit code: ").append(exitCode);}} catch (IOException | InterruptedException e) {output.append("Error executing command: ").append(e.getMessage());}return output.toString();}
}
debug 结果展示
4、资源下载
资源下载工具的作用是通过链接下载文件到本地。
1)使用 Hutool 的 HttpUtil.downloadFile 方法实现资源下载。资源下载工具类的代码如下:
public class ResourceDownloadTool {@Tool(description = "Download a resource from a given URL")public String downloadResource(@ToolParam(description = "URL of the resource to download") String url, @ToolParam(description = "Name of the file to save the downloaded resource") String fileName) {String fileDir = FileConstant.FILE_SAVE_DIR + "/download";String filePath = fileDir + "/" + fileName;try {// 创建目录FileUtil.mkdir(fileDir);// 使用 Hutool 的 downloadFile 方法下载资源HttpUtil.downloadFile(url, new File(filePath));return "Resource downloaded successfully to: " + filePath;} catch (Exception e) {return "Error downloading resource: " + e.getMessage();}}
}
5、PDF 生成
PDF 生成工具的作用是根据文件名和内容生成 PDF 文档并保存。
可以使用 itext 库 实现 PDF 生成。需要注意的是,itext 对中文字体的支持需要额外配置,不同操作系统提供的字体也不同,如果真要做生产级应用,建议自行下载所需字体。
1)给项目添加依赖:
<!-- https://mvnrepository.com/artifact/com.itextpdf/itext-core -->
<dependency><groupId>com.itextpdf</groupId><artifactId>itext-core</artifactId><version>9.1.0</version><type>pom</type>
</dependency>
<!-- https://mvnrepository.com/artifact/com.itextpdf/font-asian -->
<dependency><groupId>com.itextpdf</groupId><artifactId>font-asian</artifactId><version>9.1.0</version><scope>test</scope>
</dependency>
2)编写工具类实现代码:
public class PDFGenerationTool {@Tool(description = "Generate a PDF file with given content")public String generatePDF(@ToolParam(description = "Name of the file to save the generated PDF") String fileName,@ToolParam(description = "Content to be included in the PDF") String content) {String fileDir = FileConstant.FILE_SAVE_DIR + "/pdf";String filePath = fileDir + "/" + fileName;try {// 创建目录FileUtil.mkdir(fileDir);// 创建 PdfWriter 和 PdfDocument 对象try (PdfWriter writer = new PdfWriter(filePath);PdfDocument pdf = new PdfDocument(writer);Document document = new Document(pdf)) {// 自定义字体(需要人工下载字体文件到特定目录)
// String fontPath = Paths.get("src/main/resources/static/fonts/simsun.ttf")
// .toAbsolutePath().toString();
// PdfFont font = PdfFontFactory.createFont(fontPath,
// PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);// 使用内置中文字体PdfFont font = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H");document.setFont(font);// 创建段落Paragraph paragraph = new Paragraph(content);// 添加段落并关闭文档document.add(paragraph);}return "PDF generated successfully to: " + filePath;} catch (IOException e) {return "Error generating PDF: " + e.getMessage();}}
}
上述代码中,为了实现方便,我们是直接保存 PDF 到本地文件系统。此外,你还可以将生成的文件上传到对象存储服务,然后返回可访问的 URL 给 AI 去输出;或者将本地文件临时返回给前端,让用户直接访问。
6、集中注册
开发好了很多工具类后,可以结合我们自己的需求,可以给 AI 一次性提供所有工具,让它自己决定何时调用。所以我们可以创建 工具注册类,方便统一管理和绑定所有工具。
@Configuration
public class ToolRegistration {@Beanpublic ToolCallback[] allTools() {FileOperationTool fileOperationTool = new FileOperationTool();WebScrapingTool webScrapingTool = new WebScrapingTool();ResourceDownloadTool resourceDownloadTool = new ResourceDownloadTool();TerminalOperationTool terminalOperationTool = new TerminalOperationTool();PDFGenerationTool pdfGenerationTool = new PDFGenerationTool();return ToolCallbacks.from(fileOperationTool,webScrapingTool,resourceDownloadTool,terminalOperationTool,pdfGenerationTool);}
}
- 工厂模式:allTools()方法作为一个工厂方法,负责创建和配置多个工具实例,然后将它们包装成统一的数组返回。这符合工厂模式的核心思想 -集中创建对象并隐藏创建细节
- 注册模式:该类作为一个中央注册点,集中管理和注册所有可用的工具,使它们能够被系统其他部分统一访问。
- 适配器模式的应用:ToolCallbacks.from 方法可以看作是一种适配器,通过自动识别 @Tool注解,将各种不同的工具类转换为统一的ToolCallback 数组,使系统能够以一致的方式处理它们。
7、使用工具
在 LoveApp 类中添加工具调用的代码,通过 tools 方法绑定所有已注册的工具:
@Resource
private ToolCallback[] allTools;public String doChatWithTools(String message, String chatId) {ChatResponse response = chatClient.prompt().user(message).advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))// 开启日志,便于观察效果.advisors(new MyLoggerAdvisor()).tools(allTools).call().chatResponse();String content = response.getResult().getOutput().getText();log.info("content: {}", content);return content;
}