Spring-AI MCP源码浅析:从ToolCallback到JSON-RPC的完整链路

我最近在把内部的工单查询接口接进大模型对话流程时,第一次真正把 Spring-AI 的 MCP 源码摊开从头读了一遍。MCP 是 Model Context Protocol 的缩写,它解决的问题很直接:让大模型不只有“生成文本”的能力,还能安全地去调用外部工具、查数据库、拉文件、调接口。Spring-AI 把 MCP 客户端和服务端的抽象都做在框架里,我们平时只需要写几个@Tool注解方法,模型就能在对话里自动调用它们。

这篇文章算是一篇源码浅析,不是完整的入门教程。适合已经跑通过官方 Demo、但想知道“工具声明、JSON Schema 生成、请求分发、自动配置”这几条链路到底是怎么串起来的人。我会先讲清楚模块边界,再给最小可运行示例,然后分别拆服务端和客户端源码,最后把我在实际调试中踩过的几个坑放出来。我手头的版本是基于 M6 附近的代码,不同小版本的 builder API 可能略有调整,但核心调用链路基本没变。

1. Spring-AI 的 MCP 不是一个大模块,而是一组分层的模块

1.1 协议 SD K和 Spring AI 桥接包各管一半

很多人以为 Spring-AI 的 MCP 源码都写在org.springframework.ai.mcp下面,实际不是。你把依赖树展开就会发现,真正负责协议编解码、JSON-RPC 帧处理、握手协商的代码,在一个独立的 MCP Java SDK 里,包名是io.modelcontextprotocol。Spring-AI 这边只是用org.springframework.ai.mcp起了一套桥接层,把 Spring 的工具抽象和协议 SD K 对接起来。

这个拆分是我读源码时最先要接受的现实。如果一直在 Spring-AI 的源码里找JsonRpcMessage或protocolVersion的定义,会找不到,因为它们根本不在 Spring-AI 仓库里。反过来,如果只在协议 SD K 里找@Tool注解和ToolCallback,也找不到,那是 Spring-AI 自己的工具抽象。

io.modelcontextprotocol -> 协议层:JsonRpc、握手、工具发现、传输抽象 org.springframework.ai.mcp -> 桥接层:ToolCallback 转换、自动配置、Spring 容器整合 spring-ai-mcp-server -> 服务端模块:让 Spring 应用对外暴露 MCP 服务 spring-ai-mcp-client -> 客户端模块:让 Spring 应用连接远端 MCP 服务

为什么要拆成这么多层?因为 MCP 协议本身和 Spring 没有关系,任何 Java 项目都可以用io.modelcontextprotocol这个 SDK 去做一个 MCP 客户端或服务端。Spring-AI 的贡献在于,把这些协议能力包装成 Spring 风格的 Bean,让你在配置类里写一个McpServer,再塞进几个ToolCallback,剩下的启动、握手、注册工具都由框架完成。

1.2 先分清两种部署姿态,再看源码就不会乱

读 MCP 源码前,必须先分清同一个应用可能存在的两种姿态。

第一种是服务端姿态。Spring-AI 应用作为一个 MCP Server 跑起来,外部 MCP Client 过来连接,通过tools/list看到我们暴露的工具,通过tools/call调用我们的@Tool方法。

第二种是客户端姿态。Spring-AI 应用作为 MCP Client 去连接外部 MCP Server,把远端服务暴露的工具列表拉回来,转成 Spring-AI 的ToolCallback,然后交给大模型选。大模型决定调用某个工具时,Spring-AI 把请求发出去,通过 MCP Client 转发到远端 Server 执行。

这两种姿态对应的核心类分别是McpServer和McpClient。它们虽然都叫 MCP,但一个是处理请求,一个是发起请求。源码里所有看似复杂的逻辑,最后都归于这两个方向。

2. 源码阅读前的热身:用两段最小示例确定阅读入口

2.1 服务端:让 Spring-AI 应用变成 MCP 服务器

先看服务端。一个最小例子通常长这样:

@Configuration public class McpServerConfig { @Bean public WebMvcServerTransport mcpTransport() { return new WebMvcServerTransport("/mcp"); } @Bean public McpServer mcpServer(WebMvcServerTransport transport, List<ToolCallback> toolCallbacks) { return McpServer.sync(McpSchema.VERSION_2025_03_26) .transport(transport) .toolCallbacks(toolCallbacks) .build(); } }

这里的McpServer.sync是协议 SDK 提供的一个静态入口,transport表示传输方式。WebMvcServerTransport会注册一个 Spring MVC 的 Controller,把/mcp这个 HTTP 端点收到的 JSON-RPC 请求交给McpServer处理。工具集合则是从 Spring 容器里收集到的ToolCallback。

与此同时,业务侧只需要写一个普通的 Spring 组件:

@Component public class OrderToolService { @Tool(description = "根据订单号查询订单状态", name = "query_order") public String queryOrder(String orderId) { return "订单 " + orderId + " 状态:已发货"; } }

@Tool是 Spring-AI 自己的注解。它本身不负责 MCP 通信,只负责描述“这是一个可以被模型使用的工具”。要让 MCP 服务端识别它,还需要有一个地方把它包装成ToolCallback。这一步在很多 starter 版本里已经自动化了,但它背后的原理反而值得单独看。后面第三节我会展开。

2.2 客户端:让 Spring-AI 应用接入外部 MCP 服务

再看客户端。如果项目要去连接一个外部的 MCP Server,通常是这种写法:

@Bean public HttpClientSseClientTransport mcpClientTransport() { return new HttpClientSseClientTransport("http://localhost:8081/sse"); } @Bean public McpClient mcpClient(HttpClientSseClientTransport transport) { return McpClient.sync(McpSchema.VERSION_2025_03_26) .transport(transport) .build(); } @Bean public List<ToolCallback> remoteMcpTools(McpClient mcpClient) { return McpToolUtils.toToolCallbacks(mcpClient, "remote_"); }

McpToolUtils是 Spring-AI 桥接层里一个非常关键的类。它把远端 MCP Server 返回的工具定义,转换成本地 Spring-AI 的ToolCallback列表。有了这个列表,大模型调用工具时,Spring-AI 就能像调用本地方法一样,把调用请求转发到远端 MCP Server。

这两段示例跑通之后,源码阅读的入口就清楚了:服务端要读McpServer和ToolCallback的映射逻辑,客户端要读McpClient和McpToolUtils的适配逻辑,两头共用的是协议 SDK 的 JSON-RPC 处理部分。

3. 服务端源码重点:工具声明、请求分发、方法反射三件事

3.1 工具声明阶段:ToolCallback 如何变成 tools/list 里的工具

MCP 服务端被 MCP 客户端连接后,第一件重要的事是响应tools/list。这个响应里要告诉对方:我有哪些工具、每个工具叫什么名字、描述是什么、参数长什么样。

源码里这一段的源头是 Spring-AI 的ToolCallback接口。ToolCallback里有获取工具定义的方法,返回一个包含name、description、inputSchema的对象。简单理解,@Tool注解上的name和description会被提取到这里,方法的参数列表会被生成一份 JSON Schema。

Spring-AI 在处理@Tool方法时,并不是直接拿方法名当工具名,而是通过ToolCallback的工厂方法去构建。我建议你把断点打在tools/list的响应构造处,看一次完整的返回值,能很直观地感受到这个映射结果。它长得很像下面这段 JSON:

