一、核心原理深度解析

聊天记忆的本质是弥补大语言模型的无状态特性,核心逻辑可以总结为:

  1. 模型无状态性:LLM(大语言模型)本身不会保留任何对话信息,每次请求都是独立的,这是需要手动实现记忆的根本原因。
  2. 上下文传递:将历史对话(用户消息 + AI 回复)结构化后,作为上下文和新问题一起发送给模型,让模型 “看到” 完整对话脉络。
  3. 结构化消息:LangChain4J 通过UserMessage/AiMessage等类标准化消息格式,确保模型能正确识别对话角色和顺序。

二、完整实现流程(从基础到优化)

1. 基础实现流程(手动管理记忆)

java

运行

import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.output.ChatResponse;
import dev.langchain4j.model.qwen.QwenChatModel;
import java.util.Arrays;

public class BasicChatMemoryDemo {
    public static void main(String[] args) {
        // 1. 初始化通义千问模型(需配置API Key等信息)
        ChatLanguageModel qwenChatModel = QwenChatModel.builder()
                .apiKey("your-api-key")
                .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                .modelName("qwen-turbo")
                .build();

        // 2. 第一轮对话:无历史,仅发送当前消息
        UserMessage userMessage1 = UserMessage.userMessage("我是环环");
        ChatResponse chatResponse1 = qwenChatModel.chat(userMessage1);
        AiMessage aiMessage1 = chatResponse1.aiMessage();
        System.out.println("AI回复1:" + aiMessage1.text()); // 输出:你好呀环环😊

        // 3. 第二轮对话:携带历史消息,实现记忆
        UserMessage userMessage2 = UserMessage.userMessage("你知道我是谁吗");
        // 关键:将历史消息+新消息组合成列表发送
        ChatResponse chatResponse2 = qwenChatModel.chat(Arrays.asList(userMessage1, aiMessage1, userMessage2));
        AiMessage aiMessage2 = chatResponse2.aiMessage();
        System.out.println("AI回复2:" + aiMessage2.text()); // 输出:你是环环呀~
    }
}

关键步骤解释

  • 第一轮仅发送用户消息,模型无上下文,只能基础回应;
  • 第二轮将「第一轮用户消息→第一轮 AI 回复→第二轮用户消息」整合成列表发送,模型基于完整上下文识别出 “环环” 这个身份。
2. 优化实现(封装管理类)

基础实现需要手动拼接消息列表,扩展性差,推荐封装ChatHistoryChatService来简化开发:

java

运行

import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.Message;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.output.ChatResponse;

import java.util.ArrayList;
import java.util.List;

// 1. 消息历史管理类:统一管理对话历史
class ChatHistory {
    private final List<Message> messages = new ArrayList<>();

    // 添加用户消息
    public void addUserMessage(String content) {
        messages.add(UserMessage.userMessage(content));
    }

    // 添加AI消息
    public void addAiMessage(String content) {
        messages.add(AiMessage.aiMessage(content));
    }

    // 获取完整对话历史
    public List<Message> getMessages() {
        return messages;
    }

    // 清空历史
    public void clear() {
        messages.clear();
    }
}

// 2. 对话服务类:封装对话逻辑,对外提供简单接口
class ChatService {
    private final ChatLanguageModel model;
    private final ChatHistory history = new ChatHistory();

    public ChatService(ChatLanguageModel model) {
        this.model = model;
    }

    // 核心对话方法:自动管理历史,返回AI回复
    public String chat(String userInput) {
        try {
            // 1. 添加用户输入到历史
            history.addUserMessage(userInput);
            // 2. 发送完整历史给模型
            ChatResponse response = model.chat(history.getMessages());
            AiMessage aiMessage = response.aiMessage();
            // 3. 添加AI回复到历史
            history.addAiMessage(aiMessage.text());
            // 4. 返回回复内容
            return aiMessage.text();
        } catch (Exception e) {
            System.err.println("对话异常:" + e.getMessage());
            return "抱歉,暂时无法回复你的问题。";
        }
    }

    // 清空对话历史
    public void clearHistory() {
        history.clear();
    }
}

// 3. 测试优化后的实现
public class OptimizedChatMemoryDemo {
    public static void main(String[] args) {
        // 初始化模型
        ChatLanguageModel qwenChatModel = QwenChatModel.builder()
                .apiKey("your-api-key")
                .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                .modelName("qwen-turbo")
                .build();

        // 初始化对话服务
        ChatService chatService = new ChatService(qwenChatModel);

        // 对话测试:无需手动管理历史,调用接口即可
        String response1 = chatService.chat("我是环环");
        System.out.println("AI回复1:" + response1); // 你好环环~

        String response2 = chatService.chat("你知道我是谁吗");
        System.out.println("AI回复2:" + response2); // 你是环环呀😜

        // 清空历史后,模型丢失记忆
        chatService.clearHistory();
        String response3 = chatService.chat("你知道我是谁吗");
        System.out.println("AI回复3:" + response3); // 我不知道你是谁哦,可以告诉我~
    }
}

