此版本仍在开发中,尚未被视为稳定版。如需最新的快照版本,请使用 Spring AI 1.1.3spring-doc.cadn.net.cn

聊天客户端API

ChatClient 提供了一种流畅的API来与AI模型通信。它支持同步和流式编程模型。spring-doc.cadn.net.cn

见本文件底部有关在ChatClient中结合使用命令式和响应式编程模型的实现说明spring-doc.cadn.net.cn

该流畅API提供了构建传递给AI模型作为输入的提示的方法。 spring-doc.cadn.net.cn

Prompt包含指导AI模型输出和行为的指令性文本。从API的角度来看,提示由一系列消息组成。spring-doc.cadn.net.cn

spring-doc.cadn.net.cn

AI模型处理两种主要类型的消息:用户消息,这些是直接来自用户的输入;系统消息,这些是由系统生成的以引导对话的消息。spring-doc.cadn.net.cn

这些消息通常包含占位符,在运行时根据用户输入进行替换,以自定义AI模型对用户输入的响应。spring-doc.cadn.net.cn

也有可以指定的提示选项,例如要使用的AI模型名称以及控制生成输出随机性或创造力的温度设置。spring-doc.cadn.net.cn

创建 ChatClient

ChatClient 是使用一个 ChatClient.Builder 对象创建的。 你可以通过任何ChatModel Spring Boot 自动配置获取一个自动配置好的ChatClient.Builder 实例,或者程序化地创建一个。spring-doc.cadn.net.cn

使用自动配置的ChatClient.Builder

在最简单的使用案例中,Spring AI 提供了 Spring Boot 自动配置功能,为你创建一个原型 ChatClient.Builder bean,你可以将其注入到你的类中。 这里是一个简单的示例,展示如何检索一个针对简单用户请求的 String 响应。spring-doc.cadn.net.cn

@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 模型的响应作为 Stringspring-doc.cadn.net.cn

使用多个聊天模型

在单个应用程序中可能需要同时处理多个聊天模型的几种场景包括:spring-doc.cadn.net.cn

默认情况下,Spring AI 会自动配置一个单个的 ChatClient.Builder bean。 然而,在您的应用程序中可能需要处理多个聊天模型。以下是应对这种情况的方法:spring-doc.cadn.net.cn

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

这允许您手动创建多个ChatClient实例。spring-doc.cadn.net.cn

多个ChatClient实例与单一模型类型

此部分涵盖了这样一个常见用例:您需要创建多个 ChatClient 实例,这些实例使用相同的底层模型类型但具有不同的配置。spring-doc.cadn.net.cn

// Create ChatClient instances programmatically
ChatModel myChatModel = ... // already autoconfigured by Spring Boot
ChatClient chatClient = ChatClient.create(myChatModel);

// Or use the builder for more control
ChatClient.Builder builder = ChatClient.builder(myChatModel);
ChatClient customChatClient = builder
    .defaultSystemPrompt("You are a helpful assistant.")
    .build();

不同模型类型的ChatClient

在处理多个AI模型时,您可以为每个模型定义单独的ChatClient bean:spring-doc.cadn.net.cn

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);
    }
}

然后可以使用 @0 注解将这些bean注入到您的应用程序组件中:spring-doc.cadn.net.cn

@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;

            // Model selection
            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");
            }

            // Use the selected chat client
            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的API端点

OpenAiApiOpenAiChatModel 类提供了一个 mutate() 方法,允许您创建具有不同属性的现有实例的变体。 这在需要与多个 OpenAI 兼容 API 进行交互时特别有用。spring-doc.cadn.net.cn

@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 {
            // Derive a new OpenAiApi for Groq (Llama3)
            OpenAiApi groqApi = baseOpenAiApi.mutate()
                .baseUrl("https://api.groq.com/openai")
                .apiKey(System.getenv("GROQ_API_KEY"))
                .build();

            // Derive a new OpenAiApi for OpenAI GPT-4
            OpenAiApi gpt4Api = baseOpenAiApi.mutate()
                .baseUrl("https://api.openai.com")
                .apiKey(System.getenv("OPENAI_API_KEY"))
                .build();

            // Derive a new OpenAiChatModel for Groq
            OpenAiChatModel groqModel = baseChatModel.mutate()
                .openAiApi(groqApi)
                .defaultOptions(OpenAiChatOptions.builder().model("llama3-70b-8192").temperature(0.5).build())
                .build();

            // Derive a new OpenAiChatModel for GPT-4
            OpenAiChatModel gpt4Model = baseChatModel.mutate()
                .openAiApi(gpt4Api)
                .defaultOptions(OpenAiChatOptions.builder().model("gpt-4").temperature(0.7).build())
                .build();

            // Simple prompt for both models
            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("Error in multi-client flow", e);
        }
    }
}

ChatClient 流畅API

The ChatClient 流畅API允许您通过重载的 prompt 方法以三种不同的方式创建提示:spring-doc.cadn.net.cn

  • prompt(): 这个没有参数的方法让你能够开始使用流畅API,允许你构建用户、系统以及其他部分的提示。spring-doc.cadn.net.cn

  • prompt(Prompt prompt): 这个方法接受一个 Prompt 参数,让你可以使用 Prompt 的非流畅 API 创建的 Prompt 实例进行传递。spring-doc.cadn.net.cn

  • prompt(String content): 这是一个与上一个重载类似的便捷方法。它接收用户的文本内容。spring-doc.cadn.net.cn

ChatClient 响应

The ChatClient API 提供了几种使用流畅API格式化AI模型响应的方法。spring-doc.cadn.net.cn

返回聊天响应

spring-doc.cadn.net.cn

The response from the AI model is a rich structure defined by the type ChatResponse.spring-doc.cadn.net.cn

It includes metadata about how the response was generated and can also contain multiple responses, known as 生成s, each with its own metadata.spring-doc.cadn.net.cn

The metadata includes the number of tokens (each token is approximately 3/4 of a word) used to create the response. This information is important because hosted AI models charge based on the number of tokens used per request.spring-doc.cadn.net.cn

spring-doc.cadn.net.cn

以下是一个示例,通过调用call()方法后的chatResponse()方法,返回包含元数据的ChatResponse对象。spring-doc.cadn.net.cn

ChatResponse chatResponse = chatClient.prompt()
    .user("Tell me a joke")
    .call()
    .chatResponse();

返回实体

您经常希望返回一个由返回的String映射的实体类。entity()方法提供了此功能。spring-doc.cadn.net.cn

例如,给定以下Java记录:spring-doc.cadn.net.cn

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

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

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

也存在带有签名entity(ParameterizedTypeReference<T> type)的重载entity方法,该方法允许您指定如泛型列表这样的类型:spring-doc.cadn.net.cn

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顾问参数来利用这一功能。 您可以使用defaultAdvisors()方法在ChatClient.Builder上设置此参数以全局方式应用于所有调用,或者如下面所示按每次调用来设置它:spring-doc.cadn.net.cn

ActorFilms actorFilms = chatClient.prompt()
    .advisors(AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT)
    .user("Generate the filmography for a random actor.")
    .call()
    .entity(ActorFilms.class);
一些AI模型,如OpenAI,不原生支持对象数组。 在这种情况下,您可以使用Spring AI默认的结构化输出转换。

流式响应

stream() 方法可以让您获取异步响应,如下所示:spring-doc.cadn.net.cn

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

您也可以通过方法Flux<ChatResponse> chatResponse()流式处理ChatResponsespring-doc.cadn.net.cn

在未来,我们将提供一个方便的方法,让您可以通过响应式的stream()方法返回一个Java实体。 在此期间,您应该使用结构化输出转换器显式地将聚合响应进行转换,如下所示。 这也展示了将在文档的后续部分中详细介绍的在流畅API中使用的参数。spring-doc.cadn.net.cn

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);

提示模板

The ChatClient 流畅 API 允许您提供用户文本和系统文本作为模板,并在运行时用变量进行替换。spring-doc.cadn.net.cn

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-doc.cadn.net.cn

Spring AI 也提供了一个 NoOpTemplateRenderer,用于不需要模板处理的情况。spring-doc.cadn.net.cn

直接在 ChatClient 上通过 .templateRenderer() 配置的 TemplateRenderer 仅适用于在 ChatClient 构建器链中直接定义的提示内容(例如,通过 .user().system())。 它不会影响由 Advisors(如 QuestionAnswerAdvisor)内部使用的模板,这些顾问拥有自己的模板自定义机制(请参阅 自定义顾问模板)。

如果希望使用不同的模板引擎,可以直接将自定义的TemplateRenderer接口实现提供给ChatClient。您也可以继续使用默认的StTemplateRenderer,但需要自定义配置。spring-doc.cadn.net.cn

例如,默认情况下,模板变量由{}语法识别。 如果你的提示中包含JSON内容,为了避免与JSON语法冲突,你可能需要使用不同的分隔符。例如,你可以使用<>作为分隔符。spring-doc.cadn.net.cn

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() 方法于 ChatClient 后,响应类型有几种不同的选择。spring-doc.cadn.net.cn

  • String content(): 返回响应的内容字符串spring-doc.cadn.net.cn

  • ChatResponse chatResponse(): 返回包含多个生成结果和响应元数据的对象,例如创建响应时使用了多少个Tokens。spring-doc.cadn.net.cn

  • ChatClientResponse chatClientResponse(): 返回一个包含 ChatResponse 对象和 ChatClient 执行上下文的 ChatClientResponse 对象,让你可以访问执行顾问期间使用的附加数据(例如,在 RAG 流程中检索的相关文档)。spring-doc.cadn.net.cn

  • entity() 用于返回Java类型spring-doc.cadn.net.cn

    • entity(ParameterizedTypeReference<T> type): 用于返回实体类型的Collectionspring-doc.cadn.net.cn

    • entity(Class<T> type): 用于返回特定实体类型。spring-doc.cadn.net.cn

    • entity(StructuredOutputConverter<T> structuredOutputConverter): 用于指定一个StructuredOutputConverter的实例,将其转换为一种String实体类型。spring-doc.cadn.net.cn

  • responseEntity()用于返回完整的AI模型响应(包括元数据和生成内容)以及结构化的输出实体。这在需要在一个调用中同时访问这两者时非常有用。spring-doc.cadn.net.cn

    • responseEntity(Class<T> type): 用于返回一个包含完整ChatResponse对象和特定实体类型的ResponseEntityspring-doc.cadn.net.cn

    • responseEntity(ParameterizedTypeReference<T> type): 用于返回一个包含完整 ChatResponse 对象和实体类型列表的 ResponseEntityspring-doc.cadn.net.cn

    • responseEntity(StructuredOutputConverter<T> structuredOutputConverter): 用于返回一个包含完整ChatResponse对象和使用指定的StructuredOutputConverter转换后的实体的ResponseEntityspring-doc.cadn.net.cn

可以调用stream()方法而不是call()方法。spring-doc.cadn.net.cn

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

stream() 返回值

在为ChatClient指定stream()方法后,响应类型有几种选择:spring-doc.cadn.net.cn

  • Flux<String> content(): 返回由AI模型生成的字符串的Fluxspring-doc.cadn.net.cn

  • Flux<ChatResponse> chatResponse(): 返回一个 FluxChatResponse 对象,该对象包含关于响应的附加元数据。spring-doc.cadn.net.cn

  • Flux<ChatClientResponse> chatClientResponse(): 返回包含 ChatClientResponse 对象和 ChatResponse 对象的 Flux 对象,为您提供在顾问执行过程中使用的附加数据访问权限(例如,在RAG流程中检索的相关文档)。spring-doc.cadn.net.cn

消息元数据

The ChatClient 支持为用户消息和系统消息添加元数据。 元数据提供了关于消息的附加上下文和信息,这些信息可以被 AI 模型或下游处理使用。spring-doc.cadn.net.cn

添加元数据到用户消息

您可以使用 metadata() 方法为用户消息添加元数据:spring-doc.cadn.net.cn

// Adding individual metadata key-value pairs
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();

// Adding multiple metadata entries at once
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();

添加系统消息元数据

同样,您还可以为系统消息添加元数据:<br/>spring-doc.cadn.net.cn

// Adding metadata to system messages
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构建器级别配置默认元数据:spring-doc.cadn.net.cn

@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();
    }
}

元数据验证

ChatClient 验证元数据以确保数据完整性:spring-doc.cadn.net.cn

// This will throw an IllegalArgumentException
chatClient.prompt()
    .user(u -> u.text("Hello")
        .metadata(null, "value"))  // Invalid: null key
    .call()
    .content();

// This will also throw an IllegalArgumentException
chatClient.prompt()
    .user(u -> u.text("Hello")
        .metadata("key", null))    // Invalid: null value
    .call()
    .content();

访问元数据

生成的UserMessage和SystemMessage对象中包含了元数据,并可以通过消息的对象的getMetadata()方法进行访问。 这在处理顾问中的消息或检查对话历史时特别有用。spring-doc.cadn.net.cn

使用默认设置

创建一个ChatClient类中的默认系统文本的ChatClient简化了运行时代码。 通过设置默认值,您只需在调用ChatClient时指定用户文本,从而消除在运行时代码路径中为每个请求设置系统文本的需要。spring-doc.cadn.net.cn

默认系统文本

在以下示例中,我们将配置系统文本始终以海盗的声音回应。 为了避免在运行时代码中重复系统文本,我们将在一个@Configuration类中创建一个ChatClient实例。spring-doc.cadn.net.cn

@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来调用它:spring-doc.cadn.net.cn

@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 调用应用端点时,结果为:<br />spring-doc.cadn.net.cn

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

默认系统文本参数

在以下示例中,我们将使用系统文本中的占位符,在运行时而不是设计时指定完成的语音。spring-doc.cadn.net.cn

@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());
	}

}

通过httpie 调用应用端点的结果是:<br/>spring-doc.cadn.net.cn

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?"
}

其他默认设置

ChatClient.Builder级别,您可以指定默认提示配置。spring-doc.cadn.net.cn

  • defaultOptions(ChatOptions chatOptions): 传入在ChatOptions类中定义的可移植选项或特定于模型的选项,例如OpenAiChatOptions中的那些。 对于有关特定于模型的ChatOptions实现的更多信息,请参阅JavaDocs。spring-doc.cadn.net.cn

  • defaultFunction(String name, String description, java.util.function.Function<I, O> function): The name用于引用用户文本中的功能。
    The description解释该功能的目的,并帮助AI模型选择正确的函数以获得准确的响应。
    The function参数是一个Java函数实例,当需要时,模型将执行此函数。spring-doc.cadn.net.cn

  • defaultFunctions(String…​ functionNames): 在应用上下文中定义的 `java.util.Function` 的 bean 名称。spring-doc.cadn.net.cn

  • defaultUser(String text), defaultUser(Resource text), defaultUser(Consumer<UserSpec> userSpecConsumer): 这些方法让你定义用户文本。 Consumer<UserSpec> 允许你使用 lambda 表达式来指定用户文本和任何默认参数。spring-doc.cadn.net.cn

  • defaultAdvisors(Advisor…​ advisor): 顾问允许修改用于创建 Prompt 的数据。
    QuestionAnswerAdvisor 实现通过在提示中附加与用户文本相关的内容信息,启用 Retrieval Augmented Generation 模式。spring-doc.cadn.net.cn

  • defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer): 此方法允许您使用Consumer配置多个顾问。您可以使用AdvisorSpec来定义多个顾问,这些顾问可以修改用于创建最终Prompt的数据。 Consumer<AdvisorSpec> 让您可以指定一个 lambda 表达式来添加顾问,例如 QuestionAnswerAdvisor,它支持Retrieval Augmented Generation通过在提示中附加相关的上下文信息,基于用户文本。spring-doc.cadn.net.cn

可以使用对应的方法在运行时覆盖这些默认值,而不需要使用default前缀。spring-doc.cadn.net.cn

顾问

The 顾问API 提供了一种灵活且强大的方式,用于拦截、修改和增强您Spring应用程序中的AI驱动交互。spring-doc.cadn.net.cn

使用用户文本调用AI模型时,一个常见的模式是将上下文数据附加到提示词或扩充提示词。spring-doc.cadn.net.cn

此上下文数据可以是不同类型的。常见类型包括:spring-doc.cadn.net.cn

  • 您自己的数据:这指的是AI模型未对其进行训练的数据。 即使模型见过类似数据,附加的上下文数据在生成响应时具有优先权。spring-doc.cadn.net.cn

  • 会话历史记录: 聊天模型的API是无状态的。 如果你告诉AI模型你的名字,它在后续互动中不会记住。 为了确保之前的交互被考虑进去以生成响应,每次请求时必须发送会话历史记录。spring-doc.cadn.net.cn

ChatClient 中的 Advisor 配置

The ChatClient 流畅 API 提供了一个 AdvisorSpec 接口用于配置顾问。 此接口提供了添加参数、一次性设置多个参数以及向链中添加一个或多个顾问的方法。spring-doc.cadn.net.cn

interface AdvisorSpec {
    AdvisorSpec param(String k, Object v);
    AdvisorSpec params(Map<String, Object> p);
    AdvisorSpec advisors(Advisor... advisors);
    AdvisorSpec advisors(List<Advisor> advisors);
}
<p>将顾问添加到链中的顺序至关重要,因为它决定了它们执行的顺序。</p> <p>每个顾问以某种方式修改提示或上下文,并且一个顾问所做的更改会传递给链中的下一个顾问。</p>
ChatClient.builder(chatModel)
    .build()
    .prompt()
    .advisors(
        MessageChatMemoryAdvisor.builder(chatMemory).build(),
        QuestionAnswerAdvisor.builder(vectorStore).build()
    )
    .user(userText)
    .call()
    .content();

在该配置中,MessageChatMemoryAdvisor 将首先被执行,将对话历史添加到提示中。 随后,QuestionAnswerAdvisor 将根据用户的提问和添加的对话历史进行搜索,有可能提供更相关的结果。spring-doc.cadn.net.cn

检索增强生成

日志记录

The SimpleLoggerAdvisor 是一个顾问,记录 requestresponse 关于 ChatClient 的数据。这在调试和监控您的 AI 交互时可能会很有用。spring-doc.cadn.net.cn

Spring AI 支持对大语言模型和向量存储交互的可观测性。 参见 可观测性 指南以获取更多信息。

要启用日志记录,请在创建ChatClient时将SimpleLoggerAdvisor添加到顾问链中。建议将其放在链的末尾:spring-doc.cadn.net.cn

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

要查看日志,请将advisor包的日志级别设置为DEBUGspring-doc.cadn.net.cn

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

在您的application.propertiesapplication.yaml文件中添加此内容。spring-doc.cadn.net.cn

您可以使用以下构造函数来自定义从AdvisedRequestChatResponse记录的数据:spring-doc.cadn.net.cn

SimpleLoggerAdvisor(
    Function<ChatClientRequest, String> requestToString,
    Function<ChatResponse, String> responseToString,
    int order
)

示例用法:spring-doc.cadn.net.cn

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

这使得您能够根据特定需求定制日志信息。spring-doc.cadn.net.cn

请谨慎在生产环境中记录敏感信息。

聊天记忆

接口ChatMemory代表聊天对话历史存储。 它提供了向对话中添加消息、从对话中检索消息以及清空对话历史的方法。spring-doc.cadn.net.cn

当前有一个内置实现:MessageWindowChatMemoryspring-doc.cadn.net.cn

MessageWindowChatMemory 是一个聊天记忆实现,它维护了一个消息窗口,最大尺寸为指定的最大值(默认:20 条消息)。 当消息数量超过这个限制时,会移除较旧的消息,但保留系统消息。 如果添加了新的系统消息,则会从内存中清除所有之前的系统消息。 这确保了始终可以获取最近的上下文,并且保持内存使用量处于可控制范围内。spring-doc.cadn.net.cn

MessageWindowChatMemoryChatMemoryRepository 抽象支持,该抽象为聊天对话内存提供了存储实现。 有多种可用的实现,包括 InMemoryChatMemoryRepositoryJdbcChatMemoryRepositoryCassandraChatMemoryRepositoryNeo4jChatMemoryRepositoryCosmosDBChatMemoryRepositoryMongoChatMemoryRepositoryRedisChatMemoryRepositoryspring-doc.cadn.net.cn

对于更多详细信息和使用示例,请参阅聊天记忆文档。spring-doc.cadn.net.cn

实施说明

在框架ChatClient中结合使用命令式和响应式编程模型是一种独特的API特性。 通常,一个应用程序要么是响应式的,要么是命令式的,但不会两者兼具。spring-doc.cadn.net.cn

  • 当自定义模型实现的HTTP客户端交互时,必须配置RestClient和WebClient。spring-doc.cadn.net.cn

由于Spring Boot 3.4中存在的bug,必须设置"spring.http.client.factory=jdk"属性。 否则,默认情况下它会被设置为"reactor",这会破坏某些AI工作流,如ImageModel。spring-doc.cadn.net.cn

  • 流式处理仅通过Reactive栈支持。 imperative应用程序由于这个原因必须包含Reactive栈(例如,spring-boot-starter-webflux)。spring-doc.cadn.net.cn

  • 非流式处理仅通过Servlet堆栈支持。 响应式应用程序为了这个原因必须包含Servlet堆栈(例如spring-boot-starter-web),并且预期某些调用会阻塞。spring-doc.cadn.net.cn

  • 工具调用是强制性的,会导致阻塞的工作流。 这也导致了部分/中断的Micrometer观测(例如,ChatClient跨度和工具调用跨度没有连接,前者因此保持不完整)。spring-doc.cadn.net.cn

  • 内置顾问对标准调用执行阻塞操作,对流式调用执行非阻塞操作。 顾问的流式调用所使用的Reactor调度器可以通过每个顾问类上的Builder进行配置。spring-doc.cadn.net.cn