首页
友链
留言
统计
Search
1
NPS内网穿透服务
271 阅读
2
langchain4j ChatLanguageModel(一)
217 阅读
3
今日推软——code FA
169 阅读
4
本服务用于为你的某东每天自动获取豆豆
153 阅读
5
儿童节就要到了,大家有什么想说的麽
142 阅读
小日常
langchain4j
登录
/
注册
Search
标签搜索
langchain4j
java
数据结构
js
功能提供
小宇
累计撰写
24
篇文章
累计收到
177
条评论
首页
栏目
小日常
langchain4j
页面
友链
留言
统计
搜索到
24
篇与
的结果
2024-11-20
转至微信公众号
由于作者精力有限,后续所有文章将发至微信公众号——小宇在折腾感谢大家关注,公众号不定期更新一些好玩的文章如:LSPosed模块开发学习记录23种设计模式若智云开发记录开发过程中遇到的一些难点记录各种学习笔记JUCShell仓颉NginxVue组件通信正则表达式…………{lamp/}
2024年11月20日
31 阅读
0 评论
1 点赞
2024-06-14
博客提供功能介绍
小宇为大家提供一系列功能{tabs}{tabs-pane label="内网穿透"}当前NPS版本:0.26.17下载相应看客户端client{abtn icon="" color="#dda373" href="https://www.aoyudi.top/index.php/archives/1/" radius="22px" content="NPS内网穿透服务"/} {/tabs-pane}{tabs-pane label="openAI国内代理"}官方链接:https://api.openai.com/v1/chat/completions使用方法:使用openproxy.aoyudi.top替代api.openai.com例如https://openproxy.aoyudi.top/v1/chat/completions{/tabs-pane}{tabs-pane label="微信公众号接入智能对话服务"}详情请关注 {abtn icon="" color="#c66b2f" href="https://www.ruojy.top/" radius="22px" content="若智云"/} 的发展及规划 ,未来会提供接入及说明(可能一年,也可能两年,或许三年[关键是我一个人做的没意思啊 :@(喜极而泣) ]){/tabs-pane}{tabs-pane label="KMS激活windows及MsOffice服务"}KMS地址:kms.aoyudi.top对了,这里感谢 沸腾 提供文件{/tabs-pane}{tabs-pane label="青龙面板代挂(目前只有京东部分任务)"}KMS地址:ql.aoyudi.top{/tabs-pane}{/tabs}
2024年06月14日
75 阅读
0 评论
3 点赞
2024-05-27
langchain4j ChatLanguageModel(三)
“ 一些LLM除了生成文本外,还可以触发操作。”Tool有一个概念被称为“工具”,或“函数调用”。它允许LLM在必要时调用通常由开发人员定义的一个或多个可用工具。工具可以是任何东西:网络搜索、对外部API的调用,或者执行特定的代码段,等等。LLM本身实际上不能调用工具;相反,它们在响应中表达了调用特定工具的意图(而不是以纯文本形式响应)。作为开发人员我们应该使用提供的参数执行这个工具,并报告工具执行的结果。举个例子,我们知道LLM本身数学不是很好。如果您的用例涉及偶尔的数学计算,您可能希望为LLM提供一个“数学工具”。通过在对LLM的请求中声明一个或多个工具,它可以决定调用其中的一个工具(如果它认为合适的话)。给定一个数学问题以及一组数学工具,LLM可能会决定要正确地回答这个问题,它应该首先调用所提供的数学工具之一。Request: - messages: - UserMessage: - text: What is the square root of 475695037565? Response: - AiMessage: - text: The square root of 475695037565 is approximately 689710.我们使用以下工具再来一次Request 1: - messages: - UserMessage: - text: What is the square root of 475695037565? - tools: - sum(double a, double b): Sums 2 given numbers - squareRoot(double x): Returns a square root of a given number Response 1: - AiMessage: - toolExecutionRequests: - squareRoot(475695037565) here we are executing the squareRoot method with the "475695037565" argument and getting "689706.486532" as a result Request 2: - messages: - UserMessage: - text: What is the square root of 475695037565? - AiMessage: - toolExecutionRequests: - squareRoot(475695037565) - ToolExecutionResultMessage: - text: 689706.486532 Response 2: - AiMessage: - text: The square root of 475695037565 is 689706.486532.当然,以上只是官方给出来的示例,我用千帆大模型测试了一下下,好像tool并不太好用。@2024/5/27两个抽象级别LangChain4j 为使用tools提供了两个抽象级别Low level tool ApiToolSpecofication一个包含工具所有信息的对象name,description,parametersHigh level tool ApiToolSpecofication两种方法创建ToolSpecoficationToolSpecification toolSpecification = ToolSpecification.builder() .name("getSquareRoot") .description("Returns the weather forecast for a given city") .addParameter("number", type("string"), description("The city for which the weather forecast should be returned")) .build();2class WeatherTools { @Tool("Returns the weather forecast for a given city") String getWeather( @P("The city for which the weather forecast should be returned") String city, TemperatureUnit temperatureUnit ) { ... } } List<ToolSpecification> toolSpecifications = ToolSpecifications.toolSpecificationsFrom(WeatherTools.class);High level tool Api在更高的层次上,你可以用@Tool注释任何Java方法,并将其与AlServices一起使用。AlServices将自动将这些方法转换为ToolSpecifications,并将其包含在与LLM的每次交互的请求中。当LLM决定调用该工具时,AlService将自动执行适当的方法,方法的返回值(如果有的话)将被发送回LLM您可以在DefaultToolExecutor中找到实现细节。用@Tool注释的方法可以接受各种类型的任意数量的参数。它们还可以返回任何类型,包括void。如果方法返回类型为void,则如果方法成功返回,则将“success”字符串发送到LLM。如果方法具有字符串返回类型,则返回值将按原样发送到LLM,不进行任何转换。对于其他返回类型,返回值在发送到LLM之前被转换为JSON以下是几个示例@Tool("Searches Google for relevant URLs, given the query") public List<String> searchGoogle(@P("search query") String query) { return googleSearchService.search(query); } @Tool("Returns the content of a web page, given the URL") public String getWebPageContent(@P("URL of the page") String url) { Document jsoupDocument = Jsoup.connect(url).get(); return jsoupDocument.body().text(); }工具我是玩了两天,愁了两天,这玩意儿他也没调用过工具类 愁.jpg。annotation@Tool有2个可选字段:名称:工具的名称。如果没有提供,则该方法的名称将用作工具的名称。值:工具的描述。根据不同的工具,LLM可能在没有任何描述的情况下也能很好地理解它(例如,add(a,b)是显而易见的,但通常最好提供清晰且有意义的名称和描述。这样,LLM就有更多的信息来决定是否调用给定的工具,以及如何调用。@P方法参数可以有选择地用@P注释,有一个强制字段(value)用于提供参数的说明。@ToolMemoryId如果你的Al Service方法有一个用@MemoryId注释的参数,你也可以用@ToolMemoryld注释一个@Tool方法的参数。提供给AlService方法的值将自动传递给非@Tool方法。如果您有多个用户和/或每个用户有多个聊天记录/记忆,并且希望在@Tool方法中区分它们,则此功能非常有用。
2024年05月27日
58 阅读
0 评论
0 点赞
2024-05-27
langchain4j ChatLanguageModel(二)
接上篇文章,上次提到了ChatMessage、以及ChatMemory和一些大家看不懂的example,现在我们来到最重要的地方AI Service到目前为止,我们已经涵盖了底层组件,如聊天语言模型、聊天消息、聊天记忆等。在这个级别上工作是非常灵活的,给你完全的自由,但它也迫使你编写大量的样板代码。由于LLM驱动的应用程序通常不仅需要单个组件,而且需要多个组件协同工作(例如,提示模板、聊天内存、LLM、输出解析器、RAG组件:嵌入模型和存储),并且通常涉及多个交互,编排它们变得更加繁琐。我们希望您关注业务逻辑,而不是低层的实现细节。因此,目前在Langchain4j中有两个高级概念可以帮助解决这个问题AI Services and Chains(Chains被遗弃了,所以只剩AI Services).AI Service的一些处理操作格式化大语言模型的输入解析大语言模型输出AI Service更高级的的处理操作聊天记忆工具检索增强生成AI Services可用于构建有状态聊天机器人,以促进来回交互,以及自动化流程,其中每个对LLM的调用都是隔离的。最简单的AI Service首先,我们用一个方法chat定义一个接口,它接受一个String作为输入并返回一个String。 interface IAiService { String chat(String userMessage); }创建了我们的底层组件,这些组件将在我们的AI服务中使用。在这种情况下,我们只需要ChatLanguageModel:(下方所用的qianfanChatModel均来自此处,后边不再重复写) QianfanChatModel qianfanChatModel = QianfanChatModel.builder() .apiKey("apiKey") .secretKey("secretKey") .modelName("modelName") .build();最后,我们可以使用AiServices去create 一个我们的AI Service的一个实例:IAiService aiService = AiService .create(IAiService .class, qianfanChatModel);现在我们可以使用aiService:String answer = aiService.chat("Hello"); System.out.println(answer); // Hello, how can I help you?AI Service是如何工作的你向AiServices提供接口的Class以及底层组件,AiServices创建一个实现这个接口的代理对象。目前,它使用反射,此代理对象处理输入和输出的所有转换。在本例中,输入是一个String,但我们使用的是一个以ChatMessage为输入的ChatLanguageModel。因此,AiService会自动将其转换为UserMessage并且反射到ChatLanguageModel。由于chat方法的输出类型是String,ChatLanguageModel处理后返回的是AiMessage,在从chat方法返回之前,它将被转换成一个字符串。@SystemMessage与@UserMessage在上一篇文章中,我们有提到ChatMessage,UserMessage和SystemMessage也被介绍到了。我们这里所用到的是两个注解。我们使用@SystemMessage注解,并添加了我们希望使用的系统提示符,这将被转换为幕后的SystemMessage,并与UserMessage一起发送到大语言模型中。 interface Chinese { @SystemMessage("Please answer in Chinese. {{it}}") String chat(String userMessage); } Chinese chinese = AiServices.create(Chinese.class, qianfanChatMode); String answer = chinese.chat("Hello"); // 你好,***(中文回复)上文中也有提到过,有的模型他并没有SystemMessage这个概念,或许我们可以使用@UserMessage来达到这个目的 interface Chinese { @UserMessage("Please answer in Chinese. {{message}}") String chat(@V("message") String userMessage); } Chinese chinese = AiServices.create(Chinese.class, qianfanChatMode); String answer = chinese.chat("Hello"); // 你好,***(中文回复)很明显,在@UserMessaged的实例中,我并不是简单的将SystemMessage改成了UserMessage,当然,也不是必须要改,只是说一下{{it}}我们可以自定义名称。自定义实体类对象为返回类型如果你想接收来自LLM的结构化输出,你可以将你的AlService方法的返回类型从String改为其他类型。目前,AIService支持以下返回类型:class Person { String firstName; String lastName; LocalDate birthDate; @Override public String toString() { return "Person {" + " firstName = \"" + firstName + "\"" + ", lastName = \"" + lastName + "\"" + ", birthDate = " + birthDate + " }"; } } interface PersonExtractor { @UserMessage("Extract information about a person from {{it}}") Person extractPersonFrom(String text); } PersonExtractor personExtractor = AiServices.create(PersonExtractor.class, model); String text = """ In 1968, amidst the fading echoes of Independence Day, a child named John arrived under the calm evening sky. This newborn, bearing the surname Doe, marked the start of a new journey. He was welcomed into the world at 345 Whispering Pines Avenue a quaint street nestled in the heart of Springfield an abode that echoed with the gentle hum of suburban dreams and aspirations. """; Person person = personExtractor.extractPersonFrom(text); System.out.println(person); // // Person { firstName = "John", lastName = "Doe", birthDate = 1968-07-04 }为模型启用日志记录ChatLanguageModel model = OpenAiChatModel.builder() .apiKey(...) .logRequests(true) .logResponses(true) .build();在官方文档中logRequests()和logResponses()携带一个参数true即可,可是在我看来没什么两样(可能用法不对,也可能官方还存在缺陷@2024/5/26)以及官方下面提到的JSON模式,我看到有贡献者给的代码例示中:他都重写toString了,讨论responseFormat()有意义还重要麽?(小宇轻轻吐槽一下,确实有点搞不懂的说)。预定义的问候语让我们考虑一个简单的例子。我想为我的公司做一个聊天机器人。如果用户问候聊天机器人,我希望它使用预定义的问候语进行响应,而不依赖于LLM来生成问候语。如果用户问一个问题,我希望LLM生成使用公司(又名RAG)的内部知识库的响应。以下是如何将这项任务分解为两个独立的AI服务: interface GreetingExpert { @UserMessage("Is the following text a greeting? Text: {{it}}") boolean isGreeting(String text); } interface ChatBot { @SystemMessage("You are a polite chatbot of a company called Miles of Smiles.") String reply(String userMessage); } class MilesOfSmiles { private final GreetingExpert greetingExpert; private final ChatBot chatBot; ... public String handle(String userMessage) { if (greetingExpert.isGreeting(userMessage)) { return "Greetings from Miles of Smiles! How can I make your day better?"; } else { return chatBot.reply(userMessage); } } } GreetingExpert greetingExpert = AiServices.create(GreetingExpert.class, llama2); ChatBot chatBot = AiServices.builder(ChatBot.class) .chatLanguageModel(gpt4) .contentRetriever(milesOfSmilesContentRetriever) .build(); MilesOfSmiles milesOfSmiles = new MilesOfSmiles(greetingExpert, chatBot); String greeting = milesOfSmiles.handle("Hello"); System.out.println(greeting); // Greetings from Miles of Smiles! How can I make your day better? String answer = milesOfSmiles.handle("Which services do you provide?"); System.out.println(answer); // At Miles of Smiles, we provide a wide range of services ...上面是官方给的示例(直接复制粘贴下来的,并不能直接运行,大家需要自行修改),现在我使用的千帆大语言模型貌似并没有这种“情感方面”的判断我使用下面进行了一个小小的判断:(当然也不排除是我使用的情感词有问题) enum Sentiment { POSITIVE, NEUTRAL, NEGATIVE } interface SentimentAnalyzer { @UserMessage("Analyze sentiment of {{it}}") Sentiment analyzeSentimentOf(String text); @UserMessage("它是消极的吗? {{it}}") boolean isPositive(String text); } SentimentAnalyzer sentimentAnalyzer = AiServices.create(SentimentAnalyzer.class, chatLanguageModel); Sentiment sentiment = sentimentAnalyzer.analyzeSentimentOf("This is good!"); System.out.println(sentiment); // POSITIVE 我男孩中有几个 good,great,bad,,前两个POSITIVE,bad为null,,词穷了,中文也没试几个 boolean positive = sentimentAnalyzer.isPositive("It's terrible!");
2024年05月27日
100 阅读
0 评论
0 点赞
2024-05-26
langchain4j ChatLanguageModel(一)
想象一下,如果你能让聊天机器人不仅仅回答通用问题,还能从你自己的数据库或文件中提取信息,并根据这些信息执行具体操作,比如发邮件,那会是什么情况?Langchain 正是为了实现这一目标而诞生的。model分类ChatLanguageModel(聊天对话)EmbeddingModel(将文本转换为 Embedding)ImageModel(生成和编辑image)ModerationModel(检查文本是否包含有害内容)ScoringModel(对多段文本进行评分、排名,对 RAG 很有用。)从今天开始陆续给大家分享这些model的学习记录。并且始终使用百度千帆大模型,langchain4j官方的qianfan.md目前也由本人尽己所能参与编写及维护。导入依赖 <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> <version>{{version}}</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-qianfan</artifactId> <version>{{version}}</version> </dependency>ChatLanguageModel(聊天对话)ChatLanguageModel 是 LangChain4j 中的低级 API,提供最大的功能和灵活性。ChatMessage(消息)UserMessage来自用户的消息。 用户可以是应用程序的最终用户(人),也可以是应用程序本身。AiMessage这是由 AI 生成的消息,通常用于响应ToolExecutionResultMessageSystemMessage这是来自系统的消息。 作为开发人员,应该定义此消息的内容。 通常,你会在这里写下关于LLM在这次对话中扮演什么角色的说明, 它应该如何表现,以什么方式回答,等等。 LLM 被训练为比其他类型的消息更关注, 所以要小心,最好不要让最终用户自由访问定义或注入一些输入。 通常,它位于对话的开头以下是ChatLanguageModel提供的一种极其简便的方法,输入userMessage处理后输出其返回:public interface ChatLanguageModel { default String generate(String userMessage) { return ((AiMessage)this.generate(UserMessage.from(userMessage)).content()).text(); } }但实际的聊天,我们采用 一个或多个ChatMessage 作为输入 : public interface ChatLanguageModel { default Response<AiMessage> generate(ChatMessage... messages) { return this.generate(Arrays.asList(messages)); } Response<AiMessage> generate(List<ChatMessage> var1); }设想构建一个聊天机器人。想象一下用户和聊天机器人 (AI) 之间的简单多轮对话:User: 你好, 我是小宇。AI: 你好小宇,我能帮你做什么?User: 我叫什么名字?AI: 小宇。 UserMessage firstUserMessage = UserMessage.from("Hello, my name is xiaoyu"); AiMessage firstAiMessage = model.generate(firstUserMessage).content(); // Hi xiaoyu, how can I help you? UserMessage secondUserMessage = UserMessage.from("What is my name?"); AiMessage secondAiMessage = model.generate(firstUserMessage, firstAiMessage, secondUserMessage).content(); // xiaoyu在第二次 model.generate中,我们提供的不仅仅是一个还包含了以前的消息。手动维护这些消息会很麻烦,因此存在一个概念ChatMemoryChatMemory(聊天记忆)我们先来区分一下Memory和History历史记录使用户和 AI 之间的所有消息保持完整。历史记录是用户在 UI 中看到的内容。它代表了实际所说的内容记忆(内存)保留了一些信息,这些信息被呈现给 LLM,使其表现得好像它“记住”了对话。 记忆与历史截然不同。根据所使用的内存算法,它可以通过各种方式修改历史记录: 逐出一些消息,汇总多条消息,汇总单独的消息,从消息中删除不重要的细节, 将额外的信息(例如,用于 RAG)或指令(例如,用于结构化输出)注入到消息中,等等。LangChain4j 目前只提供“Memory”,不提供“History”。ChatMemory有以下特性Eviction policy(驱逐政策,类似于主动丢掉之前说过的话)Persistence(持久化存储)Special treatment of SystemMessage(SystemMessage的特殊处理)Special treatment of tool messages(工具消息的特殊处理)Eviction polic适合 LLM 的上下文窗口。LLM 一次可以处理的Token数量是有上限的。 在某些时候,对话可能会超过此限制。在这种情况下,应逐出某些消息。 通常,最旧的消息会被逐出。控制成本。每个Token都有成本,这使得每次调用 LLM 的成本都会逐渐增加。 逐出不必要的消息可降低成本。控制延迟。发送到 LLM 的Token越多,处理它们所需的时间就越多。Persistence默认情况下ChatMemory实现存储ChatMessages在内存中如果需要持久性,则自定义ChatMemoryStore可以实施 存储ChatMessages在您选择的任何持久性存储中public class MyChatMemoryStore implements ChatMemoryStore { @Override public List<ChatMessage> getMessages(Object memoryId) { /* 每当Chat Memory的用户请求所有消息时,都会调用getMessages方法。这通常在每次与LLM交互期间发生一次。 Object memoryId 值对应于id在创建聊天存储器期间指定。它可以用来区分多个用户和/或对话。getMessages()方法应该返回与给定内存ID相关联的所有消息。 */ } @Override public void updateMessages(Object memoryId, List<ChatMessage> messages) { /* 每次向ChatMemory添加新的ChatMessage时都会调用updateMessages。这通常在每次与LLM交互时发生两次:-次是添加新的UserMessage时另一次是添加新的AiMessage时。 updateMessages()方法应该更新与给定内存ID相关联的所有消息。聊天信息可以单独存储(例如,每个消息一个记录/行/对象)或一起存储(例如,整个聊天存储器一个记录/行/对象)。 */ } @Override public void deleteMessages(Object memoryId) { // 当调用ChatMemory.clear()是会调用该方法,可以不重写此方法/leave this method empty 。 } } // 使用: ChatMemory chatMemory = MessageWindowChatMemory.builder() .id("12345") .maxMessages(10) // 包括ai的回复 .chatMemoryStore(new PersistentChatMemoryStore()) .build();Special treatment of SystemMessageSystemMessage是message的一种特殊类型,因此,它与其他消息类型的处理方式不同一旦添加SystemMessage,将永远保留一次只能持有一个SystemMessageIf a new SystemMessage with the same content is added, it is ignored.If a new SystemMessage with different content is added, it replaces the previous one.Special treatment of tool messages大概意思好像就是:如果一个包含ToolExecutionRequests 的AiMessage被驱除,他的ToolExecutionResultMessage(s) 也会被自动驱除,以避免一些问题Example(一些代码示例)定义一个通用的IAiService,后续会多次用到。public interface IAiService { /** * AI Service提供了一种更简单、更灵活的替代方案。 您可以定义自己的 API(具有一个或多个方法的 Java 接口), 并将为其提供实现 * @param userMessage * @return String */ String chat(String userMessage); }一次极其简单的对话 QianfanChatModel qianfanChatModel = QianfanChatModel.builder() .apiKey("apiKey") .secretKey("secretKey") .modelName("Yi-34B-Chat") // 千帆大模型目前暂时免费的一个modelName .build(); String answer = model.generate("雷军"); System.out.println(answer) // 当然不止apiKey、secretKey、modelName还有以下 QianfanChatModel qianfanChatModel = QianfanChatModel.builder() .baseUrl(...) .apiKey(...) .secretKey(...) .temperature(...) .maxRetries(...) .topP(...) .modelName(...) .endpoint(...) .responseFormat(...) .penaltyScore(...) .logRequests(...) .logResponses() .build();带有一个人的记忆的聊天 QianfanChatModel qianfanChatModel = QianfanChatModel.builder() .apiKey("apiKey") .secretKey("secretKey") .modelName("Yi-34B-Chat") .build(); /* MessageWindowChatMemory functions as a sliding window, retaining the N most recent messages and evicting older ones that no longer fit. However, because each message can contain a varying number of tokens, MessageWindowChatMemory is mostly useful for fast prototyping. 我的理解:保留最新的n条消息(包括回复) */ /* TokenWindowChatMemory which also operates as a sliding window but focuses on keeping the N most recent tokens, evicting older messages as needed. Messages are indivisible. If a message doesn't fit, it is evicted completely. MessageWindowChatMemory requires a Tokenizer to count the tokens in each ChatMessage. 我的理解:更专注于保留N个最新的Token,根据需要逐出较旧的消息 */ ChatMemory chatMemory = MessageWindowChatMemory.builder() .maxMessages(10) .build(); IAiService assistant = AiServices.builder(IAiService.class) .chatLanguageModel(qianfanChatModel) // the model .chatMemory(chatMemory) // memory .build(); String answer = assistant.chat("Hello,my name is xiaoyu"); System.out.println(answer); // Hello xiaoyu!****** String answerWithName = assistant.chat("What's my name?"); System.out.println(answerWithName); // Your name is xiaoyu.****** String answer1 = assistant.chat("I like playing football."); System.out.println(answer1); // The answer String answer2 = assistant.chat("I want to go eat delicious food."); System.out.println(answer2); // The answer String answerWithLike = assistant.chat("What I like to do?"); System.out.println(answerWithLike);//Playing football.******带有多个人的记忆的聊天QianfanChatModel qianfanChatModel = QianfanChatModel.builder() .apiKey("apiKey") .secretKey("secretKey") .modelName("Yi-34B-Chat") .build(); // 此处使用chatMemoryProvider,较上方来说他会更简洁 IAiService assistant = AiServices.builder(IAiService.class) .chatLanguageModel(qianfanChatModel) // the model .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10)) // chatMemoryProvider .build(); String answer = assistant.chat(1,"Hello, my name is xiaoyu"); System.out.println(answer); // Hello xiaoyu!****** String answer1 = assistant.chat(2,"Hello, my name is xiaomi"); System.out.println(answer1); // Hello xiaomi!****** String answerWithName1 = assistant.chat(1,"What's my name?"); System.out.println(answerWithName1); // Your name is xiaoyu. String answerWithName2 = assistant.chat(2,"What's my name?"); System.out.println(answerWithName2); // Your name is xiaomi.持久化聊天记忆导入新依赖,用于存储 <dependency> <groupId>org.mapdb</groupId> <artifactId>mapdb</artifactId> <version>3.1.0</version> </dependency>class PersistentChatMemoryStore implements ChatMemoryStore { private final DB db = DBMaker.fileDB("chat-memory.db").transactionEnable().make(); private final Map<String, String> map = db.hashMap("messages", STRING, STRING).createOrOpen(); @Override public List<ChatMessage> getMessages(Object memoryId) { String json = map.get((String) memoryId); return messagesFromJson(json); } @Override public void updateMessages(Object memoryId, List<ChatMessage> messages) { String json = messagesToJson(messages); map.put((String) memoryId, json); db.commit(); } @Override public void deleteMessages(Object memoryId) { map.remove((String) memoryId); db.commit(); } } class PersistentChatMemoryTest{ public void test(){ QianfanChatModel qianfanChatModel = QianfanChatModel.builder() .apiKey("apiKey") .secretKey("secretKey") .modelName("Yi-34B-Chat") .build(); ChatMemory chatMemory = MessageWindowChatMemory.builder() .maxMessages(10) .chatMemoryStore(new PersistentChatMemoryStore()) .build(); IAiService assistant = AiServices.builder(IAiService.class) .chatLanguageModel(qianfanChatModel) .chatMemory(chatMemory) .build(); String answer = assistant.chat("My name is xiaoyu"); System.out.println(answer); // Run it once and then comment the top to run the bottom(运行一次后注释上面运行下面,他会正常输出你的名字) // String answerWithName = assistant.chat("What is my name?"); // System.out.println(answerWithName); } }流式回复LLMs generate text one token at a time, so many LLM providers offer a way to stream the response token-by-token instead of waiting for the entire text to be generated. This significantly improves the user experience, as the user does not need to wait an unknown amount of time and can start reading the response almost immediately.(许多LLM提供者提供了一种逐个token地传输响应的方法,而不是等待生成整个文本。这极大地改善了用户体验,因为用户不需要等待未知的时间,几乎可以立即开始阅读响应。)以下是一个通过StreamingResponseHandler来实现 QianfanStreamingChatModel qianfanStreamingChatModel = QianfanStreamingChatModel.builder() .apiKey("apiKey") .secretKey("secretKey") .modelName("Yi-34B-Chat") .build(); qianfanStreamingChatModel.generate(userMessage, new StreamingResponseHandler<AiMessage>() { @Override public void onNext(String token) { System.out.print(token); } @Override public void onComplete(Response<AiMessage> response) { System.out.println("onComplete: " + response); } @Override public void onError(Throwable throwable) { throwable.printStackTrace(); } });以下是另一个通过TokenStream来实现 QianfanStreamingChatModel qianfanStreamingChatModel = QianfanStreamingChatModel.builder() .apiKey("apiKey") .secretKey("secretKey") .modelName("Yi-34B-Chat") .build(); IAiService assistant = AiServices.create(IAiService.class, qianfanStreamingChatModel); TokenStream tokenStream = assistant.chatInTokenStream("Tell me a story."); tokenStream.onNext(System.out::println) .onError(Throwable::printStackTrace) .start(); 增强检索(RAG)程序自动将匹配的内容与用户问题组装成一个Prompt,向大语言模型提问,大语言模型返回答案LangChain4j has an "Easy RAG" feature that makes it as easy as possible to get started with RAG. You don't have to learn about embeddings, choose a vector store, find the right embedding model, figure out how to parse and split documents, etc. Just point to your document(s), and LangChain4j will do its magic.导入依赖langchain4j-easy-rag <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-easy-rag</artifactId> <version>{{version}}</version> </dependency>使用QianfanChatModel chatLanguageModel = QianfanChatModel.builder() .apiKey(API_KEY) .secretKey(SECRET_KEY) .modelName("Yi-34B-Chat") .build(); // All files in a directory, txt seems to be faster List<Document> documents = FileSystemDocumentLoader.loadDocuments("/home/langchain4j/documentation"); // for simplicity, we will use an in-memory one: InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>(); EmbeddingStoreIngestor.ingest(documents, embeddingStore); IAiService assistant = AiServices.builder(IAiService.class) .chatLanguageModel(chatLanguageModel) .chatMemory(MessageWindowChatMemory.withMaxMessages(10)) .contentRetriever(EmbeddingStoreContentRetriever.from(embeddingStore)) .build(); String answer = assistant.chat("The Question"); System.out.println(answer);
2024年05月26日
217 阅读
22 评论
0 点赞
1
2
...
5