优化点解释

  • ChatHistory:统一处理消息的添加、获取、清空,避免重复代码;
  • ChatService:封装模型调用和异常处理,对外仅暴露chat()clearHistory(),调用方无需关注底层逻辑;
  • 异常处理:避免因网络 / 模型调用失败导致程序崩溃,提升鲁棒性。
3. 高级记忆机制(LangChain4J 内置组件)

LangChain4J 提供了ChatMemory接口,无需手动封装,直接实现自动记忆管理:

java

运行

import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.qwen.QwenChatModel;

public class AdvancedChatMemoryDemo {
    public static void main(String[] args) {
        // 1. 初始化基础模型
        ChatLanguageModel qwenChatModel = QwenChatModel.builder()
                .apiKey("your-api-key")
                .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                .modelName("qwen-turbo")
                .build();

        // 2. 创建记忆组件:限制最多保留10条消息(避免上下文过长)
        ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);

        // 3. 包装模型,赋予记忆能力
        ChatLanguageModel modelWithMemory = ChatLanguageModel.withMemory(qwenChatModel, chatMemory);

        // 4. 对话:无需手动管理历史,模型自动记忆
        String response1 = modelWithMemory.chat("我是环环");
        System.out.println("AI回复1:" + response1); // 你好环环~

        String response2 = modelWithMemory.chat("你知道我是谁吗");
        System.out.println("AI回复2:" + response2); // 你是环环呀😜
    }
}

核心优势

  • 自动管理消息列表,无需手动添加 / 拼接;
  • 支持MessageWindowChatMemory限制消息数量,避免超出模型上下文窗口;
  • 代码极简,适合生产环境快速落地。

三、底层技术细节补充

1. 消息处理全流程

预览

查看代码

用户输入

封装为UserMessage

添加到对话历史列表

序列化消息列表为模型可识别格式

发送到LLM(包含历史+新消息)

模型解析上下文生成回复

解析回复为AiMessage

添加到对话历史

返回回复给用户

flowchart TD
    A[用户输入] --> B[封装为UserMessage]
    B --> C[添加到对话历史列表]
    C --> D[序列化消息列表为模型可识别格式]
    D --> E[发送到LLM(包含历史+新消息)]
    E --> F[模型解析上下文生成回复]
    F --> G[解析回复为AiMessage]
    G --> H[添加到对话历史]
    H --> I[返回回复给用户]

用户输入

封装为UserMessage

添加到对话历史列表

序列化消息列表为模型可识别格式

发送到LLM(包含历史+新消息)

模型解析上下文生成回复

解析回复为AiMessage

添加到对话历史

返回回复给用户

豆包

你的 AI 助手,助力每日工作学习

2. 上下文窗口限制注意事项
  • 不同模型的上下文窗口不同(如 qwen-turbo 是 8k,qwen-plus 是 32k);
  • 消息列表的总 token 数不能超过窗口大小,否则会被截断(优先保留最新消息);
  • 手动管理历史时,需自行实现 “旧消息清理” 逻辑;使用MessageWindowChatMemory可自动限制消息数量。

四、总结

  1. 核心原理:聊天记忆的本质是将历史对话(结构化消息列表)作为上下文,与新问题一起发送给无状态的 LLM,让模型基于完整上下文生成回复;
  2. 实现方式
    • 基础版:手动拼接消息列表,适合学习原理;
    • 优化版:封装ChatHistory+ChatService,适合自定义场景;
    • 高级版:使用 LangChain4J 内置ChatMemory,适合快速落地;
  3. 关键注意点:需控制上下文长度,避免超出模型窗口限制,同时做好异常处理保证稳定性。

一、ChatMemory实现聊天记忆的核心原理

ChatMemory是 LangChain4J 为解决 “手动管理对话历史繁琐” 问题设计的标准化记忆组件,其核心原理可总结为:

  1. 解耦记忆逻辑:将 “对话历史管理” 从业务代码中抽离,封装成独立组件,负责消息的存储、清理、查询;
  2. 自动上下文构建AIService作为桥梁,在每次调用模型时,自动从ChatMemory中读取历史消息,与新消息拼接成完整上下文发送给模型;
  3. 窗口化内存控制:通过MessageWindowChatMemory实现 “滑动窗口” 机制,只保留最近 N 条消息,避免上下文过长超出模型窗口限制。

