九九星石 發表於 2025-12-17 18:01:00

从 Tool Calling 到 A2A,再到 MCP. 大模型 Agent访问外部世界的桥梁

<p>随着大语言模型(LLM)能力不断增强,我们逐渐发现一个事实:</p>
<blockquote>
<p>真正有价值的,不是模型“会说话”,而是模型“能做事”。</p>
</blockquote>
<p>因为再强大的LLM,其核心优势仍然在于<strong>语言理解与推理能力</strong>,而非实时计算或外部状态获取。, 在某些简单事情上, 例如 查询当前时间, 当前地区的天气, 进行一个简单的数学运算, 其实都不是大模型擅长的事情, 我们也不需要大模型全知全能, 这不是一个正确的路线.</p>
<p>大模型应该像人类的大脑, 他只需要足够的聪明, 可以判断出做某些事情,需要什么工具. 就像人类大脑不会拣树枝, 但是可以使用手臂做这件事.</p>
<p>例如在查询天气这件事上, 我们不需要给大模型本身上报各种数据库, 而是需要大模型在判断需要查询天气的时候,调用一个外部的接口即可. 做数学运算时,调用一个计算器接口, 等等</p>
<p>而“做事”,就绕不开 <strong>工具调用、Agent 协作、以及标准化协议</strong>。</p>
<h2 id="1-tool-calling-工具调用">1. Tool Calling: 工具调用</h2>
<p><strong>Tool Calling(函数调用)</strong> 是大模型的一种能力:</p>
<blockquote>
<p>模型在推理过程中,不直接给出最终答案,而是返回一个结构化 JSON,表示“我需要调用某个工具”。</p>
</blockquote>
<p>JavaAI 框架, 例如Spring AI 等, 对于ToolCalling都有实现, 注册工具,告诉大模型可以执行该工具的条件和参数,大模型在决策需要调用时, 返回调用指令, 本地框架反射执行函数, 再将结果返回到上下文, 大模型继续决策.</p>
<p>从系统角度看,Tool Calling 做了三件事:</p>
<ol>
<li><strong>模型 → 输出结构化指令</strong></li>
<li><strong>框架 → 解析指令</strong></li>
<li><strong>本地代码 → 执行真实逻辑</strong></li>
</ol>
<p><strong>Tool Calling 的局限</strong></p>
<p>Tool Calling 虽然重要,但它有明显边界:</p>
<ul>
<li>工具调用的<strong>决策和编排</strong>只存在于单个 Agent 的推理循环中</li>
<li>不同框架(Spring AI / LangChain)工具定义不通用</li>
<li>缺乏统一的权限、发现、生命周期管理</li>
</ul>
<p>这就引出了下一阶段。</p>
<h2 id="2--mcpagent-世界的http-协议">2.MCP:Agent 世界的“HTTP 协议”</h2>
<p>MCP(Model Context Protocol,模型上下文协议) ,2024年11月底,由Anthropic 推出的一种开放标准。旨在为大语言模型(LLM)提供统一的、标准化方式与外部数据源和工具之间进行通信。</p>
<p>一句话:</p>
<blockquote>
<p><strong>MCP 是 Agent 与外部世界交互的统一协议层。</strong></p>
</blockquote>
<p><img src="https://img2024.cnblogs.com/blog/1540879/202512/1540879-20251217175257968-727783799.png" alt="image-20251216194030037" loading="lazy"></p>
<p>如上图所示, 在传统的大模型调用外部工具, 例如访问数据库,访问互联网数据的能力, 我们需要针对每一个框架, 每一个外部工具,都需要实现一套单独的调用函数, 函数的调用和代码完全耦合,</p>
<p>而有了MCP协议,在AI的世界里, 不管是调用方还是被调用方, 大家只需要都实现这套协议, 就可以无障碍,无沟通的进行联系,就像硬件世界的<code>USB</code></p>
<p><img src="https://img2024.cnblogs.com/blog/1540879/202512/1540879-20251217175257541-1032877097.png" alt="image-20251216194418193" loading="lazy"></p>
<p><strong>MCP 和 HTTP.TCP/IP协议的类比:</strong></p>
<table>
<thead>
<tr>
<th>特征</th>
<th>MCP</th>
<th>TCP/IP、HTTPS</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>本质</strong></td>
<td>协议(Protocol)</td>
<td>协议(Protocol)</td>
</tr>
<tr>
<td><strong>作用</strong></td>
<td>标准化 AI 模型与上下文来源/工具之间的数据交互方式</td>
<td>标准化 设备之间的网络通信方式</td>
</tr>
<tr>
<td><strong>目标</strong></td>
<td>让不同 AI 应用 / Agent 以统一方式访问外部资源和工具</td>
<td>让不同设备、系统可以互通数据</td>
</tr>
<tr>
<td><strong>好处</strong></td>
<td>消除碎片化集成、形成生态闭环</td>
<td>解决设备互联、实现互联网基础</td>
</tr>
</tbody>
</table>
<h3 id="21-mcp协议的两种实现方式-stdio和sse">2.1 MCP协议的两种实现方式: stdio和SSE</h3>
<p>在上文中, 我们知道了MCP是一种协议, 是规范Agent如何调用工具, 工具如何响应的规范, 而工具在这是作为Server. Agent作为Client,他们的通信是如何实现的, 有哪些实现方式.</p>
<p>官方参考实现中,主要提供了 stdio 方式,同时也支持基于 HTTP/SSE(实验 &amp; 可选) 的远程实现。 分别对应本地调用和远程调用的使用.</p>
<h4 id="211-sseserver-sent-events">2.1.1 <strong>SSE(Server-Sent Events)</strong></h4>
<p>SSE 是一种基于 HTTP 的单向流式通信方式:</p>
<ul>
<li>客户端发起 HTTP 请求</li>
<li>服务器保持连接不断开</li>
<li>服务器可以<strong>持续往客户端推送消息</strong></li>
</ul>
<p>SSE是实现Client远程调用Server的方式. Server部署在云端, 具体的工作流程如下:</p>
<pre><code>Client

