大模型应用技术之Function Calling与MCP

177 阅读28分钟

 本期导读

本文将详细讲解这两项关键技术,它们让大模型从"只会说"变成"能做事"。我们会通过实际案例,帮助你理解这两种技术的原理、差异和应用场景。

本文内容包括:

  • 通过实际问题理解为什么需要这些技术
  • Function Calling的工作原理和应用
  • MCP协议的设计理念和三种传输方式对比
  • 两种技术的详细对比分析(重点说明应用场景差异)
  • Python完整代码实现(LangChain 0.3.79)
  • MCP Inspector调试工具使用指南
  • MCP Docker toolkit实战
  • MCP Server资源推荐
  • 生产环境的实战案例

一、从一个实际需求说起

1.1 只会"说"的AI有什么局限?

假设你正在开发一个智能助手,用户可以问它各种问题。目前的AI系统已经很强大了,但看看这个场景:

用户:"帮我查一下明天北京的天气"传统大模型的回答:
"我无法直接查询实时天气信息。我的知识截止到2023年10月,
无法访问外部API。建议你访问天气网站或使用天气APP查询。"问题:
- AI知道用户想做什么,但做不了
- 只能给出"建议",而不是"帮用户完成"
- 用户体验很差,AI显得很"笨"

这就是大模型的一个根本性限制:它只能生成文本,不能执行操作

1.2 我们希望AI能做什么?

理想情况下,当用户问"明天北京的天气",我们希望AI能够:

  1. 理解用户的意图(查询天气)
  2. 知道需要调用天气API
  3. 构造正确的API调用参数(城市:北京,日期:明天)
  4. 执行API调用
  5. 将结果用自然语言呈现给用户
用户:"帮我查一下明天北京的天气"
​
理想的AI助手:
1. [内部处理] 识别需要调用天气API
2. [执行操作] 调用 get_weather(city="北京", date="明天")
3. [获取结果] {"temperature": "15-25°C", "weather": "晴", "wind": "微风"}
4. [生成回答] "明天北京天气晴朗,气温15-25°C,有微风,适合外出活动。"
​
这才是真正有用的AI助手!

1.3 更多实际场景

场景1:企业办公助手

用户:"帮我给张三发个邮件,提醒他下午3点开会"
​
需要的能力:
- 调用邮件系统API
- 查询通讯录获取张三的邮箱
- 发送邮件

场景2:数据分析助手

用户:"统计一下上个月销售额排名前10的产品"
​
需要的能力:
- 连接数据库
- 执行SQL查询
- 处理和展示数据

场景3:智能客服

用户:"我要退订这个月的订阅"
​
需要的能力:
- 查询用户订阅信息
- 调用订阅管理系统
- 执行退订操作
- 发送确认消息

这些场景的共同点是:AI需要能够调用外部工具和系统

1.4 两种解决方案的诞生

为了解决这个问题,业界出现了两种主要方案:

方案1:Function Calling(函数调用)

  • 由OpenAI在GPT-3.5/4中引入
  • 让大模型能够调用预定义的函数
  • 现在已经成为主流大模型的标准功能

方案2:MCP(Model Context Protocol)

  • 由Anthropic提出的标准化协议
  • 提供统一的工具调用接口规范
  • 目标是建立一个开放的工具生态

两种方案都是为了让AI"能做事",但实现方式和设计理念有所不同。接下来我们详细了解它们。


二、Function Calling是什么

2.1 基本概念

Function Calling(函数调用)是大语言模型的一项功能,允许模型在对话过程中主动调用开发者预先定义的函数。

简单来说:

  • 你告诉AI:"我有这些工具可以用"(函数定义)
  • 用户提出需求
  • AI判断需要用哪个工具,并提取参数
  • 系统执行函数调用
  • AI根据结果生成回答

2.2 工作流程

1步:开发者定义函数
--------------------------------
def get_weather(city: str, date: str) -> dict:
    """获取天气信息"""
    # 调用天气API
    return {"temperature": "25°C", "weather": "晴"}
​
第2步:告诉AI可用的函数
--------------------------------
functions = [{
    "name": "get_weather",
    "description": "获取指定城市的天气信息",
    "parameters": {
        "city": {"type": "string", "description": "城市名称"},
        "date": {"type": "string", "description": "日期"}
    }
}]
​
第3步:用户提问
--------------------------------
用户:"北京明天天气怎么样?"
​
第4步:AI决定调用函数
--------------------------------
AI分析:需要查询天气 → 选择get_weather函数
AI提取参数:city="北京", date="明天"
AI返回:{
    "function": "get_weather",
    "arguments": {"city": "北京", "date": "明天"}
}
​
第5步:系统执行函数
--------------------------------
result = get_weather(city="北京", date="明天")
# 返回:{"temperature": "20-28°C", "weather": "晴"}
​
第6步:AI生成最终回答
--------------------------------
AI基于结果回答:"明天北京天气晴朗,气温20-28°C。"

2.3 Function Calling的特点

优点:

  1. 简单直接:只需要定义函数和参数,模型自动理解何时调用
  2. 原生支持:主流大模型(OpenAI、Claude、通义千问等)都内置支持
  3. 灵活性高:可以定义任意函数,满足各种业务需求
  4. 参数提取准确:大模型能准确从用户输入中提取函数参数

