第3章 MCP与LLM的协同
前言
MCP的真正价值在于如何与LLM形成完美的协同。本章将深入探讨LLM如何理解和使用MCP提供的工具,以及如何形成自主决策和循环推理的能力。
3.1 MCP如何扩展LLM的能力
3.1.1 LLM的核心能力和局限
LLM的五大核心能力
graph TB
A["LLM核心能力"] --> B["理解"]
A --> C["推理"]
A --> D["生成"]
A --> E["学习"]
B --> B1["自然语言理解"]
B --> B2["上下文推断"]
B --> B3["信息抽取"]
C --> C1["逻辑推导"]
C --> C2["问题分解"]
C --> C3["多步推理"]
D --> D1["自然文本生成"]
D --> D2["代码生成"]
D --> D3["创意输出"]
E --> E1["in-context学习"]
E --> E2["提示优化"]
E --> E3["模式识别"]
LLM的三大根本局限
| 局限 | 具体表现 | 例子 |
|---|---|---|
| 知识断点 | 训练数据有截止日期 | 不知道最新的股票价格 |
| 计算能力 | 不善于精确计算 | 无法准确计算大数乘法 |
| 执行能力 | 不能主动修改外部系统 | 无法真正发送邮件或转账 |
3.1.2 MCP补充的三大维度
graph LR
A["LLM"] -->|局限| B["MCP拓展"]
B --> B1["知识补充"]
B --> B2["计算能力"]
B --> B3["执行能力"]
B1 --> B1A["实时数据"]
B1A --> B1A1["最新市场数据"]
B1A --> B1A2["企业内部信息"]
B2 --> B2A["精确计算"]
B2A --> B2A1["数据分析"]
B2A --> B2A2["统计运算"]
B3 --> B3A["真实操作"]
B3A --> B3A1["修改数据库"]
B3A --> B3A2["调用外部API"]
1. 知识补充
LLM + MCP资源 = 实时知识系统
场景:用户问"今年Q3的销售额是多少?"
LLM的困境:
- 训练数据中没有2025年的数据
- 即使知道之前的数据,也不知道Q3结束后的最新情况
MCP的解决:
1. MCP服务器暴露 resources://finance/sales/2025_q3.xlsx
2. LLM识别需要这个资源
3. MCP客户端读取资源内容
4. LLM基于实时数据回答
2. 计算能力
LLM + MCP工具 = 可靠计算引擎
场景:对1000个客户的消费数据进行统计分析
LLM的困难:
- 容易出错(特别是大数据)
- 计算步骤容易混乱
MCP的解决:
1. LLM调用 calculate_average(dataset) 工具
2. MCP服务器精确计算
3. 返回可靠的结果
3. 执行能力
LLM + MCP工具 = 自主代理系统
场景:用户说"如果销售额低于100万,自动发送预警邮件"
LLM的困难:
- 无法真正修改外部系统
- 无法发送真实的邮件
MCP的解决:
1. LLM理解业务逻辑
2. LLM调用 check_sales() 获取数据
3. LLM调用 send_alert_email() 真实发送
4. 形成完整的自动化流程
3.2 工具调用流程与循环推理
3.2.1 工具调用的完整流程
sequenceDiagram
participant User
participant LLM as Claude LLM
participant Client as MCP客户端
participant Server as MCP服务器
participant System as 业务系统
User->>LLM: 1.用户请求
Note over LLM: 分析请求,确定是否需要工具
LLM->>Client: 2.工具调用请求<br/>(需要的工具名和参数)
Client->>Server: 3.转发工具请求<br/>(MCP协议)
Server->>System: 4.执行具体操作<br/>(查询/修改数据)
System-->>Server: 5.返回结果
Server-->>Client: 6.返回工具结果<br/>(MCP响应)
Client-->>LLM: 7.展示工具结果
Note over LLM: 基于工具结果继续推理
LLM-->>User: 8.最终回复
3.2.2 循环推理(Agentic Loop)
工具调用的真正威力在于循环推理,即LLM可以多次调用工具,基于结果再决定下一步。
graph TB
A["用户请求"] --> B["LLM分析"]
B --> C{是否需要<br/>工具调用?}
C -->|否| D["直接回答"]
D --> E["输出结果"]
C -->|是| F["调用工具"]
F --> G["获得结果"]
G --> H{结果是否<br/>充分?}
H -->|是| D
H -->|否| I{是否需要<br/>其他工具?}
I -->|是| F
I -->|否| J["分析并调整"]
J --> F
style A fill:#e3f2fd
style E fill:#c8e6c9
3.2.3 循环推理的实际例子
场景:销售分析代理
用户:分析我们今年的销售表现,给出改进建议
第1轮:
LLM思考:需要获取销售数据
LLM调用:query_sales(year=2025)
结果:销售额$5M,同比增长15%
第2轮:
LLM思考:需要按产品类别分析
LLM调用:get_sales_by_category(year=2025)
结果:[电子产品$2.5M, 服装$1.5M, 食品$1M]
第3轮:
LLM思考:需要对比行业平均
LLM调用:get_industry_benchmark(industry="retail")
结果:行业平均$4.2M,我们超出19%
第4轮:
LLM思考:需要了解增长驱动因素
LLM调用:analyze_growth_factors(year=2025)
结果:新产品线贡献50%,地区扩展贡献30%
最终:
LLM综合分析4个工具的结果,生成综合建议:
"我们的销售表现超越行业平均19%,主要得益于新产品线和地区扩展..."
这展示了一个完整的多步代理循环。
3.3 上下文注入机制
3.3.1 什么是上下文注入
定义:上下文注入是将外部信息、资源、提示等注入到LLM的上下文窗口中,帮助LLM做出更准确的决策。
graph TB
A["LLM上下文"] --> B["系统提示"]
A --> C["用户输入"]
A --> D["工具定义"]
A --> E["外部资源"]
A --> F["历史对话"]
B --> B1["角色设定"]
B --> B2["行为规范"]
B --> B3["输出格式"]
D --> D1["可用工具列表"]
D --> D2["工具参数定义"]
D --> D3["工具使用场景"]
E --> E1["实时数据"]
E --> E2["知识库文档"]
E --> E3["业务规则"]
style A fill:#fff9c4
3.3.2 上下文优化策略
1. 工具描述优化
不好的描述:
{
"name": "query_db",
"description": "Query the database"
}
好的描述:
{
"name": "query_sales",
"description": "查询销售数据库中的订单和销售额信息。支持按时间范围、产品类别、地区等条件过滤。返回总销售额、订单数量、平均订单价格等指标。",
"inputSchema": {
"type": "object",
"properties": {
"start_date": {
"type": "string",
"description": "查询开始日期,格式YYYY-MM-DD。例如2025-01-01"
},
"end_date": {
"type": "string",
"description": "查询结束日期,格式YYYY-MM-DD。例如2025-12-31"
},
"category": {
"type": "string",
"enum": ["electronics", "clothing", "food", "all"],
"description": "产品类别过滤。不指定则返回所有类别"
}
},
"required": ["start_date", "end_date"]
}
}
关键改进:
- 详细的功能说明
- 返回值的具体描述
- 参数的格式要求
- 具体示例
2. 资源文档注入
某些情况下,应该把重要的参考文档注入上下文:
系统提示词:
你是一个销售分析专家。以下是我们公司的销售政策文档:
【销售折扣政策】
- 订单金额>$10k: 5%折扣
- 订单金额>$50k: 10%折扣
- VIP客户额外5%折扣
【销售目标】
- 本季度目标:$5M
- 增长率目标:15%
基于上述政策,分析用户的销售数据...
3.3.3 上下文窗口的管理
graph TB
A["上下文窗口"] -->|固定部分| B["系统提示"]
A -->|固定部分| C["工具定义"]
A -->|动态部分| D["对话历史"]
A -->|动态部分| E["相关资源"]
B --> B1["大小:500-1000字"]
B --> B1A["通常不变"]
C --> C1["大小:2000-5000字"]
C --> C1A["根据用户需求调整"]
D --> D1["大小:动态"]
D --> D1A["需要定期修剪"]
E --> E1["大小:动态"]
E --> E1A["基于当前任务选择"]
上下文管理最佳实践:
class ContextManager:
def __init__(self, max_context_tokens=4000):
self.max_tokens = max_context_tokens
self.system_prompt = "" # 固定
self.tool_definitions = {} # 固定
self.conversation_history = [] # 动态
self.injected_resources = [] # 动态
def calculate_total_tokens(self):
"""计算当前上下文的总token数"""
total = (
self.count_tokens(self.system_prompt) +
self.count_tokens(self.tool_definitions) +
sum(self.count_tokens(msg) for msg in self.conversation_history) +
sum(self.count_tokens(res) for res in self.injected_resources)
)
return total
def ensure_within_limit(self):
"""确保上下文在限制内"""
while self.calculate_total_tokens() > self.max_tokens:
# 优先删除最早的对话记录
if self.conversation_history:
self.conversation_history.pop(0)
# 然后删除最不相关的资源
elif self.injected_resources:
self.injected_resources.pop(0)
else:
break
3.4 Agent系统设计
3.4.1 什么是MCP Agent
定义:MCP Agent是一个自主决策系统,可以:
- 理解用户目标
- 规划执行步骤
- 自主调用MCP工具
- 基于结果反复调整
- 最终达成目标
graph TB
A["Agent架构"] --> B["感知层"]
A --> C["决策层"]
A --> D["执行层"]
A --> E["反馈层"]
B --> B1["理解用户需求"]
B --> B2["获取环境信息"]
B --> B3["检查工具可用性"]
C --> C1["规划执行步骤"]
C --> C2["选择合适工具"]
C --> C3["生成工具参数"]
D --> D1["调用MCP工具"]
D --> D2["处理返回结果"]
D --> D3["更新内部状态"]
E --> E1["评估是否达成目标"]
E --> E2["决定是否继续"]
E --> E3["学习和优化"]
3.4.2 Agent的状态机
stateDiagram-v2
[*] --> 初始化
初始化 --> 分析请求
分析请求 --> 是否理解?
是否理解? -->|否| 请求澄清
请求澄清 --> 分析请求
是否理解? -->|是| 规划
规划 --> 是否需要工具?
是否需要工具? -->|否| 直接回答
是否需要工具? -->|是| 选择工具
选择工具 --> 调用工具
调用工具 --> 获得结果
获得结果 --> 目标完成?
目标完成? -->|是| 总结结果
目标完成? -->|否| 还有资源?
还有资源? -->|是| 规划
还有资源? -->|否| 总结结果
总结结果 --> 直接回答
直接回答 --> [*]
3.4.3 Agent实现示例(Python伪代码)
class MCPAgent:
def __init__(self, llm, mcp_client):
self.llm = llm
self.mcp = mcp_client
self.max_iterations = 10
self.conversation_history = []
async def run(self, user_request):
"""运行Agent解决用户问题"""
self.conversation_history = []
for iteration in range(self.max_iterations):
# 1. 让LLM分析当前状态并决定下一步
response = await self.llm.complete(
messages=self.conversation_history + [
{"role": "user", "content": user_request}
],
tools=self.mcp.list_tools()
)
# 2. 检查LLM是否想调用工具
if response.stop_reason == "tool_use":
tool_use = response.tool_use[0]
tool_name = tool_use["name"]
tool_args = tool_use["input"]
# 3. 调用MCP工具
tool_result = await self.mcp.call_tool(
tool_name,
tool_args
)
# 4. 将结果添加到对话历史
self.conversation_history.append({
"role": "assistant",
"content": response.text,
"tool_use": tool_use
})
self.conversation_history.append({
"role": "user",
"content": f"工具 {tool_name} 的结果: {tool_result}"
})
else:
# LLM已经得出最终答案
return response.text
return "达到最大迭代次数,无法完成任务"
3.5 提示工程与MCP的结合
3.5.1 为MCP优化提示词
关键原则
graph TB
A["MCP提示词设计"] --> B["清晰"]
A --> C["具体"]
A --> D["上下文"]
A --> E["约束"]
B --> B1["明确工具的目的"]
B --> B2["明确何时调用"]
C --> C1["具体示例"]
C --> C2["参数要求"]
D --> D1["业务背景"]
D --> D2["相关规则"]
E --> E1["调用限制"]
E --> E2["安全边界"]
提示词模板
你是一个{角色}。你可以访问以下工具来{任务}。
## 可用工具
{工具列表和描述}
## 业务规则
- {规则1}
- {规则2}
- {规则3}
## 用户请求处理流程
1. 理解用户的具体需求
2. 分析是否需要调用工具
3. 如果需要,按照以下优先级调用:
a. 首先查询必要数据(使用query工具)
b. 然后进行分析(使用analysis工具)
c. 最后生成报告(使用reporting工具)
4. 基于工具结果给出专业建议
## 重要限制
- 不要调用tools/transfer_funds超过3次
- 所有金额操作需要经过审批
- 不要修改生产数据,仅供测试使用
## 输出格式
提供结构化的分析,包括:
1. 数据摘要
2. 关键发现
3. 建议
现在,用户的请求是:{用户请求}
3.5.2 思维链(Chain of Thought)与MCP
让LLM逐步思考,而不是直接调用工具:
用户请求:分析我们的销售数据,给出增长建议
改进的提示:
请按照以下步骤分析:
**第一步:理解问题**
- 用户想了解什么方面的销售数据?
- 需要什么时间范围的数据?
**第二步:收集信息**
使用以下工具按顺序调用:
1. query_sales(时间范围) - 获取总体销售额
2. get_sales_by_product(时间范围) - 了解产品结构
3. get_regional_performance(时间范围) - 了解地区表现
**第三步:分析数据**
基于收集的数据:
- 对比同期表现
- 找出最强和最弱的产品/地区
- 识别增长驱动因素
**第四步:生成建议**
基于分析,提出可行的改进建议
---
请开始分析:
3.6 MCP vs 函数调用 vs 原生集成
3.6.1 三种方案对比
graph TB
A["LLM能力扩展方案"] --> B["OpenAI Function Calling"]
A --> C["原生SDK集成"]
A --> D["MCP方案"]
B --> B1["优点"]
B --> B2["缺点"]
B1 --> B1A["简单易用"]
B1 --> B1B["开发快"]
B2 --> B2A["仅支持OpenAI"]
B2A --> B2A1["ChatGPT、GPT-4"]
B2A --> B2A2["格式不统一"]
B2 --> B2B["难以维护"]
B2 --> B2C["每个应用独立"]
C --> C1["优点"]
C --> C2["缺点"]
C1 --> C1A["高度集成"]
C1 --> C1B["性能优"]
C2 --> C2A["厂商锁定"]
C2 --> C2B["维护成本高"]
C2 --> C2C["难以跨项目"]
D --> D1["优点"]
D --> D2["缺点"]
D1 --> D1A["模型无关"]
D1 --> D1B["标准统一"]
D1 --> D1C["易于维护"]
D1 --> D1D["可复用性强"]
D2 --> D2A["相对复杂"]
D2 --> D2B["生态还在成长"]
3.6.2 选择矩阵
| 因素 | Function Calling | 原生SDK | MCP |
|---|---|---|---|
| 模型兼容性 | 仅OpenAI | 单一厂商 | 所有模型 |
| 学习曲线 | 陡峭 | 中等 | 平缓 |
| 开发速度 | 快(初始) | 中等 | 中等偏快 |
| 长期维护 | 困难 | 困难 | 容易 |
| 复用性 | 低 | 低 | 高 |
| 企业适用 | 小项目 | 中等 | 大型 |
| 未来扩展 | 受限 | 受限 | 灵活 |
本章总结
| 核心概念 | 关键点 |
|---|---|
| 能力补充 | 知识、计算、执行三个维度 |
| 工具调用 | 完整的请求-响应流程 |
| 循环推理 | 多轮迭代,逐步完成复杂任务 |
| 上下文注入 | 为LLM提供必要的背景信息 |
| Agent系统 | 自主决策和规划能力 |
| 提示工程 | 与MCP结合的最佳实践 |
| 方案对比 | MCP在标准化和可维护性上优势明显 |
常见问题
Q1: LLM什么时候应该调用工具? A: 当LLM判断基于自身知识无法准确回答时。关键指标包括:需要最新数据、需要精确计算、需要修改外部系统。
Q2: 如何判断一个Agent是否陷入死循环? A: 设置最大迭代次数(如10次),监控是否重复调用同样的工具组合,追踪相同的对话内容。
Q3: 上下文窗口满了怎么办? A: 优先保留系统提示和工具定义,删除最早的对话记录,根据相关度删除资源。或使用更长的上下文窗口模型。
Q4: 如何确保Agent的输出安全? A: 在调用修改性操作的工具前必须人工确认,记录所有工具调用的审计日志,限制敏感工具的调用频率。
Q5: MCP Agent相比传统编程有什么优势? A: MCP Agent更灵活、更易理解、更容易适应变化。但需要额外的监控和验证来确保可靠性。
实战要点
✅ 做好的做法
- 充分测试工具的正确性,因为Agent会依赖它们
- 为工具提供详细清晰的描述
- 设置合理的迭代限制
- 对关键操作进行人工验证
- 监控和记录所有的Agent执行过程
❌ 避免的做法
- 不要让Agent直接调用不可逆的操作(如永久删除)
- 不要给Agent不必要的工具访问权限
- 不要依赖LLM的准确性而忽视验证
- 不要在生产环境直接部署未充分测试的Agent
- 不要忽视审计和监控
延伸阅读
- 《Agent思维:从LLM到自主系统》
- MCP官方Agent文档:spec.modelcontextprotocol.io/agents
- 提示工程最佳实践:platform.openai.com/docs/guides…
下一章预告:第4章将介绍MCP生态现状,包括官方实现、社区方案、开源服务器、开发者工具等,帮助您快速找到合适的工具。