springAi工具制作流程
工具是什么
agent = LLM + 工具,如果llm是agent的大脑,工具就是agent的手脚,帮助ai与现实世界进行交互并真正的执行任务
工具在agent中的执行流程
用户提出搜索/下载资源需求 -》 agent接受用户需求并接受后端用户发送的工具上下文 -》 进行检索后向mcp服务器发送 我要调用xxx工具,具体参数为xxx -》 mcp接受信息并执行工具调用,调用完成后返回工具执行结果 -》 大模型接受结果并进行处理 -》 发送给用户
具体流程
1 工具定义与注册
2 工具调用请求
3 工具执行
4 处理工具结果
5 返回结果给模型
6 生成最终响应
第一个
工具的定义与注册
基于methods的方法,使用spring ai里的tools注解,以及toolparam注解标记类方法(用的比较多)
基于函数式接口并通过spring bean定义,复杂,需要定义请求/响应对象
代码
//基于method方法注解
class WeatherTools {
@Tool(description = "Get current weather for a location")
public String getWeather(@ToolParam(description = "The city name") String city) {
return "Current weather in " + city + ": Sunny, 25°C";
}
}
// 使用方式
ChatClient.create(chatModel)
.prompt("What's the weather in Beijing?")
.tools(new WeatherTools())
.call();
//基于函数式
@Configuration
public class ToolConfig {
@Bean
@Description("Get current weather for a location")
public Function<WeatherRequest, WeatherResponse> weatherFunction() {
return request -> new WeatherResponse("Weather in " + request.getCity() + ": Sunny, 25°C");
}
}
// 使用方式
ChatClient.create(chatModel)
.prompt("What's the weather in Beijing?")
.functions("weatherFunction")
.call();
工具的使用:
局部使用:直接在chatmodel通过tools方法附加工具
String response = ChatClient.create(chatModel)
.prompt("北京今天天气怎么样?")
.tools(new WeatherTools()) // 在这次对话中提供天气工具
.call()
.content();
全局使用:构建chatclient时直接默认注册
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultTools(new WeatherTools(), new TimeTools()) // 注册默认工具
.build();
自主开发工具:
1 文件工具:进行文件的读取和写入
public interface FileConstant {
String FILE_SAVE_DIR = System.getPorperty("user.dir") + "/tmp";
}
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(descrption="Name of the file to write") String fileName,
@ToolParam(descrption="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 writting to file" + e.getMessage();
}
}
联网搜索工具:核心 调用网页请求的url以及request 参数要知道,知道以后丢给ai让ai帮自己写请求发送以及接受请求后的关键信息提取,以webSearchTool为例
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);
// 取出返回结果的前 5 条
JSONObject jsonObject = JSONUtil.parseObj(response);
// 提取 organic_results 部分
JSONArray organicResults = jsonObject.getJSONArray("organic_results");
List<Object> objects = organicResults.subList(0, 5);
// 拼接搜索结果为字符串
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();
}
}
}
剩余同理
网页抓取:jsoup库依赖引入,创建类,创建方法,使用tool和toolparam修饰函数和函数参数,编写测试类进行测试
终端操作:通过java的processAPI实现终端命令的执行,windows和其余操作系统在实现策略稍微有区别
资源下载:Hutool的httpUtil.downloadFile方法实现
PDF生成:itext库实现了pdf的生成
工作定制好了之后可以使用集中注册的方式,后序在模型调用的时候通过tools方法也可以将工具全部传入进去
@Configuration
public class ToolRegistration {
@Value("${search-api.api-key}")
private String searchApiKey;
@Bean
public 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();
return ToolCallbacks.from(
fileOperationTool,
webSearchTool,
webScrapingTool,
resourceDownloadTool,
terminalOperationTool,
pdfGenerationTool
);
}
}
//使用
@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;
}
进阶知识:
ai调用工具依赖ToolCallBack接口,接口有四个方法:getToolDefination提供工具的定义(名称,描述,调用参数),getToolMeta:提供处理工具的附加信息,call方法,工具的执行入口,支持上下文和无上下文调用
工具上下文的获取:通过toolContext进行提供,toolContext携带用户认证信息,请求追踪和自定义配置功能,这些元信息不会给ai发送,丢给程序,目的是让程序进行处理和执行功能(本质上使用map结构进行信息保存)
浙公网安备 33010602011771号