Spring AI 源码解析:Tool Calling链路调用流程及示例
· 30 min read
Tool工具允许模型与一组API或工具进行交互,增强模型功能,主要用于:
- 信息检索:从外部数据源检索信息,如数据库、Web服务、文件系统或Web搜索引擎等
- 采取行动:可用于在软件系统中执行特定操作,如发送电子邮件、在数据库中创建新记录、触发工作流等
注:
- 本版源码解析取自Spring-ai(20250321)仓库最新代码(暂未发版),目前 最新的1.0.0.-M6有部分类和方法将过期,故不在此讨论范畴中
本文实践代码可见spingr-ai-alibaba-examples项目下的spring-ai-alibaba-tool-calling-examples
理论部分

- 在聊天请求中包含工具的定义,包括工具名称、描述、输入模式
- 当AI模型决定调用一个工具时,会发送一个响应,包含工具名称和输入参数(大模型提取文本根据输入模式转化而得)
- 应用程序将使用工具名称并使用提供的输入参数
- 工具计算结果后将结果返回给应用程序
- 应用程序再将结果发送给模型
- 模型添加工具结果作为附加的上下文信息生成最终响应
工具调用链路(核心)
下图以ChatClient调用tools(String... toolNames)方法全链路流程展示

public class DefaultChatClient implements ChatClient {
@Override
public ChatClientRequestSpec tools(String... toolNames) {
Assert.notNull(toolNames, "toolNames cannot be null");
Assert.noNullElements(toolNames, "toolNames cannot contain null elements");
this.functionNames.addAll(List.of(toolNames));
return this;
}
@Override
public ChatClientRequestSpec tools(FunctionCallback... toolCallbacks) {
Assert.notNull(toolCallbacks, "toolCallbacks cannot be null");
Assert.noNullElements(toolCallbacks, "toolCallbacks cannot contain null elements");
this.functionCallbacks.addAll(List.of(toolCallbacks));
return this;
}
@Override
public ChatClientRequestSpec tools(List<ToolCallback> toolCallbacks) {
Assert.notNull(toolCallbacks, "toolCallbacks cannot be null");
Assert.noNullElements(toolCallbacks, "toolCallbacks cannot contain null elements");
this.functionCallbacks.addAll(toolCallbacks);
return this;
}
@Override
public ChatClientRequestSpec tools(Object... toolObjects) {
Assert.notNull(toolObjects, "toolObjects cannot be null");
Assert.noNullElements(toolObjects, "toolObjects cannot contain null elements");
this.functionCallbacks.addAll(Arrays.asList(ToolCallbacks.from(toolObjects)));
return this;
}
@Override
public ChatClientRequestSpec tools(ToolCallbackProvider... toolCallbackProviders) {
Assert.notNull(toolCallbackProviders, "toolCallbackProviders cannot be null");
Assert.noNullElements(toolCallbackProviders, "toolCallbackProviders cannot contain null elements");
for (ToolCallbackProvider toolCallbackProvider : toolCallbackProviders) {
this.functionCallbacks.addAll(List.of(toolCallbackProvider.getToolCallbacks()));
}
return this;
}
}
Tool(工具注解)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Tool {
/**
* The name of the tool. If not provided, the method name will be used.
*/
String name() default "";
/**
* The description of the tool. If not provided, the method name will be used.
*/
String description() default "";
/**
* Whether the tool result should be returned directly or passed back to the model.
*/
boolean returnDirect() default false;
/**
* The class to use to convert the tool call result to a String.
*/
Class<? extends ToolCallResultConverter> resultConverter() default DefaultToolCallResultConverter.class;
}
ToolDefinition(工具定义)
public interface ToolDefinition {
// 工具的名称,提供给一个模型时要求唯一标识
String name();
// 工具描述
String description();
// 工具的输入模式
String inputSchema();
static DefaultToolDefinition.Builder builder() {
return DefaultToolDefinition.builder();
}
// 从方法中提取出工具的名称、描述、输入模式
static DefaultToolDefinition.Builder builder(Method method) {
Assert.notNull(method, "method cannot be null");
return DefaultToolDefinition.builder()
.name(ToolUtils.getToolName(method))
.description(ToolUtils.getToolDescription(method))
.inputSchema(JsonSchemaGenerator.generateForMethodInput(method));
}
static ToolDefinition from(Method method) {
return ToolDefinition.builder(method).build();
}
}
DefaultToolDefinition
public record DefaultToolDefinition(String name, String description, String inputSchema) implements ToolDefinition {
public DefaultToolDefinition {
Assert.hasText(name, "name cannot be null or empty");
Assert.hasText(description, "description cannot be null or empty");
Assert.hasText(inputSchema, "inputSchema cannot be null or empty");
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private String name;
private String description;
private String inputSchema;
private Builder() {
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder description(String description) {
this.description = description;
return this;
}
public Builder inputSchema(String inputSchema) {
this.inputSchema = inputSchema;
return this;
}
public ToolDefinition build() {
if (!StringUtils.hasText(description)) {
description = ToolUtils.getToolDescriptionFromName(name);
}
return new DefaultToolDefinition(name, description, inputSchema);
}
}
}
ToolMetadata(工具元数据)
现阶段只用于控制直接将工具结果返回,不再走模型响应
public interface ToolMetadata {
default boolean returnDirect() {
return false;
}
static DefaultToolMetadata.Builder builder() {
return DefaultToolMetadata.builder();
}
static ToolMetadata from(Method method) {
Assert.notNull(method, "method cannot be null");
return DefaultToolMetadata.builder().returnDirect(ToolUtils.getToolReturnDirect(method)).build();
}
}
DefaultToolMetadata
public record DefaultToolMetadata(boolean returnDirect) implements ToolMetadata {
public static Builder builder() {
return new Builder();
}
public static class Builder {
private boolean returnDirect = false;
private Builder() {
}
public Builder returnDirect(boolean returnDirect) {
this.returnDirect = returnDirect;
return this;
}
public ToolMetadata build() {
return new DefaultToolMetadata(returnDirect);
}
}
}
ToolCallback(工具回调)
public interface ToolCallback{
// AI模型用来确定何时以及如何调用工具的定义
ToolDefinition getToolDefinition();
// 元数据提供了额外的信息怎么操作工具
default ToolMetadata getToolMetadata() {
return ToolMetadata.builder().build();
}
// toolInput为工具的输入,最终返回结果工具的结果
String call(String toolInput);
// toolInput为工具的输入,tooContext为工具的上下文信息
default String call(String toolInput, @Nullable ToolContext tooContext) {
if (tooContext != null && !tooContext.getContext().isEmpty()) {
throw new UnsupportedOperationException("Tool context is not supported!");
}
return call(toolInput);
}
}
MethodToolCallback
核心方法主要关注call
-
将模型处理后的字符串文本,转化为对应的输入模式
-
Map<String, Object> toolArguments = extractToolArguments(toolInput);
Object[] methodArguments = buildMethodArguments(toolArguments, toolContext);
-
-
调用工具的方法+输入参数,得到工具的输出结果
-
Object result = callMethod(methodArguments);
-
-
将工具的输出结果的类型进行转化
-
Type returnType = toolMethod.getGenericReturnType();
return toolCallResultConverter.convert(result, returnType);
-
public class MethodToolCallback implements ToolCallback {
private static final Logger logger = LoggerFactory.getLogger(MethodToolCallback.class);
private static final ToolCallResultConverter DEFAULT_RESULT_CONVERTER = new DefaultToolCallResultConverter();
private static final ToolMetadata DEFAULT_TOOL_METADATA = ToolMetadata.builder().build();
private final ToolDefinition toolDefinition;
private final ToolMetadata toolMetadata;
private final Method toolMethod;
@Nullable
private final Object toolObject;
private final ToolCallResultConverter toolCallResultConverter;
public MethodToolCallback(ToolDefinition toolDefinition, @Nullable ToolMetadata toolMetadata, Method toolMethod,
@Nullable Object toolObject, @Nullable ToolCallResultConverter toolCallResultConverter) {
Assert.notNull(toolDefinition, "toolDefinition cannot be null");
Assert.notNull(toolMethod, "toolMethod cannot be null");
Assert.isTrue(Modifier.isStatic(toolMethod.getModifiers()) || toolObject != null,
"toolObject cannot be null for non-static methods");
this.toolDefinition = toolDefinition;
this.toolMetadata = toolMetadata != null ? toolMetadata : DEFAULT_TOOL_METADATA;
this.toolMethod = toolMethod;
this.toolObject = toolObject;
this.toolCallResultConverter = toolCallResultConverter != null ? toolCallResultConverter
: DEFAULT_RESULT_CONVERTER;
}
@Override
public ToolDefinition getToolDefinition() {
return toolDefinition;
}
@Override
public ToolMetadata getToolMetadata() {
return toolMetadata;
}
@Override
public String call(String toolInput) {
return call(toolInput, null);
}
@Override
public String call(String toolInput, @Nullable ToolContext toolContext) {
Assert.hasText(toolInput, "toolInput cannot be null or empty");
logger.debug("Starting execution of tool: {}", toolDefinition.name());
validateToolContextSupport(toolContext);
Map<String, Object> toolArguments = extractToolArguments(toolInput);
Object[] methodArguments = buildMethodArguments(toolArguments, toolContext);
Object result = callMethod(methodArguments);
logger.debug("Successful execution of tool: {}", toolDefinition.name());
Type returnType = toolMethod.getGenericReturnType();
return toolCallResultConverter.convert(result, returnType);
}
private void validateToolContextSupport(@Nullable ToolContext toolContext) {
var isNonEmptyToolContextProvided = toolContext != null && !CollectionUtils.isEmpty(toolContext.getContext());
var isToolContextAcceptedByMethod = Stream.of(toolMethod.getParameterTypes())
.anyMatch(type -> ClassUtils.isAssignable(type, ToolContext.class));
if (isToolContextAcceptedByMethod && !isNonEmptyToolContextProvided) {
throw new IllegalArgumentException("ToolContext is required by the method as an argument");
}
}
private Map<String, Object> extractToolArguments(String toolInput) {
return JsonParser.fromJson(toolInput, new TypeReference<>() {
});
}
// Based on the implementation in MethodInvokingFunctionCallback.
private Object[] buildMethodArguments(Map<String, Object> toolInputArguments, @Nullable ToolContext toolContext) {
return Stream.of(toolMethod.getParameters()).map(parameter -> {
if (parameter.getType().isAssignableFrom(ToolContext.class)) {
return toolContext;
}
Object rawArgument = toolInputArguments.get(parameter.getName());
return buildTypedArgument(rawArgument, parameter.getType());
}).toArray();
}
@Nullable
private Object buildTypedArgument(@Nullable Object value, Class<?> type) {
if (value == null) {
return null;
}
return JsonParser.toTypedObject(value, type);
}
@Nullable
private Object callMethod(Object[] methodArguments) {
if (isObjectNotPublic() || isMethodNotPublic()) {
toolMethod.setAccessible(true);
}
Object result;
try {
result = toolMethod.invoke(toolObject, methodArguments);
}
catch (IllegalAccessException ex) {
throw new IllegalStateException("Could not access method: " + ex.getMessage(), ex);
}
catch (InvocationTargetException ex) {
throw new ToolExecutionException(toolDefinition, ex.getCause());
}
return result;
}
private boolean isObjectNotPublic() {
return toolObject != null && !Modifier.isPublic(toolObject.getClass().getModifiers());
}
private boolean isMethodNotPublic() {
return !Modifier.isPublic(toolMethod.getModifiers());
}
@Override
public String toString() {
return "MethodToolCallback{" + "toolDefinition=" + toolDefinition + ", toolMetadata=" + toolMetadata + '}';
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private ToolDefinition toolDefinition;
private ToolMetadata toolMetadata;
private Method toolMethod;
private Object toolObject;
private ToolCallResultConverter toolCallResultConverter;
private Builder() {
}
public Builder toolDefinition(ToolDefinition toolDefinition) {
this.toolDefinition = toolDefinition;
return this;
}
public Builder toolMetadata(ToolMetadata toolMetadata) {
this.toolMetadata = toolMetadata;
return this;
}
public Builder toolMethod(Method toolMethod) {
this.toolMethod = toolMethod;
return this;
}
public Builder toolObject(Object toolObject) {
this.toolObject = toolObject;
return this;
}
public Builder toolCallResultConverter(ToolCallResultConverter toolCallResultConverter) {
this.toolCallResultConverter = toolCallResultConverter;
return this;
}
public MethodToolCallback build() {
return new MethodToolCallback(toolDefinition, toolMetadata, toolMethod, toolObject,
toolCallResultConverter);
}
}
}
FunctionToolCallback
核心方法主要关注call
- 模型提取的toolInput为json字符串,先转为定义的Request类型
I request = JsonParser.fromJson(toolInput, toolInputType);
- 工具调用,返回对应的工具结果
O response = toolFunction.apply(request, toolContext);
public class FunctionToolCallback<I, O> implements ToolCallback {
private static final Logger logger = LoggerFactory.getLogger(FunctionToolCallback.class);
private static final ToolCallResultConverter DEFAULT_RESULT_CONVERTER = new DefaultToolCallResultConverter();
private static final ToolMetadata DEFAULT_TOOL_METADATA = ToolMetadata.builder().build();
private final ToolDefinition toolDefinition;
private final ToolMetadata toolMetadata;
private final Type toolInputType;
private final BiFunction<I, ToolContext, O> toolFunction;
private final ToolCallResultConverter toolCallResultConverter;
public FunctionToolCallback(ToolDefinition toolDefinition, @Nullable ToolMetadata toolMetadata, Type toolInputType,
BiFunction<I, ToolContext, O> toolFunction, @Nullable ToolCallResultConverter toolCallResultConverter) {
Assert.notNull(toolDefinition, "toolDefinition cannot be null");
Assert.notNull(toolInputType, "toolInputType cannot be null");
Assert.notNull(toolFunction, "toolFunction cannot be null");
this.toolDefinition = toolDefinition;
this.toolMetadata = toolMetadata != null ? toolMetadata : DEFAULT_TOOL_METADATA;
this.toolFunction = toolFunction;
this.toolInputType = toolInputType;
this.toolCallResultConverter = toolCallResultConverter != null ? toolCallResultConverter
: DEFAULT_RESULT_CONVERTER;
}
@Override
public ToolDefinition getToolDefinition() {
return toolDefinition;
}
@Override
public ToolMetadata getToolMetadata() {
return toolMetadata;
}
@Override
public String call(String toolInput) {
return call(toolInput, null);
}
@Override
public String call(String toolInput, @Nullable ToolContext toolContext) {
Assert.hasText(toolInput, "toolInput cannot be null or empty");
logger.debug("Starting execution of tool: {}", toolDefinition.name());
I request = JsonParser.fromJson(toolInput, toolInputType);
O response = toolFunction.apply(request, toolContext);
logger.debug("Successful execution of tool: {}", toolDefinition.name());
return toolCallResultConverter.convert(response, null);
}
@Override
public String toString() {
return "FunctionToolCallback{" + "toolDefinition=" + toolDefinition + ", toolMetadata=" + toolMetadata + '}';
}
public static <I, O> Builder<I, O> builder(String name, BiFunction<I, ToolContext, O> function) {
return new Builder<>(name, function);
}
public static <I, O> Builder<I, O> builder(String name, Function<I, O> function) {
Assert.notNull(function, "function cannot be null");
return new Builder<>(name, (request, context) -> function.apply(request));
}
public static <O> Builder<Void, O> builder(String name, Supplier<O> supplier) {
Assert.notNull(supplier, "supplier cannot be null");
Function<Void, O> function = input -> supplier.get();
return builder(name, function).inputType(Void.class);
}
public static <I> Builder<I, Void> builder(String name, Consumer<I> consumer) {
Assert.notNull(consumer, "consumer cannot be null");
Function<I, Void> function = (I input) -> {
consumer.accept(input);
return null;
};
return builder(name, function);
}
public static class Builder<I, O> {
private String name;
private String description;
private String inputSchema;
private Type inputType;
private ToolMetadata toolMetadata;
private BiFunction<I, ToolContext, O> toolFunction;
private ToolCallResultConverter toolCallResultConverter;
private Builder(String name, BiFunction<I, ToolContext, O> toolFunction) {
Assert.hasText(name, "name cannot be null or empty");
Assert.notNull(toolFunction, "toolFunction cannot be null");
this.name = name;
this.toolFunction = toolFunction;
}
public Builder<I, O> description(String description) {
this.description = description;
return this;
}
public Builder<I, O> inputSchema(String inputSchema) {
this.inputSchema = inputSchema;
return this;
}
public Builder<I, O> inputType(Type inputType) {
this.inputType = inputType;
return this;
}
public Builder<I, O> inputType(ParameterizedTypeReference<?> inputType) {
Assert.notNull(inputType, "inputType cannot be null");
this.inputType = inputType.getType();
return this;
}
public Builder<I, O> toolMetadata(ToolMetadata toolMetadata) {
this.toolMetadata = toolMetadata;
return this;
}
public Builder<I, O> toolCallResultConverter(ToolCallResultConverter toolCallResultConverter) {
this.toolCallResultConverter = toolCallResultConverter;
return this;
}
public FunctionToolCallback<I, O> build() {
Assert.notNull(inputType, "inputType cannot be null");
var toolDefinition = ToolDefinition.builder()
.name(name)
.description(
StringUtils.hasText(description) ? description : ToolUtils.getToolDescriptionFromName(name))
.inputSchema(
StringUtils.hasText(inputSchema) ? inputSchema : JsonSchemaGenerator.generateForType(inputType))
.build();
return new FunctionToolCallback<>(toolDefinition, toolMetadata, inputType, toolFunction,
toolCallResultConverter);
}
}
}
ToolCallbackProvider(工具回调实例提供)
主要用于集中管理和提供工具回调
- getToolCallbacks:获得工具回调数组
public interface ToolCallbackProvider {
ToolCallback[] getToolCallbacks();
static ToolCallbackProvider from(List<? extends FunctionCallback> toolCallbacks) {
return new StaticToolCallbackProvider(toolCallbacks);
}
static ToolCallbackProvider from(FunctionCallback... toolCallbacks) {
return new StaticToolCallbackProvider(toolCallbacks);
}
}
MethodToolCallbackProvider
获取MethodToolCallback实例
public class MethodToolCallbackProvider implements ToolCallbackProvider {
private static final Logger logger = LoggerFactory.getLogger(MethodToolCallbackProvider.class);
private final List<Object> toolObjects;
private MethodToolCallbackProvider(List<Object> toolObjects) {
Assert.notNull(toolObjects, "toolObjects cannot be null");
Assert.noNullElements(toolObjects, "toolObjects cannot contain null elements");
this.toolObjects = toolObjects;
}
@Override
public ToolCallback[] getToolCallbacks() {
var toolCallbacks = toolObjects.stream()
.map(toolObject -> Stream
.of(ReflectionUtils.getDeclaredMethods(
AopUtils.isAopProxy(toolObject) ? AopUtils.getTargetClass(toolObject) : toolObject.getClass()))
.filter(toolMethod -> toolMethod.isAnnotationPresent(Tool.class))
.filter(toolMethod -> !isFunctionalType(toolMethod))
.map(toolMethod -> MethodToolCallback.builder()
.toolDefinition(ToolDefinition.from(toolMethod))
.toolMetadata(ToolMetadata.from(toolMethod))
.toolMethod(toolMethod)
.toolObject(toolObject)
.toolCallResultConverter(ToolUtils.getToolCallResultConverter(toolMethod))
.build())
.toArray(ToolCallback[]::new))
.flatMap(Stream::of)
.toArray(ToolCallback[]::new);
validateToolCallbacks(toolCallbacks);
return toolCallbacks;
}
private boolean isFunctionalType(Method toolMethod) {
var isFunction = ClassUtils.isAssignable(toolMethod.getReturnType(), Function.class)
|| ClassUtils.isAssignable(toolMethod.getReturnType(), Supplier.class)
|| ClassUtils.isAssignable(toolMethod.getReturnType(), Consumer.class);
if (isFunction) {
logger.warn("Method {} is annotated with @Tool but returns a functional type. "
+ "This is not supported and the method will be ignored.", toolMethod.getName());
}
return isFunction;
}
private void validateToolCallbacks(ToolCallback[] toolCallbacks) {
List<String> duplicateToolNames = ToolUtils.getDuplicateToolNames(toolCallbacks);
if (!duplicateToolNames.isEmpty()) {
throw new IllegalStateException("Multiple tools with the same name (%s) found in sources: %s".formatted(
String.join(", ", duplicateToolNames),
toolObjects.stream().map(o -> o.getClass().getName()).collect(Collectors.joining(", "))));
}
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private List<Object> toolObjects;
private Builder() {
}
public Builder toolObjects(Object... toolObjects) {
Assert.notNull(toolObjects, "toolObjects cannot be null");
this.toolObjects = Arrays.asList(toolObjects);
return this;
}
public MethodToolCallbackProvider build() {
return new MethodToolCallbackProvider(toolObjects);
}
}
}
StaticToolCallbackProvider
提供FunctionToolCallback,但目测还没有实现该功能
public class StaticToolCallbackProvider implements ToolCallbackProvider {
private final FunctionCallback[] toolCallbacks;
public StaticToolCallbackProvider(FunctionCallback... toolCallbacks) {
Assert.notNull(toolCallbacks, "ToolCallbacks must not be null");
this.toolCallbacks = toolCallbacks;
}
public StaticToolCallbackProvider(List<? extends FunctionCallback> toolCallbacks) {
Assert.noNullElements(toolCallbacks, "toolCallbacks cannot contain null elements");
this.toolCallbacks = toolCallbacks.toArray(new FunctionCallback[0]);
}
@Override
public FunctionCallback[] getToolCallbacks() {
return this.toolCallbacks;
}
}
ToolCallingManager(工具回调管理器)
public interface ToolCallingManager {
// 从配置中提取工具的定义,确保模型能正确识别和使用工具
List<ToolDefinition> resolveToolDefinitions(ToolCallingChatOptions chatOptions);
// 根据模型的响应,执行响应的工具调用,并返回执行结果
ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse);
// 构建工具调用管理器
static DefaultToolCallingManager.Builder builder() {
return DefaultToolCallingManager.builder();
}
}
DefaultToolCallingManager
核心功能如下
- 解析工具定义(resolveToolDefinitions):从ToolCallingChatOptions中解析出工具定义,确保模型能正确识别和使用工具
- 执行工具调用(executeToolCalls):根据模型响应,执行相应的工具调用,并返回工具的执行结果
- 构建工具上下文(buildToolContext):为工具调用提供上下文信息,历史的Message记录
- 管理工具回调:通过 ToolCallbackResolver 解析工具回调,支持动态工具调用
public class DefaultToolCallingManager implements ToolCallingManager {
@Override
public List<ToolDefinition> resolveToolDefinitions(ToolCallingChatOptions chatOptions) {
Assert.notNull(chatOptions, "chatOptions cannot be null");
List<FunctionCallback> toolCallbacks = new ArrayList<>(chatOptions.getToolCallbacks());
for (String toolName : chatOptions.getToolNames()) {
// Skip the tool if it is already present in the request toolCallbacks.
// That might happen if a tool is defined in the options
// both as a ToolCallback and as a tool name.
if (chatOptions.getToolCallbacks().stream().anyMatch(tool -> tool.getName().equals(toolName))) {
continue;
}
FunctionCallback toolCallback = toolCallbackResolver.resolve(toolName);
if (toolCallback == null) {
throw new IllegalStateException("No ToolCallback found for tool name: " + toolName);
}
toolCallbacks.add(toolCallback);
}
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();
}
@Override
public ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse) {
Assert.notNull(prompt, "prompt cannot be null");
Assert.notNull(chatResponse, "chatResponse cannot be null");
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");
}
AssistantMessage assistantMessage = toolCallGeneration.get().getOutput();
ToolContext toolContext = buildToolContext(prompt, assistantMessage);
InternalToolExecutionResult internalToolExecutionResult = executeToolCall(prompt, assistantMessage,
toolContext);
List<Message> conversationHistory = buildConversationHistoryAfterToolExecution(prompt.getInstructions(),
assistantMessage, internalToolExecutionResult.toolResponseMessage());
return ToolExecutionResult.builder()
.conversationHistory(conversationHistory)
.returnDirect(internalToolExecutionResult.returnDirect())
.build();
}
}
ToolCallResultConverter(工具结果转换器)
@FunctionalInterface
public interface ToolCallResultConverter {
// result:工具结果,returnType:返回类型
String convert(@Nullable Object result, @Nullable Type returnType);
}
DefaultToolCallResultConverter
ToolCallResultConverter接口类暂时的唯一实现,转为Json化的字符串
public final class DefaultToolCallResultConverter implements ToolCallResultConverter {
private static final Logger logger = LoggerFactory.getLogger(DefaultToolCallResultConverter.class);
@Override
public String convert(@Nullable Object result, @Nullable Type returnType) {
if (returnType == Void.TYPE) {
logger.debug("The tool has no return type. Converting to conventional response.");
return "Done";
}
else {
logger.debug("Converting tool result to JSON.");
return JsonParser.toJson(result);
}
}
}
ToolContext(工具上下文)
被构建于工具回调管理器
作用:
- 用于封装工具执行的上下文信息,确保上下文不可变,从而保证线程安全
- 通过getContext方法获取整个上下文,通过getToolCallHistory方法获取Message的历史记录
public class ToolContext {
public static final String TOOL_CALL_HISTORY = "TOOL_CALL_HISTORY";
private final Map<String, Object> context;
public ToolContext(Map<String, Object> context) {
this.context = Collections.unmodifiableMap(context);
}
public Map<String, Object> getContext() {
return this.context;
}
@SuppressWarnings("unchecked")
public List<Message> getToolCallHistory() {
return (List<Message>) this.context.get(TOOL_CALL_HISTORY);
}
}
ToolUtils(工具常见方法封装)
从方法上提取名称,主要根据方法上是否有Tool注解,若无则统一设置为方法名
- getToolName:获取工具名称
- getToolDescriptionFromName:根据工具名称生成工具的描述
- getToolDescription:获取工具描述
- getToolReturnDirect:判断工具是否直接返回结果
- getToolCallResultConverter:获取工具的结果转换器
- getDuplicateToolNames:检查工具回调列表中是否有重复的工具名称
public final class ToolUtils {
private ToolUtils() {
}
public static String getToolName(Method method) {
Assert.notNull(method, "method cannot be null");
var tool = method.getAnnotation(Tool.class);
if (tool == null) {
return method.getName();
}
return StringUtils.hasText(tool.name()) ? tool.name() : method.getName();
}
public static String getToolDescriptionFromName(String toolName) {
Assert.hasText(toolName, "toolName cannot be null or empty");
return ParsingUtils.reConcatenateCamelCase(toolName, " ");
}
public static String getToolDescription(Method method) {
Assert.notNull(method, "method cannot be null");
var tool = method.getAnnotation(Tool.class);
if (tool == null) {
return ParsingUtils.reConcatenateCamelCase(method.getName(), " ");
}
return StringUtils.hasText(tool.description()) ? tool.description() : method.getName();
}
public static boolean getToolReturnDirect(Method method) {
Assert.notNull(method, "method cannot be null");
var tool = method.getAnnotation(Tool.class);
return tool != null && tool.returnDirect();
}
public static ToolCallResultConverter getToolCallResultConverter(Method method) {
Assert.notNull(method, "method cannot be null");
var tool = method.getAnnotation(Tool.class);
if (tool == null) {
return new DefaultToolCallResultConverter();
}
var type = tool.resultConverter();
try {
return type.getDeclaredConstructor().newInstance();
}
catch (Exception e) {
throw new IllegalArgumentException("Failed to instantiate ToolCallResultConverter: " + type, e);
}
}
public static List<String> getDuplicateToolNames(List<FunctionCallback> toolCallbacks) {
Assert.notNull(toolCallbacks, "toolCallbacks cannot be null");
return toolCallbacks.stream()
.collect(Collectors.groupingBy(FunctionCallback::getName, Collectors.counting()))
.entrySet()
.stream()
.filter(entry -> entry.getValue() > 1)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
public static List<String> getDuplicateToolNames(FunctionCallback... toolCallbacks) {
Assert.notNull(toolCallbacks, "toolCallbacks cannot be null");
return getDuplicateToolNames(Arrays.asList(toolCallbacks));
}
}
tool工具实战(FunctionToolCallback && MethodToolCallBack版)
application.yml
server:
port: 8083
spring:
application:
name: Tool-Calling
ai:
dashscope:
api-key: ${DASHSCOPE_API_KEY}
toolcalling:
baidutranslate:
enabled: true
app-id : ${BAIDU_TRANSLATE_APP_ID}
secret-key: ${BAIDU_TRANSLATE_SECRET_KEY}
time:
enabled: true
weather:
enabled: true
api-key: ${WEATHER_API_KEY}
百度翻译API接入文档:https://api.fanyi.baidu.com/product/113
天气预测API接入文档:https://www.weatherapi.com/docs/
