目录

写在前面:

ChatClient作用:

1、defaultFunctions() 的作用 :

2、温度参数(Temperature)

3、核心接口: FunctionCallback

概念:

getName()作用

getDescriptoin()作用

getInputTypeSchema()作用

call(String functionInput)作用:

4、执行ChatClientChat返回的Response包含的信息:

5、ChatClient的使用示例:

1. 普通同步调用

2. 流式调用

(1)这里使用了system、user、prompt,做一个解释

system

prompt

user

(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 发送给前端
设计价值 :解耦组件、灵活扩展、实时监控。

总结:一次完整的函数调用是如何发生的?

  1. 用户提问chatClient.prompt() 发送请求。
  2. 模型思考:LLM 发现需要工具,返回 ToolCall 指令。
  3. 框架执行:Spring AI 根据指令找到对应的 FunctionCallback,调用 call() 方法。
  4. 结果回填:执行结果再次发给 LLM。
  5. 最终回答:LLM 结合工具返回的数据,生成最终的自然语言回复。

---------------------------------------------------------分割线---------------------------------------------------------------

本篇文章到此结束,感谢阅读!
完结撒花 ✿✿✿✿✿✿

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