大模型核心,MCP(模型上下文协议)和Session API

今天来聊两个今年在Java社区讨论度很高的方向------MCP(模型上下文协议)Session API

这两个都是Spring AI在1.1版本之后重点推进的特性。MCP解决的是"AI怎么跟外部系统打交道"的标准问题,Session API解决的是"长对话记忆怎么管理"的工程问题。一个打通了AI和业务系统之间的连接,一个解决了多轮对话的稳定性。


第一部分:MCP ------ 从「Function Call乱战」到「AI时代的USB接口」

先说说MCP。

一、MCP是什么?为什么需要它?

MCP(Model Context Protocol,模型上下文协议)由Anthropic于2024年11月开源发布,是LLM调用外部工具的一个标准化协议。

在MCP出现之前,各家AI模型的Function Calling走的是各自为政的路线。OpenAI有OpenAI的一套,文心有文心的一套,你写代码的时候,每家的JSON Schema定义、调用方式、参数格式都长得不一样。代码里一堆条件判断,if用的是GPT走方案A,else if用的是Claude走方案B,else if用的是Gemini走方案C------维护成本高得离谱。

MCP相当于AI领域的JDBC标准。它定义了统一的接口规范,你用Spring AI开发好一个MCP Server,可以同时被Claude、Cursor、各种AI应用调用,完全解耦。

二、环境搭建与依赖配置

MCP Server的核心依赖只有一个------spring-ai-starter-mcp-server

xml 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server</artifactId>
    <version>1.1.2</version>
</dependency>

如果想用HTTP/SSE/Streamable HTTP模式,需要额外引入对应的starter。Spring AI官方提供了三种传输方式的Starter:

Starter 传输方式 适用场景
spring-ai-starter-mcp-server Stdio 本地调用,Claude Desktop / Cursor 直接拉起,最常见
spring-ai-starter-mcp-server-webmvc Streamable HTTP 远程工具,团队共享,适合微服务架构
spring-ai-starter-mcp-server-webflux Streamable HTTP 响应式编程,高并发场景

如果选择Stdio模式,配置文件里需要显式关闭Banner和Web容器:

yaml 复制代码
spring:
  main:
    banner-mode: off
    web-application-type: none
  ai:
    mcp:
      server:
        stdio: true

为什么需要关Banner和Web容器? MCP通过Stdio协议通信,任何额外输出到控制台的文本都会破坏协议通信。Log日志也会污染通信流,这一点稍后会说。

三、MCP Server开发实战

假设我要构建一个企业内部的知识助手,MCP Server需要暴露两个业务能力:

  1. 根据员工ID查询剩余年假天数(调用内部HR系统)
  2. 根据订单号查询物流状态(调用物流系统)

这两个能力分别对应两个工具。用Spring AI开发MCP Server,核心工具类定义如下:

java 复制代码
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;

@Component
public class EnterpriseTools {

    @Tool(description = "查询指定员工的年假余额")
    public String getUserAnnualLeave(@ToolParam(description = "员工ID") String userId) {
        // 实际业务:调用内部HR系统接口
        // 伪代码:String result = hrApi.getLeaveBalance(userId);
        return "员工 " + userId + " 的年假余额为 12 天";
    }

    @Tool(description = "查询订单的物流状态")
    public String getOrderStatus(@ToolParam(description = "订单号") String orderId) {
        // 实际业务:调用物流系统查询接口
        // 伪代码:String status = logisticsApi.track(orderId);
        return "订单 " + orderId + " 当前状态为:已发货,预计3天内送达";
    }
}

@Tool 注解的作用很关键------它会自动把Java方法暴露成符合MCP规范的Tool,框架在启动时会扫描所有被标记的Bean,自动生成Tool定义。

整个服务端就这几行核心代码。打包成可执行JAR后,任何支持MCP协议的AI客户端(Claude Desktop、Cursor等)都可以直接加载调用。

版本提示 :Spring AI 1.x 版本使用 @Tool 注解,2.0 版本已升级为 @McpTool,写法上有细微差异,使用时请核对版本号。

四、MCP Client开发:把多个Server的能力集成到一起

MCP Server开发好之后,自然需要MCP Client来连接和使用这些能力。

Spring AI提供了McpSyncClient作为统一的客户端接口。在集成工具时,代码可以写成这样:

