恶土小队免安装绿色中文版
7.52G · 2025-11-04
兄弟们,搞大模型应用的,是不是都遇到过这个坎儿:你满怀激情地用 Spring AI 撸了个对话机器人,开始聊得还行,但多聊几句,它就把前面说过的话忘得一干二净,跟个“七秒记忆的金鱼”一样。用户体验直接拉胯。
这背后其实就是上下文管理的锅。大模型本身是无状态的 (Stateless),它不会自动记住你之前的对话。你每发一次请求,对它来说都是一次“全新的邂逅”。要想让它变得智能,能联系上下文,我们就必须手动把历史对话“喂”给它。
今天,我们就来聊聊如何用 Spring AI,结合一种我称之为 MCP (Model Context Protocol,大模型上下文协议) 的模式,构建一个能真正“记住”对话历史的智能应用。这篇文章不扯虚的,直接上代码,从零到一,保证你读完就能上手。
别被这个名词吓到,这不是什么官方标准,而是我从实践中总结出来的一套简单有效的设计思路。它的核心思想很简单:
这个协议层专门负责:
User: ..., AI: ...)拼接成一个完整的上下文提示 (Prompt)。听起来是不是很简单?说白了,就是把“聊天记录”的管理工作,从你的业务代码里解耦出来,让代码更清晰,也更容易扩展。
Maven 依赖 (pom.xml) :
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        <version>0.8.0</version> 
    </dependency>
</dependencies>
<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
配置文件 (application.yml) :
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY} # 强烈建议使用环境变量
      options:
        model: gpt-3.5-turbo