│ HTTP POST /agent/run(这里就已经把请求发完了)

Server

│ 200 OK
│ Content-Type: text/event-stream

│ 开始SSE
│ data: ...
│ data: ...
│ data: ...

</code></pre>
<p>注意, Client发送HTTP请求Server提出问题的流程,并不是SSE协议的部分, 这只是一个普通的HTTP请求. 只需要遵循相应的请求规范.</p>
<p>但是此时客户端将会与Server建立一个基于HTTP协议的长链接,一直监听Server对于此问题的回答,此时才是<strong>SSE所描述的Server-Sent Events流程</strong>.</p>
<p>网上对于SSE的描述,都是说SSE是一个单向流式输出的通道, 但不是理解成 <strong>SSE 是一个“只能服务器说话,客户端不能说话”的通道</strong></p>
<p><strong>正确理解</strong></p>
<blockquote>
<p>SSE 是一次普通 HTTP 请求 + 一个不断写数据的响应</p>
</blockquote>
<p>下面是一个天气预报查询的示例, 展示通过SSE协议, Agent作为Client 如何与天气预报Server交互</p>
<ol>
<li>客户端 → Server(普通 HTTP 请求)</li>
</ol>
<pre><code class="language-java">POST /agent/run
Content-Type: application/json

{
"input": "杭州今天天气怎么样?"
}
</code></pre>
<p>这一步 <strong>不是 SSE</strong><br>
只是一个普通 HTTP POST。</p>
<ol start="2">
<li>Server 内部</li>
</ol>
<pre><code class="language-txt">接收请求

Server 调用天气 API(这是 Server → 外部)

拿到结果
</code></pre>
<ol start="3">
<li>Server → Client(SSE 开始推送)</li>
</ol>
<pre><code class="language-text">data: {"type":"thinking","content":"需要查询天气"}
data: {"type":"tool_call","name":"weather","args":{"city":"杭州"}}
data: {"type":"observation","content":"晴 26℃"}
data: {"type":"final","content":"杭州今天晴,26℃"}
</code></pre>
<p>客户端从头到尾只发了一次请求</p>
<p><strong>SSE的优缺点:</strong></p>
<p>优点:</p>
<ul>
<li>基于 HTTP,简单</li>
<li>浏览器原生支持</li>
<li>适合流式 AI 输出</li>
</ul>
<p>缺点:</p>
<ul>
<li>单向(Server → Client),</li>
<li>连接数多时压力较大</li>
</ul>
<h4 id="212-stdio">2.1.2 stdio</h4>
<p>stdio 的本质</p>
<p><strong>stdio = 标准输入 / 标准输出</strong></p>
<pre><code class="language-lua">stdin→ 程序输入
stdout → 程序输出
stderr → 错误输出
</code></pre>
<p>这是 <strong>一个基于操作系统级别的进程通信方式</strong>。</p>
<p>所以基于stdio实现的Server都是通过本地进程的, 因为Client和Server 只能通过<strong>OS级别</strong>的通道进行交互</p>
<p>MCP 官方和目前大部分Server大部分都是使用 stdio. 这是一个设计取舍问题</p>
<p>原因 1:<strong>安全</strong></p>
<ul>
<li>不需要监听端口</li>
<li>不暴露网络服务</li>
</ul>
<p>原因 2:本地工具友好,可以实现更多样化的功能</p>
<ul>
<li>Git</li>
<li>FileSystem</li>
<li>SQLite</li>
<li>Docker</li>
</ul>
<p>对于stdio本地通信的理解:</p>
<p><strong>stdio 只能“本地部署”</strong><br>
不等于:只能访问本地数据<br>
真正含义是:<strong>通信通道是本机进程级的. 具体的功能可以访问外部数据</strong></p>
<p>stdio的工作流程如下:</p>
<pre><code class="language-lua">Agent需要调用某个工具

