You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LangChain4j中RAG结合对话记忆时上下文窗口重复信息的解决方法

LangChain4j中RAG结合对话记忆时上下文窗口重复信息的解决方法

我完全懂你遇到的困扰——每次调用对话接口,RAG检索到的内容都会被硬塞进用户消息里存进对话记忆,导致后续请求里这些内容被反复发送,既浪费token又显得冗余。咱们来拆解问题根源,再一步步解决它:

问题根源

默认情况下,LangChain4j的AiServices会把RAG检索到的内容直接附加到用户输入消息的前面,然后把这个拼接了RAG内容的完整消息存入对话记忆。这样下一次对话时,历史消息里已经包含了之前的RAG内容,新的请求又会再追加一次检索结果,自然就造成了重复发送的问题。

另外你的代码里还有几个小细节需要修正:注解的大小写错误(@memoryid应该是@MemoryId,@Usermessage应该是@UserMessage),这些虽然不影响功能,但规范写法能避免潜在问题。

解决方法:自定义检索增强逻辑,分离RAG内容与对话记忆

核心思路是:让RAG检索到的内容只作为当前对话的临时系统上下文,不被存入对话记忆。我们可以通过自定义RetrievalAugmentor来实现这个效果:

步骤1:修正接口注解并定义对话接口

首先修正接口的注解大小写,确保符合LangChain4j的规范:

interface ChatInterface extends ChatMemoryAccess {
    // 纯用户消息,不包含RAG内容
    String chat(@MemoryId int memoryId, @UserMessage String userMessage);
}

步骤2:自定义RetrievalAugmentor

创建一个自定义的检索增强器,把RAG内容封装成系统消息,只在当前请求中传给AI模型,不会被存入对话记忆:

RetrievalAugmentor customAugmentor = context -> {
    // 把检索到的所有内容拼接成字符串
    String retrievedContent = context.retrievedContents().stream()
            .map(Content::text)
            .reduce((a, b) -> a + "\n" + b)
            .orElse("");
    // 封装成系统消息,作为当前对话的上下文
    SystemMessage systemMsg = SystemMessage.from("请基于以下信息回答用户的问题:\n" + retrievedContent);
    return RetrievalAugmentor.Result.from(systemMsg);
};

步骤3:整合到AiServices配置中

把自定义的增强器加入AiServices的构建流程,替换默认的拼接逻辑:

完整修改后的代码

import java.util.ArrayList;
import java.util.List;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiChatModelName;
import dev.langchain4j.rag.RetrievalAugmentor;
import dev.langchain4j.rag.content.Content;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.query.Query;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.memory.ChatMemoryAccess;

interface ChatInterface extends ChatMemoryAccess {
    String chat(@MemoryId int memoryId, @UserMessage String userMessage);
}

public class RagHistoryFix {
    public static void main(String args[]) throws Exception {
        String apiKey = "你的API密钥或本地模型配置";
        OpenAiChatModel chatModel = OpenAiChatModel.builder()
                .apiKey(apiKey)
                .modelName(OpenAiChatModelName.GPT_4_O_MINI)
                .build();

        // 自定义RAG内容检索器(和你原来的逻辑一致)
        ContentRetriever contentRetriever = new ContentRetriever() {
            @Override
            public List<Content> retrieve(Query q) {
                List<Content> list = new ArrayList<>();
                list.add(Content.from("I have a cat called Pulasy. It likes mice very much"));
                return list;
            }
        };

        // 自定义检索增强器:将RAG内容作为临时系统上下文,不存入对话记忆
        RetrievalAugmentor customAugmentor = context -> {
            String retrievedContent = context.retrievedContents().stream()
                    .map(Content::text)
                    .reduce((a, b) -> a + "\n" + b)
                    .orElse("");
            SystemMessage systemMsg = SystemMessage.from("请基于以下信息回答用户问题:\n" + retrievedContent);
            return RetrievalAugmentor.Result.from(systemMsg);
        };

        // 构建AI服务
        ChatInterface assistant = AiServices.builder(ChatInterface.class)
                .chatModel(chatModel)
                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(11))
                .contentRetriever(contentRetriever)
                .retrievalAugmentor(customAugmentor) // 应用自定义增强器
                .build();

        // 测试对话
        System.out.println(assistant.chat(1, "What is the name of my cat?"));
        System.out.println(assistant.chat(1, "Do my cat like mice?"));
        System.out.println(assistant.chat(1, "Is Pulasy a lion?"));

        // 检查对话记忆:确认只有纯用户消息和AI回复
        ChatMemory mem = assistant.getChatMemory(1);
        List<ChatMessage> messages = mem.messages();
        for (ChatMessage msg : messages) {
            System.out.println("Msg:" + msg);
        }
    }
}

效果验证

运行修改后的代码,你会发现对话记忆里的用户消息是纯原始提问(比如“What is the name of my cat?”),RAG内容只会作为每次请求的系统上下文被发送一次,不会被存入记忆。这样后续请求就不会再重复发送相同的RAG内容,完美解决了重复问题。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 12:45:27