{ "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "query_order", "description": "根据订单号查询订单状态", "inputSchema": { "type": "object", "properties": { "orderId": { "type": "string", "description": "订单号" } }, "required": ["orderId"] } } ] } }

如果 JSON Schema 生成正确,说明ToolCallback到 MCP 工具的声明阶段没问题。接下来的焦点就是调用阶段。

3.2 调用阶段:tools/call 请求如何命中 @Tool 方法

当大模型决定调用某个工具时,MCP 客户端会发来一个tools/call请求,请求体里有工具名和参数。服务端收到后,源码里要走这么一条路径:

WebMvcServerTransport -> McpServer 内的请求分发器 -> ToolCallingManager -> 根据工具名找到对应 ToolCallback -> 解析参数 -> 反射调用 @Tool 方法 -> 包装返回结果

ToolCallingManager是 Spring-AI 执行工具调用的统一入口。它的职责很简单:你给我一个工具调用请求,我根据工具名找到对应的执行器,然后把参数装填好,执行业务方法,最后把结果封装成 MCP 能识别的文本内容。

有一点要注意:@Tool方法并不一定就是被 MCP 请求直接触发的。它先被包装成ToolCallback,然后注册到ToolCallingManager,MCP Server 只是这个调用链路的入口之一。换句话说,同一个ToolCallback,既能被 MCP 请求触发,也能被本地其他逻辑显式调用。理解这一点,就不会把“MCP Server”和“工具执行器”混为一谈。

返回结果到 MCP 客户端时,通常会包装成一个content数组。这也是我最初不太习惯的地方。普通 Java 方法返回一个字符串就够了,但 MCP 的返回模型要求是结构化内容:

{ "jsonrpc": "2.0", "id": 2, "result": { "content": [ { "type": "text", "text": "订单 A1001 状态:已发货" } ], "isError": false } }

3.3 源码阅读视角:协议层和桥接层要来回切换

读服务端源码时,最大的麻烦是经常在协议 SDK 和 Spring-AI 桥接代码之间跳来跳去。我自己的经验是,凡是看到McpSchema、McpAsyncServer、JsonRpcMessage,先默认它们是协议 SDK 的;凡是看到ToolCallback、ToolCallingManager、McpToolUtils,先默认它们是 Spring-AI 的。

有了这个视角,代码层次会清晰很多。MCP Server 收到一个协议帧后,只会做非常有限的拆解,把请求转成内部执行请求;真正的工具发现、参数生成、方法反射,全都在 Spring-AI 这一侧完成。反过来也一样,McpServer 并不关心你的@Tool方法里是查数据库还是调外部接口,它只负责把框架交给它的工具执行器暴露出去。

4. 客户端源码重点:远端工具是怎么变成本地 ToolCallback 的

4.1 初始化握手里发生了什么

相比服务端,客户端源码更容易被忽略。其实McpClient.sync(...).build()这行代码并不只是创建对象,它会立刻触发一次 MCP 握手。

握手阶段做的事情包括:交换协议版本、交换客户端能力、拿到服务端信息、初始化工具列表。源码里这个阶段通常会产生一个初始化完成的结果。那之后客户端和服务端才进入可以正常通信的状态。

如果两边的协议版本不一致,握手结果里会明确标记出不匹配。这也是很多新手遇到“工具列表为空”或“调用后无响应”的根源,后面第六节我会专门展开。读客户端源码时,我建议直接从握手方法进入,因为后面所有工具调用都依赖握手成功。

4.2 McpToolUtils 的适配逻辑是客户端的核心

McpToolUtils.toToolCallbacks(mcpClient, "remote_")这行代码,在我第一次读的时候觉得它像个黑盒。后来拆开看,它做的事情其实非常朴素:

  1. 调用mcpClient.listTools(),向远端 MCP Server 要一次工具列表。
  2. 拿到协议层定义的Tool列表。
  3. 每个Tool对应创建一个ToolCallback。
  4. 在创建回调时,把工具名和远端调用逻辑绑在一起。

也就是说,McpToolUtils本质上是一个适配器,把协议层的“工具描述 + 调用指令”翻译成 Spring-AI 能理解的ToolCallback。当大模型选择这个工具时,ToolCallback的调用实现会重新走回McpClient,发起一个tools/call请求,把参数传给远端 MCP Server。

这个设计非常典型:协议层保持通用,框架层做适配。如果你需要在自己的代码里手动构建ToolCallback,理解这个适配思路会很有帮助,因为它不是简单地给一个本地方法套壳,而是把远端调用包装成了本地回调。

4.3 工具名前缀为什么重要

McpToolUtils.toToolCallbacks(mcpClient, "remote_")里的第二个参数,很多人会忽略。它会在每个远端工具名称前面加一个前缀,比如远端工具叫get_weather,转换后本地工具名就变成remote_get_weather。

这个前缀不是随便加着玩的。当一个 Spring-AI 应用同时连接多个 MCP Server 时,不同服务端很可能定义同名工具。如果不加前缀,ToolCallingManager在按工具名匹配时就会冲突。加了前缀后,每个工具在本地都有唯一的名称,模型能准确区分该调用哪个远端服务。

从源码角度来说,这个前缀是在构建ToolCallback时直接拼到工具定义里的,后面所有调用匹配都会基于拼完后的名字。所以如果你发现模型一直调用不到某个远端工具,先确认前缀是不是拼对了,这比调试半天序列化问题要有效得多。

5. 自动配置源码:starter 如何把 MCP 接进 Spring 容器

5.1 自动配置类之间的条件装配

如果项目用的是 Spring-AI 的 starter,你会发现即使不写上一节那些配置类,应用也能正常跑起来。这是因为源码里有一套自动配置逻辑,常见的关键类是 MCP Server 自动配置和 MCP Client 自动配置。

这些自动配置类大量使用条件装配。比如检测到 classpath 里有McpServer类,才会加载服务端相关配置;检测到配置项里有spring.ai.mcp.client.transport=http,才会创建 HTTP 传输需要的 Bean。这种做法让 starter 在不同项目里表现不同,但源码本身是清晰的两条线:一条是服务端自动配置线,一条是客户端自动配置线。

我读自动配置源码时,习惯先把META-INF下面的自动配置注册文件打开,看看哪些类被注册了,再逐个去看@ConditionalOnClass、@ConditionalOnMissingBean这些注解。这里的核心思考是:框架不能假设用户一定用 WebMVC 或 WebFlux,也不能假设用户一定希望自动创建某个 Bean,所以必须留出条件装配的手动覆盖空间。

5.2 spring.ai.mcp.* 配置项最终绑定了什么

自动配置的背后通常有配置属性类。spring.ai.mcp.*这一组前缀,最终会绑定到若干个Properties类上。我实际使用中最常接触的配置项大概有下面这些:

配置项作用
spring.ai.mcp.server.transport服务端传输方式,可选 webmvc、webflux、stdio 等
spring.ai.mcp.server.name服务端暴露给客户端的名称
spring.ai.mcp.client.transport客户端传输方式,比如 http、stdio
spring.ai.mcp.client.url客户端连接远端 MCP Server 的地址

看源码时,不要把这些配置项当成魔法字段。它们的本质工作就是:从Environment里读出值,绑定到Properties对象,然后在自动配置方法里决定创建什么类型、什么参数的 Bean。当你想自定义传输参数时,直接看对应的Properties类,比看文档更快。

5.3 为什么建议先看自动配置类再看核心 Bean

我个人的源码阅读顺序是先看自动配置类,再看核心 Bean。因为自动配置类决定了“在什么条件下,项目里会出现什么容器实例”。

比如你想知道为什么项目里已经有一个McpServer,但自定义配置却没有生效,大概率是因为自动配置类上有@ConditionalOnMissingBean,看到这个注解你就能推理出:框架只在容器里没有对应 Bean 时才创建默认实现。你要做的是先提供一个自己的McpServerBean,或者排除掉自动配置类,而不是在配置文件里反复试spring.ai.mcp.*。

这个道理适用于所有 Spring Boot starter。MCP starter 只是把它应用到了协议工具上。

6. 源码浅析过程中最常踩的四个坑

6.1 WebMVC 与 WebFlux 传输选错,启动直接 404 或连接拒绝

MCP 支持多种传输方式,服务端有 WebMVC、WebFlux,客户端有 HTTP 和 stdio。很多人没有意识到的一点是:如果项目本身是 WebFlux 应用,却使用 WebMVC 传输,MCP 端点可能根本不会被正确注册。

这类问题在源码里的表现是自动配置根据你依赖的 Web 框架类型来选择默认传输。比如 classpath 里存在DispatcherHandler,自动配置可能偏 WebFlux;但你手动 new 了一个WebMvcServerTransport,两者不对应,请求就可能 404。

我的建议是:服务端传输选型前,先看项目里引入的是哪个 Web 模块。如果确实需要 WebMVC 和 WebFlux 混用,那就要用不同的路径或端口,手动注册对应的传输 Bean,不要依赖默认推断。

6.2 协议版本不一致:握手失败而不是调用失败

MCP 协议版本是带在握手请求里的。如果客户端配的版本是2025_03_26,服务端用的是另一个版本,很多情况下握手就会失败。但失败方式并不像 HTTP 状态码那样显眼,它常常表现为:工具列表拉不到、调用没有结果、日志里只有一条淡淡的警告。

源码里,握手结果会记录双方协议的匹配情况。一旦不匹配,后续很多请求都不会正常分发。排查时不要先怀疑业务代码,先确认两端配置的McpSchema.VERSION是不是同一个值。如果是通过自动配置连的远端服务,再看远端服务端到底支持哪个版本,必要时显式降低本地版本去兼容。

6.3 参数类型太复杂时,JSON Schema 生成会出问题

@Tool方法的参数会被用来生成 JSON Schema。如果参数是 String、Integer 这类简单类型,生成过程很顺;如果参数是泛型嵌套、复杂类型,或者包含无法序列化的对象,生成过程就可能报错,或者生成出的 schema 模型完全不知道该怎么填参数。

我遇到过的典型问题是把 Java 8 的时间类型直接放在@Tool方法参数里,生成的 JSON Schema 不够直观,导致模型传参时给了一个不符合预期的字符串。源码里负责这块的是一个 JSON Schema 生成器,它不会魔法般理解业务类型。

建议是让@Tool方法的参数尽量简单,必要时用一个普通的 DTO 类接收,并且在 DTO 字段上加清晰的描述。模型是靠工具描述和参数描述来理解“该传什么”的,这一层做不好,源码再对也没用。

6.4 排查时优先看协议帧,而不是猜来猜去

MCP 本质上是 JSON-RPC 交互。排查问题时,最好的调试方式就是把协议帧打开,看客户端到底发了什么、服务端到底回了什么。实际操作时,可以在配置里把日志级别调高:

logging: level: io.modelcontextprotocol: DEBUG org.springframework.ai.mcp: DEBUG

打开后,initialize、tools/list、tools/call这些关键帧都会打印出来。很多“工具没注册上”“调用没反应”的问题,在这个日志里一眼就能定位,不需要反复猜测是自动配置还是传输层的问题。

我读源码时有个习惯,遇到问题先把调用栈和协议帧对齐,再去看对应的类。比如看到tools/list返回空数组,就说明要么没收集到ToolCallback,要么服务端的工具注册集合是空的;看到tools/call返回错误,再去看方法反射和参数序列化部分。这样源码阅读不是漫无目的地扫代码,而是带着明确问题去验证。

相关新闻

React Native登录页开发:从表单状态到Token闭环的完整指南

React Native登录页开发:从表单状态到Token闭环的完整指南

前两周有个朋友问我&#xff1a;一个登录页到底要开发多久&#xff1f;我说按“两个输入框加一个按钮”算&#xff0c;半天能交&#xff1b;但如果把键盘遮挡、错误提示、重复点击、Token过期、自动填充、无障碍、多端安全区都算进去&#xff0c;一周可能都不宽裕。他愣了一下&…

2026/10/20 8:54:27 阅读更多 →
CTF PWN实战:格式化字符串与栈溢出组合利用Mary_Morton

CTF PWN实战:格式化字符串与栈溢出组合利用Mary_Morton

1. 从一道经典PWN题说起&#xff1a;为什么Mary_Morton值得反复琢磨如果你刚开始接触CTF的PWN方向&#xff0c;或者已经刷了一些基础题但遇到格式化字符串和栈溢出结合的题目就卡壳&#xff0c;那Mary_Morton这道题大概率会成为你绕不过去的一个坎。这道题同时包含格式化字符串…

2026/10/20 8:54:27 阅读更多 →
告别AI工具焦虑:开发者极简工作流如何让效率提升70%

告别AI工具焦虑:开发者极简工作流如何让效率提升70%

大概从某段时间开始&#xff0c;我的信息流里全是各种AI工具的安利帖。今天有人说“用了某助手效率翻倍”&#xff0c;明天有人晒“零代码搭了一个自动化流程”&#xff0c;后天又有人整理出一份几十个工具的清单。作为一个常年写代码、带项目、改文档的人&#xff0c;我一度也…

2026/10/20 8:53:26 阅读更多 →

最新新闻

P0087轨压过低别急着换泵:柴油共轨免拆诊断实战

P0087轨压过低别急着换泵:柴油共轨免拆诊断实战

下午两点多&#xff0c;拖车送来一台道奇2500&#xff0c;客户第一句话就是&#xff1a;昨天还好好的&#xff0c;今天死活打不着。接上诊断仪一读&#xff0c;故障码P0087油轨压力过低。旁边的小修理厂已经下了结论——高压泵坏了&#xff0c;换泵报价高得吓人。我在维修行当干…

2026/10/20 9:42:38 阅读更多 →
PyTorch图像分割入门:从Unet复现到数据质量诊断

PyTorch图像分割入门:从Unet复现到数据质量诊断

简介&#xff1a;本资源是面向深度学习初学者与进阶实践者的PyTorch模型复现代码合集&#xff0c;聚焦图像分割、视觉Transformer及生成式建模等核心方向&#xff0c;涵盖Unet、Vision Transformer&#xff08;ViT&#xff09;、MAE、DDPM等主流架构的完整可运行实现。压缩包共…

2026/10/20 9:42:38 阅读更多 →
8086汇编四例:大数相乘、计算器、文本比较与贪吃蛇的避坑指南

8086汇编四例:大数相乘、计算器、文本比较与贪吃蛇的避坑指南

简介&#xff1a;北理工《汇编语言与接口技术》课程配套汇编资料&#xff0c;覆盖大数相乘、计算器、文本比较与贪吃蛇四个典型项目&#xff0c;适合需要完成汇编上机作业或课设的本科生参考。其中大数相乘实现100位以上十进制整数乘法&#xff0c;计算器结合Windows界面与浮点…

2026/10/20 9:42:38 阅读更多 →
计算机视觉学术速递怎么做:从选题筛选到内容可持续的完整拆解

计算机视觉学术速递怎么做:从选题筛选到内容可持续的完整拆解

1. 从"学术速递"这个栏目名说起&#xff1a;为什么值得单独做一期拆解做计算机视觉方向的人&#xff0c;大概都有过这样的体验&#xff1a;每天刷预印本平台&#xff0c;新论文像流水一样涌出来&#xff0c;标题扫过去全是"某某某: A Novel Framework for ...&q…

2026/10/20 9:42:38 阅读更多 →
手写BP神经网络:从零实现前向传播、反向传播与权重更新

手写BP神经网络:从零实现前向传播、反向传播与权重更新

简介&#xff1a;本资源是一份面向深度学习初学者与PyTorch入门实践者的BP神经网络回归建模完整实现包&#xff0c;聚焦连续数值预测任务&#xff0c;适用于课程设计、算法复现及小型回归实验场景。压缩包共15个文件&#xff0c;含4个核心Python源码&#xff08;model.py、util…

2026/10/20 9:42:38 阅读更多 →
OpenClaw任务编排实战:从能跑到顺手的性能调优与异常处理

OpenClaw任务编排实战:从能跑到顺手的性能调优与异常处理

1. 从“能跑起来”到“用得顺手”之间&#xff0c;隔着哪些认知鸿沟很多人部署完 OpenClaw 之后&#xff0c;看着终端里跳出的第一行日志&#xff0c;心里那块石头就落地了——觉得“搞定了”。但真正用上一周就会发现&#xff0c;能跑起来和用得顺手完全是两码事。上篇我们聊了…

2026/10/20 9:41:28 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →