04_Spring AI 干货笔记之对话客户端 API

一、对话客户端 API

ChatClient 提供了用于与 AI 模型通信的流式 API,支持同步和流式两种编程模型。

关于 ChatClient 中命令式与响应式编程模型结合使用的说明,请参阅本文档末尾的实现说明。

该流式 API 提供了一系列方法,用于构建传递给 AI 模型作为输入的 Prompt 的各个组成部分。Prompt 包含指导 AI 模型输出和行为的指令文本。从 API 的角度来看,提示词由消息集合构成。

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

这些消息通常包含占位符,运行时根据用户输入进行替换,以定制 AI 模型对用户输入的响应。

还可以指定 Prompt 选项,例如要使用的 AI 模型名称和控制生成输出随机性或创造性的温度设置。

二、创建 ChatClient

ChatClient 通过 ChatClient.Builder 对象创建。您可以为任何 Spring Boot 自动配置的 ChatModel 获取一个自动配置的 ChatClient.Builder 实例,或者以编程方式创建一个。

2.1 使用自动配置的 ChatClient.Builder

在最简单的用例中,Spring AI 提供 Spring Boot 自动配置,创建一个原型 ChatClient.Builder bean 供您注入到类中。以下是一个检索简单用户请求的字符串响应的示例:

java 复制代码
@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 模型的响应作为字符串返回。

2.2 使用多个对话模型

在单个应用程序中可能需要使用多个对话模型的几种场景:

  • 为不同类型的任务使用不同模型(例如,复杂推理使用功能强大的模型,简单任务使用更快、更便宜的模型)

  • 当一个模型服务不可用时实现回退机制

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

  • 根据用户偏好提供模型选择

  • 组合专用模型(一个用于代码生成,另一个用于创意内容等)

默认情况下,Spring AI 自动配置一个单一的 ChatClient.Builder bean。但是,您可能需要在应用程序中使用多个对话模型。以下是处理此场景的方法:

在所有情况下,您都需要通过设置属性 spring.ai.chat.client.enabled=false 来禁用 ChatClient.Builder 的自动配置。

这允许您手动创建多个 ChatClient 实例。

2.2.1 具有单一模型类型的多个 ChatClient

本节介绍一个常见用例:需要创建多个都使用相同底层模型类型但配置不同的 ChatClient 实例。

java 复制代码
// 以编程方式创建 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();

2.2.2 针对不同模型类型的 ChatClient

当使用多个 AI 模型时,您可以为每个模型定义独立的 ChatClient bean:

java 复制代码
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 注入到应用程序组件中:

java 复制代码
@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("\n选择您的 AI 模型:");
            System.out.println("1. OpenAI");
            System.out.println("2. Anthropic");
            System.out.print("输入您的选择 (1 或 2): ");

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

            if (choice.equals("1")) {
                chat = openAiChatClient;
                System.out.println("使用 OpenAI 模型");
            } else {
                chat = anthropicChatClient;
                System.out.println("使用 Anthropic 模型");
            }

            // 使用选定的对话客户端
            System.out.print("\n输入您的问题: ");
            String input = scanner.nextLine();
            String response = chat.prompt(input).call().content();
            System.out.println("助手: " + response);

            scanner.close();
        };
    }
}

2.2.3 多个 OpenAI 兼容 API 端点

OpenAiApi 和 OpenAiChatModel 类提供了一个 mutate() 方法,允许您创建具有不同属性的现有实例的变体。这在需要与多个 OpenAI 兼容的 API 一起工作时特别有用。

java 复制代码
@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 派生新的 OpenAiChatModel
            OpenAiChatModel groqModel = baseChatModel.mutate()
                .openAiApi(groqApi)
                .defaultOptions(OpenAiChatOptions.builder().model("llama3-70b-8192").temperature(0.5).build())
                .build();

            // 为 GPT-4 派生新的 OpenAiChatModel
            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) 响应: {}", groqResponse);
            logger.info("OpenAI GPT-4 响应: {}", gpt4Response);
        }
        catch (Exception e) {
            logger.error("多客户端流程出错", e);
        }
    }
}

三、ChatClient 流式 API

ChatClient 流式 API 通过重载的 prompt 方法提供了三种不同的方式来创建提示词:

  • prompt(): 此无参数方法让您开始使用流式 API,允许您构建用户、系统消息和提示词的其他部分。

  • prompt(Prompt prompt): 此方法接受一个 Prompt 参数,允许您传入使用 Prompt 的非流式 API 创建的 Prompt 实例。

  • prompt(String content): 这是一个便捷方法,类似于上一个重载。它接受用户的文本内容。

四、ChatClient 响应

ChatClient API 通过流式 API 提供了几种格式化 AI 模型响应的方法。

4.1 返回 ChatResponse

AI 模型的响应是一个由 ChatResponse 类型定义的丰富结构。它包含有关响应生成方式的元数据,并且还可以包含多个响应(称为 Generations),每个响应都有自己的元数据。元数据包括用于创建响应的令牌数量(每个令牌大约相当于 3/4 个单词)。此信息很重要,因为托管 AI 模型根据每个请求使用的令牌数量收费。

下面通过调用 call() 方法后的 chatResponse() 方法展示了返回包含元数据的 ChatResponse 对象的示例:

java 复制代码
ChatResponse chatResponse = chatClient.prompt()
    .user("Tell me a joke")
    .call()
    .chatResponse();

4.2 返回实体

您通常希望返回一个从返回的字符串映射而来的实体类。entity() 方法提供了此功能。

例如,给定 Java 记录:

java 复制代码
record ActorFilms(String actor, List<String> movies) {}

您可以使用 entity() 方法轻松地将 AI 模型的输出映射到此记录,如下所示:

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

还有一个重载的 entity 方法,签名是 entity(ParameterizedTypeReference type),允许您指定类型,例如泛型 List:

java 复制代码
List<ActorFilms> actorFilms = chatClient.prompt()
    .user("Generate the filmography of 5 movies for Tom Hanks and Bill Murray.")
    .call()
    .entity(new ParameterizedTypeReference<List<ActorFilms>>() {});

4.3 流式响应

stream() 方法允许您获得异步响应,如下所示:

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

您也可以使用方法 Flux chatResponse() 来流式传输 ChatResponse。

未来,我们将提供一个便捷方法,让您能够通过响应式的 stream() 方法返回 Java 实体。目前,您应该使用结构化输出转换器来显式转换聚合响应,如下所示。这也演示了流式 API 中参数的使用,文档后续章节将对此进行更详细的讨论。

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 允许您提供带有变量的用户和系统文本作为模板,这些变量在运行时被替换。

java 复制代码
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 使用 StTemplateRenderer 实现,该实现基于 Terence Parr 开发的开源 StringTemplate 引擎。

Spring AI 还提供了一个 NoOpTemplateRenderer,用于不需要模板处理的情况。

直接在 ChatClient 上配置的 TemplateRenderer(通过 .templateRenderer())仅适用于在

ChatClient 构建器链中直接定义的提示词内容(例如,通过 .user(), .system())。它不影响像

QuestionAnswerAdvisor 这样的 Advisor 内部使用的模板,这些模板有自己的模板定制机制(请参阅自定义 Advisor 模板)。

如果您希望使用不同的模板引擎,可以直接向 ChatClient 提供 TemplateRenderer 接口的自定义实现。您也可以继续使用默认的 StTemplateRenderer,但使用自定义配置。

例如,默认情况下,模板变量由 {} 语法标识。如果您计划在提示词中包含 JSON,可能希望使用不同的语法以避免与 JSON 语法冲突。例如,您可以使用 < 和 > 分隔符。

java 复制代码
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() 返回值

在 ChatClient 上指定 call() 方法后,有几种不同的响应类型选项:

  • String content(): 返回响应的字符串内容。

  • ChatResponse chatResponse(): 返回包含多个生成结果以及响应元数据(例如,创建响应使用了多少令牌)的 ChatResponse 对象。

  • ChatClientResponse chatClientResponse(): 返回一个包含 ChatResponse 对象和 ChatClient 执行上下文的 ChatClientResponse 对象,让您可以访问执行 Advisor 期间使用的额外数据(例如,在 RAG 流程中检索到的相关文档)。

    • entity() 返回 Java 类型:

    • entity(ParameterizedTypeReference type): 用于返回实体类型的集合。

    • entity(Class type): 用于返回特定的实体类型。

    • entity(StructuredOutputConverter structuredOutputConverter): 用于指定 StructuredOutputConverter 的实例以将字符串转换为实体类型。

  • responseEntity() 同时返回 ChatResponse 和 Java 类型。这在您需要同时访问完整的 AI 模型响应(包含元数据和生成结果)和结构化输出实体的单个调用中非常有用:

    • responseEntity(Class type): 用于返回包含完整 ChatResponse 对象和特定实体类型的 ResponseEntity。

    • responseEntity(ParameterizedTypeReference type): 用于返回包含完整 ChatResponse 对象和实体类型集合的 ResponseEntity。

    • responseEntity(StructuredOutputConverter structuredOutputConverter): 用于返回包含完整 ChatResponse 对象和使用指定 StructuredOutputConverter 转换的实体的 ResponseEntity。