java 复制代码
@Configuration
public class AiConfig {
    @Bean
    public ChatClient chatClient(ChatClient.Builder builder, List<McpSyncClient> mcpSyncClients) {
        return builder
            .defaultSystem("你是一个企业AI助手,可以调用系统中的工具回答员工问题。")
            .defaultToolCallbacks(mcpSyncClients)
            .build();
    }
}

实际项目中的场景比单个Server更复杂。比如企业有HR系统、订单系统、知识库系统,分别被封装成不同的MCP Server,分布在不同的端口上。MCP Client需要连接多个MCP Server,统一协调工具列表

五、集成测试与调试

MCP Server发布后,用Baeldung提供的测试策略来验证工具是否正常注册和调用:

java 复制代码
@SpringBootTest
@AutoConfigureMockMvc
public class McpToolIntegrationTest {

    @Autowired
    private ApplicationContext context;

    @Test
    void testToolIsRegistered() {
        // 验证MCP Server是否正确加载并注册了所有工具
        McpServer mcpServer = context.getBean(McpServer.class);
        assertThat(mcpServer).isNotNull();
    }
}

集成测试的核心是验证三件事:工具是否被正确扫描注册、参数是否能正确解析、返回值格式是否符合预期。MCP Tool和LLM响应不同,它是确定性代码,完全支持自动化单元测试

调试方面,Spring AI官方推荐使用 MCP Inspector 工具。这是一个可视化的调试工具,可以连接到MCP Server,手动测试tools/list和tools/call,实时查看工具列表和调用结果。

六、生产环境避坑指南

MCP Server在企业落地过程中,有几个坑值得提前知道。

坑一:Stdio模式下,Log会破坏通信。 如果选择了Stdio协议,绝对不能往控制台打印Log。Stdio协议走的是控制台输入输出流,任何额外的输出都会让协议解析失败。解决方法:把日志重定向到文件,或者用logback配置将控制台日志输出关掉。

坑二:Client连接Server时,路径要严格对齐。 MCP Server的端点暴露路径必须和Client配置的url和endpoint完全一致,否则Client扫描不到任何工具,AI模型会提示"no tools available"。

坑三:JDK版本兼容性问题。 MCP功能依赖Java 17以上版本,如果还在用Java 8或者11,需要先升级,否则会遇到莫名其妙的类加载错误。


第二部分:Session API ------ 从「ChatMemory」到「事件源记忆」

聊完MCP,再说Session API。

一、为什么需要Session API?

传统的ChatMemory存储对话历史的方式有一个致命问题:它把对话当成一个扁平的Message列表来存,窗口满了就从最老的消息开始删。

这在简单场景下勉强能用,但在工具调用场景中会出大问题------想象一下,模型调用了一个工具,返回结果还没来得及处理,工具调用的记录就被窗口截断了。模型拿到一个孤立的结果,不知道是从哪来的。结果就是整个对话上下文全乱掉。

Session API的思路完全不同。它用事件溯源 的方式记录对话:每个UserMessageAssistantMessageToolCallToolResult都是独立的事件,每条事件都带UUID、sessionId、timestamp。窗口满了不再机械地按消息条数截断,而是在Turn(一次对话轮次)的边界上做压缩,确保截断后模型不会看到孤立的消息。

二、核心概念:Session和Turn

在Session API中,最核心的两个概念是Session和Turn。

Session 代表一个完整的对话会话,存储sessionId、userId、TTL等信息。Turn是原子对话单元------从用户发出一条UserMessage开始,到模型回复、工具调用、工具结果,直到下一个UserMessage出现前结束。

所有压缩策略都在Turn边界上执行,不会把ToolCall和ToolResult拆散。

三、从ChatMemory迁移到Session API

用Session API创建一个会话:

java 复制代码
SessionService service = new DefaultSessionService(
    InMemorySessionRepository.builder().build()
);

Session session = service.create(
    CreateSessionRequest.builder()
        .userId("alice")
        .build()
);

service.appendMessage(session.id(), new UserMessage("帮我查一下订单"));
service.appendMessage(session.id(), new AssistantMessage("好的,请提供订单号"));

获取完整对话历史:

java 复制代码
List<Message> history = service.getMessages(session.id());

四、实战演练:构建多会话的客服助手

假设一个客服系统需要同时处理多个用户的对话。客服Agent要根据历史记录上下文来回答问题,而且客服Agent内部可能还使用了MCP Server提供的工具。

