AI 超级智能体项目教程 - 6 - 工具调用 笔记

记录一下springAI调用工具

1. ToolCallingManager这个对象管理整个流程

java
复制代码
/* 这是接口定义,最要是三件事 1. 解析我们定义的工具方法 2. 执行AI需要的工具调用 3. 创建这个manager */ public interface ToolCallingManager { List<ToolDefinition> resolveToolDefinitions(ToolCallingChatOptions chatOptions); ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse); static DefaultToolCallingManager.Builder builder() { return DefaultToolCallingManager.builder(); } }

2. springAi注入了ToolCallingManager对象

java
复制代码
@Bean @ConditionalOnMissingBean ToolCallingManager toolCallingManager( ToolCallbackResolver toolCallbackResolver, ToolExecutionExceptionProcessor toolExecutionExceptionProcessor, ObjectProvider<ObservationRegistry> observationRegistry ) { return ToolCallingManager .builder() .observationRegistry( (ObservationRegistry)observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP)) .toolCallbackResolver(toolCallbackResolver) .toolExecutionExceptionProcessor(toolExecutionExceptionProcessor) .build(); }

详细看懂这三个参数

2.1 工具解析器ToolCallbackResolver

接口定义

java
复制代码
public interface ToolCallbackResolver { @Nullable FunctionCallback resolve(String toolName); }

SpringAi中的具体实现类SpringBeanToolCallbackResolver中的resolve方法

