深入探索ChatClient:简化AI模型交互的强大工具
前言
在人工智能飞速发展的当下,大语言模型(LLM)的应用越来越广泛。然而,构建基于LLM的应用程序并非易事,通常需要多个组件协同工作,如提示词模板、聊天记忆、LLM模型、输出解析器、RAG组件(嵌入模型和存储)等,这使得编码过程变得复杂繁琐。ChatClient的出现,如同为开发者在这片复杂的领域中开辟了一条捷径。它提供了与AI模型通信的Fluent API,支持同步和反应式编程模型,将与LLM及其他组件交互的复杂性隐藏在背后,极大地简化了开发流程,让开发者能够更专注于业务逻辑的实现。接下来,让我们深入了解ChatClient的强大功能和使用方法。
ChatClient简介
ChatClient类似于应用程序开发中的服务层,直接为应用程序提供AI服务。开发者可以利用其Fluent API快速组装一整套AI交互流程。它具有丰富的功能,涵盖基础与高级两个层面。
基础功能
- 定制和组装模型的输入(Prompt):通过灵活的设置,为模型提供精准的指令输入,引导模型生成符合预期的输出。
- 格式化解析模型的输出(Structured Output):将模型输出转化为结构化的数据,便于应用程序进一步处理和使用。
- 调整模型交互参数(ChatOptions):根据不同的需求,对模型交互的参数进行优化,提升交互效果。
高级功能
- 聊天记忆(Chat Memory):能够记录对话历史,让模型在后续交互中考虑之前的信息,实现更自然、连贯的对话体验。
- 工具/函数调用(Function Calling):允许在与模型交互时调用特定的函数,扩展模型的能力,使其能更好地处理复杂任务。
- RAG(检索增强生成):通过检索相关信息并将其融入模型输入,提升模型生成内容的准确性和相关性。
创建ChatClient
使用自动配置的ChatClient.Builder
在Spring Boot项目中,可以轻松地自动注入由Spring Boot自动配置创建的默认ChatClient.Builder实例。如下代码展示了一个简单的ChatController,通过注入该实例来构建ChatClient,并实现根据用户提问获取模型文本回答的功能:
@RestController
public class ChatController {private final ChatClient chatClient;public ChatController(ChatClient.Builder builder) {this.chatClient = builder.build();}@GetMapping("/chat")public String chat(String input) {return this.chatClient.prompt().user(input).call().content();}
}
以编程方式创建ChatClient
当需要多个聊天模型一起使用时,可以通过设置属性spring.ai.chat.client.enabled=false
来禁用ChatClient.Builder bean的自动配置,然后以编程方式创建ChatClient.Builder。例如:
ChatModel myChatModel = ... // usually autowired
ChatClient.Builder builder = ChatClient.builder(myChatModel);
// or create a ChatClient with the default builder settings:
ChatClient chatClient = ChatClient.create(myChatModel);
处理ChatClient响应
返回ChatResponse
AI模型的响应是一个由ChatResponse类型定义的丰富结构,包含响应生成相关的元数据,还可能包含多个子响应(Generation)。通过调用chatResponse()
方法可以获取这样的响应,示例如下:
ChatResponse chatResponse = chatClient.prompt().user("Tell me a joke").call().chatResponse();
返回实体类(Entity)
Spring AI框架支持自动将模型输出的字符串转换为预先定义好的实体类型。使用entity()
方法即可完成这一转换。例如,定义一个Java record(POJO):
record ActorFilms(String actor, List<String> movies) {}
通过以下代码将AI模型的输出映射到ActorFilms类型:
ActorFilms actorFilms = chatClient.prompt().user("Generate the filmography for a random actor.").call().entity(ActorFilms.class);
对于泛型类型,如List<ActorFilms>
,可以使用entity(ParameterizedTypeReference<T> type)
重载方法:
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
方法提供了一种异步、持续获取模型响应的方式。可以返回Flux<String>
类型的字符串流,也可以使用Flux<ChatResponse> chatResponse()
方法获得包含元数据的ChatResponse响应数据流。示例如下:
Flux<String> output = chatClient.prompt().user("Tell me a joke").stream().content();
定制ChatClient默认值
设置默认System Message
可以通过defaultSystem
方法为ChatClient设置默认的system message。例如,让ChatClient以海盗风格回答所有问题:
@Configuration
class Config {@BeanChatClient chatClient(ChatClient.Builder builder) {return builder.defaultSystem("You are a friendly chat bot that answers question in the voice of a Pirate").build();}
}
也可以使用模板形式,在每次调用前修改请求参数,增加灵活性:
@Configuration
class Config {@BeanChatClient 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",chatClient.prompt().system(sp -> sp.param("voice", voice)).user(message).call().content());}
}
其他默认设置
除了defaultSystem
,还可以在ChatClient.Builder级别指定其他默认提示:
- defaultOptions(ChatOptions chatOptions):传入可移植选项或特定于模型实现的选项。
- defaultFunction(String name, String description, java.util.function.Function<I, O> function):定义在用户文本中引用的函数及其相关信息。
- defaultFunctions(String… functionNames):应用程序上下文中定义的函数bean名称。
- defaultUser(String text)、defaultUser(Resource text)、defaultUser(Consumer userSpecConsumer):用于定义用户消息输入。
- defaultAdvisors(RequestResponseAdvisor… advisor)、defaultAdvisors(Consumer advisorSpecConsumer):Advisors可修改用于创建Prompt的数据,实现如Retrieval Augmented Generation模式。
并且在运行时,可以使用ChatClient提供的不带default前缀的相应方法覆盖这些默认值。
Advisors
在构建Prompt调用AI模型时,使用上下文数据扩充Prompt是常见的模式。这些上下文数据包括自己的数据(如特定领域知识、产品文档)、对话历史记录等。以检索增强生成(RAG)为例,当用户问题发送到AI模型时,QuestionAnswerAdvisor会在向量数据库中查询相关文档,并将响应附加到用户消息Prompt中。假设已将数据加载到VectorStore,可以通过以下方式执行RAG:
ChatResponse response = ChatClient.builder(chatModel).build().prompt().advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults())).user(userText).call().chatResponse();
通过SearchRequest
的FILTER_EXPRESSION
参数,还可以在运行时动态更新过滤表达式,实现对搜索结果的灵活筛选。
聊天记忆
ChatMemory接口负责存储聊天对话历史记录,提供添加、检索和清除消息的方法。目前有InMemoryChatMemory和CassandraChatMemory两种实现方式,分别提供内存存储和time - to - live类型的持久存储。例如,创建一个包含time - to - live配置的CassandraChatMemory:
CassandraChatMemory.create(CassandraChatMemoryConfig.builder().withTimeToLive(Duration.ofDays(1)).build());
同时,有多种Advisor实现利用ChatMemory接口来增强Prompt,如MessageChatMemoryAdvisor、PromptChatMemoryAdvisor和VectorStoreChatMemoryAdvisor,它们在将对话历史记录添加到Prompt的方式上各有不同。
日志记录
SimpleLoggerAdvisor用于记录ChatClient的request和response数据,对调试和监控AI交互十分有用。启用日志记录只需在创建ChatClient时将其添加到Advisor链中,并将Advisor包的日志记录级别设置为DEBUG。此外,还可以通过构造函数自定义记录数据的方式,满足特定需求。
总结
ChatClient为开发者提供了一个强大且便捷的工具,极大地简化了与AI模型的交互过程。通过其丰富的功能,包括灵活的输入定制、多样的输出处理、便捷的默认值设置以及对高级特性如聊天记忆、RAG的支持,开发者能够高效地构建出功能强大的AI应用程序。无论是处理简单的文本交互,还是实现复杂的业务逻辑,ChatClient都能发挥重要作用。在实际应用中,合理运用ChatClient的各项功能,可以显著提升开发效率,降低开发成本,为用户带来更优质的AI交互体验。随着人工智能技术的不断发展,相信ChatClient也将持续演进,为开发者提供更多的便利和可能性。让我们充分利用这一工具,在AI应用开发的道路上迈出更坚实的步伐。