导语:只有“脑子”的悲剧

让我们回到上一章。你的 Agent 刚刚通过 ToT(思维树)学会了运筹帷幄,它制定了一个完美的计划:“第一步查询数据库,第二步清洗数据,第三步发送邮件”。逻辑严密,无懈可击。

你满怀期待地看着它,仿佛在看一个即将拯救世界的超级英雄。
然后,它自信地迈出了第一步,试图去按那个“查询数据库”的按钮。

啪叽。
它摔倒了。

因为你突然发现:它根本没有手。

或者更准确地说,你给它装了一双“假手”。

  • 它试图调用 query_database,却凭空捏造了一个不存在的参数 sql_query,导致数据库报错。
  • 它试图调用 send_email,却因为你昨天刚改了 API 接口,它还在用旧的 Schema,导致请求失败。
  • 它试图写周报,却不知道公司内网的 ERP 系统根本没有 API,只有网页界面。

逻辑满分,操作零分。
这就是目前大多数 Agent 的现状:“思想上的巨人,行动上的矮子”。

它们被困在文本的牢笼里,对着物理世界的门锁(API、GUI)束手无策。本章,我们将给这个“缸中之脑”装上真正的、标准化的义肢。


第一部分:史前时代——Function Calling 的觉醒与混沌

1.1 JSON Mode:卑微的乞求

在 GPT-3 时代,如果你想查天气,你得这样写 System Prompt:

“你是天气助手。如果用户问天气,请千万不要说废话,请只返回一个 JSON,格式必须是 {"action": "get_weather", "city": "xxx"}。求你了!”

用户问:“北京天气咋样?”
模型可能心情好,返回了 JSON。但更多时候,它会返回:

“好的亲,这是你要的 JSON:{"action": "get_weather", "city": "Beijing"},还需要别的吗?”

痛点: 代码里的 JSON.parse() 直接报错。因为模型不理解“调用”,它只是在模仿文本格式。

1.2显微镜下的 Function Calling:数据流全景解析

Function Calling 不是魔法,它本质上是模型微调后对特定 Token 的敏感反应。
让我们把时间放慢,看看当用户说出**“帮我查查上海的天气”**时,系统底层到底发生了什么。

Step 1: 构造请求 (Inject Schema)

在发送给大模型的 API 请求中,除了用户的聊天记录,你必须额外塞入一个 tools 数组。这是告诉模型“你有哪些超能力”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// [Request] 发送给 LLM 的 Payload
{
"model": "gpt-4-turbo",
"messages": [
{"role": "user", "content": "帮我查一下上海现在的气温"}
],
// --- 关键点:显式定义工具 Schema ---
// 你必须在 Agent 代码里硬编码这段 JSON
"tools": [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "获取指定城市的实时天气信息",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名称" },
"unit": { "type": "string", "enum": ["c", "f"] }
},
"required": ["city"]
}
}
}
]
}

Step 2: 模型决策 (The Call)

模型接收到请求,分析语义,发现用户的意图(查天气)与 get_current_weather 匹配。
此时,模型不再返回文本,而是返回一个特殊的 tool_calls 对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// [Response] LLM 返回的 Payload
{
"choices": [{
"message": {
"role": "assistant",
"content": null, // 注意:这里是空的,模型没有说话
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_current_weather",
// 模型生成了严格符合 Schema 的 JSON 字符串
"arguments": "{\"city\": \"Shanghai\", \"unit\": \"c\"}"
}
}
]
},
"finish_reason": "tool_calls" // 告诉程序:停下来,去执行代码!
}]
}

Step 3: 代码执行与回填 (Execution Loop)

你的后端代码(Java/Python)捕获到 finish_reason="tool_calls"

  1. 解析: 读取 arguments 字符串,解析为 JSON 对象。
  2. 执行: 调用真实的 Java 方法 weatherService.query("Shanghai")
  3. 结果: 假设 API 返回了 25
  4. 回填: 将结果封装成 ToolMessage再次发给模型。
1
2
3
4
5
6
7
8
9
10
11
// [Request] 再次发送给 LLM,带上结果
{
"messages": [
... (之前的消息),
{
"role": "tool",
"tool_call_id": "call_abc123", // 必须对应上面的 id
"content": "{\"temperature\": 25, \"condition\": \"Sunny\"}"
}
]
}

Step 4: 最终响应

