1. Agent 与工具调用的结合
MCP 解决了「工具如何暴露」的问题,但 Agent 还面临另一层问题:模型如何决定调用哪个工具、按什么顺序调用、调用结果如何反馈到下一步思考。这两层合在一起,才是完整的「行动式 AI」。
一句话:MCP 是给工具装上的统一插座,Agent 框架则是那个会思考「该插哪个插座、拔下后再插哪个」的机械臂。
1.1 一个 Agent 工具调用循环
模型思考(ReAct 中的 Thought)
↓ 输出 tool_call
执行工具(可能经过 MCP 转发)
↓ 返回 tool_result
模型再思考(Observation)
↓ 直到给出最终答案
这个循环在实现上分为两个流派:ReAct(把思考/行动写进提示词)与 Function Calling(模型原生输出结构化调用意图)。
2. MCP 与 ReAct / Function Calling 的结合
| 维度 | ReAct | Function Calling |
|---|---|---|
| 机制 | 提示词引导输出 Thought/Action/Action Input | 模型 API 原生返回 tool_calls 字段 |
| 可移植性 | 任何模型可用 | 依赖供应商支持 |
| 结构化 | 弱,靠解析文本 | 强,JSON 原生 |
| 与 MCP 关系 | 通过提示词把 MCP 工具名注入模板 | 把 MCP 工具列表转成 API 的 tools 参数 |
2.1 ReAct 提示词模板(注入 MCP 工具)
你是一个能调用外部工具执行任务的智能体。可用工具如下:
{工具清单,例如:
- search_docs(查询语句): 检索技术文档
- query_sql(查询语句): 查询业务数据库
- send_email(收件人, 内容): 发送邮件}
请严格按以下格式思考与行动:
Thought: 你当前的思考
Action: 工具名(必须来自上面清单)
Action Input: {"参数名": "参数值"}
Observation: 工具返回结果
……(可多轮)
Thought: 我已得到答案
Final Answer: 最终回复
模型按模板产出 Action 后,编排层解析文本、解析 JSON 参数、调用 MCP 工具,再把结果作为 Observation 拼回下一轮。
2.2 Function Calling 将 MCP 工具转为 API schema
from mcp import ClientSession, StdioServerParameters
import asyncio, json
async def tools_to_openai_schema(session: ClientSession):
"""把 MCP 工具列表转换为 OpenAI 的 tools 参数"""
result = await session.list_tools()
return [
{
"type": "function",
"function": {
"name": tool.name,
"description": tool.description or "",
"parameters": tool.inputSchema, # JSON Schema 直接可用
},
}
for tool in result.tools
]
一句话:MCP 的工具描述 + JSON Schema 恰好是 Function Calling 需要的全部输入——协议层顺手就把桥搭好了。
3. LangChain 适配层
LangChain 通过 MCPAdapter 把 MCP 服务器暴露成 LangChain Tool,从而复用其 Agent 编排能力。
from langchain_mcp_adapters.tools import load_mcp_tools
from langchain.agents import create_react_agent
from langchain_openai import ChatOpenAI
async def build_agent():
# 1. 用 MCP 客户端连接服务器(stdio 传输)
server_params = StdioServerParameters(
command="npx",
args=["-y", "tsx", "/path/to/mcp-server/src/index.ts"],
)
# 2. 加载 MCP 工具为 LangChain Tool
tools = await load_mcp_tools(server_params)
# 3. 组装 ReAct Agent
model = ChatOpenAI(model="gpt-4o", temperature=0)
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(model, tools, prompt)
# 4. 调用
result = await agent.ainvoke({"input": "查询北京当前天气"})
print(result["output"])
3.1 适配层做了什么
load_mcp_tools 内部完成了三件事:
- 发起
initialize握手并协商能力; - 调用
tools/list拉取工具清单,逐条转换成 LangChain 的Tool对象; - 把每次
invoke转成tools/call请求,并把isError映射为 LangChain 可识别的错误。
# 手动转换:理解底层映射
async def mcp_tool_to_langchain(name, mcp_tool):
def invoke(args_str: str):
args = json.loads(args_str)
result = asyncio.run(session.call_tool(name, args))
if result.isError:
raise ToolException(result.content[0].text)
return result.content[0].text
return Tool(name=name, func=invoke, description=mcp_tool.description)
4. LlamaIndex 适配层
LlamaIndex 的 MCPAgent / MCPQueryEngine 把 MCP 工具包装为 FunctionTool,并能把工具结果写入它的索引体系:
from llama_index.tools.mcp import BasicMCPClient, McpToolSpec
from llama_index.core.agent import FunctionCallingAgentWorker
def build_llama_index_agent():
# 连接 MCP 服务器
mcp_client = BasicMCPClient(
"npx -y tsx /path/to/mcp-server/src/index.ts"
)
tool_spec = McpToolSpec.from_client(mcp_client)
tools = tool_spec.to_tool_list()
# 构建 Function Calling Agent
agent = FunctionCallingAgentWorker.from_tools(
tools,
llm=OpenAI(model="gpt-4o"),
verbose=True,
).as_agent()
return agent
一句话:LangChain 与 LlamaIndex 的适配层思路同构——把 MCP 工具「翻译」成各自框架的 Tool 对象,其余编排逻辑全部复用框架能力。
5. 工具选择的置信度
工具越多,模型选错的概率越高。工程上可以从三个维度压制误选率:
| 手段 | 做法 | 效果 |
|---|---|---|
| 描述工程 | 在 description 写明「何时用 / 何时不用」 | 减少语义歧义 |
| 显式拒绝 | 描述末尾加「不要用于 X 场景」 | 引导排除 |
| 低置信重试 | 模型犹豫(低 logprob)时退回用户确认 | 避免错误执行 |
5.1 低置信度处理
def should_confirm(tool_call, threshold=0.7) -> bool:
"""基于 logprob 判断工具选择是否可信"""
conf = sum(arg.finish_reason == "tool_calls"
for arg in [tool_call]) # 简化示意
# 实际可读取 API 返回的 token logprob
avg_prob = tool_call.get("avg_logprob", 0.0)
return avg_prob < threshold
当置信度不足时,不直接执行,而是把「将要调用 X 工具、参数为 Y」抛给用户确认,把决策权交还给人类。
6. 多步调用的状态管理
现实任务几乎都是多步的:「先查用户订单,再根据金额算折扣,最后发邮件」。每一步都依赖上一步的输出,这就对状态管理提出要求。
6.1 状态建模
interface AgentStep {
stepId: string;
tool: string;
args: Record<string, unknown>;
result: string; // 精简后的结果
createdAt: number;
tokensUsed: number;
}
class AgentMemory {
private steps: AgentStep[] = [];
append(step: AgentStep): void {
this.steps.push(step);
// 裁剪最旧步骤,防止上下文无限膨胀
while (this.totalTokens() > MAX_STEP_TOKENS) {
this.steps.shift();
}
}
toContext(): string {
// 只保留「结论性摘要」而非全部原始结果
return this.steps
.map((s) => `Step${s.stepId} [${s.tool}]: ${summarize(s.result)}`)
.join("\n");
}
}
6.2 把中间结果传给下一步
# 第二步的参数引用第一步的结果
step1 = await agent.call_tool("query_orders", {"user_id": 42})
order_total = extract_amount(step1.result)
step2 = await agent.call_tool(
"compute_discount", {"amount": order_total, "tier": "gold"}
)
step3 = await agent.call_tool(
"send_email",
{"to": "user@example.com", "body": f"您的折扣后金额为 {step2.result}"},
)
一句话:多步调用的状态管理核心只有两条——只保留摘要、把上一步结论喂给下一步,否则历史会像雪球一样把上下文窗口滚爆。
7. Claude / OpenAI 集成实践
7.1 Claude 侧:原生工具 + MCP 配置
Claude 桌面端通过配置文件直接挂载 MCP 服务器,工具自动进入模型视野,无需手写适配层:
{
"mcpServers": {
"weather": {
"command": "npx",
"args": ["-y", "tsx", "/path/to/mcp-server/src/index.ts"]
},
"db": {
"url": "https://mcp.example.com/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}
7.2 OpenAI 侧:手写 Function Calling 循环
OpenAI 的 API 需要自己管理循环:发消息 → 收到 tool_calls → 执行 → 把结果拼回 messages → 再发。
from openai import OpenAI
client = OpenAI()
def run_with_mcp_tools(messages, tools):
while True:
resp = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
)
choice = resp.choices[0]
if not choice.message.tool_calls:
return choice.message.content
messages.append(choice.message) # 带上 assistant 的 tool_calls
for call in choice.message.tool_calls:
# 经 MCP 客户端执行工具
mcp_result = asyncio.run(
session.call_tool(call.function.name,
json.loads(call.function.arguments))
)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": mcp_result.content[0].text,
})
| 集成对象 | 集成方式 | 适配成本 |
|---|---|---|
| Claude | 配置文件挂载 MCP,原生支持 | 最低 |
| OpenAI | 手写 Function Calling 循环 | 中 |
| LangChain | MCPAdapter → LangChain Tool | 低 |
| LlamaIndex | McpToolSpec → FunctionTool | 低 |
8. 总结
MCP 与 Agent 框架的集成,本质是「协议层的能力」与「决策层的智能」的拼装:
| 层面 | 职责 | 落点 |
|---|---|---|
| 协议层(MCP) | 工具统一暴露与调用 | 工具列表、JSON Schema、结果格式 |
| 决策层(Agent) | 何时调、调什么、下一步做什么 | ReAct / Function Calling 循环 |
| 适配层 | 两种能力的翻译 | LangChain / LlamaIndex Adapter |
| 状态层 | 多步结果传递与记忆 | 摘要化记忆 + 参数引用 |
把工具接进来只是第一步,如何让模型在有限上下文里高效使用这些工具,则属于「上下文工程」的范畴。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。