收藏本站,收获最前沿的人工智能与编程资讯!!

Chat Client API 完整使用教程|Spring AI 聊天客户端实战指南

技术文档 17℃ 0

聊天客户端API

ChatClient 提供流式风格API,用于与AI模型进行通信。它同时支持同步编程模型和流式编程模型。

请参阅本文档末尾的实现说明,了解在 ChatClient 中命令式编程模型与响应式编程模型的组合使用方式。

该流式API提供了构建提示词(Prompt)各组成部分的方法,提示词将作为输入传递给AI模型。提示词包含引导AI模型输出内容与行为的指令文本。从API视角来看,提示词由一组消息集合构成。

AI模型主要处理两类消息:用户消息(来自用户的直接输入)和系统消息(由系统生成,用于引导对话流程)。

这些消息通常包含占位符,程序运行时会根据用户输入替换占位符内容,从而定制AI模型对用户输入的响应结果。

同时还可配置提示词相关选项,例如指定要使用的AI模型名称、温度参数(用于控制生成内容的随机性与创意程度)。

创建聊天客户端

通过 ChatClient.Builder 对象创建 ChatClient。你可以借助Spring Boot自动配置获取已自动装配的 ChatClient.Builder 实例并注入业务类,也可以通过编码方式手动创建实例。

使用自动配置的ChatClient.Builder

在最简单的使用场景中,Spring AI 提供Spring Boot自动配置,自动创建原型类型的 ChatClient.Builder Bean,可直接注入到自定义类中。以下是一个简单示例,演示如何获取用户请求对应的字符串响应结果。

@RestController
class MyController {

    private final ChatClient chatClient;

    public MyController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    @GetMapping("/ai")
    String generation(String userInput) {
        return this.chatClient.prompt()
            .user(userInput)
            .call()
            .content();
    }
}

在该示例中,用户输入赋值给用户消息内容。call() 方法向AI模型发起请求,content() 方法将AI模型的响应以字符串形式返回。

多聊天模型适配

在单个应用中,常会遇到需要集成多个聊天模型的场景:

  • 为不同类型任务分配不同模型(如高性能模型处理复杂推理,轻量低成本模型处理简单任务)

  • 当某个模型服务不可用时,实现故障降级兜底机制

  • 对不同模型或配置进行A/B测试

  • 允许用户根据个人偏好自主选择模型

  • 组合专用领域模型(代码生成模型、创意内容生成模型等)

默认情况下,Spring AI 仅自动配置单个 ChatClient.Builder Bean。若应用需要集成多个聊天模型,可按以下方式处理:

首先需关闭 ChatClient.Builder 自动配置,配置参数:spring.ai.chat.client.enabled=false,之后即可手动创建多个 ChatClient 实例。

同模型类型创建多个ChatClient

适用于底层使用同一模型、但配置参数不同的多实例创建场景。

// 编码方式创建ChatClient实例
ChatModel myChatModel = ... // 已由Spring Boot自动装配
ChatClient chatClient = ChatClient.create(myChatModel);

// 借助构建器实现更精细化配置
ChatClient.Builder builder = ChatClient.builder(myChatModel);
ChatClient customChatClient = builder
    .defaultSystemPrompt("You are a helpful assistant.")
    .build();

不同模型类型创建独立ChatClient

接入多个AI模型时,可为每个模型单独定义 ChatClient Bean:

import org.springframework.ai.chat.ChatClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatClientConfig {

    @Bean
    public ChatClient openAiChatClient(OpenAiChatModel chatModel) {
        return ChatClient.create(chatModel);
    }

    @Bean
    public ChatClient anthropicChatClient(AnthropicChatModel chatModel) {
        return ChatClient.create(chatModel);
    }
}

随后可通过 @Qualifier 注解将指定Bean注入业务组件:

@Configuration
public class ChatClientExample {

    @Bean
    CommandLineRunner cli(
            @Qualifier("openAiChatClient") ChatClient openAiChatClient,
            @Qualifier("anthropicChatClient") ChatClient anthropicChatClient) {

        return args -> {
            var scanner = new Scanner(System.in);
            ChatClient chat;

            // 模型选择
            System.out.println("\nSelect your AI model:");
            System.out.println("1. OpenAI");
            System.out.println("2. Anthropic");
            System.out.print("Enter your choice (1 or 2): ");

            String choice = scanner.nextLine().trim();

            if (choice.equals("1")) {
                chat = openAiChatClient;
                System.out.println("Using OpenAI model");
            } else {
                chat = anthropicChatClient;
                System.out.println("Using Anthropic model");
            }

            // 使用选中的聊天客户端
            System.out.print("\nEnter your question: ");
            String input = scanner.nextLine();
            String response = chat.prompt(input).call().content();
            System.out.println("ASSISTANT: " + response);

            scanner.close();
        };
    }
}