模型看到了工具返回的 25,终于生成了给用户的回复:

“上海现在的气温是 25 度,天气晴朗。”

1.3 Function Calling 的痛点:耦合地狱 (The n×m Problem)

Function Calling 虽然解决了“准”的问题,但带来了工程上的灾难。

场景: 你的公司有 10 个不同的 Agent(客服 Agent、差旅 Agent、活动策划 Agent…),它们都需要用到“查询天气”这个能力。

  • 操作: 你需要在 10 个 Agent 的 Java 代码里,硬编码 10 次 上面 Step 1 中那个冗长的 JSON Schema。
  • 灾难: 一旦天气 API 升级了(比如 unit 字段变成了必填),你需要修改 10 个项目的代码并重新部署。

工具的定义(Schema)与 Agent 的业务逻辑紧密耦合。这就像每买一台电脑,都要自己手写打印机驱动一样荒谬。我们需要解耦。


第二部分:工业革命——MCP (模型上下文协议) 的标准化

面对适配地狱,Anthropic 在 2024 年推出了 MCP (Model Context Protocol)。它的野心是成为 AI 工具世界的 USB-C 接口

2.1 核心逻辑:从“硬编码”到“握手发现”

MCP 的核心哲学是将**“工具的定义”**从 Agent 代码中彻底剥离,放入独立的 MCP Server 中。Agent 不再背诵说明书,而是通过标准协议与 Server 握手,动态下载驱动。

2.2 显微镜下的 MCP 交互:握手全景解析

让我们看看在 MCP 架构下,同样的“查询天气”流程是如何运作的。我们将经历从服务端开发底层协议握手的全过程。

Step 1: 服务端实现 (The Weather Station)
我们搭建一个独立运行的微服务,它只负责定义和执行工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 使用 Spring AI MCP SDK
@McpServer
public class WeatherMcpServer {

// 关键点:使用注解定义工具
// 这个代码写在 Server 端,Agent 端完全不需要知道!
@Tool(name = "get_current_weather", description = "查询实时天气")
public String getWeather(
@ToolParam(description = "城市名") String city
) {
return realWeatherApi.query(city);
}

// MCP 标准端点:暴露工具清单
// 访问 http://localhost:8080/mcp/manifest 会自动返回标准 Schema
}

Step 2: 客户端配置 (The Agent Config)
在你的 10 个 Agent 项目中,你再也不用硬编码 Schema 了。你只需要在配置文件(application.yml)里告诉 Agent 天气站在哪:

1
2
3
4
5
6
ai:
mcp:
clients:
weather-service:
url: http://localhost:8080/mcp/manifest # 指向 Server 地址
transport: sse # 使用 SSE 长连接

— 此时,开发者工作结束。以下是 Agent 启动后,底层自动发生的魔法 —

Step 3: 启动与握手 (The Handshake)
Agent(Client)启动,读取配置,自动向 localhost:8080 发起握手。

1
2
3
4
5
6
7
8
9
// [Request] Client -> Server: 初始化请求
{
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": { "tools": {} }
}
}

Step 4: 工具发现 (Discovery)
MCP Server 收到请求,翻看自己的注解代码,自动生成工具清单返回给 Agent。
注意:这是解耦的关键!Schema 是由服务端动态告知的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// [Response] Server -> Client: 暴露能力
{
"jsonrpc": "2.0",
"result": {
"serverInfo": { "name": "WeatherStation", "version": "1.0" },
"capabilities": { "tools": {} }
}
}

// [Request] Client -> Server: "把你有的工具列出来"
{ "jsonrpc": "2.0", "method": "tools/list" }

// [Response] Server -> Client: "我有这个 get_current_weather..."
{
"jsonrpc": "2.0",
"result": {
"tools": [{
"name": "get_current_weather",
"description": "查询实时天气",
"inputSchema": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}]
}
}

Step 5: 动态注册 (Registration)
Agent 收到上述 Schema 后,在内存中动态构建工具列表,并注入到 LLM 的上下文(Context)中。
关键点: Agent 的源代码里一行 Schema 都没有,全是运行时动态“下载”的。

Step 6: 标准化调用 (The Call)
当用户问“查查上海天气”时,LLM 决定调用 get_current_weather
Agent 不再执行本地 Java 方法,而是通过 MCP 协议向 Server 发送执行指令:

1
2
3
4
5
6
7
8
9
// [Request] Client -> Server: 远程执行
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_current_weather",
"arguments": { "city": "Shanghai" }
}
}

Server 执行完 Java 代码后,返回结果:

1
2
3
4
5
6
7
// [Response] Server -> Client: 返回结果
{
"jsonrpc": "2.0",
"result": {
"content": [{ "type": "text", "text": "25度,晴" }]
}
}

2.3 架构师启示

通过这 6 步流程,我们实现了彻底的解耦
如果明天天气服务升级了参数(比如加了 unit),你只需要修改 Step 1 中的 Server 代码。所有的 Agent(客服、差旅)在下一次握手时(Step 4),会自动同步到最新的 Schema。

这就是**“即插即用”**的工业级标准。


第三部分:注入灵魂——Skills (技能) 与 SOP 封装

MCP 完美解决了“连接”问题,但它只提供了原子工具(一把菜刀)。

新痛点:有工具,没手艺
用户对 Agent 说:“我是户外活动策划,帮我盯着上海的天气,如果有暴雨预警,就自动给所有报名者发通知取消活动。

Agent 懵了。

  • 它会调 MCP 的 get_current_weather
  • 它也会调 MCP 的 send_notification
  • 但是:什么样的天气算“暴雨预警”?取消通知的文案怎么写?发给谁?

这些复杂的业务逻辑(SOP),就是 Skills 的用武之地。

3.1 显微镜下的 Skills:SOP 的动态注入流程

Skills 的本质是**“SOP 的按需加载”**。我们来看看数据流是如何变化的。

Step 1: 索引建立 (The Index)

Agent 启动时,只加载一个极简的技能目录到 System Prompt 中。

1
2
3
Available Skills:
- [Event_Safety]: 用于监控天气风险并触发活动熔断机制。
- [Report_Writer]: 用于撰写销售周报。

Step 2: 意图识别与激活 (Activation)

用户输入:“盯着天气,有暴雨就取消活动。”
Agent 的路由层(Router)分析意图,命中 [Event_Safety] 技能。
系统在后台读取该 Skill 的完整定义文件(SKILL.md),并动态插入到当前的对话上下文中。

Step 3: 专家模式下的思维链 (The Expert Trace)

此时,Agent 的 Context 里多出了以下 SOP(来自 SKILL.md):

SOP 规则:

  1. 调用 get_current_weather
  2. condition 含 “Storm”,则为红色预警。
  3. 红色预警必须调用 cancel_event 并通知 admin

Agent 的思考过程 (Thought Process):

  1. Observation (MCP Call): 调用 get_current_weather("Shanghai") -> 返回 {"condition": "Heavy Storm"}
  2. Reasoning (SOP Match):“SOP 规定 ‘Storm’ 属于红色预警。根据规则 3,我必须取消活动。”
  3. **Action (Skill Execution):*调用 MCP 工具 cancel_event(reason="Red Alert: Heavy Storm")*。
  4. **Action (Notification):*调用 MCP 工具 send_notification(to="admin", msg="紧急熔断...")*。

3.3 深度辨析:Skills vs Workflow

这是一个 90% 的开发者都会混淆的架构点。Skills 和我们在第 21 章讲的 Workflow(工作流)到底有什么区别?它们是互斥的吗?

绝对不是。它们是互补的。

形象比喻:厨师与厨房

Workflow (工作流) 是“中央厨房的流水线”。

  • 它规定了:先把菜送到清洗区 -> 再送到切配区 -> 再送到烹饪区 -> 最后送到装盘区。
  • 这是由代码(Graph Edge)硬性规定的路径,谁也改不了。
  • 特点: 宏观、僵化、确定性强。

Skills (技能) 是“厨师脑子里的菜谱”。

  • 当菜流转到“烹饪区”时,厨师拿到了“做红烧肉”的 Skill。
  • Skill 告诉厨师:先放糖炒色,再放肉,大火收汁。
  • 这是由模型(LLM)阅读后自主执行的微观步骤。
  • 特点: 微观、灵活、依赖模型智商。

技术架构对比

