LangChain4J 使用实践
这里写目录标题
- 大模型应用场景:
- 创建一个测试示例
- AIService
- 聊天记忆实现
- 简单实现聊天记录记忆
- MessageWindowChatMemory实现聊天记忆
- 隔离聊天记忆
- 聊天记忆持久化
- 添加AI提示词
大模型应用场景:
创建一个测试示例
导入依赖
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-spring-boot-starter</artifactId><version>1.0.1-beta6</version></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai-spring-boot-starter</artifactId><version>1.0.1-beta6</version></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId><version>1.0.1</version></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId><version>1.0.1</version></dependency></dependencies>
在配置文件中配置好对应的api
langchain4j.open-ai.chat-model.api-key=${OPENAI_API_KEY}
langchain4j.open-ai.chat-model.model-name=gpt-4o
langchain4j.open-ai.chat-model.log-requests=true
langchain4j.open-ai.chat-model.log-responses=true
...
测试效果
package com.aidemo.demo;import dev.langchain4j.model.chat.request.ChatRequest;
import dev.langchain4j.model.openai.OpenAiChatModel;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class DemoApplicationTests {@Autowiredprivate OpenAiChatModel openAiChatModel;@Testvoid contextLoads() {String test = openAiChatModel.chat("你好");System.out.println(test);}}
AIService
package com.aidemo.demo.assistant;import dev.langchain4j.service.spring.AiService;@AiService
public interface Assistant {String chat(String message);
}
用于自动配置 AI 服务 , RAG, 工具 等
测试调用
@Autowiredprivate Assistant assistant;@Testvoid contextLoads() {String test = assistant.chat("我是谁");System.out.println(test);}
聊天记忆实现
测试是否有记忆功能
@Autowiredprivate Assistant assistant;@Testvoid contextLoads() {String test = assistant.chat("我是彭于晏");System.out.println(test);String testa = assistant.chat("我是谁");System.out.println(testa);}
AI没有记住名字
简单实现聊天记录记忆
每次问答都把把上一次的问答内容输出给ai
MessageWindowChatMemory实现聊天记忆
目前,LangChain4j 提供了 2 个开箱即用的实现:
更简单的一个,MessageWindowChatMemory,作为一个滑动窗口工作,保留最近的 N 条消息,并移除不再符合条件的老消息。 然而,因为每条消息可以包含不同数量的 token, MessageWindowChatMemory 主要适用于快速原型开发。
一个更复杂的选项是 TokenWindowChatMemory,它同样以滑动窗口方式运行,但专注于保留最近的 N 个 token, 根据需要移除旧消息。 消息是不可分割的。如果消息不适用,它将被完全移除。 TokenWindowChatMemory 需要一个 TokenCountEstimator 来计算每个 ChatMessage 中的 token 数量。
@Configuration
public class LangChainMemoryConfig {@Beanpublic ChatMemory chatMemory() {return MessageWindowChatMemory.withMaxMessages(10); // 保留最近 10 条消息}
}
隔离聊天记忆
创建记忆提供器,通过id区分用户 在这个场景中,ChatMemory 将由 ChatMemoryProvider 提供,每个内存 ID 对应一个实例。
package com.aidemo.demo.assistant;import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;@Configuration
public class LangChainMemoryConfig {private static final Logger logger = LoggerFactory.getLogger(LangChainMemoryConfig.class);// 可以通过配置文件或构造函数注入private final int maxMessagesPerUser = 10;@Beanpublic ChatMemoryProvider chatMemoryProvider() {ConcurrentMap<String, ChatMemory> memoryMap = new ConcurrentHashMap<>();return new ChatMemoryProvider() {@Overridepublic ChatMemory get(Object memoryId) {if (memoryId == null) {logger.error("Memory ID cannot be null");throw new IllegalArgumentException("Memory ID cannot be null");}if (!(memoryId instanceof String)) {logger.error("Memory ID must be a String, but was: {}", memoryId.getClass());throw new IllegalArgumentException("Memory ID must be a String");}String userId = (String) memoryId;return memoryMap.computeIfAbsent(userId, id -> {logger.info("Creating new chat memory for user: {}", id);return MessageWindowChatMemory.withMaxMessages(maxMessagesPerUser);});}};}
}
实现多用户接口
@AiService(wiringMode =EXPLICIT, // 表示必须手动指定依赖 BeanchatModel = "openAiChatModel", // 绑定具体的模型 Bean 名称chatMemoryProvider = "chatMemoryProvider" // 指定记忆提供器 Bean 名称
)
public interface SeparateChatAssistant {String chat(@MemoryId String userId, @UserMessage String message);
}
调用测试
@Autowiredprivate SeparateChatAssistant separateChatAssistant;@Testvoid contextLoads2() {// 发起对话,测试记忆能力separateChatAssistant.chat("1","我是彭于晏");String reply = separateChatAssistant.chat("1","我是谁?");System.out.println("模型回答:" + reply);String reply2 = separateChatAssistant.chat("12","我是谁?");System.out.println("模型回答:" + reply2);// 理想返回应为:“你是彭于晏” 或 类似记忆体现的内容}
聊天记忆持久化
使用MongoDB的形式
spring.data.mongodb.uri=mongodb://localhost:27017/chat_memory_db
package com.aidemo.demo.bean;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document("chat_messages")
public class ChatMessages {@Idprivate ObjectId id;private int messageId;//存储当前聊天记录列表的json字符串private String content;
}
创建持久化类
package com.aidemo.demo.store;import com.aidemo.demo.bean.ChatMessages;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.ChatMessageDeserializer;
import dev.langchain4j.data.message.ChatMessageSerializer;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;import java.util.LinkedList;
import java.util.List;
import java.util.Queue;@Component
public class MongoChatMemoryStore implements ChatMemoryStore {@Resourceprivate MongoTemplate mongoTemplate;/*** 根据 memoryId 获取消息* @param memoryId* @return*/@Overridepublic List<ChatMessage> getMessages(Object memoryId) {Criteria criteria = Criteria.where("memoryId").is(memoryId);Query query = new Query(criteria);ChatMessages storeChatMessage = mongoTemplate.findOne(query, ChatMessages.class);if (storeChatMessage == null) {return new LinkedList<>();}return ChatMessageDeserializer.messagesFromJson(storeChatMessage.getContent());}@Overridepublic void updateMessages(Object memoryId, List<ChatMessage> list) {Criteria criteria = Criteria.where("memoryId").is(memoryId);Query query = new Query(criteria);Update update = new Update();update.set("content", ChatMessageSerializer.messagesToJson(list));mongoTemplate.upsert(query, update, ChatMessages.class);}@Overridepublic void deleteMessages(Object memoryId) {Criteria criteria = Criteria.where("memoryId").is(memoryId);Query query = new Query(criteria);mongoTemplate.remove(query, ChatMessages.class);}}
package com.aidemo.demo.assistant;import com.aidemo.demo.store.MongoChatMemoryStore;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;@Configuration
public class SeparateChatAssistantConfig {@Resourceprivate MongoChatMemoryStore mongoChatMemoryStore;@Beanpublic ChatMemoryProvider chatMemoryProvider() {return memoryId -> MessageWindowChatMemory.builder().id(memoryId).maxMessages(10).chatMemoryStore(mongoChatMemoryStore) // 持久化到 mongoDB.build();}
}
添加AI提示词
作用是塑造ai的身份。