多兼容OpenAI接口端点适配

OpenAiApi 和 OpenAiChatModel 提供 mutate() 方法,可基于现有实例修改属性生成新实例,非常适合对接多个兼容OpenAI协议的API服务。

@Service
public class MultiModelService {

    private static final Logger logger = LoggerFactory.getLogger(MultiModelService.class);

    @Autowired
    private OpenAiChatModel baseChatModel;

    @Autowired
    private OpenAiApi baseOpenAiApi;

    public void multiClientFlow() {
        try {
            // 为Groq(Llama3)生成新的OpenAiApi实例
            OpenAiApi groqApi = baseOpenAiApi.mutate()
                .baseUrl("https://api.groq.com/openai")
                .apiKey(System.getenv("GROQ_API_KEY"))
                .build();

            // 为OpenAI GPT-4生成新的OpenAiApi实例
            OpenAiApi gpt4Api = baseOpenAiApi.mutate()
                .baseUrl("https://api.openai.com")
                .apiKey(System.getenv("OPENAI_API_KEY"))
                .build();

            // 基于Groq API创建专用ChatModel
            OpenAiChatModel groqModel = baseChatModel.mutate()
                .openAiApi(groqApi)
                .defaultOptions(OpenAiChatOptions.builder().model("llama3-70b-8192").temperature(0.5).build())
                .build();

            // 基于GPT-4 API创建专用ChatModel
            OpenAiChatModel gpt4Model = baseChatModel.mutate()
                .openAiApi(gpt4Api)
                .defaultOptions(OpenAiChatOptions.builder().model("gpt-4").temperature(0.7).build())
                .build();

            // 通用测试提示词
            String prompt = "What is the capital of France?";

            String groqResponse = ChatClient.builder(groqModel).build().prompt(prompt).call().content();
            String gpt4Response = ChatClient.builder(gpt4Model).build().prompt(prompt).call().content();

            logger.info("Groq (Llama3) response: {}", groqResponse);
            logger.info("OpenAI GPT-4 response: {}", gpt4Response);
        }
        catch (Exception e) {
            logger.error("多客户端调用异常", e);
        }
    }
}

ChatClient流式风格API

ChatClient流式API通过重载的 prompt 方法提供三种创建提示词的方式,以此开启流式调用链路:

  • prompt():无参方法,从零开始链式构建用户消息、系统消息等完整提示词结构

  • prompt(Prompt prompt):接收Prompt对象,可传入通过非流式API手动创建的Prompt实例

  • prompt(String content):便捷重载方法,直接传入用户文本内容

ChatClient响应处理

ChatClient API通过流式调用提供多种AI模型响应格式化方式。

返回ChatResponse对象

AI模型的原始响应为 ChatResponse 复杂结构,包含生成元数据、多组生成结果(Generations)及令牌消耗信息(单个令牌约等效3/4个英文单词)。大模型服务商通常按单次请求消耗令牌数计费。

调用 call() 后链式调用 chatResponse() 可获取带元数据的完整响应对象:

ChatResponse chatResponse = chatClient.prompt()
    .user("Tell me a joke")
    .call()
    .chatResponse();
映射为实体类

可将模型返回文本直接映射为Java实体类,通过 entity() 方法实现。

定义Java记录类示例:

record ActorFilms(String actor, List<String> movies) {}

快速映射模型输出到实体:

ActorFilms actorFilms = chatClient.prompt()
    .user("Generate the filmography for a random actor.")
    .call()
    .entity(ActorFilms.class);

支持泛型集合类型重载方法:

List<ActorFilms> actorFilms = chatClient.prompt()
    .user("Generate the filmography of 5 movies for Tom Hanks and Bill Murray.")
    .call()
    .entity(new ParameterizedTypeReference<List<ActorFilms>>() {});
原生结构化输出

当下多数AI模型原生支持结构化输出,可在调用ChatClient时通过 AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT 顾问参数开启该能力。可通过 ChatClient.Builder 的 defaultAdvisors() 全局配置,也可单次调用单独配置:

ActorFilms actorFilms = chatClient.prompt()
    .advisors(AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT)
    .user("Generate the filmography for a random actor.")
    .call()
    .entity(ActorFilms.class);

部分模型(如OpenAI)不原生支持对象数组格式,此类场景可使用Spring AI默认结构化输出转换能力。

流式响应

通过 stream() 方法获取异步流式响应:

Flux<String> output = chatClient.prompt()
    .user("Tell me a joke")
    .stream()
    .content();