简单来说:ChatMemory是 “记忆仓库”,AIService是 “仓库管理员 + 对话调度员”,二者配合让你无需手动拼接消息列表,只需调用简单的chat()方法就能实现带记忆的多轮对话。

二、完整实现流程(附可运行代码)

1. 前置准备

首先确保引入 LangChain4J 相关依赖(以 Maven 为例):

xml

<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j</artifactId>
    <version>0.34.0</version>
</dependency>
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-qwen</artifactId>
    <version>0.34.0</version>
</dependency>
2. 分步实现(从基础到优化)
步骤 1:定义 AIService 接口(核心)

AIService需要一个接口来定义对话方法,这是 LangChain4J 的 “声明式” 设计,框架会自动为接口生成实现类:

java

运行

import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;

// 定义助手接口,通过注解简化消息处理
interface Assistant {
    // 可选:SystemMessage定义模型的行为准则(全局生效)
    @SystemMessage("你是一个友好的助手,必须记住用户的姓名和关键信息,回复要亲切")
    // UserMessage标记该方法接收用户输入
    String chat(@UserMessage String userInput);
}
步骤 2:创建 ChatMemory 实例

使用MessageWindowChatMemory创建带窗口限制的记忆组件:

java

运行

import dev.langchain4j.memory.chat.MessageWindowChatMemory;

// 构建记忆组件:最多保留10条消息,超出则自动删除最早的
MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
        .maxMessages(10)  // 核心参数:消息窗口大小
        .id("user_12345") // 可选:为记忆实例设置唯一ID,区分不同用户
        .build();
步骤 3:构建 AIService 并集成模型 + 记忆

将模型、记忆组件注入AIService,框架会自动完成集成:

java

运行

import dev.langchain4j.model.qwen.QwenChatModel;
import dev.langchain4j.service.AiServices;

public class ChatMemoryDemo {
    public static void main(String[] args) {
        // 1. 初始化通义千问模型(替换为你的API Key)
        QwenChatModel qwenChatModel = QwenChatModel.builder()
                .apiKey("your-api-key")
                .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                .modelName("qwen-turbo")
                .temperature(0.7) // 可选:调整回复随机性
                .build();

        // 2. 创建记忆组件(步骤2的代码)
        MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
                .maxMessages(10)
                .id("user_12345")
                .build();

        // 3. 构建AIService:集成模型+记忆
        Assistant assistant = AiServices.builder(Assistant.class)
                .chatLanguageModel(qwenChatModel) // 绑定模型
                .chatMemory(chatMemory)           // 绑定记忆
                .build();

        // 4. 多轮对话测试(核心:无需手动管理历史)
        try {
            // 第一轮:告知姓名
            String answer1 = assistant.chat("我是环环");
            System.out.println("AI回复1:" + answer1); 
            // 预期输出:你好环环!很高兴认识你😊

            // 第二轮:验证记忆
            String answer2 = assistant.chat("我是谁?");
            System.out.println("AI回复2:" + answer2); 
            // 预期输出:你是环环呀~我记得你的名字😜

            // 第三轮:基于记忆的扩展对话
            String answer3 = assistant.chat("帮我规划一个环环的专属周末计划");
            System.out.println("AI回复3:" + answer3); 
            // 预期输出:环环的专属周末计划来啦...
        } catch (Exception e) {
            System.err.println("对话异常:" + e.getMessage());
            // 兜底回复
            System.out.println("抱歉,我暂时无法回复你的问题,请稍后再试~");
        }
    }
}
3. 底层流转流程(可视化)

预览

查看代码

LLMLLM (Qwen)ChatMemoryAIServiceUserLLMLLM (Qwen)ChatMemoryAIServiceUser调用chat("我是环环")添加用户消息"我是环环"到记忆获取当前完整历史(仅"我是环环")发送上下文(历史+新消息)返回回复"你好环环!很高兴认识你😊"添加AI回复到记忆返回AI回复调用chat("我是谁?")添加用户消息"我是谁?"到记忆获取完整历史("我是环环"+AI回复+"我是谁?")发送完整上下文返回回复"你是环环呀~我记得你的名字😜"添加AI回复到记忆返回AI回复

