기술나눔

3. LangChain4j의 채팅 메모리

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

채팅 메모리 소개

채팅 메모리사용자와 대형 모델 간의 과거 대화를 저장하는 것을 채팅 메모리라고 하며, 대형 모델은 이러한 과거 대화를 사용하여 사용자가 최근에 말한 내용과 그 의미를 이해할 수 있습니다.

그러나 채팅 메모리에 과거 대화를 계속 저장하면 더 많은 저장 공간이 필요하므로 ChatMemory는 창 제한, 제거 메커니즘, 지속성 메커니즘 등과 같은 확장 기능도 지원합니다.


ChatMemory는 기본적으로 두 가지 구현 클래스를 제공하는 인터페이스입니다.

  • 메시지창채팅메모리

컬렉션의 크기에 따라 오래된 메시지는 제거됩니다.슬라이딩 윈도우로 유지N 최신 뉴스와 더 이상 맞지 않는 오래된 뉴스를 제거합니다.그러나 각 메시지에는 서로 다른 수의 토큰이 포함될 수 있으므로MessageWindowChatMemory 신속한 프로토타이핑에 매우 유용합니다.

  • 토큰윈도우챗메모리

토큰의 크기에 따라 오래된 메시지는 제거됩니다.슬라이딩 윈도우로도 실행되지만 유지에 중점을 둡니다.N 최신 토큰을 사용하고 필요에 따라 오래된 메시지를 제거합니다. 정보는 분할될 수 없습니다. 메시지가 적합하지 않으면 완전히 제거됩니다. TokenWindowChatMemory에는Tokenizer 각각 계산하기ChatMessage .


TokenWindowChatMemory와 MessageWindowChatMemory의 차이점

TokenWindowChatMemory는 MessageWindowChatMemory와 유사합니다. 차이점은 용량을 계산하는 방법이 다르다는 것입니다. MessageWindowChatMemory는 List를 직접 가져옵니다.<ChatMessage> 크기이며 TokenWindowChatMemory는 지정된 Tokenizer를 사용하여<ChatMessage> 해당 토큰 수를 추정한 후 설정된 maxTokens와 비교합니다. 해당 수가 maxTokens를 초과하면 해당 토큰도 제거되며 가장 오래된 ChatMessage도 제거됩니다.

Tokenizer는 기본적으로 제공되는 OpenAiTokenizer 구현 클래스로, ChatMessage에 해당하는 토큰 수를 추정하는 데 사용됩니다. 많은 대형 모델의 API는 사용된 토큰 수에 따라 비용을 청구하므로 사용하는 것이 좋습니다. 비용은 민감합니다. TokenWindowChatMemory는 세션에서 사용되는 총 토큰 수를 제어하는 ​​데 사용됩니다.

두 구현 클래스 모두 내부에 ChatMemoryStore 속성이 있습니다. ChatMemoryStore에도 기본적으로 InMemoryChatMemoryStore 구현 클래스가 있습니다. 시간이 지남에 따라 SQL 데이터베이스, 문서 저장소 등과 같은 인기 있는 저장소에 즉시 사용 가능한 구현이 추가될 것입니다.한편, 이 인터페이스를 구현하여 원하는 스토리지에 연결할 수 있습니다.


사례 실습

사례 1: ChatMemory를 기반으로 인기 있는 명명 마스터 만들기
  1. public class NameDemo {
  2. interface NamingMaster {
  3. String talk(String desc);
  4. }
  5. public static void main(String[] args) {
  6. ChatLanguageModel chatModel = ZhipuAiChatModel.builder()
  7. .apiKey("智普apikey")
  8. .build();
  9. ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
  10. NamingMaster namingMaster = AiServices.builder(NamingMaster.class)
  11. .chatLanguageModel(chatModel)
  12. .chatMemory(chatMemory)
  13. .build();
  14. System.out.println(namingMaster.talk("我姓李,帮我取一个好听的女孩名字,就一个你觉得最好的"));
  15. System.out.println("---");
  16. System.out.println(namingMaster.talk("换一个"));
  17. }
  18. }
사례 2: ChatMemoryStore를 사용자 지정하여 ChatMessage를 디스크에 유지합니다.

Maven 종속성 소개

  1. <dependency>
  2. <groupId>org.mapdb</groupId>
  3. <artifactId>mapdb</artifactId>
  4. <version>3.0.9</version>
  5. <exclusions>
  6. <exclusion>
  7. <groupId>org.jetbrains.kotlin</groupId>
  8. <artifactId>kotlin-stdlib</artifactId>
  9. </exclusion>
  10. </exclusions>
  11. </dependency>

 영구 저장을 달성하기 위해 ChatMemoryStore를 사용자 정의하십시오.

  1. public class PersistentChatMemoryStore implements ChatMemoryStore {
  2. private final DB db = DBMaker.fileDB("chat-memory.db").transactionEnable().make();
  3. private final Map<String, String> map = db.hashMap("messages", Serializer.STRING, Serializer.STRING).createOrOpen();
  4. @Override
  5. public List<ChatMessage> getMessages(Object memoryId) {
  6. String json = map.get((String) memoryId);
  7. return ChatMessageDeserializer.messagesFromJson(json);
  8. }
  9. @Override
  10. public void updateMessages(Object memoryId, List<ChatMessage> messages) {
  11. String json = ChatMessageSerializer.messagesToJson(messages);
  12. map.put((String) memoryId, json);
  13. db.commit();
  14. }
  15. @Override
  16. public void deleteMessages(Object memoryId) {
  17. map.remove((String) memoryId);
  18. db.commit();
  19. }
  20. }

  코드 테스트

  1. public class PersistentDemo {
  2. interface NamingMaster {
  3. String talk(String desc);
  4. }
  5. public static void main(String[] args) {
  6. ChatLanguageModel chatModel = ZhipuAiChatModel.builder()
  7. .apiKey("智普apikey")
  8. .build();
  9. ChatMemory chatMemory = MessageWindowChatMemory.builder()
  10. .chatMemoryStore(new PersistentChatMemoryStore())
  11. .maxMessages(10)
  12. .build();
  13. NamingMaster namingMaster = AiServices.builder(NamingMaster.class)
  14. .chatLanguageModel(chatModel)
  15. .chatMemory(chatMemory)
  16. .build();
  17. System.out.println(namingMaster.talk("我姓李,帮我取一个好听的女孩名字,就一个你觉得最好的"));
  18. System.out.println("---");
  19. System.out.println(namingMaster.talk("换一个"));
  20. }
  21. }
사례 3: 각 사용자별로 채팅 기록을 별도로 저장합니다.
  1. public class NameDemo {
  2. interface NamingMaster {
  3. String talk(@MemoryId Integer userId, @UserMessage String desc);
  4. }
  5. public static void main(String[] args) {
  6. ChatLanguageModel chatModel = ZhipuAiChatModel.builder()
  7. .apiKey("智普apikey")
  8. .build();
  9. NamingMaster namingMaster = AiServices.builder(NamingMaster.class)
  10. .chatLanguageModel(chatModel)
  11. .chatMemoryProvider(userId -> MessageWindowChatMemory.withMaxMessages(10))
  12. .build();
  13. System.out.println(namingMaster.talk(1, "我姓李,帮我取一个好听的女孩名字,就一个你觉得最好的"));
  14. System.out.println("---");
  15. System.out.println(namingMaster.talk(2, "我姓赵,帮我取一个好听的男孩名字,就一个你觉得最好的"));
  16. System.out.println("---");
  17. System.out.println(namingMaster.talk(1, "换一个"));
  18. }
  19. }