这里写目录标题
- 大模型应用场景:
- 创建一个测试示例
- 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 {
@Autowired
private OpenAiChatModel openAiChatModel;
@Test
void 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, 工具 等
测试调用
@Autowired
private Assistant assistant;
@Test
void contextLoads() {
String test = assistant.chat("我是谁");
System.out.println(test);
}
聊天记忆实现
测试是否有记忆功能
@Autowired
private Assistant assistant;
@Test
void 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 {
@Bean
public 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;
@Bean
public ChatMemoryProvider chatMemoryProvider() {
ConcurrentMap<String, ChatMemory> memoryMap = new ConcurrentHashMap<>();
return new ChatMemoryProvider() {
@Override
public 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, // 表示必须手动指定依赖 Bean
chatModel = "openAiChatModel", // 绑定具体的模型 Bean 名称
chatMemoryProvider = "chatMemoryProvider" // 指定记忆提供器 Bean 名称
)
public interface SeparateChatAssistant {
String chat(@MemoryId String userId, @UserMessage String message);
}
调用测试
@Autowired
private SeparateChatAssistant separateChatAssistant;
@Test
void 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 {
@Id
private 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 {
@Resource
private MongoTemplate mongoTemplate;
/**
* 根据 memoryId 获取消息
* @param memoryId
* @return
*/
@Override
public 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());
}
@Override
public 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);
}
@Override
public 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 {
@Resource
private MongoChatMemoryStore mongoChatMemoryStore;
@Bean
public ChatMemoryProvider chatMemoryProvider() {
return memoryId -> MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(10)
.chatMemoryStore(mongoChatMemoryStore) // 持久化到 mongoDB
.build();
}
}
添加AI提示词
作用是塑造ai的身份。