sequenceDiagram
    participant User
    participant AIService
    participant ChatMemory
    participant LLM (Qwen)

    User->>AIService: 调用chat("我是环环")
    AIService->>ChatMemory: 添加用户消息"我是环环"到记忆
    AIService->>ChatMemory: 获取当前完整历史(仅"我是环环")
    AIService->>LLM: 发送上下文(历史+新消息)
    LLM->>AIService: 返回回复"你好环环!很高兴认识你😊"
    AIService->>ChatMemory: 添加AI回复到记忆
    AIService->>User: 返回AI回复
    
    User->>AIService: 调用chat("我是谁?")
    AIService->>ChatMemory: 添加用户消息"我是谁?"到记忆
    AIService->>ChatMemory: 获取完整历史("我是环环"+AI回复+"我是谁?")
    AIService->>LLM: 发送完整上下文
    LLM->>AIService: 返回回复"你是环环呀~我记得你的名字😜"
    AIService->>ChatMemory: 添加AI回复到记忆
    AIService->>User: 返回AI回复

LLMLLM (Qwen)ChatMemoryAIServiceUserLLMLLM (Qwen)ChatMemoryAIServiceUser调用chat("我是环环")添加用户消息"我是环环"到记忆获取当前完整历史(仅"我是环环")发送上下文(历史+新消息)返回回复"你好环环!很高兴认识你😊"添加AI回复到记忆返回AI回复调用chat("我是谁?")添加用户消息"我是谁?"到记忆获取完整历史("我是环环"+AI回复+"我是谁?")发送完整上下文返回回复"你是环环呀~我记得你的名字😜"添加AI回复到记忆返回AI回复

三、关键技术细节解析

1. MessageWindowChatMemory的内部机制
  • 数据存储:内部维护一个Deque(双端队列),新消息从队尾添加,超出maxMessages时从队头删除最早的消息;
  • 消息类型:自动区分UserMessage/AiMessage,无需手动封装;
  • 用户隔离:通过id参数为不同用户创建独立的记忆实例,避免多用户记忆混淆(核心生产级特性)。
2. AIService的核心能力
  • 注解驱动@SystemMessage/@UserMessage自动封装消息类型,无需手动创建UserMessage对象;
  • 自动上下文拼接:每次调用chat()时,自动从ChatMemory读取历史,拼接成模型可识别的格式;
  • 异常封装:框架会捕获模型调用的基础异常,你只需外层捕获业务异常即可。
3. 优化配置(生产级建议)

java

运行

// 1. 更精细的ChatMemory配置
MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
        .maxMessages(8)  // 根据模型窗口调整(qwen-turbo建议8-10条)
        .id("user_" + userId) // 动态生成用户ID,实现多用户隔离
        .build();

// 2. AIService增强配置(添加系统指令+超时)
Assistant assistant = AiServices.builder(Assistant.class)
        .chatLanguageModel(qwenChatModel)
        .chatMemory(chatMemory)
        .systemMessage("你是专业的个人助手,严格记住用户的姓名、偏好,回复简洁友好,不超过50字")
        .timeout(30) // 超时时间(秒)
        .build();

四、总结

  1. 核心原理ChatMemory是 “自动记忆仓库”,AIService是 “调度员”,二者配合将 “手动拼接历史” 的工作完全封装,只需调用chat()方法即可实现带记忆的多轮对话;
  2. 关键流程:定义接口→创建记忆组件→构建 AIService→调用 chat () 方法,四步完成多轮对话,无需关注底层消息拼接;
  3. 核心优势:自动管理消息窗口(避免超模型上下文)、多用户隔离、代码极简(相比手动管理减少 80% 冗余代码),是 LangChain4J 推荐的生产级聊天记忆实现方式。

使用 AIService 实现记忆对话智能体

一、核心原理

通过将ChatMemory组件与AIService集成,让智能体自动管理、利用对话历史,实现上下文感知的多轮对话。核心是结合AIService简洁的 API 和ChatMemory的记忆能力,无需手动维护对话历史。

二、核心实现步骤

1. 配置 ChatMemory Bean(记忆管理)

java

运行

@Configuration
public class MemoryChatAssistantConfig {
    @Bean
    ChatMemory chatMemory() {
        // 推荐:扩展配置版(指定ID+消息窗口大小)
        return MessageWindowChatMemory.builder()
                .maxMessages(10)       // 最多保存10条消息,超出则移除最早的
                .id("default-chat-memory")  // 多会话场景用于区分记忆实例
                .build();
        
        // 简洁版(仅指定消息数)
        // return MessageWindowChatMemory.withMaxMessages(10);
    }
}

关键参数maxMessages设置消息滑动窗口大小,自动维护最近 N 条对话,避免超出模型上下文限制。

2. 定义 Assistant 接口(AI 服务契约)

java

运行

@AiService(
    wiringMode = EXPLICIT,        // 显式绑定组件
    chatModel = "qwenChatModel"   // 指定使用的聊天模型Bean名称
)
public interface Assistant {
    /** 基础记忆聊天 */
    String chat(String userMessage);