您也可以调用 stream() 方法而不是 call()。

调用 call() 方法并不会真正触发 AI 模型的执行。它只是指示 Spring AI 使用同步调用还是流式调用。实际的 AI 模型调用是在调用诸如 content(), chatResponse(), 和 responseEntity() 等方法时发生的。

七、stream() 返回值

在 ChatClient 上指定 stream() 方法后,有几种响应类型选项:

  • Flux content(): 返回 AI 模型正在生成的字符串的 Flux。

  • Flux chatResponse(): 返回 ChatResponse 对象的 Flux,其中包含有关响应的额外元数据。

  • Flux chatClientResponse(): 返回包含 ChatResponse 对象和 ChatClient 执行上下文的 ChatClientResponse 对象的 Flux,让您可以访问执行 Advisor 期间使用的额外数据(例如,在 RAG 流程中检索到的相关文档)。

八、消息元数据

ChatClient 支持向用户消息和系统消息添加元数据。元数据提供了关于消息的额外上下文和信息,可供 AI 模型或下游处理使用。

8.1 向用户消息添加元数据

您可以使用 metadata() 方法向用户消息添加元数据:

java 复制代码
// 添加单个元数据键值对
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();

8.2 向系统消息添加元数据

类似地,您也可以向系统消息添加元数据:

java 复制代码
// 向系统消息添加元数据
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();

8.3 默认元数据支持

您还可以在 ChatClient 构建器级别配置默认元数据:

java 复制代码
@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();
    }
}

8.4 元数据验证

ChatClient 会验证元数据以确保数据完整性:

  • 元数据键不能为 null 或空

  • 元数据值不能为 null

  • 传递 Map 时,键和值都不能包含 null 元素

java 复制代码
// 这将抛出 IllegalArgumentException
chatClient.prompt()
    .user(u -> u.text("Hello")
        .metadata(null, "value"))  // 无效: null 键
    .call()
    .content();

// 这也将抛出 IllegalArgumentException
chatClient.prompt()
    .user(u -> u.text("Hello")
        .metadata("key", null))    // 无效: null 值
    .call()
    .content();

8.5 访问元数据

元数据包含在生成的 UserMessage 和 SystemMessage 对象中,可以通过消息的 getMetadata() 方法访问。这在处理 Advisor 中的消息或检查对话历史记录时特别有用。

九、使用默认值

在 @Configuration 类中创建具有默认系统文本的 ChatClient 可以简化运行时代码。通过设置默认值,您只需在调用 ChatClient 时指定用户文本,从而无需在运行时代码路径中为每个请求设置系统文本。

9.1 默认系统文本

在以下示例中,我们将系统文本配置为始终以海盗的语气回答。为了避免在运行时代码中重复系统文本,我们将在 @Configuration 类中创建一个 ChatClient 实例。

java 复制代码
@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:

java 复制代码
@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());
	}
}

通过 curl 调用应用程序端点时,结果是:

java 复制代码
❯ curl localhost:8080/ai/simple
{"completion":"Why did the pirate go to the comedy club? To hear some arrr-rated jokes! Arrr, matey!"}

9.2 带参数的默认系统文本

在以下示例中,我们将在系统文本中使用占位符,以便在运行时而非设计时指定回答的语气。

java 复制代码
@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:

java 复制代码
@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());
	}

}

通过 httpie 调用应用程序端点时,结果是:

java 复制代码
http localhost:8080/ai voice=='Robert DeNiro'
{
    "completion": "You talkin' to me? Okay, here's a joke for ya: Why couldn't the bicycle stand up by itself? Because it was two tired! Classic, right?"
}

9.3 其他默认值

在 ChatClient.Builder 级别,您可以指定默认的提示词配置。

  • defaultOptions(ChatOptions chatOptions): 传入在 ChatOptions 类中定义的可移植选项或模型特定选项,例如 OpenAiChatOptions 中的选项。有关模型特定的 ChatOptions 实现的更多信息,请参阅 JavaDocs。

  • defaultFunction(String name, String description, java.util.function.Function<I, O> function): 名称用于在用户文本中引用该函数。描述解释了函数的用途,并帮助 AI 模型选择正确的函数以获取准确的响应。函数参数是一个 Java 函数实例,模型将在必要时执行该函数。

  • defaultFunctions(String...​ functionNames): 应用程序上下文中定义的 java.util.Function 的 bean 名称。

  • defaultUser(String text), defaultUser(Resource text), defaultUser(Consumer userSpecConsumer): 这些方法允许您定义用户文本。Consumer 允许您使用 lambda 表达式来指定用户文本和任何默认参数。

  • defaultAdvisors(Advisor...​ advisor): Advisor 允许修改用于创建 Prompt 的数据。QuestionAnswerAdvisor 实现通过向提示词附加与用户文本相关的上下文信息来启用检索增强生成模式。

  • defaultAdvisors(Consumer advisorSpecConsumer): 此方法允许您定义一个 Consumer 来使用 AdvisorSpec 配置多个 Advisor。Advisor 可以修改用于创建最终 Prompt 的数据。Consumer 允许您指定一个 lambda 表达式来添加 Advisor,例如 QuestionAnswerAdvisor,它通过根据用户文本向提示词附加相关上下文信息来支持检索增强生成。

您可以在运行时使用不带 default 前缀的相应方法来覆盖这些默认值。

  • options(ChatOptions chatOptions)

  • function(String name, String description, java.util.function.Function<I, O> function)

  • functions(String...​ functionNames)

  • user(String text), user(Resource text), user(Consumer userSpecConsumer)

  • advisors(Advisor...​ advisor)

  • advisors(Consumer advisorSpecConsumer)

十、Advisor

Advisor API 提供了一种灵活而强大的方式来拦截、修改和增强 Spring 应用程序中 AI 驱动的交互。

使用用户文本调用 AI 模型时,一个常见的模式是向提示词追加或增强上下文数据。

这种上下文数据可以有不同类型。常见类型包括:

  • 您自己的数据:这是 AI 模型未训练过的数据。即使模型见过类似数据,附加的上下文数据在生成响应时也具有优先权。

  • 对话历史记录:聊天模型的 API 是无状态的。如果您告诉 AI 模型您的名字,它不会在后续交互中记住。必须随每个请求发送对话历史记录,以确保在生成响应时考虑到先前的交互。

10.1 ChatClient 中的 Advisor 配置

ChatClient 流式 API 提供了一个 AdvisorSpec 接口用于配置 Advisor。此接口提供了添加参数、一次性设置多个参数以及向链中添加一个或多个 Advisor 的方法。

java 复制代码
interface AdvisorSpec {
    AdvisorSpec param(String k, Object v);
    AdvisorSpec params(Map<String, Object> p);
    AdvisorSpec advisors(Advisor... advisors);
    AdvisorSpec advisors(List<Advisor> advisors);
}

Advisor 添加到链中的顺序至关重要,因为它决定了它们的执行顺序。每个 Advisor 都会以某种方式修改提示词或上下文,并且一个 Advisor 所做的更改会传递给链中的下一个。

java 复制代码
ChatClient.builder(chatModel)
    .build()
    .prompt()
    .advisors(
        MessageChatMemoryAdvisor.builder(chatMemory).build(),
        QuestionAnswerAdvisor.builder(vectorStore).build()
    )
    .user(userText)
    .call()
    .content();

在此配置中,MessageChatMemoryAdvisor 将首先执行,将对话历史记录添加到提示词中。然后,QuestionAnswerAdvisor 将基于用户的问题和添加的对话历史记录执行其搜索,可能会提供更相关的结果。

了解问答顾问

10.2 检索增强生成

请参阅 检索增强生成指南。

10.3 日志记录

SimpleLoggerAdvisor 是一个记录 ChatClient 请求和响应数据的 Advisor。这对于调试和监控 AI 交互非常有用。

Spring AI 支持 LLM 和向量存储交互的可观测性。有关更多信息,请参阅 可观测性指南。

要启用日志记录,请在创建 ChatClient 时将 SimpleLoggerAdvisor 添加到 Advisor 链中。建议将其添加到链的末尾:

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