也可通过 chatResponse() 获取流式 ChatResponse 对象。

后续版本将支持响应式流式调用直接返回Java实体类,现阶段需手动通过结构化输出转换器拼接并解析流式结果:

var converter = new BeanOutputConverter<>(new ParameterizedTypeReference<List<ActorsFilms>>() {});

Flux<String> flux = this.chatClient.prompt()
    .user(u -> u.text("""
                        Generate the filmography for a random actor.
                        {format}
                      """)
            .param("format", this.converter.getFormat()))
    .stream()
    .content();

String content = this.flux.collectList().block().stream().collect(Collectors.joining());

List<ActorsFilms> actorFilms = this.converter.convert(this.content);

提示词模板

ChatClient流式API支持将用户文本、系统文本定义为带变量的模板,运行时动态替换变量值。

String answer = ChatClient.create(chatModel).prompt()
    .user(u -> u
            .text("Tell me the names of 5 movies whose soundtrack was composed by {composer}")
            .param("composer", "John Williams"))
    .call()
    .content();

ChatClient 内部通过 PromptTemplate 处理模板文本,依托 TemplateRenderer 实现变量替换。Spring AI 默认采用基于StringTemplate引擎的 StTemplateRenderer,同时提供无模板处理的 NoOpTemplateRenderer。

通过 ChatClient 配置的 TemplateRenderer 仅作用于链式构建的提示词内容,不影响顾问(Advisor)内部自定义模板。开发者可自定义 TemplateRenderer 实现,也可修改默认模板语法分隔符。

自定义模板分隔符示例:

String answer = ChatClient.create(chatModel).prompt()
    .user(u -> u
            .text("Tell me the names of 5 movies whose soundtrack was composed by <composer>")
            .param("composer", "John Williams"))
    .templateRenderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build())
    .call()
    .content();

call() 方法返回值

链式调用 call() 后,支持多种响应类型获取方式:

  • String content():返回模型响应字符串内容

  • ChatResponse chatResponse():返回包含多组生成结果与令牌元数据的完整响应对象

  • ChatClientResponse chatClientResponse():包含ChatResponse与ChatClient执行上下文,可获取顾问执行过程中的附加数据(如RAG检索文档)

  • entity系列方法:映射为Java实体类型

  • responseEntity系列方法:同时返回完整ChatResponse与结构化实体对象

注:call() 仅指定同步/调用模式,并不会立即触发模型调用;真正发起请求执行模型调用,是在调用 content()、chatResponse() 等取值方法时触发。

stream() 方法返回值

链式调用 stream() 后,流式响应支持以下返回类型:

  • Flux<String> content():返回模型逐段生成的字符串流

  • Flux<ChatResponse> chatResponse():返回带元数据的流式ChatResponse对象

  • Flux<ChatClientResponse> chatClientResponse():返回包含执行上下文的流式响应对象

消息元数据

ChatClient 支持为用户消息和系统消息附加自定义元数据,用于传递上下文信息,供模型推理或后续业务处理使用。

为用户消息添加元数据
// 逐个添加元数据键值对
String response = chatClient.prompt()
    .user(u -> u.text("What's the weather like?")
        .metadata("messageId", "msg-123")
        .metadata("userId", "user-456")
        .metadata("priority", "high"))
    .call()
    .content();

// 批量添加元数据
Map<String, Object> userMetadata = Map.of(
    "messageId", "msg-123",
    "userId", "user-456",
    "timestamp", System.currentTimeMillis()
);

String response = chatClient.prompt()
    .user(u -> u.text("What's the weather like?")
        .metadata(userMetadata))
    .call()
    .content();
为系统消息添加元数据
String response = chatClient.prompt()
    .system(s -> s.text("You are a helpful assistant.")
        .metadata("version", "1.0")
        .metadata("model", "gpt-4"))
    .user("Tell me a joke")
    .call()
    .content();
默认元数据配置

可在ChatClient构建器层面配置全局默认元数据:

@Configuration
class Config {
    @Bean
    ChatClient chatClient(ChatClient.Builder builder) {
        return builder
            .defaultSystem(s -> s.text("You are a helpful assistant")
                .metadata("assistantType", "general")
                .metadata("version", "1.0"))
            .defaultUser(u -> u.text("Default user context")
                .metadata("sessionId", "default-session"))
            .build();
    }
}
元数据校验规则
  • 元数据键不能为空或空字符串

  • 元数据值不能为null

  • 批量传入Map时,键和值均不能包含null元素

非法示例(会抛出参数异常):