启动这个进程

通过 stdin 发送 JSON

通过 stdout/stderr 接收 JSON/
</code></pre>
<ul>
<li>
<p>Agent <strong>启动</strong> 这个 MCP Server 进程</p>
</li>
<li>
<p>通过 <strong>管道(pipe)</strong> 通信</p>
</li>
<li>
<p>这两个进程 <strong>必须在同一台机器上</strong></p>
</li>
</ul>
<p>所以当我们需要使用某个工具时, 需要在Agent本机部署一个对应Server即可, 网上有很多这样的实现, 例如<code>https://github.com/modelcontextprotocol/servers</code> 下载需要的服务即可.</p>
<p>例如一个天气预报的调用方式:</p>
<pre><code class="language-lua">LLM Agent
   │stdio
   ▼
MCP Server
   │
   │ HTTP 请求
   ▼
天气 API(公网)
</code></pre>
<p>本机的Server封装了对远程HTTPAPI的调用</p>
<p><strong>对比:</strong></p>
<table>
<thead>
<tr>
<th>对比项</th>
<th>SSE</th>
<th>stdio</th>
</tr>
</thead>
<tbody>
<tr>
<td>层级</td>
<td>网络(HTTP)</td>
<td>操作系统</td>
</tr>
<tr>
<td>是否流式</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>通信方向</td>
<td>单向(推送)</td>
<td>双向</td>
</tr>
<tr>
<td>是否跨机器</td>
<td>✅</td>
<td>❌(本地)</td>
</tr>
<tr>
<td>适合场景</td>
<td>Web / 云服务</td>
<td>本地工具</td>
</tr>
<tr>
<td>MCP 常用</td>
<td>🌟🌟</td>
<td>🌟🌟🌟</td>
</tr>
</tbody>
</table>
<h3 id="22-mcp的使用">2.2 MCP的使用</h3>
<p>下面将通过一个简单的示例演示一下如何在cursor中 使用MCP.</p>
<p>首先需要在MCP Servers 网站上找到自己需要的服务. 我这里使用的是 <code>https://www.mcpservers.cn/</code> , 也可以使用其他的, 例如 <code>smithery.ai</code>等.</p>
<p>然后搜索需要的服务, 例如MySQL的连接工具. 并进入它的github官网(https://github.com/designcomputer/mysql_mcp_server),找到配置方式. 例如:</p>
<pre><code class="language-json">    "mysql": {
      "type": "stdio",
      "command": "uvx",
      "args": [
          "--from",
          "mysql-mcp-server",
          "mysql_mcp_server"
      ],
      "env": {
      "MYSQL_HOST": "121.36.**.**",
      "MYSQL_PORT": "3306",
      "MYSQL_USER": "root",
      "MYSQL_PASSWORD": "****",
      "MYSQL_DATABASE": "*****"
      }
    }
</code></pre>
<p>配置中指定协议方式为 stdio, 指令为 <code>uvx</code> 是 python的一个管理包指令, 需要在本地提前下载好. 将通过这个指定下载MCP服务,并启动,而且这个MCP服务就是Python语言编写的, <code>env</code> 配置为连接数据库的参数.最后执行的指令 <code>uvx --from mysql-mcp-server mysql_mcp_server</code></p>
<p>在cursor中的设置中配置如下:</p>
<pre><code class="language-json">{
"mcpServers": {
      "mysql": {
      "type": "stdio",
      "command": "uvx",
      "args": [
          "--from",
          "mysql-mcp-server",
          "mysql_mcp_server"
      ],
      "env": {
      "MYSQL_HOST": "121.36.**.**",
      "MYSQL_PORT": "3306",
      "MYSQL_USER": "root",
      "MYSQL_PASSWORD": "****",
      "MYSQL_DATABASE": "*****"
      }
    }
}
}
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/1540879/202512/1540879-20251217175257133-797676609.png" alt="image-20251217163041969" loading="lazy"></p>
<p>配置成功后,这里会有一个小绿点, 代表工具加载成功. 注意, 在配置前,最好先在本地命令行执行一遍命令<code>uvx --from mysql-mcp-server mysql_mcp_server</code>,把错误清理一遍,并且把该下载的包都下载好再进行配置. 因为只要有错误日志, 或者下载包的日志, 都不符合stdio的格式, 都会报错.</p>
<p>询问数据库中数据. cursor将会自动通过mcp查询数据库</p>
<p><img src="https://img2024.cnblogs.com/blog/1540879/202512/1540879-20251217175256766-172078741.png" alt="image-20251217164632899" loading="lazy"></p>
<h2 id="3-a2aagent-to-agent">3. A2A(Agent To Agent)</h2>
<p>谷歌,25年4月10日发布开源的、应用层协议 A2A(Agent-to-Agent 协议),即Agent-to-Agent。其设计目的是使智能体(Agent)间能够以一种自然的模态进行协作,类似于<strong>人与人</strong>之间的互动。</p>
<p>在使用的感官上, A2A 和MCP非常相似, 都是定义了Agent如何与外界沟通的规范,甚至网上有声音, 有了MCP, A2A 完全没有必要,</p>
<p>例如, 在一个AgentA 通过 A2A协议, 访问 另外一个AgentB时, 我们同样可以将AgentB 打包封装成一个MCP服务,AgentA 仍然可以通过MCP协议方式访问AgentB的功能, 这看似是合理的.</p>
<p>但是, A2A协议和MCP是不同的, MCP协议负责的是教会Agent如何感知工具,使用工具, 而A2A需要解决的是两个智能体之间如何协作, 是一个<strong>互相发现和识别</strong>的过程,如下图</p>
<p><img src="https://img2024.cnblogs.com/blog/1540879/202512/1540879-20251217175256006-1674935337.png" alt="image-20251217171856759" loading="lazy"></p>
<p><strong>MCP</strong> 关注的是 <strong>Agent 如何使用工具</strong> (Agent-to-Tool/Context)。它让 Agent 更方便地连接和使用各种外部资源(如 API、数据库)。</p>
<p><strong>A2A</strong> 关注的是 <strong>Agent 如何互相合作</strong> (Agent-to-Agent)。它让不同的 Agent 能够像一个团队一样协同工作</p>
<p>本身程序就是对现实世界的抽象, 虽然功能相似,但不能混为一谈(<strong>你不能把人当工具使!!!,来自一个工具人的呐喊</strong>)</p>
<p><strong>特性对比</strong></p>
<table>
<thead>
<tr>
<th>特性</th>
<th>A2A</th>
<th>MCP</th>
</tr>
</thead>
<tbody>
<tr>
<td>核心目标</td>
<td>标准化 Agent 之间的通信和协作</td>
<td>标准化 Agent/应用与外部工具/数据源之间的上下文交互</td>
</tr>
<tr>
<td>交互层面</td>
<td>Agent ↔ Agent (水平集成)</td>
<td>Agent/应用 ↔ 工具/数据源 (垂直集成)</td>
</tr>
<tr>
<td>解决问题</td>
<td>如何让不同来源、不同框架的 Agent 互相发现、对话、委托任务、协调工作流?</td>
<td>如何让一个 Agent/LLM 标准化、安全、高效地调用外部 API、访问数据库、获取实时数据等“工具”?</td>
</tr>
<tr>
<td>通信内容</td>
<td>任务指令、状态更新、协作请求、结果工件、上下文共享、协商</td>
<td>传递给模型的结构化上下文、工具列表、工具调用请求、工具执行结果</td>
</tr>
<tr>
<td>设想类比</td>
<td>Agent 之间的内部消息总线或协作框架</td>
<td>AI 应用连接外部工具的“USB-C”接口</td>
</tr>
<tr>
<td>主要发起者</td>
<td>Google</td>
<td>Anthropic</td>
</tr>
<tr>
<td>典型场景</td>
<td>多 Agent 系统、复杂工作流自动化、跨平台协作</td>
<td>单个 Agent 需要调用多种外部工具、增强 LLM 的上下文理解和执行能力</td>
</tr>
</tbody>
</table><br><br>
来源:https://www.cnblogs.com/xjwhaha/p/19363488
頁: [1]
查看完整版本: 从 Tool Calling 到 A2A,再到 MCP. 大模型 Agent访问外部世界的桥梁