局限性:

  1. 缺乏标准化:不同厂商的实现方式略有差异
  2. 工具定义冗长:每个工具都需要详细的JSON schema定义
  3. 上下文占用:函数定义会占用模型的上下文窗口
  4. 无状态管理:需要开发者自己管理工具的状态和生命周期

三、MCP是什么

3.1 基本概念

MCP(Model Context Protocol)是Anthropic提出的一个开放协议,旨在标准化大模型与外部工具之间的交互方式。

可以把MCP理解为:

  • USB接口的AI版本:就像USB统一了电脑外设的接口,MCP统一了AI工具的接口
  • 工具的"应用商店" :有了标准协议,可以构建一个工具生态系统
  • 即插即用:遵循MCP协议的工具可以直接被任何支持MCP的AI系统使用

3.2 MCP的设计理念

传统方式的问题:

每个AI系统都自己定义工具接口
→ 工具开发者需要适配多个平台
→ 重复劳动,生态碎片化

MCP的解决方案:

统一的协议标准
→ 工具开发一次,处处可用
→ AI系统接入MCP,自动支持所有MCP工具
→ 生态繁荣,互利共赢

3.3 MCP的架构

MCP采用客户端-服务器架构:

┌─────────────┐         MCP协议         ┌─────────────┐
│             │  ←──────────────────→   │             │
│  AI应用     │                          │  MCP服务器  │
│ (MCP客户端) │   1. 发现可用工具         │ (工具提供者)│
│             │   2. 调用工具            │             │
│             │   3. 获取结果            │             │
└─────────────┘                          └─────────────┘
​
MCP服务器可以提供:
- Resources(资源):文件、数据、配置等
- Tools(工具):可执行的功能
- Prompts(提示词模板):预定义的提示词

3.4 MCP的核心组件

1. Resources(资源)

提供静态或动态的数据资源
例如:文件内容、数据库记录、API响应等

2. Tools(工具)

可被AI调用的功能
例如:发送邮件、执行搜索、数据分析等

3. Prompts(提示词)

预定义的提示词模板
帮助用户快速开始对话

4. Sampling(采样)

允许MCP服务器请求AI进行推理
实现更复杂的交互模式

3.5 MCP的三种传输方式

MCP支持三种不同的传输协议,每种都有自己的特点和适用场景:

3.5.1 STDIO(标准输入输出)

工作原理:

AI应用启动MCP服务器作为子进程
通过stdin/stdout进行通信

特点:

  • 最简单的方式,适合本地开发
  • 进程间通信,不需要网络
  • 服务器随客户端启动和关闭

适用场景:

  • 本地工具集成
  • 桌面应用(如Claude Desktop)
  • 开发和测试环境

配置示例:

{
  "mcpServers": {
    "my-server": {
      "type": "stdio",
      "command": "python",
      "args": ["mcp_server.py"]
    }
  }
}
3.5.2 SSE(Server-Sent Events)

工作原理:

客户端通过HTTP连接到MCP服务器
服务器可以主动推送消息给客户端
基于HTTP长连接

特点:

  • 单向通信(服务器 → 客户端)
  • 适合需要实时推送的场景
  • 客户端请求通过HTTP POST发送

适用场景:

  • 远程MCP服务
  • 需要服务器主动推送消息的场景
  • Web应用集成

配置示例:

{
  "mcpServers": {
    "my-server": {
      "type": "sse",
      "url": "http://localhost:3000/sse"
    }
  }
}
3.5.3 Streamable HTTP

工作原理:

完全基于HTTP的双向通信
每次请求-响应都是独立的
支持流式响应

特点:

  • 标准的HTTP协议
  • 支持负载均衡和CDN
  • 易于部署和扩展
  • 无需维护长连接

适用场景:

  • 云端MCP服务
  • 需要横向扩展的场景
  • 通过API网关访问
  • 生产环境部署

配置示例:

{
  "mcpServers": {
    "my-server": {
      "type": "streamable-http",
      "url": "http://localhost:3000/mcp"
    }
  }
}

3.6 三种传输方式对比

维度STDIOSSEStreamable HTTP
复杂度最简单中等中等
网络要求不需要HTTP长连接HTTP短连接
部署方式本地进程远程服务远程服务
扩展性中等
实时性中等
推荐场景本地开发需要推送的远程服务云端生产环境

3.7 SSE与Streamable HTTP详细对比

MCP ClientMCP ServerSSE (Server-Sent Events)HTTP GET /sse (建立长连接)连接保持打开状态event: messageevent: notificationevent: updateHTTP POST /request (独立请求)Response连接始终保持Streamable HTTPHTTP POST /mcp (请求1)Response (可能是流式)连接关闭HTTP POST /mcp (请求2)Response (可能是流式)每次请求都是新连接MCP ClientMCP Server

关键区别:

  1. 连接模式

    • SSE: 保持一个长连接用于服务器推送
    • HTTP: 每次请求都是独立的连接
  2. 消息流向

    • SSE: 服务器可以主动推送(单向流)
    • HTTP: 严格的请求-响应模式(双向但分离)
  3. 适用场景

    • SSE: 需要实时通知、进度更新的场景
    • HTTP: 标准的API调用、云服务部署