完整代码结构如下:

java 复制代码
@Service
public class CustomerServiceSessionManager {
    private final SessionService sessionService;
    private final ChatClient chatClient;

    public CustomerServiceSessionManager(ChatClient.Builder builder) {
        this.sessionService = new DefaultSessionService(
            JdbcSessionRepository.builder()
                .dataSource(dataSource)
                .build()
        );
        this.chatClient = builder.build();
    }

    // 获取或创建用户会话
    public Session getOrCreateSession(String userId) {
        Optional<Session> existing = sessionService.findByUserId(userId);
        return existing.orElseGet(() -> 
            sessionService.create(
                CreateSessionRequest.builder()
                    .userId(userId)
                    .ttl(Duration.ofDays(7))
                    .build()
            )
        );
    }

    // 处理用户消息(同时集成MCP工具调用)
    public String processMessage(String userId, String userMessage) {
        Session session = getOrCreateSession(userId);
        
        // 记录用户消息
        sessionService.appendMessage(session.id(), new UserMessage(userMessage));
        
        // 获取完整历史记录(包含之前的工具调用记录)
        List<Message> history = sessionService.getMessages(session.id());
        
        // 调用AI模型
        String response = chatClient.prompt()
            .messages(history)
            .call()
            .content();
        
        // 记录AI回复
        sessionService.appendMessage(session.id(), new AssistantMessage(response));
        
        return response;
    }
}

代码逻辑很简单:sessionId绑定userId,系统根据userId自动找回历史上下文。如果客服Agent调用MCP工具获取了订单信息,getMessages()会返回包含ToolCallToolResult的完整事件流。

五、避坑指南

Session API当前在spring-ai-community仓库中孵化,目标是Spring AI 2.1版本(预计2026年11月)正式GA ,届时ChatMemory会被标记为废弃。目前不建议在生产环境使用,但可以在测试项目中提前熟悉API设计。


写在最后

MCP和Session API代表了Spring AI最近迭代的两个方向。

MCP用标准化的协议连接外部工具,解决了AI应用的核心痛点------如何让LLM安全、可控地调用真实系统能力。它让Java开发者可以用自己熟悉的方式暴露业务能力,同时这些能力可以被不同AI客户端复用。Session API用事件溯源的方式管理对话历史,解决了长对话场景下的状态管理难题,多Agent分支隔离、按Turn压缩、可搜索召回存储这些能力,都是传统ChatMemory无法实现的。

MCP打通的是"AI能做什么",Session API解决的是"AI怎么做才稳"。

一个是向外扩展能力边界,一个是向内优化对话稳定。两者同时使用,恰好覆盖了生产级AI应用的两个关键维度。

下一篇文章准备聊聊Spring AI Agentic Patterns的其他内容,比如AutoMemoryTools和Task Subagents,这些都是值得探索的方向。如果大家在生产环境用Spring AI遇到了什么新问题或者有什么好玩的实践,欢迎留言交流。

相关推荐
zore_c2 小时前
【C++】C++类和对象实现日期类项目——时间计算器!!!
java·c语言·数据库·c++·笔记·算法·排序算法
草莓熊Lotso2 小时前
Linux 线程同步与互斥(二):线程同步从条件变量到生产者消费者模型全解,原理 + 源码彻底吃透
linux·运维·服务器·c语言·开发语言·数据库·c++
SEO_juper2 小时前
内容被 AI 摘录了,但没带你的网址?GEO 溯源这样补
人工智能·谷歌·seo·geo·ai时代·跨境电商推广·内容创作者
Rubin智造社2 小时前
OpenClaw实操指南19|SOUL.md + AGENTS.md实战:给AI注入性格、边界和判断力
人工智能·soul.md·openclaw实操·agents.md·ai性格配置·行为边界·workspace配置
lsx2024062 小时前
CSS 分组和嵌套
开发语言
Hello.Reader2 小时前
从零构建大语言模型特殊 Token 与 BPE 字节对编码 — 让分词器处理任何未知词(五)
人工智能·语言模型·自然语言处理
并不喜欢吃鱼2 小时前
C++中使用memcpy的拷贝问题
开发语言·c++
小郑加油2 小时前
python学习Day6-7天:条件判断与基本综合应用
java·服务器·apache
苏瞳儿7 小时前
java对数据库的增删改查
java·数据库·oracle