前两篇文章是对LangChain4j比较全面的介绍,从本篇文章开始从某一个点进行分析。我们先从ChatMemory开始!
对于聊天记忆的场景、实现原理,在Spring AI 专栏中的# Spring AI 聊天上下文记忆源码分析以及实战文章有介绍,在这里就不多介绍直入主题。
实现聊天记忆实现起来非常简单,就是把用户所有的提问、大模型回答/产生的内容,放在一个List
中,随着用户提问将List一并发送给大模型,让大模型具备了聊天记忆功能。实现虽然简单,大家想一想有没有问题呢?
手动维护和管理 ChatMessage
很麻烦。因此,LangChain4j 提供了一个 ChatMemory
抽象以及多个开箱即用的实现。ChatMemory
可以用作独立的低级组件,也可以用作高级组件(如 AI Services)的一部分。
LangChain4j 目前只提供“内存”,没有提供持久化的方式,如有特殊需求需要自行实现!
ChatMessage
容器,对ChatMessage
进行管理。ChatMessage
不会过多。SystemMessage
特殊处理。出于以下几个原因,数据淘汰机制是必要的:
适应 LLM的上下文窗口。一次LLM可以处理的token数量是有上限的。
一般情况将最旧的消息淘汰,如果有特殊需求,可以实现更复杂的算法。
控制成本。每个token都有成本,这使得每次调用LLM越来越昂贵。逐出不必要的token可降低成本。
Token=金钱,目前大模型的收费基本上都是根据Token收费。
控制延迟。发送到 LLM的token越多,处理它们所需的时间就越多。
public interface ChatMemory {
// ChatMemory的ID
Object id();
// 将message添加到ChatMemory中
void add(ChatMessage message);
// 从ChatMemory中获取消息,怎么取取决于实现
List messages();
// 清空ChatMemory中的消息
void clear();
}
MessageWindowChatMemory
「简单」:滑动窗口,保留 N
最新的消息并驱逐不再适合的旧消息。
TokenWindowChatMemory
「复杂」:滑动窗口运行, N
但专注于保留最新的令牌,根据需要驱逐较旧的消息。需要Tokenizer
配合使用计算ChatMessage
的Token的数量。
默认情况下,ChatMemory
实现是将 ChatMessage
存储在内存中的,如果需要持久性,可以实现自定义 ChatMemoryStore
,将ChatMessage
存储在您选择的任何持久性存储中。
可以自定义持久化策略!!!!
public interface ChatMemoryStore {
// 根据memoryId从指定的ChatMemoryStore中获取消息
List getMessages(Object memoryId);
// 根据memoryId,更新存储的消息
void updateMessages(Object memoryId, List messages);
// 根据memoryId删除存储的消息
void deleteMessages(Object memoryId);
}
public class InMemoryChatMemoryStore implements ChatMemoryStore {
private final Map
LanChain4j仅提供一个基于内存实现的存储,如果有特殊需求,我们可以实现ChatMemoryStore
接口,自定义逻辑。
SystemMessage
特殊处理SystemMessage
是一种特殊类型的消息,因此它与其他消息类型的处理方式不同:
SystemMessage
。SystemMessage
。SystemMessage
。SystemMessage
内容,则将替换前一个内容。所谓的工具消息就是函数调用请求消息和函数调用执行结果的消息。
如果包含ToolExecutionRequest
的AiMessage
被淘汰,则与其关联的返回消息ToolExecutionResultMessage
需要一同淘汰,否则会影响大模型的生成效果。
dev.langchain4j
langchain4j-spring-boot-starter
${langchain4j.version}
dev.langchain4j
langchain4j-open-ai-spring-boot-starter
${langchain4j.version}
package org.ivy.chatmemory;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import org.ivy.chatmemory.service.Assistant;
public class ChatMemoryJavaExample {
public static void main(String[] args) {
ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(OpenAiChatModel.builder()
.baseUrl("xxxx")
.apiKey("xxxx")
.build()
)
.chatMemory(chatMemory)
.build();
String answer = assistant.chat("Hello! My name is Klaus.");
System.out.println(answer); // Hello Klaus! How can I assist you today?
String answerWithName = assistant.chat("What is my name?");
System.out.println(answerWithName); // Your name is Klaus.
}
}
执行测试结果:
Hello Klaus! How can I assist you today?
Your name is Klaus.
package org.ivy.chatmemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import org.ivy.chatmemory.service.EachUserAssistant;
public class ChatMemoryEachUserExample {
public static void main(String[] args) {
EachUserAssistant assistant = AiServices.builder(EachUserAssistant.class)
.chatLanguageModel(
OpenAiChatModel.builder()
.baseUrl("xxx")
.apiKey("xxx")
.build())
.chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10))
.build();
System.out.println(assistant.chat(1, "Hello, my name is Klaus"));
// Hi Klaus! How can I assist you today?
System.out.println(assistant.chat(2, "Hello, my name is Francine"));
// Hello Francine! How can I assist you today?
System.out.println(assistant.chat(1, "What is my name?"));
// Your name is Klaus.
System.out.println(assistant.chat(2, "What is my name?"));
}
}
执行测试结果
Hi Klaus! How can I assist you today?
Hello Francine! How can I assist you today?
Your name is Klaus.
Your name is Francine.
定义memoryId,根据memoryId来获取是否是同一组上下文信息。
package org.ivy.chatmemory.persistent;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import org.mapdb.DB;
import org.mapdb.DBMaker;
import java.util.List;
import java.util.Map;
import static dev.langchain4j.data.message.ChatMessageDeserializer.messagesFromJson;
import static dev.langchain4j.data.message.ChatMessageSerializer.messagesToJson;
import static org.mapdb.Serializer.INTEGER;
import static org.mapdb.Serializer.STRING;
/**
* 自定义的持久化聊天存储器
*/
public class PersistentChatMemoryStore implements ChatMemoryStore {
private final DB db = DBMaker.fileDB("./chat-memory.db").transactionEnable().make();
private final Map map = db.hashMap("messages", INTEGER, STRING).createOrOpen();
@Override
public List getMessages(Object memoryId) {
String json = map.get((int) memoryId);
return messagesFromJson(json);
}
@Override
public void updateMessages(Object memoryId, List messages) {
String json = messagesToJson(messages); // 序列化消息
map.put((int) memoryId, json);
db.commit(); // 提交事务
}
@Override
public void deleteMessages(Object memoryId) {
map.remove((int) memoryId);
db.commit();
}
}
自定义的关键几点:
mapdb
文件形式。Github 地址
对LangChain4j聊天记忆进行了分析,并介绍了ChatMemory四个特性。提供了使用示例,包括共享ChatMemory、会话维度隔离的ChatMemory,自定义存储机制等。实战代码可以参考Github。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
作为一名热心肠的互联网老兵,我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。
但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
所有资料 ⚡️ ,朋友们如果有需要全套 《LLM大模型入门+进阶学习资源包》,扫码获取~
大模型入门要点,扫盲必看!
既然要系统的学习大模型,那么学习路线是必不可少的,这份路线能帮助你快速梳理知识,形成自己的体系。
光学理论是没用的,要学会跟着一起做,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
《中国大模型落地应用案例集》 收录了52个优秀的大模型落地应用案例,这些案例覆盖了金融、医疗、教育、交通、制造等众多领域,无论是对于大模型技术的研究者,还是对于希望了解大模型技术在实际业务中如何应用的业内人士,都具有很高的参考价值。 (文末领取)
《2024大模型行业应用十大典范案例集》 汇集了文化、医药、IT、钢铁、航空、企业服务等行业在大模型应用领域的典范案例。
观看零基础学习书籍和视频,看书籍和视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。 (文末领取)
包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。
保证100%免费
】