3.8 如何选择传输方式?

决策树:

开始
  ↓
本地开发/测试?
  ├─ 是 → 选择 STDIO
  └─ 否 ↓
  
需要服务器主动推送消息?
  ├─ 是 → 选择 SSE
  └─ 否 ↓
  
需要云端部署/负载均衡?
  ├─ 是 → 选择 Streamable HTTP
  └─ 否 → 选择 SSE

推荐:

  • 开发阶段: STDIO (最简单)
  • 有推送需求: SSE (实时性好)
  • 生产环境: Streamable HTTP (扩展性好)

四、Function Calling vs MCP - 小白必看的应用场景区别

很多初学者会混淆这两种技术,认为它们是"二选一"的关系。实际上,它们解决的问题层次不同

4.1 核心差异(小白版)

对比维度Function CallingMCP
本质一个功能特性一个通信协议
类比你直接喊你的助手做事你的助手通过标准流程调用其他部门
工具定义在哪在你的代码里在独立的MCP服务器里
可复用性只能在你的应用用所有支持MCP的应用都能用
学习成本低(几行代码就能用)中(需要理解协议和服务器)

4.2 什么时候用Function Calling?

场景1: 快速给AI增加几个简单功能

# 你只想让AI能查天气和发邮件
# 10分钟就能搞定@tool
def get_weather(city: str):
    return "晴天"@tool  
def send_email(to: str, content: str):
    return "已发送"# 绑定到AI,完成!

这种情况选Function Calling:

  • 功能很简单(1-5个工具)
  • 只在当前应用使用
  • 不需要其他人复用
  • 快速开发原型

场景2: 调用公司内部API

# 公司有个用户信息API,你想让AI能查询@tool
def get_user_info(user_id: str):
    # 直接调用内部API
    return requests.get(f"http://internal-api/users/{user_id}")
​
# 简单直接,Function Calling就够了

4.3 什么时候用MCP?

场景1: 构建可复用的工具平台

你开发了一个"数据分析工具包"
包含10个数据处理工具
希望:
- Claude Desktop能用
- 你自己的AI应用能用  
- 同事的项目也能用
- 将来出现新的AI应用也能用

→ 这时候应该用MCP
  写一个MCP服务器,所有人都能接入

场景2: 集成第三方MCP服务

你发现有人已经做了一个"天气查询MCP服务"
你想在自己的应用里用

如果是Function Calling:
→ 你需要自己写天气查询代码

如果是MCP:
→ 直接在配置文件添加这个MCP服务器地址
→ 立即就能用,不需要写代码

场景3: 企业级应用,需要统一管理工具

场景:
- 公司有50个不同的AI应用
- 每个都需要查询数据库、发送邮件等通用能力
- 需要统一的权限控制和审计

如果每个应用都用Function Calling:
- 50个应用都要写一遍数据库查询代码
- 权限控制要写50遍  
- 代码改动要改50个地方

如果用MCP:
- 写1个MCP服务器,提供统一的数据库查询工具
- 所有应用都连接这个MCP服务器
- 权限和审计都在MCP服务器统一处理
- 改代码只需要改1个地方

4.4 实际应用举例

个人项目: 用Function Calling

# 场景:你在做个人的AI助手,需要3个功能

# 功能1:查天气
@tool
def weather(city: str):
    return f"{city}天气晴朗"

# 功能2:设置提醒  
@tool
def reminder(time: str, msg: str):
    return f"已设置{time}的提醒"

# 功能3:记笔记
@tool
def note(content: str):
    return "已保存笔记"

# 总共20行代码,完成!

企业项目: 用MCP

场景:公司的AI客服系统,需要:
- 查询订单(连接订单系统)
- 查询物流(连接物流系统)  
- 查询用户信息(连接CRM系统)
- 创建工单(连接工单系统)

架构设计:
┌─────────┐      ┌────────────────┐
│ AI客服  │      │  订单MCP服务   │
│ 应用    │─────▶│  (订单团队维护)│
│         │      └────────────────┘
│         │      ┌────────────────┐
│         │─────▶│  物流MCP服务   │
│         │      │  (物流团队维护)│
│         │      └────────────────┘
│         │      ┌────────────────┐
│         │─────▶│  CRM MCP服务   │
│         │      │  (CRM团队维护) │
└─────────┘      └────────────────┘

好处:
- 每个团队维护自己的MCP服务
- AI应用不需要了解底层实现
- 新增AI应用时,直接复用现有MCP服务

4.5 能不能一起用?

可以!而且很常见:

# 简单的本地功能 → Function Calling
@tool
def calculate(expression: str):
    """简单计算器"""
    return eval(expression)

# 复杂的企业服务 → MCP
mcp_client.connect("order-system-mcp")  # 连接订单系统MCP服务
mcp_client.connect("crm-mcp")           # 连接CRM MCP服务

# AI可以同时使用两种工具:
# - 用Function Calling做简单计算  
# - 用MCP查询订单和客户信息

4.6 决策指南

如果你是新手,这样选:

  1. 先学Function Calling

    • 更简单,10分钟就能上手
    • 满足80%的个人项目需求
  2. 这些情况考虑MCP:

    • 你的工具想给别人复用
    • 你想用别人写好的MCP服务
    • 公司要求统一的工具管理
    • 需要跨多个AI应用使用
  3. 实际项目中:

    • 从Function Calling开始
    • 发现需要复用时,再把工具改成MCP
    • 大多数情况下,两种技术会混合使用

五、Function Calling详解与实战

5.1 工作原理深入解析

Function Calling的完整流程可以分为三个阶段:

阶段1:函数注册

开发者定义函数 → 描述函数功能 → 定义参数Schema → 注册到系统

阶段2:意图识别

用户输入 → LLM分析意图 → 判断是否需要调用函数 → 选择合适的函数 → 提取参数

阶段3:执行和回复

系统执行函数 → 获取结果 → LLM生成自然语言回复 → 返回用户

5.2 函数定义最佳实践

一个好的函数定义应该包含:

{
    "name": "search_products",  # 函数名:清晰、动词开头
    "description": "在产品数据库中搜索商品。支持按名称、类别、价格范围搜索。",  # 描述:详细、包含使用场景
    "parameters": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "搜索关键词,例如'iPhone'、'笔记本电脑'"
            },
            "category": {
                "type": "string",
                "enum": ["electronics", "clothing", "books", "food"],
                "description": "产品类别,可选值:electronics(电子产品)、clothing(服装)、books(图书)、food(食品)"
            },
            "min_price": {
                "type": "number",
                "description": "最低价格(元),例如100"
            },
            "max_price": {
                "type": "number",
                "description": "最高价格(元),例如5000"
            },
            "limit": {
                "type": "integer",
                "description": "返回结果数量限制,默认10,最大100",
                "default": 10
            }
        },
        "required": ["query"]  # 必填参数
    }
}

关键点:

  1. description要详细:LLM主要根据description判断何时调用
  2. 参数要有示例:帮助LLM理解参数格式
  3. 枚举值要说明:如果是enum,给出每个值的含义
  4. 设置默认值:减少必填参数,提高调用成功率

5.3 Python实战(LangChain 0.3.79)

5.3.1 环境准备
# 安装依赖
pip install langchain==0.3.79
pip install langchain-openai
pip install langchain-core
pip install openai

# 设置API Key
export OPENAI_API_KEY="你的API密钥"
5.3.2 基础示例:天气查询
"""
Function Calling基础示例 - LangChain 0.3.79
功能:实现一个能查询天气的AI助手
"""

from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
from typing import Annotated

# 步骤1:定义工具函数
@tool
def get_weather(
    city: Annotated[str, "城市名称,例如:北京、上海"],
    date: Annotated[str, "日期,例如:今天、明天、2024-01-01"] = "今天"
) -> str:
    """
    获取指定城市的天气信息
    
    Args:
        city: 城市名称
        date: 查询日期
        
    Returns:
        天气信息的JSON字符串
    """
    # 这里模拟调用天气API
    # 实际应用中,这里应该调用真实的天气API
    weather_data = {
        "北京": {"今天": "晴,15-25°C", "明天": "多云,18-28°C"},
        "上海": {"今天": "小雨,20-26°C", "明天": "阴,19-25°C"},
        "深圳": {"今天": "晴,25-32°C", "明天": "晴,26-33°C"}
    }
    
    result = weather_data.get(city, {}).get(date, "暂无数据")
    return f"{city}{date}的天气:{result}"


# 步骤2:初始化模型并绑定工具
llm = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0
)

# 绑定工具到模型
llm_with_tools = llm.bind_tools([get_weather])


# 步骤3:创建处理循环
def run_agent(user_input: str):
    """运行AI代理,处理用户输入"""
    messages = [HumanMessage(content=user_input)]
    
    print(f"用户: {user_input}\n")
    
    # 调用模型
    response = llm_with_tools.invoke(messages)
    messages.append(response)
    
    # 检查是否需要调用工具
    if response.tool_calls:
        print("AI决定调用工具:")
        
        for tool_call in response.tool_calls:
            print(f"  函数: {tool_call['name']}")
            print(f"  参数: {tool_call['args']}")
            
            # 执行工具调用
            if tool_call['name'] == 'get_weather':
                result = get_weather.invoke(tool_call['args'])
                print(f"  结果: {result}\n")
                
                # 将工具结果添加到消息历史
                messages.append(ToolMessage(
                    content=result,
                    tool_call_id=tool_call['id']
                ))
        
        # 基于工具结果生成最终回答
        final_response = llm_with_tools.invoke(messages)
        print(f"AI: {final_response.content}")
    else:
        print(f"AI: {response.content}")


# 使用示例
if __name__ == "__main__":
    # 测试1:查询天气
    run_agent("北京明天天气怎么样?")
    print("\n" + "="*60 + "\n")
    
    # 测试2:不需要调用工具的对话
    run_agent("你好,你是谁?")
    print("\n" + "="*60 + "\n")
    
    # 测试3:查询今天的天气
    run_agent("上海今天的天气如何?")

输出示例:

用户: 北京明天天气怎么样?

AI决定调用工具:
  函数: get_weather
  参数: {'city': '北京', 'date': '明天'}
  结果: 北京明天的天气:多云,18-28°C

AI: 北京明天的天气是多云,温度在18-28°C之间。建议带件薄外套。

============================================================

用户: 你好,你是谁?

AI: 你好!我是一个AI助手,可以帮你查询天气信息和回答问题。有什么我可以帮你的吗?
5.3.3 进阶示例:多工具协同
"""
Function Calling进阶示例 - 多工具协同
功能:实现一个办公助手,支持查日历、发邮件、搜索文档
"""

from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate
from datetime import datetime, timedelta

# 定义多个工具

@tool
def get_calendar_events(date: str) -> str:
    """
    获取指定日期的日历事件
    
    Args:
        date: 日期,格式:YYYY-MM-DD 或 "今天"、"明天"
    """
    # 模拟日历数据
    events = {
        "2024-01-15": [
            {"time": "10:00", "title": "团队周会", "location": "会议室A"},
            {"time": "14:00", "title": "项目评审", "location": "线上会议"}
        ],
        "2024-01-16": [
            {"time": "09:30", "title": "客户拜访", "location": "客户公司"}
        ]
    }
    
    # 处理"今天"、"明天"
    if date == "今天":
        date = datetime.now().strftime("%Y-%m-%d")
    elif date == "明天":
        date = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d")
    
    events_list = events.get(date, [])
    if not events_list:
        return f"{date}没有安排日程"
    
    result = f"{date}的日程安排:\n"
    for event in events_list:
        result += f"- {event['time']} {event['title']} ({event['location']})\n"
    
    return result


@tool
def send_email(
    to: str,
    subject: str,
    body: str
) -> str:
    """
    发送邮件
    
    Args:
        to: 收件人邮箱或姓名
        subject: 邮件主题
        body: 邮件正文
    """
    # 模拟发送邮件
    print(f"\n发送邮件:")
    print(f"收件人: {to}")
    print(f"主题: {subject}")
    print(f"内容: {body}\n")
    
    return f"邮件已成功发送给{to}"


@tool
def search_documents(
    query: str,
    doc_type: str = "all"
) -> str:
    """
    搜索文档
    
    Args:
        query: 搜索关键词
        doc_type: 文档类型,可选值:all(全部)、report(报告)、contract(合同)、memo(备忘录)
    """
    # 模拟文档搜索
    documents = {
        "销售": [
            {"title": "2023Q4销售报告", "type": "report", "date": "2024-01-05"},
            {"title": "华东区销售合同模板", "type": "contract", "date": "2023-12-20"}
        ],
        "技术": [
            {"title": "系统架构设计文档", "type": "memo", "date": "2024-01-10"},
            {"title": "API接口文档", "type": "report", "date": "2023-12-15"}
        ]
    }
    
    # 简单的搜索逻辑
    results = []
    for key, docs in documents.items():
        if query in key or any(query in doc['title'] for doc in docs):
            for doc in docs:
                if doc_type == "all" or doc['type'] == doc_type:
                    results.append(doc)
    
    if not results:
        return f"没有找到与'{query}'相关的文档"
    
    result = f"找到{len(results)}个相关文档:\n"
    for doc in results:
        result += f"- {doc['title']} ({doc['type']}, {doc['date']})\n"
    
    return result


# 创建Agent

# 1. 定义工具列表
tools = [get_calendar_events, send_email, search_documents]

# 2. 创建Prompt模板
prompt = ChatPromptTemplate.from_messages([
    ("system", """你是一个智能办公助手,可以帮助用户:
1. 查询日历事件
2. 发送邮件
3. 搜索文档

请根据用户需求,选择合适的工具来完成任务。
如果需要多个步骤,请逐步执行。
"""),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}")
])

# 3. 初始化LLM
llm = ChatOpenAI(model="gpt-4o", temperature=0)

# 4. 创建Agent
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,  # 显示中间步骤
    max_iterations=5
)


# 使用示例
if __name__ == "__main__":
    # 场景1:查询日历
    print("="*60)
    print("场景1:查询日历")
    print("="*60)
    result = agent_executor.invoke({
        "input": "明天我有什么安排?"
    })
    print(f"\n最终回答: {result['output']}\n\n")
    
    # 场景2:组合操作 - 先查日历,再发邮件
    print("="*60)
    print("场景2:查日历并发邮件")
    print("="*60)
    result = agent_executor.invoke({
        "input": "查一下明天的日程,然后给张三发邮件提醒他明天的客户拜访"
    })
    print(f"\n最终回答: {result['output']}\n\n")
    
    # 场景3:搜索文档
    print("="*60)
    print("场景3:搜索文档")
    print("="*60)
    result = agent_executor.invoke({
        "input": "帮我找一下销售相关的报告文档"
    })
    print(f"\n最终回答: {result['output']}\n\n")

5.4 Function Calling最佳实践

1. 函数设计原则

  • 单一职责:每个函数只做一件事
  • 幂等性:多次调用相同参数应返回相同结果
  • 错误处理:返回明确的错误信息
  • 性能考虑:避免长时间运行的操作

2. 参数设计技巧

  • 使用清晰的参数名
  • 提供详细的描述和示例
  • 合理设置默认值
  • 使用枚举类型限制输入

3. 调试技巧

import logging

# 添加日志记录
logger = logging.getLogger(__name__)

@tool
def my_function(param: str) -> str:
    """函数描述"""
    logger.info(f"函数被调用,参数:{param}")
    try:
        result = do_something(param)
        logger.info(f"函数执行成功,结果:{result}")
        return result
    except Exception as e:
        logger.error(f"函数执行失败:{str(e)}")
        return f"错误:{str(e)}"

4. 安全考虑

  • 验证输入参数
  • 限制操作权限
  • 记录审计日志
  • 敏感操作需要确认

六、MCP详解与实战

6.1 MCP协议架构

MCP(Model Context Protocol)是一个客户端-服务器协议,定义了AI应用如何与工具提供者进行通信。

核心架构:

┌──────────────────┐
│   AI Application │
│  (MCP Client)    │
└────────┬─────────┘
         │
         │ MCP Protocol
         │ (JSON-RPC 2.0)
         │
┌────────┴─────────┐
│   MCP Server     │
│ (Tool Provider)  │
└──────────────────┘

通信流程:

  1. 连接建立
Client: 发送 initialize 请求
Server: 返回服务器能力信息
Client: 发送 initialized 通知
  1. 工具发现
Client: 发送 tools/list 请求
Server: 返回可用工具列表及其Schema
  1. 工具调用
Client: 发送 tools/call 请求(工具名 + 参数)
Server: 执行工具
Server: 返回执行结果
  1. 资源访问
Client: 发送 resources/list 请求
Server: 返回可用资源列表
Client: 发送 resources/read 请求
Server: 返回资源内容

6.2 Python实现(LangChain 0.3.79集成MCP)

6.2.1 安装依赖
pip install mcp
pip install langchain==0.3.79
pip install langchain-openai
1. STDIO(本地 CLI 进程)

适用场景:客户端通过子进程方式调用工具,适合本地集成。

# server_stdio.py
from fastmcp import FastMCP
​
mcp = FastMCP("StdioDemo")
​
@mcp.tool()
def add(a: int, b: int) -> int:
    return a + b
​
if __name__ == "__main__":
    mcp.run(transport="stdio")

客户端示例

# client_stdio.py
import asyncio
from fastmcp import Client
from fastmcp.client.transports import PythonStdioTransport
​
async def main():
    transport = PythonStdioTransport("python", ["server_stdio.py"])
    client = Client(transport)
    async with client:
        res = await client.call_tool("add", {"a": 2, "b": 3})
        print("Result:", res.result)  # → 5
​
asyncio.run(main())

2. SSE(HTTP with Server‑Sent Events,Legacy)

适用场景:远程 HTTP,服务器通过 SSE 向客户端推送事件。现已 Deprecated,仅用于兼容旧系统。

# server_sse.py
from fastmcp import FastMCP
​
mcp = FastMCP("SseDemo")
​
@mcp.tool()
def greet(name: str) -> str:
    return f"Hello, {name}!"if __name__ == "__main__":
    mcp.run(transport="sse", host="127.0.0.1", port=8001)

客户端示例

# client_sse.py
import asyncio
from fastmcp import Client
​
async def main():
    client = Client("http://127.0.0.1:8001/sse")
    async with client:
        res = await client.call_tool("greet", {"name": "Bob"})
        print(res.result)  # → Hello, Bob!
​
asyncio.run(main())

3. Streamable HTTP(推荐,现代模式)

适用场景:单一 HTTP 端点,支持普通响应及 SSE 流,热更新、断点重连、会话 ID。

# server_http.py
from fastmcp import FastMCP
​
mcp = FastMCP("HttpDemo")
​
@mcp.tool()
async def count_up(to: int, ctx):
    for i in range(1, to + 1):
        await ctx.info(f"count:{i}")
    return {"done": True}
​
if __name__ == "__main__":
    mcp.run(transport="http", host="127.0.0.1", port=8002, path="/mcp")

客户端示例

# client_http.py
import asyncio
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
​
async def main():
    transport = StreamableHttpTransport(url="http://127.0.0.1:8002/mcp")
    client = Client(transport)
    async with client:
        res = await client.call_tool("count_up", {"to": 3})
        print("Final result:", res.result)
asyncio.run(main())

七、MCP Inspector调试工具使用指南

MCP Inspector是MCP官方提供的可视化测试和调试工具,帮助开发者快速测试MCP服务器。

7.1 什么是MCP Inspector?

MCP Inspector是一个Web界面工具,提供:

  • 可视化的MCP服务器连接
  • 交互式工具测试
  • 实时请求/响应查看
  • 资源浏览
  • Prompt测试

7.2 安装和启动

方法1:使用npx(推荐,无需安装)

# 直接运行,会自动下载并启动
npx @modelcontextprotocol/inspector

方法2:全局安装

# 安装
npm install -g @modelcontextprotocol/inspector
​
# 运行
mcp-inspector

启动后,浏览器会自动打开 http://localhost:6274

7.3 连接MCP服务器

7.3.1 连接STDIO服务器
# 启动Inspector并连接你的MCP服务器
npx @modelcontextprotocol/inspector python mcp_server.py

或者在UI界面中:

  1. 选择Transport: STDIO
  2. Command: python
  3. Arguments: mcp_server.py
  4. 点击"Connect"
7.3.2 连接SSE服务器
# 命令行方式
npx @modelcontextprotocol/inspector http://localhost:3000/sse

或者在UI界面中:

  1. 选择Transport: SSE
  2. Server URL: http://localhost:3000/sse
  3. 点击"Connect"
7.3.3 连接Streamable HTTP服务器
# 命令行方式  
npx @modelcontextprotocol/inspector http://localhost:3000/mcp --transport http

或者在UI界面中:

  1. 选择Transport: Streamable HTTP
  2. Server URL: http://localhost:3000/mcp
  3. 点击"Connect"

7.4 使用配置文件

创建配置文件 mcp-config.json:

{
  "mcpServers": {
    "my-weather-server": {
      "type": "stdio",
      "command": "python",
      "args": ["mcp_server.py"],
      "env": {
        "API_KEY": "your-api-key"
      }
    },
    "remote-server": {
      "type": "sse",
      "url": "http://localhost:3000/sse"
    },
    "http-server": {
      "type": "streamable-http",
      "url": "http://api.example.com/mcp"
    }
  }
}

使用配置文件启动:

# 连接配置文件中的指定服务器
npx @modelcontextprotocol/inspector --config mcp-config.json --server my-weather-server

# 如果配置文件中只有一个服务器,可以省略--server参数
npx @modelcontextprotocol/inspector --config mcp-config.json

7.5 Inspector界面功能

1. Tools标签页

  • 查看所有可用工具
  • 测试工具调用
  • 查看工具Schema
  • 查看调用历史

2. Resources标签页

  • 浏览可用资源
  • 读取资源内容
  • 查看资源元数据

3. Prompts标签页

  • 测试预定义Prompt
  • 查看Prompt模板
  • 测试不同参数组合

4. 配置标签页

  • 调整连接参数
  • 查看服务器信息
  • 导出配置

7.6 CLI模式(命令行模式)

Inspector也支持纯命令行模式,适合脚本和自动化:

# 列出所有工具
npx @modelcontextprotocol/inspector --cli python mcp_server.py --method tools/list

# 调用特定工具
npx @modelcontextprotocol/inspector --cli python mcp_server.py \
  --method tools/call \
  --tool-name get_weather \
  --tool-arg city=北京 \
  --tool-arg date=今天

# 使用JSON格式的参数
npx @modelcontextprotocol/inspector --cli python mcp_server.py \
  --method tools/call \
  --tool-name get_weather \
  --tool-arg 'city=北京'

# 连接远程服务器并调用工具
npx @modelcontextprotocol/inspector --cli http://localhost:3000/mcp \
  --transport http \
  --method tools/call \
  --tool-name get_weather \
  --tool-arg city=上海

7.7 调试技巧

1. 查看详细日志

# 启动时开启详细日志
DEBUG=mcp:* npx @modelcontextprotocol/inspector python mcp_server.py

2. 使用请求历史

Inspector会记录所有请求和响应,可以:

  • 重放之前的请求
  • 导出为curl命令
  • 复制为代码

3. 测试错误处理

在Inspector中故意传入错误参数,测试:

  • 参数验证是否正确
  • 错误消息是否清晰
  • 是否正确返回错误状态

八、MCP Docker Toolkit使用

对于生产环境或复杂的部署场景,可以使用Docker来管理MCP服务器。

8.1 创建MCP Server Docker镜像

Dockerfile:

# Dockerfile for MCP Server
FROM python:3.11-slim

WORKDIR /app

# 安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制MCP服务器代码
COPY mcp_server.py .

# 暴露端口(如果使用SSE或HTTP传输)
EXPOSE 3000

# 启动命令
CMD ["python", "mcp_server.py"]

requirements.txt:

mcp>=1.0.0
pydantic
uvicorn  # 如果使用SSE或HTTP
starlette  # 如果使用SSE或HTTP

构建镜像:

docker build -t my-mcp-server:latest .

8.2 运行MCP Server容器

方式1:STDIO(不推荐用Docker)

STDIO传输方式不适合Docker,因为Docker容器的stdin/stdout难以直接交互。

方式2:SSE传输

# 运行MCP服务器
docker run -d \
  --name mcp-server \
  -p 3000:3000 \
  my-mcp-server:latest

# 测试连接
npx @modelcontextprotocol/inspector http://localhost:3000/sse

方式3:Streamable HTTP传输

# 运行MCP服务器
docker run -d \
  --name mcp-server \
  -p 3000:3000 \
  -e API_KEY=your-secret-key \
  my-mcp-server:latest

# 测试连接
npx @modelcontextprotocol/inspector \
  http://localhost:3000/mcp \
  --transport http \
  --header "Authorization: Bearer your-secret-key"

8.3 使用Docker Compose管理多个MCP服务

docker-compose.yml:

version: '3.8'

services:
  # 天气MCP服务
  weather-mcp:
    build:
      context: ./weather-mcp
      dockerfile: Dockerfile
    ports:
      - "3001:3000"
    environment:
      - WEATHER_API_KEY=${WEATHER_API_KEY}
    restart: unless-stopped

  # 数据库MCP服务
  database-mcp:
    build:
      context: ./database-mcp
      dockerfile: Dockerfile
    ports:
      - "3002:3000"
    environment:
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_NAME=myapp
    depends_on:
      - postgres
    restart: unless-stopped

  # CRM MCP服务
  crm-mcp:
    build:
      context: ./crm-mcp
      dockerfile: Dockerfile
    ports:
      - "3003:3000"
    environment:
      - CRM_API_URL=${CRM_API_URL}
    restart: unless-stopped

  # PostgreSQL数据库(如果database-mcp需要)
  postgres:
    image: postgres:15
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:

启动所有服务:

# 启动
docker-compose up -d

# 查看状态
docker-compose ps

# 查看日志
docker-compose logs -f weather-mcp

# 停止
docker-compose down

8.4 MCP服务的健康检查

在Dockerfile中添加健康检查:

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY mcp_server.py .
COPY healthcheck.py .

EXPOSE 3000

# 添加健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD python healthcheck.py || exit 1

CMD ["python", "mcp_server.py"]

healthcheck.py:

import sys
import httpx

try:
    response = httpx.get("http://localhost:3000/health", timeout=2.0)
    if response.status_code == 200:
        sys.exit(0)
    else:
        sys.exit(1)
except Exception:
    sys.exit(1)


九、MCP Server资源推荐

9.1 官方资源

官方MCP Servers库

  • GitHub: github.com/modelcontex…
  • 包含官方维护的高质量MCP服务器
  • 涵盖常见功能:文件系统、数据库、Git等

推荐服务器:

  • @modelcontextprotocol/server-filesystem: 文件系统操作
  • @modelcontextprotocol/server-postgres: PostgreSQL数据库
  • @modelcontextprotocol/server-github: GitHub集成
  • @modelcontextprotocol/server-brave-search: Brave搜索
  • @modelcontextprotocol/server-google-maps: Google Maps集成

9.2 社区资源平台

1. MCP.so

  • 网站: mcp.so/
  • 社区驱动的MCP服务器目录
  • 按类别浏览和搜索MCP服务器
  • 包含使用说明和示例

2. Smithery.ai

  • 网站: smithery.ai/
  • MCP服务器市场和管理平台
  • 一键安装和配置MCP服务器
  • 提供服务器评分和评论

3. MCP Higress

  • 网站: mcp.higress.ai/
  • 阿里云Higress团队维护的MCP资源
  • 专注于云原生和微服务场景
  • 提供中文文档和支持

4. Awesome MCP Servers

9.3 热门第三方MCP服务器

开发工具类:

  • mcp-server-docker: Docker容器管理
  • mcp-server-kubernetes: K8s集群操作
  • mcp-server-aws: AWS云服务集成
  • mcp-server-vercel: Vercel部署管理

数据库类:

  • mcp-server-mysql: MySQL数据库
  • mcp-server-mongodb: MongoDB集成
  • mcp-server-redis: Redis缓存操作
  • mcp-server-elasticsearch: ES搜索集成

通信协作类:

  • mcp-server-slack: Slack消息发送
  • mcp-server-email: 邮件发送
  • mcp-server-calendar: 日历管理
  • mcp-server-notion: Notion集成

数据处理类:

  • mcp-server-pandas: 数据分析
  • mcp-server-scraper: 网页爬取
  • mcp-server-pdf: PDF处理
  • mcp-server-image: 图像处理


十、常见问题

Q1: Function Calling会增加API调用成本吗?

回答:会有一定增加,因为:

  1. 函数定义会占用输入tokens
  2. 可能需要多轮调用(识别意图 → 执行函数 → 生成回答)

优化建议:

  • 精简函数描述
  • 使用更便宜的模型(如gpt-4o-mini)
  • 缓存常见查询结果

Q2: Function Calling的准确率如何?

回答:主流大模型(GPT-4、Claude)的Function Calling准确率很高(>95%),但需要注意:

  • 函数描述要清晰
  • 参数类型要明确
  • 避免歧义

Q3: MCP现在成熟吗?

回答:MCP是比较新的标准(2024年推出),目前:

  • 协议规范已经稳定
  • 工具生态正在快速增长
  • 主要由Anthropic和社区推动

建议:

  • 学习阶段可以了解MCP
  • 小项目优先使用Function Calling
  • 企业项目考虑MCP的长期价值

Q4: 三种MCP传输方式该如何选择?

回答:

  • 开发测试: STDIO(最简单)
  • 需要推送: SSE(实时性好)
  • 生产环境: Streamable HTTP(扩展性好)

Q5: Function Calling和Agent有什么关系?

回答:

  • Function Calling是能力
  • Agent是应用模式

Agent通常使用Function Calling来:

  1. 调用工具
  2. 获取信息
  3. 执行操作

可以说:Function Calling是构建Agent的核心技术之一。

Q6: 如何确保MCP服务器的安全性?

最佳实践:

  1. 认证授权: 使用API Key或OAuth
  2. 参数验证: 严格验证输入参数
  3. 审计日志: 记录所有工具调用
  4. 速率限制: 防止滥用
  5. 网络隔离: 使用VPC或防火墙

十一、总结

核心要点

  1. Function Calling让AI能够调用函数,执行实际操作
  2. MCP提供了标准化的工具调用协议,促进生态发展
  3. MCP支持三种传输方式:STDIO(本地)、SSE(推送)、HTTP(云端)
  4. Function Calling适合快速开发和简单场景
  5. MCP适合工具复用和企业级应用
  6. 两者可以结合使用,发挥各自优势