要查看日志,请将 Advisor 包的日志记录级别设置为 DEBUG:

logging.level.org.springframework.ai.chat.client.advisor=DEBUG

将此添加到您的 application.properties 或 application.yaml 文件中。

您可以使用以下构造函数自定义记录哪些来自 AdvisedRequest 和 ChatResponse 的数据:

java 复制代码
SimpleLoggerAdvisor(
    Function<ChatClientRequest, String> requestToString,
    Function<ChatResponse, String> responseToString,
    int order
)

用法示例:

xml 复制代码
SimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
    request -> "Custom request: " + request.prompt().getUserMessage(),
    response -> "Custom response: " + response.getResult(),
    0
);

这允许您根据特定需求定制记录的信息。

在生产环境中记录敏感信息时要小心。

十一、对话记忆

ChatMemory 接口表示聊天对话记忆的存储。它提供了向对话添加消息、从对话检索消息和清除对话历史记录的方法。

目前有一个内置实现:MessageWindowChatMemory。

MessageWindowChatMemory 是一种聊天记忆实现,它维护一个达到指定最大大小(默认值:20 条消息)的消息窗口。当消息数量超过此限制时,较旧的消息会被逐出,但系统消息会被保留。如果添加了新的系统消息,则所有先前的系统消息都会从内存中移除。这确保了对话始终可用的最新上下文,同时保持内存使用有界。

MessageWindowChatMemory 由 ChatMemoryRepository 抽象支持,该抽象为聊天对话记忆提供了存储实现。有几种可用的实现,包括 InMemoryChatMemoryRepository、JdbcChatMemoryRepository、CassandraChatMemoryRepository 和 Neo4jChatMemoryRepository。

有关更多详细信息和用法示例,请参阅 对话记忆文档。

十二、实现说明

  • ChatClient 中命令式和响应式编程模型的结合使用是 API 的一个独特方面。通常,应用程序要么是响应式的,要么是命令式的,但不会同时是两者。

  • 当自定义 Model 实现的 HTTP 客户端交互时,必须同时配置 RestClient 和 WebClient。

    由于 Spring Boot 3.4 中的一个错误,必须设置属性 "spring.http.client.factory=jdk"。否则,它默认设置为 "reactor",这会破坏某些 AI 工作流,如图像模型 ImageModel。

  • 流式传输仅通过响应式栈支持。因此,命令式应用程序必须包含响应式栈(例如 spring-boot-starter-webflux)。

  • 非流式传输仅通过 Servlet 栈支持。因此,响应式应用程序必须包含 Servlet 栈(例如 spring-boot-starter-web),并预期某些调用是阻塞的。

  • 工具调用是命令式的,导致阻塞工作流。这也导致部分/中断的 Micrometer 观测(例如,ChatClient 跨度与工具调用跨度没有连接,第一个跨度因此保持不完整)。

  • 内置 Advisor 对标准调用执行阻塞操作,对流式调用执行非阻塞操作。用于 Advisor 流式调用的 Reactor Scheduler 可以通过每个 Advisor 类上的构建器进行配置。

相关推荐
xieyan08111 小时前
什么情况下使用强化学习
人工智能
执笔论英雄1 小时前
【RL】Slime异步原理(单例设计模式)6
人工智能·设计模式
da_vinci_x1 小时前
PS 结构参考 + Firefly:零建模量产 2.5D 等轴游戏资产
人工智能·游戏·设计模式·prompt·aigc·技术美术·游戏美术
是小崔啊1 小时前
【SAA】01 - Spring Ai Alibaba快速入门
java·人工智能·spring
semantist@语校1 小时前
第五十一篇|构建日本语言学校数据模型:埼玉国际学院的城市结构与行为变量分析
java·大数据·数据库·人工智能·百度·ai·github
想要成为计算机高手1 小时前
π*0.6: 从实践中学习 -- 2025.11.17 -- Physical Intelligence (π) -- 未开源
人工智能·学习·机器人·多模态·具身智能·vla
黑客思维者1 小时前
LLM底层原理学习笔记:模型评估的基准测试体系与方法论
人工智能·笔记·神经网络·学习·模型评估·基准测试
他们叫我技术总监1 小时前
从 WM_CONCAT 到 LISTAGG:Oracle 字符串聚合按时间排序完整方案
数据库·人工智能·oracle