Spring AI 开发笔记:ChatClient 的创建、配置与工具函数注册
本文介绍了SpringAI框架中ChatClient的核心功能与实现方式。ChatClient作为统一LLM客户端,提供标准化API支持多种大语言模型交互,主要功能包括:1)支持Prompt链式构建;2)通过FunctionCallback实现工具函数注册与调用;3)温度参数控制生成多样性(0.0-1.0范围);4)流式响应与批量请求处理。重点解析了函数调用的实现机制,包括FunctionCall
目录
4、执行ChatClientChat返回的Response包含的信息:
(1)这里使用了system、user、prompt,做一个解释
(2)在这里使用了Consumer进行回调,对Consumer传递机制解释:
写在前面:
本文说明了(1)如何创建ChatClient并且使用,(2)实现让ChatClient调用函数FunctionCallback。(3)温度参数的效果(4)Consumer传递的机制。
在 Spring AI 结合 ChatClient 实现可调用函数(工具调用)的整体流程中,判断是否调用函数、调用哪一个函数、生成函数入参,全部由当前 ChatClient 所绑定的大模型 LLM 来决定;程序运行时,会和大模型服务后端产生多次网络交互、多轮消息传递:首先 Spring AI 会自动将我们注册的自定义工具函数信息、用户问题一起传给 LLM,由 LLM 判断并返回需要调用的函数名称与参数,接着本地程序执行对应的自定义业务函数,再把函数执行结果回传给 LLM,最后 LLM 结合工具返回的数据,整合生成最终回答。而这整套多轮对话拼接、工具信息封装、LLM 返回内容解析、本地函数触发执行、多轮请求循环的复杂逻辑,全都由 Spring AI 框架自动完成,开发者只需简单注册工具函数、使用 ChatClient 调用即可,无需手动编写多轮交互和函数解析的底层代码。
ChatClient作用:
ChatClient 是 Spring AI 框架提供的统一 LLM 客户端 ,用于简化与各种大语言模型的交互。
统一接口 :无论哪种 LLM 提供商,使用相同的 API
调用 Prompt 构建: 支持链式调用构建复杂提示词
函数调用 :支持注册工具函数,实现 LLM 的工具调用能力
流式响应 :支持实时流式输出
批量调用 :支持一次发送多个请求
//创建带 Function Calling 支持的 ChatClient
public ChatClient createClientWithFunctions(String apiUrl, String apiKey,
String model, Double temperature,
List<FunctionCallback> functions) {
// 使用构造函数创建OpenAiApi(支持自定义baseUrl)
//Spring AI 的 OpenAiApi 会自行拼接 /v1/chat/completions, 所以这里不能有 /v1/chat/completions 后缀
OpenAiApi openAiApi = new OpenAiApi(apiUrl,apiKey);
// 创建OpenAI兼容的ChatModel并配置选项
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model(model)
.temperature(temperature)
.build();
ChatModel chatModel = new OpenAiChatModel(openAiApi, options);
//创建构建器
ChatClient.Builder builder = ChatClient.builder(chatModel);
//注册工具函数
builder.defaultFunctions(functions.toArray(new FunctionCallback[0]));
return builder.build();
}
1、defaultFunctions() 的作用 :
1. 注册工具函数 :将自定义函数注册到 ChatClient
2. 启用工具调用 :使 LLM 具备调用外部工具的能力
3. 扩展 LLM 能力 :让 LLM 可以访问实时数据、执行计算、使用业务技能
2、温度参数(Temperature)
用于控制 LLM 生成文本时的随机性和多样性,标准范围是 0.0 -1.0(有的是2.0)
温度值 效果 适用场景
0.0 完全确定,只选择最可能的词 事实性回答、代码生成
0.7 平衡确定性和多样性 日常对话、内容创作
1.0 较高随机性 创意写作、头脑风暴
>1.0 非常随机,可能产生无意义内容 艺术创作、探索性生成
3、核心接口: FunctionCallback
概念:
项目中使用 Spring AI 的 FunctionCallback 接口来定义可被 LLM 调用的工具函数。
FunctionCallback 是 Spring AI 提供的标准接口,定义了工具函数的基本结构,实现这个接口的函数可以作为工具函数。
public interface FunctionCallback {
String getName(); // 函数名称(LLM 调用时使用)
String getDescription(); // 函数描述(帮助 LLM 理解用途)
String getInputTypeSchema(); // 输入参数的 JSON Schema
String call(String functionInput); // 函数执行逻辑
}
getName()作用
LLM 识别函数的唯一标识(在同一组函数中必须唯一),在函数调用时,LLM 会指定调用哪个函数。
getDescriptoin()作用
告诉 LLM 这个函数是做什么的,帮助 LLM 判断何时应该调用这个函数,描述越详细,LLM 判断越准确。
getInputTypeSchema()作用
定义函数的输入参数结构,告诉 LLM 需要传递什么参数
格式:JSON Schema 格式字符串
内容:参数名、类型、描述、是否必填
例如:
在JSON 中的 Object 是一种键值对的集合
@Override
public String getInputTypeSchema() {
return """
{
"type": "object", //1.数据类型定义
"properties": { //2.属性列表(具体的字段)
"city": {"type": "string",//字段类型
"description": "城市名称"},//字段描述
"date": {"type": "string",
"description": "查询日期(可选)"}
},
"required": ["city"]//必填项声明
}
""";
}
call(String functionInput)作用:
职责:解析参数、执行业务逻辑、返回结果
输入:JSON 格式的参数字符串
返回结果 :字符串形式的执行结果。
例如:
@Override
public String call(String functionInput) {
// 1. 解析输入参数
JSONObject params = new JSONObject(functionInput);
String city = params.getString("city");
// 2. 执行实际逻辑
WeatherData weather = weatherService.getWeather(city);
// 3. 返回结果
return weather.toString();
}
4、执行ChatClientChat返回的Response包含的信息:
// 调用时自动判断是否需要调用工具
ChatResponse response = chatClient.prompt()
.system("你可以调用工具获取信息")
.user("请查看 weather 技能的详情")
.call()
.chatResponse();
// 1. 获取生成的文本内容
String content = response.getResult().getOutput().getContent();
// 2. 获取消息角色
String role = response.getResult().getOutput().getRole();
// 3. 获取 Token 统计
var usage = response.getMetadata().getUsage();
int inputTokens = usage.getPromptTokens(); // 输入 Token 数
int outputTokens = usage.getGenerationTokens(); // 输出 Token 数
int totalTokens = usage.getTotalTokens(); // 总 Token 数
// 4. 获取模型名称
String model = response.getMetadata().getModel();
//5、 获取工具调用列表
List<ToolCall> toolCalls = response.getResult().getOutput().getToolCalls();
5、ChatClient的使用示例:
1. 普通同步调用
ChatResponse response = chatClient.prompt()
.system("你是一个友好的助手") // 系统提示
.user("今天天气怎么样?") // 用户输入
.call() // 执行调用
.chatResponse(); // 获取响应
// 获取结果
String content = response.getResult().getOutput().getContent();
2. 流式调用
// 1. 创建一个 StringBuilder 来累积内容
StringBuilder accumulated = new StringBuilder();
// 2. 流式调用并累积
chatClient.prompt()
.system("你是一个故事生成器")
.user("请讲一个冒险故事")
.stream() // 启用流式输出
.content() // 获取内容流
.doOnNext(chunk -> {
// 每收到一个块,追加到 StringBuilder
accumulated.append(chunk);
//可以通过Consumer实时传递给前端用户
Map<String, Object> data = new HashMap<>();
data.put("chunk", chunk);
data.put("accumulated", accumulated.toString());
progressCallback.accept(
ExecutionEvent.Progress("生成中",data);
);
})
.blockLast(); // 等待流结束
// 3. 获取完整输出
String fullContent = accumulated.toString();
(1)这里使用了system、user、prompt,做一个解释
system
具有最高的优先级,通常不会被用户的输入覆盖,这是给 AI 的系统级指令。它定义了 AI 在整个对话中的身份、行为准则和边界。
prompt
这里的prompt()是创建一个提示词构建器。
而Prompt的定义是:Prompt 是你发送给大模型(LLM)的完整输入指令集合。它不仅仅是一句话,而是一个包含上下文、指令和问题的结构化数据包。
user
这是用户(也就是你或你的应用使用者)向 AI 提出的具体问题、指令或输入数据。
(2)在这里使用了Consumer进行回调,对Consumer传递机制解释:
传递机制的核心要点 :
1. 单一源头 :回调函数在 Controller 中创建,定义最终处理逻辑,在传递过程中共享同一个回调实例 :所有组件使用同一个按钮(如下)。
//__Controller.java - 回调创建点
Consumer<ExecutionEvent> eventCallback = event -> {
// 最终处理:通过 SSE 发送给前端
emitter.send(SseEmitter.event()
.name(event.getEventType())
.data(event));
};
2. 链式传递 :沿着调用链(Controller → Engine → Executor)传递
3. 多点触发 :每个组件都可以调用 accept() 发送事件,每调用一次 accept() 都会执行最初定义的逻辑。
4. 统一出口 :所有事件最终都通过 SSE 发送给前端
设计价值 :解耦组件、灵活扩展、实时监控。
总结:一次完整的函数调用是如何发生的?
- 用户提问:
chatClient.prompt()发送请求。 - 模型思考:LLM 发现需要工具,返回
ToolCall指令。 - 框架执行:Spring AI 根据指令找到对应的
FunctionCallback,调用call()方法。 - 结果回填:执行结果再次发给 LLM。
- 最终回答:LLM 结合工具返回的数据,生成最终的自然语言回复。
---------------------------------------------------------分割线---------------------------------------------------------------
本篇文章到此结束,感谢阅读!
完结撒花 ✿✿✿✿✿✿
更多推荐


所有评论(0)