java
复制代码
public ToolCallback resolve(String toolName) { Assert.hasText(toolName, "toolName cannot be null or empty"); logger.debug("ToolCallback resolution attempt from Spring application context"); // 1. 这里去map里面看是否已经存在了,即缓存 ToolCallback resolvedToolCallback = (ToolCallback)toolCallbacksCache.get(toolName); if (resolvedToolCallback != null) { return resolvedToolCallback; } else { // 每一个定义的工具方法是一个bean // 2. 没有的话就解析,即我们定义的工具方法的一些信息,让放入缓存中 ResolvableType toolType = TypeResolverHelper.resolveBeanType(this.applicationContext, toolName); ResolvableType toolInputType = ResolvableType.forType(Supplier.class).isAssignableFrom(toolType) ? ResolvableType.forType(Void.class) : TypeResolverHelper.getFunctionArgumentType(toolType, 0); String toolDescription = this.resolveToolDescription(toolName, toolInputType.toClass()); Object bean = this.applicationContext.getBean(toolName); resolvedToolCallback = this.buildToolCallback(toolName, toolType, toolInputType, toolDescription, bean); toolCallbacksCache.put(toolName, resolvedToolCallback); return resolvedToolCallback; } }

2.2 工具异常处理器ToolExecutionExceptionProcessor

接口定义

java
复制代码
public interface ToolExecutionExceptionProcessor { String process(ToolExecutionException exception); }

默认实现DefaultToolExecutionExceptionProcessor

java
复制代码
public String process(ToolExecutionException exception) { Assert.notNull(exception, "exception cannot be null"); if (this.alwaysThrow) { throw exception; } else { logger.debug("Exception thrown by tool: {}. Message: {}", exception.getToolDefinition().name(), exception.getMessage()); return exception.getMessage(); } }

2.3 工具观测处理器ObjectProvider

这个还没弄明白

3. 工具调用执行,ToolCallingManager中两个方法的默认实现

3.1 DefaultToolCallingManager中的resolveToolDefinitions实现

java
复制代码
public List<ToolDefinition> resolveToolDefinitions(ToolCallingChatOptions chatOptions) { Assert.notNull(chatOptions, "chatOptions cannot be null"); // 1. 获取对话中的工具列表(我们发给AI的工具列表) // FunctionCallback是ToolCallback的父类 `public interface ToolCallback extends FunctionCallback` List<FunctionCallback> toolCallbacks = new ArrayList(chatOptions.getToolCallbacks()); // 2. 根据工具名字去添加工具 for(String toolName : chatOptions.getToolNames()) { if (!chatOptions.getToolCallbacks().stream().anyMatch((tool) -> tool.getName().equals(toolName))) { // toolCallbackResolver有工具缓存的,没有才会解析 FunctionCallback toolCallback = this.toolCallbackResolver.resolve(toolName); if (toolCallback == null) { throw new IllegalStateException("No ToolCallback found for tool name: " + toolName); } toolCallbacks.add(toolCallback); } } // 3. 返回AI可用的工具列表定义 return toolCallbacks.stream().map((functionCallback) -> { if (functionCallback instanceof ToolCallback toolCallback) { return toolCallback.getToolDefinition(); } else { return ToolDefinition.builder() .name(functionCallback.getName()) .description(functionCallback.getDescription()) .inputSchema(functionCallback.getInputTypeSchema()) .build(); } }).toList(); }

3.2 DefaultToolCallingManager中的executeToolCalls实现

java
复制代码
public ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse) { Assert.notNull(prompt, "prompt cannot be null"); Assert.notNull(chatResponse, "chatResponse cannot be null"); // 1. 获取AI需要调用的工具getToolCalls()中的第一个工具 Optional<Generation> toolCallGeneration = chatResponse.getResults() .stream() .filter((g) -> !CollectionUtils.isEmpty(g.getOutput().getToolCalls())) .findFirst(); if (toolCallGeneration.isEmpty()) { throw new IllegalStateException("No tool call requested by the chat model"); } else { AssistantMessage assistantMessage = ((Generation)toolCallGeneration.get()).getOutput(); ToolContext toolContext = buildToolContext(prompt, assistantMessage); // 2. 组装信息,进行工具调用 InternalToolExecutionResult internalToolExecutionResult = this.executeToolCall(prompt, assistantMessage, toolContext); // 3. 存信息历史 返回结果 List<Message> conversationHistory = this.buildConversationHistoryAfterToolExecution(prompt.getInstructions(), assistantMessage, internalToolExecutionResult.toolResponseMessage()); return ToolExecutionResult.builder() .conversationHistory(conversationHistory) .returnDirect(internalToolExecutionResult.returnDirect()) .build(); } }

具体调用

java
复制代码
private InternalToolExecutionResult executeToolCall(Prompt prompt, AssistantMessage assistantMessage, ToolContext toolContext) { List<FunctionCallback> toolCallbacks = List.of(); ChatOptions var7 = prompt.getOptions(); if (var7 instanceof ToolCallingChatOptions toolCallingChatOptions) { toolCallbacks = toolCallingChatOptions.getToolCallbacks(); } else { var7 = prompt.getOptions(); if (var7 instanceof FunctionCallingOptions functionOptions) { toolCallbacks = functionOptions.getFunctionCallbacks(); } } List<ToolResponseMessage.ToolResponse> toolResponses = new ArrayList(); Boolean returnDirect = null; // 1. 变量AI需要调用的所有工具 for(AssistantMessage.ToolCall toolCall : assistantMessage.getToolCalls()) { logger.debug("Executing tool call: {}", toolCall.name()); // 工具名、参数 String toolName = toolCall.name(); String toolInputArguments = toolCall.arguments(); FunctionCallback toolCallback = (FunctionCallback)toolCallbacks.stream() .filter((tool) -> toolName.equals(tool.getName())) /* 优先级机制:显式提供的工具优先于全局工具 覆盖能力:允许会话级工具覆盖全局工具 简化逻辑:避免复杂的冲突处理 */ .findFirst() .orElseGet(() -> this.toolCallbackResolver.resolve(toolName)); if (toolCallback == null) { throw new IllegalStateException("No ToolCallback found for tool name: " + toolName); } // 是否直接返回工具调用结果,不需要再和AI对话 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) { returnDirect = false; } String toolResult; try { // 真正调用工具方法 // 我们在@Tool中写的 returnType 会在这里面生效 toolResult = toolCallback.call(toolInputArguments, toolContext); } catch (ToolExecutionException ex) { toolResult = this.toolExecutionExceptionProcessor.process(ex); } toolResponses.add(new ToolResponseMessage.ToolResponse(toolCall.id(), toolName, toolResult)); } return new InternalToolExecutionResult(new ToolResponseMessage(toolResponses, Map.of()), returnDirect); }

疑问

  1. resolveToolDefinitions中的chatOptions.getToolCallbacks()是什么意思,还有其他之前的工具?这样的话如何控制AI可用范围?resolveToolDefinitions方法返回的是给AI看的工具列表?

ToolCallingChatOptions chatOptions是我们给AI的工具配置对象。chatOptions.getToolCallbacks()就是我给这次对话的工具列表。控制AI可以用范围即控制对话的时候给AI可以用的工具列表。

  1. executeToolCalls中的ChatResponse是什么?g.getOutput().getToolCalls()是AI返回的信息中它需要调用的工具列表?

ChatResponse是AI返回给我们的响应。g.getOutput().getToolCalls()是AI想要调用的工具

  1. executeToolCall中通过name寻找对应工具时为什么findFirst(),有同名的?assistantMessage.getToolCalls()这个是什么?

这个我看的AI说的,其实我觉得或许不太对。(有佬可以指点一下,感谢)

  • 优先级机制:显式提供的工具优先于全局工具
  • 覆盖能力:允许会话级工具覆盖全局工具
  • 简化逻辑:避免复杂的冲突处理

总结

  1. 我们向AI发起对话,同时告诉了AI可以用的工具(resolveToolDefinitions(ToolCallingChatOptions chatOptions)
  2. AI返回响应,它需要调用工具,它会返回调用的列表(ChatResponse chatResponse-> AssistantMessage->List<Generation> generations->List<ToolCall> toolCalls
  3. springAi去ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse);执行,里面包括获取工具、解析工具、调用工具
  4. 看是否直接返回。直接返回即返回工具调用的结果,不和AI再交互

不是十分精确,请佬多指点,感谢

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
小汉同学
作者分享
26届,春招,校招。 不知道怎么弄了。。。焦虑中,请教以下各位大佬。 最先的几个月,我在慢吞吞的做项目,感觉需要自己手过一遍才算自己的。然后,又要自己准备得感觉差不多才心里踏实。现在感觉没有方向了。 我前不久准备好了简历,然后复习了两周的面试题。想着开始投简历。但是,我觉得很多岗位的JD我匹配不上,也不知道该不该投。我看了老鱼简历中的信息列表,点进去看感觉都不适合我。。。 看了抖音上的一些视频,去国聘行动,就业平台等,我看了。有一些岗位,但是,它们都必须在它们的平台上弄好简历,还不能上传我做好的简历。要填的东西很多。让我感觉会填很久,然后也不一定有响应。 之前待了一家实习,实习半个月,找我谈签三方,但是我觉得是Python路线,我就放弃了。在学校我线下去投递过,最近这几天去投了来校内的几家,目前还没有消息,感觉是没了。 图片是简历,已经脱敏。希望大佬们可以给我指点一下。目前不知道怎么弄了。 问题: 在哪里找中小公司的校招岗位? 如果JD自我感觉不匹配,是不是不需要投递了? 智联招聘、BOSS是不是和网上说的不适合校招毕业生? 麻烦各位大佬的回复了。谢谢。
4
小汉同学 - AI 超级智能体教程 - 4 - RAG 知识库基础 笔记
4
小汉同学 - AI 超级智能体教程 - 3 - AI 应用开发 笔记
3
小汉同学 - AI 超级智能体 - 2 - AI 大模型接入 笔记
5
各位大佬晚上好。想问一下问题,蓝桥杯该怎么准备。我在官方网站上面看见有对应的培训课程。但是有点贵(比较穷)。之前过完了B站满老师的数据结构课程,算是入门小白了吧(我也不知道是不是)。目前在根据满老师提供的文档进行复习。因为自己比较迟钝。所以想各位大佬给我提提方法,如何准备才够这个比赛。如果是选择他们官方的课程的话,我想去pdd去找一下有没有去年课程的,便宜一点😂 感谢各位大佬,感谢感谢!!!😁
3
下载 APP