① 当我们希望向模型提供一个工具时,我们会将其定义包含在聊天请求(提示词)中,并调用 ChatModel API 将请求发送至 AI 模型。
public String doChatWithTools(String message, String chatId) {
ChatResponse response = chatClient.prompt().user(message)
// 传入ToolCallback[]类型的参数allTools, 供AI自己选择要使用的工具
.tools(allTools).call().chatResponse();
}
② 当模型决定调用工具时,它会发送一个包含工具名称及根据定义的模式建模的输入参数的响应(ChatResponse)。
③ ChatModel 将工具调用请求发送至 ToolCallingManager API。
④ ToolCallingManager 负责识别要调用的工具并使用提供的输入参数执行该工具。
⑤ 工具调用的结果返回给 ToolCallingManager。
⑥ ToolCallingManager 将工具执行结果返回给 ChatModel。
⑦ ChatModel 将工具执行结果(ToolResponseMessage)发送回 AI 模型。
⑧ AI 模型使用工具调用结果作为额外上下文生成最终响应
对④⑤两个过程涉及的核心方法executeToolCalls解析:
/**
* 执行工具调用并返回结果(外层包装)
* @param prompt 用户提供的初始提示信息
* @param chatResponse 模型返回的聊天响应
* @return 包含执行结果和对话历史的ToolExecutionResult对象
*/
public ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse) {
// 1. 从聊天响应中提取包含工具调用的Generation
Optional<Generation> toolCallGeneration = chatResponse.getResults()
.stream()
.filter(g -> !CollectionUtils.isEmpty(g.getOutput().getToolCalls())) // 确保toolCalls不为空
.findFirst(); // 只取第一个匹配的Generation
// 2. 校验是否存在工具调用
if (toolCallGeneration.isEmpty()) {
throw new IllegalStateException("No tool call requested by the chat model");
}
// 3. 获取包含工具调用信息的大模型回复消息(包含工具调用的详细信息,如工具名称、参数等)
AssistantMessage assistantMessage = toolCallGeneration.get().getOutput();
// 4. 构建工具调用上下文
ToolContext toolContext = buildToolContext(prompt, assistantMessage);
// 5. ❤执行实际的工具调用 ,调用具体工具生成工具响应消息
InternalToolExecutionResult internalToolExecutionResult = executeToolCall(
prompt, assistantMessage, toolContext);
// 6. 构建更新后的对话历史
List<Message> conversationHistory = buildConversationHistoryAfterToolExecution(
prompt.getInstructions(), // 原始指令
assistantMessage, // AI请求消息
internalToolExecutionResult.toolResponseMessage()); // 工具响应消息
// 7. 构建并返回最终结果
return ToolExecutionResult.builder()
.conversationHistory(conversationHistory) // 新的对话历史记录
.returnDirect(internalToolExecutionResult.returnDirect()) // 是否直接返回结果(不进一步请求AI作结果处理 适合文件下载等工具)
.build();
}
/**
* 真正实际执行工具调用
*/
private InternalToolExecutionResult executeToolCall(Prompt prompt, AssistantMessage assistantMessage,
ToolContext toolContext) {
// 1. 从用户请求的Prompt配置中获取工具回调函数ToolCallback
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;
// 2. 从大模型回复消息中获取每个工具调用请求(包含工具名称和参数)
for (AssistantMessage.ToolCall toolCall : assistantMessage.getToolCalls()) {
String toolName = toolCall.name();
String toolInputArguments = toolCall.arguments();
// 在注册的工具回调中查找匹配项 优先使用显式注册的回调,否则动态解析(如Spring Bean)
FunctionCallback toolCallback = toolCallbacks.stream()
.filter(tool -> toolName.equals(tool.getName()))
.findFirst()
.orElseGet(() -> toolCallbackResolver.resolve(toolName));
// 如果找不到对应工具,抛出异常(说明大模型瞎回复要找一个不存在的工具)
if (toolCallback == null) {
throw new IllegalStateException("未找到名称为 " + toolName + " 的工具");
}
// 3. (不重要)判断是否需要直接返回结果(兼容新旧接口)
if (toolCallback instanceof ToolCallback callback) {
// 如果是新式工具接口,获取其返回策略
returnDirect = returnDirect == null
? callback.getToolMetadata().returnDirect()
: returnDirect && callback.getToolMetadata().returnDirect();
}
else {
// 旧版兼容:默认不直接返回
returnDirect = false;
}
// 4. 执行工具调用toolCallback.call
String toolResult;
try {
// 工具调用参数是从大模型返消息中获取的+工具调用上下文中的参数
toolResult = toolCallback.call(toolInputArguments, toolContext);
}
catch (ToolExecutionException ex) {
// 异常处理:生成替代结果
toolResult = toolExecutionExceptionProcessor.process(ex);
}
// 5. 将工具调用结果存入响应列表
toolResponses.add(new ToolResponseMessage.ToolResponse(
toolCall.id(), toolName, toolResult
));
}
// 返回最终结果(包含所有工具响应)和是否直接返回的标记
return new InternalToolExecutionResult(
new ToolResponseMessage(toolResponses, Map.of()),
returnDirect
);
}