    /** 带系统提示的记忆聊天 */
    @SystemMessage("你是专业助手,回答详细准确且记住对话历史")
    String chatWithSystemPrompt(String userMessage);

    /** 生成指定主题/长度的内容(带记忆) */
    String generateContent(String topic, int length);
}

注解说明

  • @AiService:标记为 AI 服务接口,Spring 自动识别并生成实现类;
  • @SystemMessage:为对话添加固定系统提示,约束 AI 回复风格。

3. 两种使用方式

方式 1:Spring 自动注入(推荐,生产环境)

java

运行

@Autowired
private Assistant assistant;

@Test
public void testAutoInjectAssistant() {
    try {
        // 多轮对话,自动维护记忆
        String answer1 = assistant.chat("我是环环");
        String answer2 = assistant.chat("我是谁"); // AI能识别"环环"的身份
        
        System.out.println("回复1:" + answer1);
        System.out.println("回复2:" + answer2);
    } catch (Exception e) {
        System.err.println("对话失败:" + e.getMessage());
    }
}

工作原理:Spring 启动时自动检测@AiService接口,发现ChatMemory Bean 后自动集成,注入的实例直接具备记忆能力。

方式 2:手动构建(特殊场景,如动态配置)

java

运行

@Test
public void testManualBuildAssistant() {
    // 1. 创建记忆实例
    MessageWindowChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
    
    // 2. 手动构建AI服务(需注入聊天模型)
    Assistant assistant = AiServices.builder(Assistant.class)
            .chatLanguageModel(qwenChatModel)  // 注入指定的聊天模型
            .chatMemory(chatMemory)            // 绑定记忆组件
            .build();
    
    // 3. 调用(效果与自动注入一致)
    String answer1 = assistant.chat("我是环环");
    String answer2 = assistant.chat("我是谁");
}

三、底层技术机制

1. 记忆存储:滑动窗口机制

MessageWindowChatMemory内部通过滑动窗口管理对话历史:

  • 新消息追加到窗口末尾;
  • 消息数超maxMessages时,自动移除最早的消息;
  • 仅保留最近 N 条消息,平衡上下文完整性和模型性能。

2. AIService + ChatMemory 集成流程

预览

查看代码

下一轮对话

用户输入

AIService捕获消息

从ChatMemory读取历史

构建完整上下文

调用AI模型生成回复

捕获模型回复

更新ChatMemory(添加用户+AI消息)

返回回复给用户

flowchart LR
    A[用户输入] --> B[AIService捕获消息]
    B --> C[从ChatMemory读取历史]
    C --> D[构建完整上下文]
    D --> E[调用AI模型生成回复]
    E --> F[捕获模型回复]
    F --> G[更新ChatMemory(添加用户+AI消息)]
    G --> H[返回回复给用户]
    H -->|下一轮对话| A

下一轮对话

用户输入

AIService捕获消息

从ChatMemory读取历史

构建完整上下文

调用AI模型生成回复

捕获模型回复

更新ChatMemory(添加用户+AI消息

四、代码优化要点

优化方向 优化前(基础版) 优化后(生产版)
接口能力 仅单一场景的chat方法 扩展多方法(带系统提示、内容生成)
配置灵活性 仅指定消息数 增加实例 ID,支持多会话区分
异常处理 无异常捕获 增加 try-catch,友好处理调用失败场景

五、技术对比与适用场景

实现方式 代码复杂度 维护难度 功能丰富度 适用场景
手动管理对话历史 简单对话、学习 / 调试
ChatMemory + AIService 复杂多轮对话、生产环境
仅 AIService(无记忆) 单次问答、无上下文需求场景

典型应用场景

  1. 个人助手:记住用户偏好、日程、历史对话;
  2. 客服系统:关联订单号、历史问题,提供连贯服务;
  3. 教育辅导:跟踪学习进度、薄弱点,针对性解答;
  4. 创意协作:保留项目背景、讨论历史,持续优化创意。

六、核心优势

  1. 开发效率:无需手动编写对话历史管理逻辑,几行代码实现记忆对话;
  2. 用户体验:上下文感知,对话更自然、个性化;
  3. 系统性能:滑动窗口自动控内存,仅发送必要历史,提升响应速度。

总结

使用 AIService 实现记忆对话智能体的核心是:

  1. 配置ChatMemory定义记忆规则(消息窗口大小);
  2. @AiService定义简洁的服务接口;
  3. 通过 Spring 自动注入 / 手动构建集成记忆能力;
  4. 直接调用接口即可实现上下文感知的多轮对话。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