Обмен технологиями

3. Чат-память LangChain4j

2024-07-12

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

Введение в память чата

Память чатаЭто называется памятью чата, в которой хранятся исторические разговоры между пользователями и большими моделями, так что большие модели могут использовать эти исторические разговоры, чтобы понять, что недавно сказали пользователи и что они имеют в виду.

Однако, если вы продолжаете хранить исторические разговоры в памяти чата, для этого потребуется все больше и больше места для хранения. Поэтому ChatMemory также поддерживает расширенные функции, такие как ограничение окон, механизм исключения, механизм сохранения и т. д.


ChatMemory — это интерфейс, который по умолчанию предоставляет два класса реализации:

  • СообщениеОкноЧатПамять

В зависимости от размера коллекции старые сообщения удаляются.В качестве раздвижного окна держитеN Последние новости и выселяем старые новости, которые уже не подходят.Однако, поскольку каждое сообщение может содержать разное количество токенов,MessageWindowChatMemory Очень полезно для быстрого прототипирования.

  • TokenWindowChatMemory

В зависимости от размера токена старые сообщения удаляются.Он также работает как скользящее окно, но фокусируется на сохраненииN последний токен и при необходимости удаляйте старые сообщения. Информация неделима. Если сообщение не подходит, оно будет полностью удалено. Для TokenWindowChatMemory требуетсяTokenizer рассчитать каждыйChatMessage токен в .


Разница между TokenWindowChatMemory и MessageWindowChatMemory

TokenWindowChatMemory аналогичен MessageWindowChatMemory. Разница в том, что способ расчета емкости другой. MessageWindowChatMemory напрямую использует список.<ChatMessage> размер, а TokenWindowChatMemory будет использовать указанный Tokenizer для<ChatMessage> Соответствующее количество токенов оценивается, а затем сравнивается с установленным maxTokens. Если число превышает maxTokens, оно также будет удалено, а также будет удалено самое старое сообщение ChatMessage.

Tokenizer — это интерфейс. Класс реализации OpenAiTokenizer предоставляется по умолчанию. Он используется для оценки количества токенов, соответствующих API-интерфейсам ChatMessage, взимаемых в зависимости от количества используемых токенов. Стоимость чувствительна. TokenWindowChatMemory используется для управления общим количеством токенов, используемых в сеансе.

Оба класса реализации имеют внутри атрибут 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. }