目錄
- 前言
- 一、Spring AI 中的工具調用(Tool Calling)
- 1.1、概念
- 1.2、工作原理
- 1.3、技術選型
- 1.4、原理解析
- 1.4.1、實現接口
- 1.4.2、工具調用
- 二、工具調用(Tool Calling)開發
- 2.1、文件操作
- 2.1.1、概念描述
- 2.1.2、概念描述
- 2.2、聯網搜索
- 2.2.1、概念描述
- 2.2.2、開發步驟
- 2.3、網頁抓取
- 2.3.1、概念描述
- 2.3.2、開發步驟
- 2.4、資源下載
- 2.4.1、概念描述
- 2.4.2、開發步驟
- 2.5、PDF生成
- 2.5.1、概念描述
- 2.5.2、開發步驟
- 2.6、工具集中注冊
- 2.6.1、概念描述
- 2.6.2、開發步驟
前言
若對您有幫助的話,請點贊收藏加關注哦,您的關注是我持續創作的動力!有問題請私信或聯系郵箱:funian.gm@gmail.com
一、Spring AI 中的工具調用(Tool Calling)
1.1、概念
- Spring AI 中的工具調用(Tool Calling)是指讓 AI 大模型借助外部工具來完成自身無法直接處理的任務。這些外部工具可以是網頁搜索、外部 API 調用、訪問外部數據或執行特定代碼等多種形式。例如,當用戶詢問成都最新天氣時,AI 本身沒有該實時數據,就可以調用 “查詢天氣工具” 來獲取并返回結果。
1.2、工作原理
- **工具調用的流程并非 AI 服務器直接調用工具或執行工具代碼,而是由應用程序進行控制,這種設計的關鍵在于安全性,AI 模型無法直接接觸 API 或系統資源,所有操作都必須通過應用程序執行,這樣可以完全控制 AI 的行為。**具體流程如下:
- 用戶提出問題。
- 程序將問題傳遞給大模型。
- 大模型分析問題,判斷是否需要使用工具以及使用何種工具,并確定所需參數。
- 大模型輸出工具名稱和參數。
- 程序接收工具調用請求,執行相應的工具操作。
- 工具執行操作并返回結果數據。
- 程序將抓取結果傳回給大模型。
- 大模型分析工具返回的內容,生成最終回答。
- 程序將大模型的回答返回給用戶。
1.3、技術選型
-
Spring AI 實現工具調用的流程包括工具定義、工具選擇、返回意圖、工具執行、結果返回和繼續對話。為了簡化開發,推薦使用 Spring AI、LangChain 等開發框架,部分 AI 大模型服務商提供的 SDK 也能起到簡化代碼的作用。需要注意的是,并非所有大模型都支持工具調用,可在 Spring AI 官方文檔中查看各模型的支持情況。
-
工具定義模式。在 Spring AI 中,定義工具主要有基于 Methods 方法和 Functions 函數式編程兩種模式:
- 基于 Methods 方法:使用 @Tool 和 @ToolParam 注解標記類方法,語法簡單直觀,支持大多數 Java 類型作為參數和返回類型,包括基本類型、POJO、集合等,幾乎支持所有可序列化類型作為返回類型,包括 void,適合大多數新項目開發,支持按需注冊和全局注冊,自動處理類型轉換,通過注解提供描述文檔。
- Functions 函數式編程:使用函數式接口并通過 Spring Bean 定義,語法較復雜,需要定義請求 / 響應對象,不支持基本類型、Optional、集合類型作為參數和返回類型,適合與現有函數式 API 集成,通常在配置類中預先定義,需要更多手動配置進行類型轉換,通過 Bean 描述和 JSON 屬性注解提供文檔支持。
-
一般推薦學習基于 Methods 方法來定義工具,因為其更容易編寫和理解,支持的參數和返回類型更多。
1.4、原理解析
1.4.1、實現接口
- 工具底層數據結構:Spring AI 工具調用的核心在于ToolCallback接口,它是所有工具實現的基礎。該接口中:
- getToolDefinition()提供工具的基本定義,包括名稱、描述和調用參數,這些信息傳遞給 AI 模型,幫助模型了解何時調用工具及如何構造參數。
- getToolMetadata()提供處理工具的附加信息,如是否直接返回結果等控制選項。
- 兩個call()方法是工具的執行入口,分別支持有上下文和無上下文的調用場景。
- 工具定義類ToolDefinition包含名稱、描述和調用工具的參數。
public interface ToolCallback {/*** Definition used by the AI model to determine when and how to call the tool.*/ToolDefinition getToolDefinition();/*** Metadata providing additional information on how to handle the tool.*/ToolMetadata getToolMetadata();/*** Execute tool with the given input and return the result to send back to the AI model.*/String call(String toolInput);/*** Execute tool with the given input and context, and return the result to send back to the AI model.*/String call(String toolInput, ToolContext tooContext);
}
-
注解定義工具的原理:當使用注解定義工具時,Spring AI 會在幕后做大量工作,包括:
- JsonSchemaGenerator解析方法簽名和注解,自動生成符合 JSON Schema 規范的參數定義,作為ToolDefinition的一部分提供給 AI 大模型。
- ToolCallResultConverter負責將各種類型的方法返回值統一轉換為字符串,便于傳遞給 AI 大模型處理。
- MethodToolCallback實現對注解方法的封裝,使其符合ToolCallback接口規范。這種設計讓開發者可以專注于業務邏輯實現,無需關心底層通信和參數轉換的復雜細節。如果需要更精細的控制,可以自定義ToolCallResultConverter來實現特定的轉換邏輯,例如對某些特殊對象的自定義序列化。
-
工具上下文:在實際應用中,工具執行可能需要額外的上下文信息,如登錄用戶信息、會話 ID 或者其他環境參數,Spring AI 通過ToolContext提供了這一能力。
1.4.2、工具調用
- 執行模式與核心組件:Spring AI 提供了框架控制的工具執行和用戶控制的工具執行兩種工具執行模式,這兩種模式都依賴核心組件ToolCallingManager。
- ToolCallingManager接口是管理 AI 工具調用全過程的核心組件,負責根據 AI 模型的響應執行對應的工具并返回執行結果給大模型,還支持異常處理,可統一處理工具執行過程中的錯誤情況。
/*** Execute the tool call and return the response message. To ensure backward* compatibility, both {@link ToolCallback} and {@link FunctionCallback} are* supported.*/private InternalToolExecutionResult executeToolCall(Prompt prompt, AssistantMessage assistantMessage,ToolContext toolContext) {List<FunctionCallback> toolCallbacks = List.of();if (prompt.getOptions() instanceof ToolCallingChatOptions toolCallingChatOptions) {toolCallbacks = toolCallingChatOptions.getToolCallbacks();}else if (prompt.getOptions() instanceof FunctionCallingOptions functionOptions) {toolCallbacks = functionOptions.getFunctionCallbacks();}List<ToolResponseMessage.ToolResponse> toolResponses = new ArrayList<>();Boolean returnDirect = null;for (AssistantMessage.ToolCall toolCall : assistantMessage.getToolCalls()) {logger.debug("Executing tool call: {}", toolCall.name());String toolName = toolCall.name();String toolInputArguments = toolCall.arguments();FunctionCallback toolCallback = toolCallbacks.stream().filter(tool -> toolName.equals(tool.getName())).findFirst().orElseGet(() -> toolCallbackResolver.resolve(toolName));if (toolCallback == null) {throw new IllegalStateException("No ToolCallback found for tool name: " + toolName);}if (returnDirect == null && toolCallback instanceof ToolCallback callback) {returnDirect = callback.getToolMetadata().returnDirect();}else if (toolCallback instanceof ToolCallback callback) {returnDirect = returnDirect && callback.getToolMetadata().returnDirect();}else if (returnDirect == null) {// This is a temporary solution to ensure backward compatibility with// FunctionCallback.// TODO: remove this block when FunctionCallback is removed.returnDirect = false;}String toolResult;try {toolResult = toolCallback.call(toolInputArguments, toolContext);}catch (ToolExecutionException ex) {toolResult = toolExecutionExceptionProcessor.process(ex);}toolResponses.add(new ToolResponseMessage.ToolResponse(toolCall.id(), toolName, toolResult));}return new InternalToolExecutionResult(new ToolResponseMessage(toolResponses, Map.of()), returnDirect);}
-
其有兩個核心方法,resolveToolDefinitions從模型的工具調用選項中解析工具定義,executeToolCalls執行模型請求對應的工具調用。如果使用任何 Spring AI 相關的 Spring Boot Starter,都會默認初始化一個DefaultToolCallingManager,其中包含工具觀察器、工具解析器、工具執行異常處理器的定義。
-
工具調用判斷:ToolCallingManager通過從 AI 返回的toolCalls參數中獲取要調用的工具來判斷是否要調用工具,由于實現可能會更新,建議通過查看源碼來分析。
-
框架控制的工具執行:這是默認且最簡單的模式,由 Spring AI 框架自動管理整個工具調用流程。在這種模式下,框架會自動檢測模型是否請求調用工具,自動執行工具調用并獲取結果,自動將結果發送回模型,管理整個對話流程直到得到最終答案。
二、工具調用(Tool Calling)開發
- 自主開發前提:當在社區中找不到合適的工具時,就需要進行自主開發。同時要注意,對于 AI 自身能夠實現的功能,通常沒必要將其定義為額外的工具,因為這會增加一次額外的交互,工具應被用于解決 AI 無法直接完成的任務。
- 在開發過程中,要格外注意工具描述的定義,因為它會影響 AI 決定是否使用該工具。
2.1、文件操作
2.1.1、概念描述
- 功能:文件操作工具主要有保存文件和讀取文件兩大功能。
- 存儲目錄:由于文件操作會影響系統資源,所以需要將文件統一存放到一個隔離的目錄。在constant包下新建文件常量類,約定文件保存目錄為項目根目錄下的/tmp目錄,相關代碼通過public interface FileConstant定義,其中String FILE_SAVE_DIR = System.getProperty(“user.dir”) + “/tmp”;指定了具體的保存路徑。
- 注意事項:建議將這個/tmp目錄添加到.gitignore文件中,以避免提交隱私信息。
- 后續任務:接下來需要編寫文件操作工具類,并通過注解式定義工具。
2.1.2、概念描述
- 1、定義文件保存目錄
package com.funian.agent.constant;/*** @Auther FuNian* @Major Computer Software*/
public interface FileConstant {/*** 文件保存目錄*/String FILE_SAVE_DIR = System.getProperty("user.dir") + "/tmp";
}
- 2、文件讀取和保存。
package com.funian.agent.tools;import cn.hutool.core.io.FileUtil;
import com.funian.agent.constant.FileConstant;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;/*** @Auther FuNian* @Major Computer Software*//*** 文件操作工具類(提供文件讀寫功能)*/
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 a 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();}}
}
- 3、單元測試類。
package com.funian.agent.tools;import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;import static org.junit.jupiter.api.Assertions.*;/*** @Auther FuNian* @Major Computer Software*/
class FileOperationToolTest {@Testvoid readFile() {FileOperationTool fileOperationTool = new FileOperationTool();String fileName = "Spring AI.txt";String result = fileOperationTool.readFile(fileName);Assertions.assertNotNull(result);}@Testvoid writeFile() {FileOperationTool fileOperationTool = new FileOperationTool();String fileName = "Spring AI.txt";String content = "Spring AI交流社區";String result = fileOperationTool.writeFile(fileName, content);Assertions.assertNotNull(result);}
}
- 4、測試驗證。
2.2、聯網搜索
2.2.1、概念描述
- 作用:聯網搜索工具的作用是依據關鍵詞來搜索網頁列表。
- 實現方式:可以使用專業的網頁搜索 API,像 Search API,它能夠實現從多個網站搜索內容,不過這類服務一般是按量計費的。另外,也能夠直接使用 Google 或者 Bing 的搜索 API,甚至還可以通過爬蟲和網頁解析從某個搜索引擎獲取內容。
- 任務要求:需要閱讀 Search API 的官方文檔,重點關注 API 的請求參數和返回結果,并且從 API 返回的結果里,只提取關鍵部分。
{"organic_results": [...{"position": 2,"title": "【動物星球】動物星球商城_Animal Planet是什么牌子","link": "https://pinpai.smzdm.com/59685/","displayed_link": "什么值得買","snippet": "實時推薦動物星球(Animal Planet)商城正品特價。結合動物星球評測與動物星球最新資訊,全方位介紹Animal Planet是什么牌子?什么值得買綜合各類動物星球優惠信息,計算最優購買方案,幫您輕松搞定正品...","snippet_highlighted_words": ["Animal", "Planet"],"thumbnail": "https://t8.baidu.com/it/u=1026803159,4238637210&fm=217&app=126&size=f242,150&n=0&f=JPEG&fmt=auto?s=01F65C9344640CAA12FCF17B0300D030&sec=1714842000&t=c3db150577185f3a818a8bbe73ddd2c4"},...]
}
2.2.2、開發步驟
- 1、申請API_KEY:申請或者登錄網站,申請Key。key千萬別暴露。
- 2、配置文件添加。
# searchApi
search-api:api-key: 自己的 API Key
- 3、撰寫工具類。
package com.funian.agent.tools;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** @Auther FuNian* @Major Computer Software*//*** 網頁搜索工具類*/
public class WebSearchTool {// SearchAPI 的搜索接口地址private static final String SEARCH_API_URL = "https://www.searchapi.io/api/v1/search";private final String apiKey;public WebSearchTool(String apiKey) {this.apiKey = apiKey;}@Tool(description = "Search for information from Baidu Search Engine")public String searchWeb(@ToolParam(description = "Search query keyword") String query) {Map<String, Object> paramMap = new HashMap<>();paramMap.put("q", query);paramMap.put("api_key", apiKey);paramMap.put("engine", "baidu");try {String response = HttpUtil.get(SEARCH_API_URL, paramMap);// 取出返回結果的前 10條JSONObject jsonObject = JSONUtil.parseObj(response);// 提取 organic_results 部分JSONArray organicResults = jsonObject.getJSONArray("organic_results");List<Object> objects = organicResults.subList(0, 10);// 拼接搜索結果為字符串String result = objects.stream().map(obj -> {JSONObject tmpJSONObject = (JSONObject) obj;return tmpJSONObject.toString();}).collect(Collectors.joining(","));return result;} catch (Exception e) {return "Error searching Baidu: " + e.getMessage();}}
}
- 4、單元測試類。
package com.funian.agent.tools;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;/*** @Auther FuNian* @ClassName:WebSearchToolTest*/@SpringBootTest
public class WebSearchToolTest {@Testpublic void testSearchWeb() {WebSearchTool tool = new WebSearchTool("oJQtz4cpK4QgbfjyGV7Vsw6g");String query = "Spring AI官網";String result = tool.searchWeb(query);assertNotNull(result);}
}
- 5、測試驗證。
2.3、網頁抓取
2.3.1、概念描述
- 作用:網頁抓取工具的作用是依據網址解析網頁的內容。
- 實現方式:使用 jsoup 庫來實現網頁內容抓取和解析。
2.3.2、開發步驟
- 1、引入jsoup庫。
<dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.19.1</version>
</dependency>
- 2、編寫網頁抓取工具類。
package com.funian.agent.tools;/*** @Auther FuNian* @Major Computer Software*/import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;/*** 網頁抓取工具*/
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 document = Jsoup.connect(url).get();return document.html();} catch (Exception e) {return "Error scraping web page: " + e.getMessage();}}
}
- 3、單元測試。
package com.funian.agent.tools;import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;/*** @Auther FuNian* @Major Computer Software*/
@SpringBootTest
class WebScrapingToolTest {@Testvoid scrapeWebPage() {WebScrapingTool webScrapingTool = new WebScrapingTool();String url = "https://www.aliyun.com/resources?spm=5176.29677750.J_4VYgf18xNlTAyFFbOuOQe.d_menu_3.275b154aRdMaua";String result = webScrapingTool.scrapeWebPage(url);Assertions.assertNotNull(result);}
}
- 4、驗證。
2.4、資源下載
2.4.1、概念描述
- 作用:資源下載工具的作用是通過鏈接將文件下載到本地。
- 實現方式:使用 Hutool 的HttpUtil.downloadFile方法來實現資源下載,后續需要編寫資源下載工具類的代碼。
2.4.2、開發步驟
- 1、引入Hutool包。
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.37</version></dependency>
- 2、編寫下載類。
package com.funian.agent.tools;import cn.hutool.core.io.FileUtil;
import cn.hutool.http.HttpUtil;import com.funian.agent.constant.FileConstant;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;import java.io.File;/*** @Auther FuNian* @Major Computer Software*//*** 資源下載工具類*/
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();}}
}
- 3、 編寫單元測試類。
@SpringBootTest
public class ResourceDownloadToolTest {@Testpublic void testDownloadResource() {ResourceDownloadTool tool = new ResourceDownloadTool();String url = "https://home.console.aliyun.com/home/dashboard/Cost/logo.png";String fileName = "logo.png";String result = tool.downloadResource(url, fileName);assertNotNull(result);}
}
- 4、測試驗證。
2.5、PDF生成
2.5.1、概念描述
- 作用:PDF 生成工具的作用是根據文件名和內容生成 PDF 文檔并保存。
- 實現方式:可以使用 iText 庫來實現 PDF 生成。需要注意的是,iText 對中文字體的支持需要額外配置,不同操作系統提供的字體也有所不同,如果要做生產級應用,建議自行下載所需字體。不過對于學習來說,不建議在此處浪費太多時間,可以使用內置中文字體(不引入 font-asian 字體依賴也可以使用)。
- 拓展操作:上述代碼為了實現方便,直接將 PDF 保存到本地文件系統。此外,還可以將生成的文件上傳到對象存儲服務,然后返回可訪問的 URL 給 AI 去輸出;或者將本地文件臨時返回給前端,讓用戶直接訪問。
2.5.2、開發步驟
- 1、引入依賴itext。
<!-- 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、編寫工具實現類。
package com.funian.agent.tools;import cn.hutool.core.io.FileUtil;
import com.funian.agent.constant.FileConstant;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;import java.io.IOException;/*** @Auther FuNian* @Major Computer Software*//*** PDF 生成工具*/
public class PDFGenerationTool {@Tool(description = "Generate a PDF file with given content", returnDirect = false)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();}}
}
- 3、編寫單元測試。
package com.funian.agent.tools;import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;/*** @Auther FuNian* @ClassName:PDFGenerationToolTest*/
@SpringBootTest
class PDFGenerationToolTest {@Testpublic void testGeneratePDF() {PDFGenerationTool tool = new PDFGenerationTool();String fileName = "Spring AI.pdf";String content = "Spring AI項目 https://docs.spring.io/spring-ai/reference/api/chat/comparison.html";String result = tool.generatePDF(fileName, content);assertNotNull(result);}
}
- 4、測試驗證。
2.6、工具集中注冊
2.6.1、概念描述
- 集中注冊:在開發好眾多工具類后,結合自身需求,可以創建工具注冊類,一次性給 AI 提供所有工具,讓 AI 自行決定何時調用,這樣方便統一管理和綁定所有工具。
- 設計模式:相關代碼暗含多種設計模式。有了這個注冊類,添加或移除工具只需修改這一個類,更利于維護。
- 工廠模式(allTools () 方法作為工廠方法創建和配置多個工具實例并包裝成統一數組返回)。
- 依賴注入模式(通過 @Value 注解注入配置值,將工具通過 Spring 容器注入到需要的組件中)。
- 注冊模式(該類作為中央注冊點集中管理和注冊所有可用工具)。
- 適配器模式(ToolCallbacks.from 方法將不同工具類轉換為統一的 ToolCallback 數組)。
2.6.2、開發步驟
- 1、實現集中注冊類。
package com.funian.agent.tools;import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbacks;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @Auther FuNian* @Major Computer Software*//*** 集中的工具注冊類*/
@Configuration
public class ToolRegistration {@Value("${search-api.api-key}")private String searchApiKey;@Beanpublic ToolCallback[] allTools() {FileOperationTool fileOperationTool = new FileOperationTool();WebSearchTool webSearchTool = new WebSearchTool(searchApiKey);WebScrapingTool webScrapingTool = new WebScrapingTool();ResourceDownloadTool resourceDownloadTool = new ResourceDownloadTool();TerminalOperationTool terminalOperationTool = new TerminalOperationTool();PDFGenerationTool pdfGenerationTool = new PDFGenerationTool();TerminateTool terminateTool = new TerminateTool();return ToolCallbacks.from(fileOperationTool,webSearchTool,webScrapingTool,resourceDownloadTool,terminalOperationTool,pdfGenerationTool,terminateTool);}
}
- 2、使用集中工具。
// AI 調用工具能力@Resourceprivate ToolCallback[] allTools;/*** AI 旅游報告功能(支持調用工具)** @param message* @param chatId* @return*/public String doChatWithTools(String message, String chatId) {ChatResponse chatResponse = chatClient.prompt().user(message).advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 20))// 開啟日志,便于觀察效果.advisors(new LoggerAdvisor()).tools(allTools).call().chatResponse();String content = chatResponse.getResult().getOutput().getText();log.info("content: {}", content);return content;}
- 3、單元測試。
@Testvoid doChatWithTools() {// 測試聯網搜索問題的答案testMessage("周末想去成都,推薦幾個適合的小眾打卡地?");// 測試網頁抓取:旅游案例分析testMessage("如何制定旅游攻略");// 測試資源下載:圖片下載testMessage("下載一張成都SKP的照片");// 測試文件操作:保存用戶檔案testMessage("保存我的旅游攻略為文件");// 測試 PDF 生成testMessage("生成一份‘成都旅游計劃’PDF,包括具體路線、消費");}private void testMessage(String message) {String chatId = UUID.randomUUID().toString();String answer = TravelApp.doChatWithTools(message, chatId);Assertions.assertNotNull(answer);}
- 4、測試驗證。