最新快照版本请使用Spring AI 1.1.0spring-doc.cadn.net.cn

聊天客户端API

聊天客户端提供流畅的API与AI模型通信。 它支持同步和流式编程模式。spring-doc.cadn.net.cn

请参阅本文档底部关于命令式编程和响应式编程模型结合使用的实现说明聊天客户端spring-doc.cadn.net.cn

流利 API 有方法构建提示词的组成部分,这些提示作为输入传递给 AI 模型。 这提示包含指导AI模型输出和行为的教学文本。从API的角度看,提示词由一组消息组成。spring-doc.cadn.net.cn

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

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

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

创建聊天客户端

聊天客户端是通过ChatClient.Builder对象。 你可以获得自动配置ChatClient.Builder实例用于任何 ChatModel Spring Boot 自动配置,或通过程序创建。spring-doc.cadn.net.cn

使用自动配置的 ChatClient.Builder

在最简单的使用场景中,Spring AI 提供 Spring Boot 自动配置,创建原型ChatClient.Builder给你注入班级的豆子。 这里有一个简单的示例,如何检索字符串对简单用户请求的回应。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模型发送请求,且内容()方法返回 AI 模型的响应为字符串.spring-doc.cadn.net.cn

多聊天模型的协作

有几种情况下,你可能需要在单一应用中使用多个聊天模型:spring-doc.cadn.net.cn

默认情况下,Spring AI 会自动配置一个ChatClient.Builder豆。不过,你可能需要在申请中同时使用多个聊天模型。以下是处理这种情况的方法:spring-doc.cadn.net.cn

无论哪种情况,你都需要禁用ChatClient.Builder通过设置属性进行自动配置spring.ai.chat.client.enabled=false.spring-doc.cadn.net.cn

这允许你创建多个聊天客户端实例是手动的。spring-doc.cadn.net.cn

单一型号类型的多个聊天客户端

本节介绍了一个常见的用例,即你需要创建多个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();

不同型号类型的聊天客户端

在处理多个AI模型时,你可以定义独立聊天客户端每种型号的豆子: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);
    }
}

然后你可以用@Qualifier注解: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类提供变异()这种方法允许你创建具有不同属性的现有实例的变体。当你需要使用多个兼容 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 Fluent API

聊天客户端Fluent API 允许你用 overloaded 以三种不同的方式创建提示词提示启动流流 API 的方法:spring-doc.cadn.net.cn

  • 提示():这种无参数的方法允许你开始使用流流API,从而构建用户、系统及提示词的其他部分。spring-doc.cadn.net.cn

  • 提示(提示提示):该方法接受提示争论,让你通过提示你用提示词的非流变API创建的实例。spring-doc.cadn.net.cn

  • 提示(字符串内容):这是一种类似于之前超载的便利方法。它会接收用户的文本内容。spring-doc.cadn.net.cn

聊天客户端回复

聊天客户端API 提供了多种方式,利用流畅 API 格式化 AI 模型的响应。spring-doc.cadn.net.cn

返回ChatResponse

AI模型的响应是一个丰富的结构,由类型定义聊天回应. 它包含关于回复如何生成的元数据,也可以包含多个响应,称为 Generations,每个响应都有自己的元数据。 元数据包括用于创建回应的Tokens数量(每个Tokens约为单词的3/4个词)。 这些信息很重要,因为托管的AI模型是根据每次请求使用的Tokens数量收费的。spring-doc.cadn.net.cn

返回聊天回应包含元数据的对象如下,通过调用聊天回应()call()方法。spring-doc.cadn.net.cn

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

返还实体

你通常想返回一个从返回的实体类映射出来的字符串. 这实体()方法提供了这一功能。spring-doc.cadn.net.cn

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

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

你可以用实体()方法如下所示:spring-doc.cadn.net.cn

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

还有一个超载实体带有签名的方法entity(ParameterizedTypeReference<T>类型)这允许你指定诸如通用列表等类型: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>>() {});

流媒体回应

stream()方法可以让你得到如下所示的异步响应:spring-doc.cadn.net.cn

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

你也可以流媒体播放聊天回应使用该方法Flux<ChatResponse>chatResponse().spring-doc.cadn.net.cn

未来,我们将提供一种便捷方法,允许你带响应式返回 Java 实体stream()方法。 与此同时,你应使用结构化输出转换器,明确转换为汇总响应,如下所示。 这也展示了流域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);

提示模板

聊天客户端流利的 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();

在内部,聊天客户端使用提示模板类处理用户和系统文本,并用运行时提供的值替换变量,依赖给定的模板渲染器实现。 默认情况下,Spring AI 使用StTemplateRenderer该系统基于Terence Parr开发的开源StringTemplate引擎。spring-doc.cadn.net.cn

Spring AI 还提供NoOpTemplateRenderer适用于不需要模板处理的情况。spring-doc.cadn.net.cn

模板渲染器直接在聊天客户端(经由.templateRenderer()仅适用于直接定义在聊天客户端建造链(例如,通过.user(),.system()). 它影响顾问内部使用的模板,比如问答顾问这些模板都有自己的模板自定义机制(参见自定义顾问模板)。

如果你更想用不同的模板引擎,你可以提供一个自定义的实现模板渲染器直接与聊天客户端接口。你也可以继续使用默认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()方法聊天客户端,有几种不同的响应类型选项。spring-doc.cadn.net.cn

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

  • 聊天回复 chatResponse(): 返回聊天回应包含多代的对象以及关于响应的元数据,例如创建响应时使用了多少Tokens。spring-doc.cadn.net.cn

  • 聊天客户响应 chat客户响应(): 返回聊天客户响应包含聊天回应对象和ChatClient执行上下文,提供你访问执行顾问过程中使用的额外数据(例如在RAG流程中检索的相关文档)。spring-doc.cadn.net.cn

  • ResponseEntity<?> responseEntity(): 返回响应实体包含完整的HTTP响应,包括状态码、头部和正文。当你需要访问响应的低层HTTP细节时,这非常有用。spring-doc.cadn.net.cn

  • 实体()返回 Java 类型spring-doc.cadn.net.cn

    • entity(ParameterizedTypeReference<T>类型): 用于返回收集实体类型。spring-doc.cadn.net.cn

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

    • entity(结构化输出转换器<T>结构化输出转换器): 用于指定 a 的实例结构化输出转换器将 a 转换字符串到实体类型。spring-doc.cadn.net.cn

你也可以调用stream()方法代替call().spring-doc.cadn.net.cn

呼叫call()方法实际上并不会触发 AI 模型的执行。相反,它只指示 Spring AI 是使用同步调用还是流调用。实际的AI模型调用发生在以下方法:内容(),聊天回应()responseEntity()被叫到了。

stream() 返回值

在指定stream()方法聊天客户端,有几种响应类型选项:spring-doc.cadn.net.cn

  • Flux<String> content()返回 a通量字符串由AI模型生成。spring-doc.cadn.net.cn

  • Flux<ChatResponse>chatResponse()返回 a通量关于聊天回应对象,包含关于该响应的额外元数据。spring-doc.cadn.net.cn

  • Flux<ChatClientResponse> chatClientResponse(): 返回通量关于聊天客户响应包含聊天回应对象和ChatClient执行上下文,提供你访问执行顾问过程中使用的额外数据(例如在RAG流程中检索的相关文档)。spring-doc.cadn.net.cn

使用默认值

创建聊天客户端@Configuration类简化了运行时代码。 通过设置默认值,你只需在调用时指定用户文本聊天客户端,消除了为运行时代码路径中每个请求设置系统文本的需求。spring-doc.cadn.net.cn

默认系统文本

在下面的例子中,我们将配置系统文本始终以海盗的声音回复。 为了避免在运行时代码中重复系统文本,我们将创建一个聊天客户端实例@Configuration类。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 调用应用端点时,结果为: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 调用应用端点时,结果为: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(聊天选项聊天选项): 传递定义在聊天选项类别或型号特定的选项,如以下OpenAiChatOptions.关于特定型号的更多信息聊天选项实现,则指 JavaDocs。spring-doc.cadn.net.cn

  • defaultFunction(String name, String description, java.util.function.Function<I, O> function):这名称用于指代用户文本中的函数。这描述解释函数的目的,并帮助AI模型选择正确的功能以获得准确的响应。这功能参数是模型在必要时执行的Java函数实例。spring-doc.cadn.net.cn

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

  • defaultUser(字符串文本),defaultUser(资源文本),defaultUser(Consumer<UserSpec> userSpecConsumer)这些方法允许你定义用户文本。这Consumer<UserSpec>允许你用 lambda 来指定用户文本和默认参数。spring-doc.cadn.net.cn

  • defaultAdvisors(顾问...顾问): 顾问允许修改用于创建提示.这问答顾问实现使 的模式得以实现检索增强生成通过在提示词中附加与用户文本相关的上下文信息。spring-doc.cadn.net.cn

  • defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer):该方法允许你定义一个消费者顾问规格.顾问可以修改用于创建最终考核所需的数据提示.这消费者<顾问规格(ConsumerAdvisorSpec>允许你指定一个lambda来添加顾问,比如问答顾问,支持检索增强生成通过根据用户文本附加相关的上下文信息。spring-doc.cadn.net.cn

你可以在运行时使用相应的方法来覆盖这些默认设置,而无需默认值前缀。spring-doc.cadn.net.cn

顾问

Advisors 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 配置

ChatClient 流利 API 提供了顾问规格用于配置顾问的界面。该接口提供了添加参数、同时设置多个参数以及向链中添加一个或多个顾问的方法。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);
}
顾问加入链条的顺序至关重要,因为它决定了顾问的执行顺序。每位顾问都会以某种方式修改提示或上下文,而一位顾问所做的更改会传递给链中的下一位顾问。
ChatClient.builder(chatModel)
    .build()
    .prompt()
    .advisors(
        MessageChatMemoryAdvisor.builder(chatMemory).build(),
        QuestionAnswerAdvisor.builder(vectorStore).build()
    )
    .user(userText)
    .call()
    .content();

在此配置下,消息聊天记忆顾问将先执行,并将对话历史添加到提示中。然后,问答顾问将根据用户的问题和新增的对话历史进行搜索,可能提供更相关的结果。spring-doc.cadn.net.cn

检索增强生成

Logging

SimpleLoggerAdvisor是一个记录请求响应数据聊天客户端. 这对于调试和监控你的AI交互非常有用。spring-doc.cadn.net.cn

Spring AI 支持大型语言模型(LLM)和向量存储交互的可观察性。更多信息请参阅可观测性指南。

要启用日志记录,请添加SimpleLoggerAdvisor创建ChatClient时,会回到顾问链。 建议在连锁链的末端添加:spring-doc.cadn.net.cn

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

要查看日志,请将顾问包的日志等级设置为调试:spring-doc.cadn.net.cn

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

把这个加到你的application.propertiesapplication.yaml文件。spring-doc.cadn.net.cn

你可以自定义来自哪些数据建议请求聊天回应通过以下构造函数进行日志记录:spring-doc.cadn.net.cn

SimpleLoggerAdvisor(
    Function<ChatClientRequest, String> requestToString,
    Function<ChatResponse, String> responseToString,
    int order
)
SimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
    request -> "Custom request: " + request.prompt().getUserMessage(),
    response -> "Custom response: " + response.getResult(),
    0
);

这允许您根据具体需求定制记录信息。spring-doc.cadn.net.cn

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

聊天记忆

界面聊天记忆代表聊天对话内存的存储。它提供了添加消息到对话中、从对话中检索消息以及清除对话历史的方法。spring-doc.cadn.net.cn

目前有一个内置实现:消息WindowChatMemory(记忆窗口聊天).spring-doc.cadn.net.cn

消息WindowChatMemory(记忆窗口聊天)是一种聊天内存实现,它维护一个最大大小(默认:20条消息)的消息窗口。当消息数量超过该限制时,旧消息会被驱逐,但系统消息会被保留。如果新增系统消息,所有之前的系统消息都会从内存中移除。这确保了对话中始终可用最新的上下文,同时保持内存使用受限。spring-doc.cadn.net.cn

消息WindowChatMemory(记忆窗口聊天)聊天记忆仓库抽象为聊天对话内存提供存储实现。有几种实现方式,包括InMemoryChatMemoryRepository,JdbcChatMemoryRepository,CassandraChatMemoryRepositoryNeo4jChatMemoryRepository.spring-doc.cadn.net.cn

更多细节和使用示例,请参见Chat Memory文档。spring-doc.cadn.net.cn

实现说明

命令式和响应式规划模型的结合应用聊天客户端是API的一个独特方面。 通常应用要么是被动的,要么是命令式的,但不会两者兼具。spring-doc.cadn.net.cn

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

由于 Spring Boot 3.4 中的一个 bug,必须设置“spring.http.client.factory=jdk” 属性。否则默认设置为“反应器”,这会破坏某些AI工作流程,比如ImageModel。spring-doc.cadn.net.cn

  • 流媒体支持仅通过Reactive stack。因此,命令式应用程序必须包含响应式栈(例如 spring-boot-starter-webflux)。spring-doc.cadn.net.cn

  • 非流式传输只能通过Servlet栈支持。响应式应用程序必须包含 Servlet 栈(例如 spring-boot-starter-web),并预期部分调用会阻塞。spring-doc.cadn.net.cn

  • 工具调用是必不可少的,导致工作流程被阻塞。这也会导致微米观测的部分或中断(例如,ChatClient的跨度和工具调用的跨度不连接,且第一个跨度因此保持不完整)。spring-doc.cadn.net.cn

  • 内置的顾问对标准通话执行阻塞作,对流式通话执行非阻塞作。用于顾问流调用的反应堆调度器可以通过每个顾问类的构建器配置。spring-doc.cadn.net.cn