// 非法:键为null
chatClient.prompt()
    .user(u -> u.text("Hello")
        .metadata(null, "value"))
    .call()
    .content();

// 非法:值为null
chatClient.prompt()
    .user(u -> u.text("Hello")
        .metadata("key", null))
    .call()
    .content();
元数据获取

元数据会封装在 UserMessage 和 SystemMessage 对象中,通过消息的 getMetadata() 方法获取,适用于顾问处理、对话历史解析等场景。

默认配置使用

在配置类中为ChatClient设置默认系统提示词,可简化业务代码,无需每次请求重复指定系统文本。

固定默认系统文本
@Configuration
class Config {

    @Bean
    ChatClient chatClient(ChatClient.Builder builder) {
        return builder.defaultSystem("You are a friendly chat bot that answers question in the voice of a Pirate")
                .build();
    }

}

控制器调用示例:

@RestController
class AIController {

	private final ChatClient chatClient;

	AIController(ChatClient chatClient) {
		this.chatClient = chatClient;
	}

	@GetMapping("/ai/simple")
	public Map<String, String> completion(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
		return Map.of("completion", this.chatClient.prompt().user(message).call().content());
	}
}
带参数的默认系统文本

支持在默认系统文本中使用占位符,运行时动态传入参数替换:

@Configuration
class Config {

    @Bean
    ChatClient chatClient(ChatClient.Builder builder) {
        return builder.defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}")
                .build();
    }

}
@RestController
class AIController {
	private final ChatClient chatClient;

	AIController(ChatClient chatClient) {
		this.chatClient = chatClient;
	}

	@GetMapping("/ai")
	Map<String, String> completion(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message, String voice) {
		return Map.of("completion",
				this.chatClient.prompt()
						.system(sp -> sp.param("voice", voice))
						.user(message)
						.call()
						.content());
	}

}
其他全局默认配置

在 ChatClient.Builder 中可配置多项全局默认项:默认模型参数、默认工具函数、默认用户文本、默认顾问链路等,运行时可通过同名非default前缀方法覆盖全局配置。

顾问机制(Advisors)

Advisors API 提供灵活强大的能力,可拦截、修改、增强Spring应用中的AI交互流程,常用于为提示词追加上下文数据。

常用上下文数据类型:私有业务数据、对话历史记录。

顾问执行顺序至关重要,前一个顾问的修改结果会传递给下一个顾问。

ChatClient.builder(chatModel)
    .build()
    .prompt()
    .advisors(
        MessageChatMemoryAdvisor.builder(chatMemory).build(),
        QuestionAnswerAdvisor.builder(vectorStore).build()
    )
    .user(userText)
    .call()
    .content();

上述配置中,先通过会话内存顾问追加对话历史,再通过问答检索顾问基于历史和用户问题检索关联上下文,提升回答精准度。

日志记录

SimpleLoggerAdvisor 是内置顾问,用于记录ChatClient的请求与响应数据,便于调试和监控AI交互。

启用方式:构建ChatClient时加入日志顾问链路,建议放在链路末尾;配置日志级别为DEBUG即可打印日志。

ChatResponse response = ChatClient.create(chatModel).prompt()
        .advisors(new SimpleLoggerAdvisor())
        .user("Tell me a joke?")
        .call()
        .chatResponse();

日志配置:logging.level.org.springframework.ai.chat.client.advisor=DEBUG

支持自定义日志打印格式与顺序,生产环境需谨慎避免打印敏感数据。

聊天记忆(Chat Memory)

ChatMemory 接口定义对话内存存储规范,提供消息新增、查询、清空等方法。内置默认实现为 MessageWindowChatMemory。

MessageWindowChatMemory 维护固定数量的消息窗口(默认20条),超出上限自动淘汰旧消息,保留系统消息;新增系统消息时会清空历史系统消息,兼顾上下文完整性与内存占用可控性。

底层通过 ChatMemoryRepository 实现存储,提供内存、JDBC、Cassandra、Neo4j 等多种存储实现。

实现说明

  • ChatClient 支持命令式与响应式编程模型混用,是该API的特色设计

  • 自定义模型HTTP客户端时,需同时配置 RestClient 和 WebClient

  • Spring Boot 3.4 存在已知Bug,需配置 spring.http.client.factory=jdk 避免部分AI功能异常

  • 流式响应仅支持响应式技术栈,命令式应用需引入webflux依赖

  • 非流式调用仅适配Servlet技术栈,响应式应用需引入web依赖并兼容阻塞调用

  • 工具函数调用为阻塞式,会导致Micrometer链路追踪不完整

  • 内置顾问普通调用为阻塞式,流式调用为非阻塞式,可单独配置顾问调度器

相关推荐