咱们先不管 Spring AI,先来实现 MCP 的核心——上下文管理器。这里我们用最简单的方式,直接存在内存里。用一个 Map 来存,key 是会话 ID (比如用户 ID),value 是这个会話的聊天记录 List<Message>。
Spring AI 提供了 Message 这个抽象,正好拿来用。它有好几种实现,比如 UserMessage 和 AssistantMessage,非常适合用来区分用户和 AI 的发言。
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.messages.AssistantMessage;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
// 为了简单,我们把它做成一个 Bean
@Component
public class ConversationContextManager {
    // 使用线程安全的 Map 来存储多用户的会话
    private final Map<String, List<Message>> conversationHistory = new ConcurrentHashMap<>();
    // 定义一个最大历史记录数,防止上下文无限膨胀
    private static final int MAX_HISTORY_SIZE = 10;
    /**
     * 添加一条消息到指定会话
     * @param sessionId 会话ID
     * @param message 消息
     */
    public void addMessage(String sessionId, Message message) {
        // 如果会话不存在,就创建一个新的
        conversationHistory.computeIfAbsent(sessionId, k -> new CopyOnWriteArrayList<>());
        
        List<Message> messages = conversationHistory.get(sessionId);
        messages.add(message);
        // 实现上下文修剪 (Pruning) 策略
        if (messages.size() > MAX_HISTORY_SIZE) {
            // 简单粗暴:移除最早的一条记录
            messages.remove(0); 
        }
    }
    /**
     * 获取指定会话的完整历史记录
     * @param sessionId 会话ID
     * @return 消息列表
     */
    public List<Message> getHistory(String sessionId) {
        return conversationHistory.getOrDefault(sessionId, new CopyOnWriteArrayList<>());
    }
    /**
     * 清除会话历史
     * @param sessionId 会话ID
     */
    public void clearHistory(String sessionId) {
        conversationHistory.remove(sessionId);
    }
}
代码解读:
ConcurrentHashMap 和 CopyOnWriteArrayList:为了应付多线程环境,直接用 JUC 包里的线程安全集合,省心。addMessage: 核心方法。每次有新的对话,不管是用户的还是 AI 的,都往里塞。同时,这里实现了最简单的“修剪”策略——超过10条就扔掉最旧的。在实际项目中,你可以换成更复杂的策略,比如基于 token 数计算,或者做一些总结性的压缩。getHistory: 从存储里把历史记录捞出来,准备“喂”给大模型。接下来,写一个 Controller 来接收用户的请求。关键点在于,我们需要一个 sessionId 来区分不同的用户对话。
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class ChatController {
    private final ChatClient chatClient;
    private final ConversationContextManager contextManager;
    @Autowired
    public ChatController(ChatClient chatClient, ConversationContextManager contextManager) {
        this.chatClient = chatClient;
        this.contextManager = contextManager;
    }
    @GetMapping("/chat")
    public String chat(@RequestParam String sessionId, @RequestParam String message) {
        // 1. 构建 Prompt:将新消息和历史消息组合起来
        
        // 先把用户这次的提问存起来
        contextManager.addMessage(sessionId, new UserMessage(message));
        
        // 从 ContextManager 获取当前会话的完整历史
        List<Message> history = contextManager.getHistory(sessionId);
        // 创建 Prompt 对象
        Prompt prompt = new Prompt(history);
        // 2. 调用大模型
        String aiResponse = chatClient.call(prompt).getResult().getOutput().getContent();
        // 3. 将 AI 的回答也存入上下文,为下一次对话做准备
        contextManager.addMessage(sessionId, new AssistantMessage(aiResponse));
        return aiResponse;
    }
}
代码解读,这是整个流程的核心:
注入 ChatClient 和 ConversationContextManager:ChatClient 是 Spring AI 的核心,用来和 AI 模型交互。ConversationContextManager 就是我们上一步写的上下文管理器。
获取 sessionId:这里我们简单地通过 URL 参数传来。实际项目中,可以从用户登录的 Session、JWT Token 或者其他地方获取,保证每个用户的对话隔离。
MCP 模式的体现:
contextManager.addMessage(...) 负责把用户的提问和 AI 的回答都记录下来。contextManager.getHistory(sessionId) 获取完整的历史记录,然后 new Prompt(history) 将其构建成一个可以发送给大模型的请求。这一步完美地将上下文“注入”了请求。循环:用户提问 -> 存入上下文 -> 连同历史记录一起发给 AI -> AI 回答 -> 把 AI 回答也存入上下文 -> 等待下一次提问。一个完美的闭环形成了!
启动你的 Spring Boot 应用。然后打开浏览器或者 Postman,我们来模拟一次对话。
第一次请求:
http://localhost:8080/chat?sessionId=user123&message=你好,我叫Lander,你是什么模型?
第二次请求(注意,还是同一个 sessionId):
http://localhost:8080/chat?sessionId=user123&message=你还记得我叫什么名字吗?
成功了!AI “记住”了我们的对话。因为它在第二次回答时,看到的 Prompt 大概是这样的:
User: 你好,我叫Lander,你是什么模型?
AI: 你好 Lander!我是一个由 OpenAI 训练的大型语言模型。有什么可以帮助你的吗?
User: 你还记得我叫什么名字吗?
有了前面的对话作为“记忆”,它自然就能正确回答了。
我们上面实现的只是最基础的内存版上下文管理,但 MCP 模式的优势在于它的可扩展性。
更换存储介质:不想存在内存里?应用一重启就丢了。很简单,把 ConversationContextManager 的实现换成基于 Redis 或者数据库的。比如用 Redis 的 List 数据结构,每个 sessionId 对应一个 List,简直完美。
优化修剪策略:简单的保留最近N条,有时候会丢失重要的初始信息(比如用户最开始设定的角色)。你可以实现更智能的策略:
系统级指令 (System Prompt) :你可以在 getHistory 的时候,总是在列表的最前面插入一条 SystemMessage,比如 new SystemMessage("你是一个专业的 Java 开发助手")。这样可以给你的 AI 机器人设定一个全局的角色,让它的回答更专业。
今天我们通过一个简单的实战,掌握了用 Spring AI 构建多轮对话应用的核心技术。关键在于理解并实现 MCP(大模型上下文协议) 这个思路,将上下文管理从业务逻辑中解耦出来。
记住这三个核心步骤:存储、构建、修剪。掌握了它,你就能告别“金鱼记忆”,打造出真正智能、连贯的 AI 应用。
代码已经很简单了,但背后的思想才是最重要的。
                                2025-11-04
                            兆易创新推出 GD32F503/505 系列 MCU 芯片:采用 Arm Cortex-M33 内核,12 月起量产供货
                                2025-11-04
                            三星 Galaxy S26 标准版被曝厚 6.96 毫米,手机壳渲染图曝光