维度 Workflow (工作流) Skills (技能)
控制权 Code-Driven (代码驱动)<br>开发者写死的 if-else 或图结构。 Model-Driven (模型驱动)<br>模型阅读 Prompt 后自主规划步骤。
颗粒度 粗粒度 (Coarse)<br>定义节点间的跳转 (Router -> Writer)。 细粒度 (Fine)<br>定义节点内部的执行逻辑 (先查库再写文案)。
容错性 高<br>可以设计强制的回退机制 (Retry Loop)。 中<br>依赖模型遵循 SOP 的能力,可能跑偏。
关系 容器 (Container)<br>Workflow 的 Node 是 Skills 的载体。 内容 (Content)<br>Skills 填充在 Workflow 的 Node 中。

架构师结论:
在企业级应用中,我们通常构建 “Workflow of Skills”

  • Workflow 保证业务大方向不跑偏(比如:必须经过审批节点)。
  • Skills 赋予每个节点专业的执行能力(比如:审批节点加载“合规审查 SOP”)。

第四部分:未来已来——Computer Use (GUI Agent) 的挑战

Skills 虽然强大,但它依然依赖于 API(MCP)。世界上 90% 的软件(如 Photoshop、旧 ERP、网银客户端)是没有 API 的。

为了打破 API 的限制,Anthropic 在 2024 年底推出了 Computer Use。这是一个能直接“看”屏幕、“动”鼠标的 Agent。

4.1 场景:没有 API 怎么查天气?

假设你需要查一个极其冷门的“农业气象网”,它只有网页,没有 API。
MCP 和 Skills 对此束手无策。

Computer Use 的解法:

  1. 看 (Vision): Agent 截图,看到浏览器里的地图上显示上海区域是蓝色(代表雨)。
  2. 动 (Action): Agent 发送指令 Click(x=500, y=200) 点击地图上的上海区域。
  3. 反馈 (Loop): 再次截图,看到弹出了详细数据框。

4.2 架构挑战与实操难点 (The Hard Truth)

虽然 Computer Use 是终极形态,但目前(2025年)落地面临三大实操难点

1. 延迟 (Latency)

  • 现象: 每次操作都需要截图 -> 上传图片 -> 视觉模型推理 -> 返回坐标。
  • 痛点: 点击一个按钮的端到端延迟可能高达 3-5秒。相比之下,API 调用只需 200ms。这使得它无法用于高频交易或实时响应场景。

2. 幻觉与精度 (Hallucination & Accuracy)

  • 现象: 网页稍微改了个版,按钮往右移了 10 像素,或者弹出了一个广告弹窗。
  • 痛点: Agent 可能会点错。如果这是个“删除订单”按钮,后果不堪设想。GUI 的稳定性远低于 API。

3. 沙箱与安全 (Sandboxing)

  • 现象: Agent 拥有了控制鼠标键盘的最高权限。
  • 痛点: 如果模型产生幻觉,输入了 rm -rf / 或者把敏感数据发给了黑客怎么办?
  • 解法: 必须运行在 Docker 容器E2B 专用沙箱 中,与宿主机物理隔离,并对网络请求做白名单限制。

结语:全副武装的智能体

至此,我们梳理出了一条清晰的工具演进链条:

  1. Function Calling: Agent 学会了用 JSON 准确地 查天气,但不仅要写代码,还要硬编码 Schema。
  2. MCP: Agent 学会了通过协议 标准化 地查天气,实现了与工具的解耦。
  3. Skills: Agent 学会了结合 业务逻辑 (SOP) 查天气,懂得了“暴雨要报警,小雨只提醒”。
  4. Computer Use: Agent 学会了在 没 API 的情况下,看着屏幕查天气。

现在的 Agent,脑子聪明(ToT 规划),记忆超群(MemGPT),连接广泛(MCP),手艺精湛(Skills),甚至能操作电脑(GUI)。

但是,拥有能力并不代表拥有智慧。
想象一下,这个战士挥舞利剑去砍怪(执行代码),结果剑断了(报错),或者砍偏了(逻辑错误)。
目前的 Agent 会怎么做?
它通常会陷入一种“执着的愚蠢”:它会捡起断剑,以同样的姿势、同样的角度,再一次砍向同一个地方,期望这次能有不同的结果。

它懂得如何行动,但不懂得如何从失败中学习。
它缺乏一面镜子,来看清自己的错误。

在下一篇章 《赋予 AI 自省能力:Reflexion (反思) 模式与 Actor-Critic 架构》 中,我们将补齐智能的最后一块拼图。我们将教 Agent 如何阅读报错日志,如何自我批评,并基于错误修正自己的计划,实现不依赖人类的自我进化

敬请期待。