ChatClient 提供了一套流畅式API,用于与 AI 模型进行通信。它支持同步和流式编程模型。
该流畅式 API 提供了用于构建传递给 AI 模型作为输入的 Prompt 的各个组成部分的方法。Prompt 包含指导 AI 模型输出和行为的指令性文本。从 API 的角度来看,Prompt 由一系列消息组成。
AI 模型处理两种主要类型的消息:用户消息,这些是用户直接输入的内容;以及系统消息,这些是由系统生成的,用于引导对话。
这些消息通常包含占位符,这些占位符会在运行时根据用户输入进行替换,以根据用户的输入定制 AI 模型的响应。
此外,还可以指定 Prompt 选项,例如要使用的 AI 模型的名称以及控制生成输出的随机性或创造性的temperature设置。
创建一个ChatClient
ChatClient
是通过 ChatClient.Builder
对象创建的。您可以为任何 ChatModel
获取一个由 Spring Boot 自动配置的 ChatClient.Builder
实例,或者通过编程方式手动创建一个。
使用自动配置的ChatClient.Builder
在最简单的使用场景中,Spring AI 提供了 Spring Boot 自动配置,为您创建了一个prototype作用域的 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 模型的响应以字符串的形式返回。
编程式创建ChatClient
您可以通过设置属性 spring.ai.chat.client.enabled=false
来禁用 ChatClient.Builder 的自动配置。如果需要同时使用多个聊天模型,这将非常有用。然后,为每个需要的 ChatModel 编程式地创建一个 ChatClient.Builder 实例:
ChatModel myChatModel = ... // usually autowired
ChatClient.Builder builder = ChatClient.builder(this.myChatModel);
// or create a ChatClient with the default builder settings:
ChatClient chatClient = ChatClient.create(this.myChatModel);
提示词模版
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 使用基于 Terence Parr 开发的开源 StringTemplate 引擎的 StTemplateRenderer 实现。
Spring AI 还提供了 NoOpTemplateRenderer,用于那些不需要模板处理的情况。
直接在 ChatClient 上配置的 TemplateRenderer(通过
.templateRenderer()
设置)仅适用于直接在 ChatClient 构建链(build chain)中定义的提示内容(例如,通过.user()
、.system()
)。它不会影响像 QuestionAnswerAdvisor 这样的顾问(Advisors)内部使用的模板,因为这些advisor有自己的模板自定义机制。
如果您更愿意使用不同的模板引擎,可以直接向 ChatClient 提供 TemplateRenderer 接口的自定义实现。您也可以继续使用默认的 StTemplateRenderer,但对其进行自定义配置。
例如,默认情况下,模板变量通过{}
语法标识。如果您计划在提示中包含 JSON,您可能希望使用不同的语法以避免与 JSON 语法冲突。例如,您可以使用 <
和 >
作为分隔符。
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
对象,该对象包含多个生成结果以及关于响应的元数据,例如生成响应所使用的 token 数量。ChatClientResponse chatClientResponse()
:返回一个ChatClientResponse
对象,该对象包含ChatResponse
对象以及 ChatClient 的执行上下文,使您能够访问在顾问执行过程中使用到的额外数据(例如在 RAG 流程中检索到的相关文档)。entity()
:返回一个 Java 类型。entity(ParameterizedTypeReference<T> type)
:用于返回一个实体类型的集合。entity(Class<T> type)
:用于返回一个特定的实体类型。entity(StructuredOutputConverter<T> structuredOutputConverter)
:用于指定一个StructuredOutputConverter
的实例,以将字符串转换为实体类型。
您也可以调用 stream()
方法,而不是 call()
方法。
stream()方法的返回值
在 ChatClient 上指定了 stream()
方法之后,响应类型有以下几种选择:
Flux<String> content()
:返回一个由 AI 模型生成的字符串的 Flux。Flux<ChatResponse> chatResponse()
:返回一个包含关于响应的额外元数据的ChatResponse
对象的 Flux。Flux<ChatClientResponse> chatClientResponse()
:返回一个包含ChatResponse
对象和 ChatClient 执行上下文的ChatClientResponse
对象的 Flux,使您能够访问在顾问执行过程中使用到的额外数据(例如在 RAG 流程中检索到的相关文档)。
当我们使用Flux<String> content()
的时候可能会遇到中文打印乱码的情况,此时需要设置response的字符集为utf-8:
@GetMapping("/origin/ai/genFlux")
public Flux<String> generateFlux(@RequestParam(name = "message", defaultValue = "生成一个可以用于每日一言的句子") String message
, HttpServletResponse httpServletResponse) {
// 设置响应头,如果不设置的话中文打印乱码
httpServletResponse.setCharacterEncoding("UTF-8");
Message tip = ...;
Prompt prompt = ...;
Flux<String> content = chatClient.prompt(prompt)
.stream()
.content();
return content;
}
如果是get请求,我们直接在浏览器中请求对应的地址就可以看到屏幕持续打印响应信息。
