【langchain4j】AIservices能够实现更加灵活的chain 文章目录一. AI service介绍1. 如何工作的2. AiServices提供的能力3. 支持的返回形式二. 简单的例子接收用户消息并按规定返回1. 接收单个变量2. 接收更多动态变量三. advanced RAG四. Chaining multiple AI Services多个AiSerives合并到一起五. 相关教程[LangChain4j AiServices Tutorial](https://www.sivalabs.in/langchain4j-ai-services-tutorial/)一. AI service介绍AI Service提供了比chain更加简单、灵活的能力AI Services provide a simpler and more flexible alternative to chains.langchain4j在# Introduction 也介绍了Chains为每个常见用例建立一个链例如聊天机器人、RAG 等。链组合了多个低级组件并协调它们之间的交互。它们的主要问题是如果您需要定制某些东西它们就太僵化了。 LangChain4j 仅实现了两个 ChainConversationalChain 和 ConversationalRetrievalChain。推荐使用AiService而且langchain4j也不会开发更多的链Represents a chain step that takes an input and produces an output. Chains are not going to be developed further, it is recommended to use AiServices instead.1. 如何工作的AiServices提供了调用大模型的能力将用户输入的信息转换为ChatMessage[!NOTE]用户定义接口AiServices转换这个接口You provide theClassof your interface toAiServicesalong with the low-level components, andAiServicescreates a proxy object implementing this interface. Currently, it uses reflection, but we are considering alternatives as well. This proxy object handles all the conversions for inputs and outputs. In this case, the input is a singleString, but we are using aChatLanguageModelwhich takesChatMessageas input.将用户的消息转换为UserMessage并调用的ChatLanguageModelSo,AiServicewill automatically convert it into aUserMessageand invokeChatLanguageModel. Since the output type of thechatmethod is aString, afterChatLanguageModelreturnsAiMessage, it will be converted into aStringbefore being returned from thechatmethod.2. AiServices提供的能力参考代码dev.langchain4j.service.AiServices目前AiServices提供了以下能力message相关message templatesStatic system message templates, configured via SystemMessage annotation on top of the methodsystem message templatesDynamic system message templates, configured via systemMessageProvider(Function)user message templatesStatic user message templates, configured via UserMessage annotation on top of the methodDynamic user message templates, configured via method parameter annotated with UserMessageChatMemory相关Single (shared) ChatMemory, configured via chatMemory(ChatMemory)Separate (per-user) ChatMemory, configured via chatMemoryProvider(ChatMemoryProvider) and a method parameter annotated with MemoryIdRAG的能力RAG, configured via contentRetriever(ContentRetriever) or retrievalAugmentor(RetrievalAugmentor)相关工具Tools, configured via tools(List) or tools(Object…) and methods annotated with Tool输出结构Various method return types (output parsers), see more details below返回流Streaming (use TokenStream as a return type)StructuredPrompting结构化提示作为方法参数Structured prompts as method arguments (see StructuredPrompt)自动审核Auto-moderation, configured via Moderate annotation注意暂不支持多模态AI services currently do not support multimodality, please use the low-level API for this.3. 支持的返回形式定义的接口返回形式是灵活的可按需选择直接回答如果你想直接获取模型生成的文本可以使用String或AiMessage。这对于简单的对话或问题回答非常有用。集合类型如果你希望模型将回答作为一个有序集合返回比如以列表或项目符号的形式列出多个项可以使用ListString或SetString。分类任务如果你希望模型执行分类任务比如判断文本的类别你可以使用枚举类型Enum或布尔值boolean。这意味着模型会返回一个有限的类别集合或一个简单的真/假值。数据提取当你希望从模型的回答中提取特定数据时可以使用基本类型如int、Double等或者 Java 的日期、时间和数字类型如Date、LocalDateTime、BigDecimal等。例如你可以从模型的回答中提取一个特定的数字或者日期。自定义对象如果你希望模型返回的结果是某个自定义的 Java 对象POJO你可以定义自己的 Java 类并让 LLM 将答案映射为这些对象。这对于需要结构化输出的场景非常有用。访问额外信息如令牌使用量如果你希望不仅获取模型的核心输出还希望获取额外的元数据如令牌使用量、来源信息等你可以使用ResultT类型。在这种情况下T代表你想要的具体数据类型比如ResultString或ResultMyCustomPojo。JSON 模式对于自定义 POJO使用 JSON 格式来接收数据是一种常见做法。如果 LLM 支持 JSON 格式OpenAI 模型支持通过设置responseFormat(json_object)来启用 JSON 模式这使得从模型的响应中提取结构化数据更加容易。简单的说langchain4j会基于你定义的返回格式而返回而不是像llm只返回markdown格式的内容。这比langchain确实要灵活二. 简单的例子接收用户消息并按规定返回1. 接收单个变量定义接口注解不同注解代表不同类型的消息可以接收Prompt消息返回值可由用户自定义的类型返回如下表示了用户定义了四种情绪langchain4j根据用户的输入基于大模型只返回对应的感情词。//因为只有一个变量不用使用注解定义接收的变量名publicclassTest{publicstaticvoidmain(String[]args){SentimentAnalyzerassistantAiServices.builder(SentimentAnalyzer.class).chatLanguageModel(OpenAiChatModel.withApiKey(OPENAI_API_KEY))// Deprecated it should use OpenAI LLM.build();Sentimentsentimentassistant.analyzeSentimentOf(I love you);//输出结果是用户自定义的枚举中的一个System.out.println(sentiment);// POSITIVE}}enumSentiment{POSITIVE,NEUTRAL,NEGATIVE}interfaceSentimentAnalyzer{UserMessage(Analyze sentiment of {{it}})SentimentanalyzeSentimentOf(Stringtext);}2. 接收更多动态变量接收两个变量需要通过注解来说明文本system、user的Prompt中哪些变量被替换/** * 接收user和system的消息 * 动态接收language、text的值 */interfaceTranslator{SystemMessage(You are a professional translator into {{language}})UserMessage(Translate the following text: {{text}})Stringtranslate(V(text)Stringtext,V(language)Stringlanguage);}了解了AiServices的基础使用下面我们尝试一些RAG的能力三. advanced RAG提供如下能力QueryTransformer转换查询QueryRouter路由查询ContentRetriever检索queryContentAggregator内容聚合ContentInjector内容注入如下图展示了这些组件是如何配合构成一个复杂的chain过程如下用户生成一个 UserMessage该消息被转换为一个 Query。QueryTransformer 将 Query 转换为一个或多个 Query。每个 Query 由 QueryRouter 路由到一个或多个 ContentRetriever。每个 ContentRetriever 为每个 Query 检索相关的内容。ContentAggregator 将所有检索到的内容合并成一个最终的排序列表。这个内容列表被注入到原始的 UserMessage 中。最后包含原始查询和注入的相关内容的 UserMessage 被发送到 LLM。四. Chaining multiple AI Services多个AiSerives合并到一起AI Services可以与if/else、switch、for/while进行结合判断[!NOTE]AI Services can be used as and combined with regular (deterministic) software components:You can call one AI Service after another (aka chaining).You can use deterministic and LLM-poweredif/elsestatements (AI Services can return aboolean).You can use deterministic and LLM-poweredswitchstatements (AI Services can return anenum).You can use deterministic and LLM-poweredfor/whileloops (AI Services can returnintand other numerical types).You can mock an AI Service (since it is an interface) in your unit tests.You can integration test each AI Service in isolation.You can evaluate and find the optimal parameters for each AI Service separately.如下连接两个AiServices的例子基于大模型的返回java判断如果是简单的问候则直接输出预知好的文字AiServicesChatBot。//1.我们使用了成本较低的 **Llama2** 来完成判断文本是否为问候语的简单任务而使用成本较高的 **GPT-4** 结合内容检索器RAG来处理更复杂的任务。//2.可以分别对 **GreetingExpert** 和 **ChatBot** 进行评估找到每个子任务的最优参数或者从长远来看甚至为每个特定子任务微调一个小型的专用模型。packagecom.qihoo.middle.nltosql.langchain4j.service;importdev.langchain4j.service.AiServices;importdev.langchain4j.service.SystemMessage;importdev.langchain4j.service.UserMessage;publicclassTest{publicstaticvoidmain(String[]args){GreetingExpertgreetingExpertAiServices.create(GreetingExpert.class,llama2);ChatBotchatBotAiServices.builder(ChatBot.class).chatLanguageModel(gpt4).contentRetriever(milesOfSmilesContentRetriever).build();MilesOfSmilesmilesOfSmilesnewMilesOfSmiles(greetingExpert,chatBot);StringgreetingmilesOfSmiles.handle(Hello);System.out.println(greeting);// Greetings from Miles of Smiles! How can I make your day better?StringanswermilesOfSmiles.handle(Which services do you provide?);System.out.println(answer);// At Miles of Smiles, we provide a wide range of services ...}}interfaceGreetingExpert{UserMessage(Is the following text a greeting? Text: {{it}})booleanisGreeting(Stringtext);}interfaceChatBot{SystemMessage(You are a polite chatbot of a company called Miles of Smiles.)Stringreply(StringuserMessage);}classMilesOfSmiles{privatefinalGreetingExpertgreetingExpert;privatefinalChatBotchatBot;//通过java的逻辑来判断与大模型对话的意图。publicStringhandle(StringuserMessage){if(greetingExpert.isGreeting(userMessage)){returnGreetings from Miles of Smiles! How can I make your day better?;}else{returnchatBot.reply(userMessage);}}}对于更复杂AiServices的交互逻辑我们可以通过使用java的设计模型来串联起这些复杂的逻辑。五. 相关教程LangChain4j AiServices TutorialLangChain4j AiServices Tutorial by Siva