AI Agent智能体与MCP Server开发实践

AI Agent智能体与MCP Server开发实践

基础概念

模型调用

模型调用就是通过API接口与大语言模型对话,就像你在微信上跟朋友聊天一样。你发送消息,模型回复你

typescript 复制代码
// 基础的大模型调用示例

const apiKey ='xxx'

async function callLLM(prompt: string) {
  try {
    const response = await fetch('https://api.openai.com/v1/chat/completions', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${apiKey}`
      },
      body: JSON.stringify({
        model: 'qwen-plus',
        messages: [
          {
            role: 'system',
            content: '你是一个有用的AI助手,请用中文回答用户的问题。'
          },
          {
            role: 'user',
            content: prompt
          }
        ],
        max_tokens: 1000,
        temperature: 0.7,
        response_format: { type: "json_object" }  //指定返回为json
      })
    });

    const data = await response.json();
    return data.choices[0].message.content;
  } catch (error) {
    console.error('调用大模型失败:', error);
    throw error;
  }
}

// messages 中 role 的类型说明:
// 1. 'system': 系统角色,用于设定AI的行为、风格、能力等,通常放在对话开始
// 2. 'user': 用户角色,表示用户输入的消息
// 3. 'assistant': 助手角色,表示AI的回复消息
// 4. 'function': 函数角色,用于函数调用的特殊消息类型(某些模型支持)
// 5. 'tool': 工具角色,用于工具调用的消息类型(某些模型支持)

// 使用示例
const response = await callLLM('你好,请介绍一下人工智能');
console.log(response);

重要参数说明:max_tokens控制回复的最大长度,数字越大回复越长;temperature控制创造性程度,0-1之间,0最保守,1最有创意;model选择哪个模型,不同模型能力不同。


提示词编写技巧

提示词就是你给大模型的"指令",就像你告诉朋友"帮我写一篇文章"一样。好的提示词能让模型更准确地理解你的需求。提示词的核心原则包括明确性(说清楚要什么,不要模棱两可)、具体性(给出具体的例子和要求)、结构化(要求输出特定的格式)和角色定位(给模型设定明确的身份)。

结构化输出

大模型默认输出的是自然语言,结构化输出就是让大模型输出特定的格式,比如JSON,而不是自然语言。这样做的好处是程序可以直接解析使用,数据格式统一,便于后续程序化的处理,减少人工处理。

typescript 复制代码
// 使用更严格的输出控制,确保获得可解析的数据
function createStrictPrompt(userCode: string): string {

  return `
      你是一个代码优化专家。请优化以下代码:

      ===code===
        function calculateTotal(items) {
                  let total = 0;
                  for (let i = 0; i < items.length; i++) {
                    total += items[i].price;
                  }
                  return total;
        }
      ===code===

      要求:
      1. 必须输出有效的JSON格式
      2. 不要添加任何其他文字
      3. 严格按照以下结构输出:

      {
        "originalCode": "这里返回原始代码",
        "optimizedCode": "完整的优化后代码",
        "changes": [
          {
            "type": "修改类型",
            "description": "修改描述",
            "before": "修改前",
            "after": "修改后"
          }
        ],
        "summary": "优化总结",
        "performance": "性能提升说明"
      }

      注意:JSON必须完全有效,不要有任何语法错误。
        `;
      }



const prompt = createStrictPrompt(userCode);
const response = await callLLM(prompt); // 大模型返回的是JSON格式的字符串
// 大模型返回的内容
/**
```json
{
  "originalCode": "function calculateTotal(items) {\n  let total = 0;\n  for (let i = 0; i < items.length; i++) {\n    total += items[i].price;\n  }\n  return total;\n}",
  "optimizedCode": "function calculateTotal(items) {\n  return items.reduce((total, item) => total + item.price, 0);\n}",
  "changes": [
    {
      "type": "重构",
      "description": "将传统的 for 循环替换为数组的 reduce 方法",
      "before": "for (let i = 0; i < items.length; i++) {\n  total += items[i].price;\n}",
      "after": "return items.reduce((total, item) => total + item.price, 0);"
    }
  ],
  "summary": "使用 reduce 方法简化代码逻辑,提高可读性和简洁性。",
  "performance": "减少手动循环控制,利用内置 reduce 方法提升执行效率,同时避免了显式索引管理和边界检查。"
}
```*/

**/
// 解析响应 - 将JSON字符串转换为JavaScript对象
console.log('大模型原始响应(字符串):', response);
try {
  // 提取 ```json 和 ``` 之间的内容
  const jsonBlockMatch = response.match(/```json\s*([\s\S]*?)\s*```/);
  if (jsonBlockMatch) {
    const jsonString = jsonBlockMatch[1].trim();
    console.log('提取的JSON字符串:', jsonString);
    
    const result = JSON.parse(jsonString);
    console.log('解析成功!');
    
    // 现在可以从解析后的对象中直接获取结构化数据
    const optimizedCode = result.optimizedCode;
    console.log('优化后的代码:', optimizedCode);
    console.log('性能提升:', result.performance);
    console.log('修改详情:', result.changes);
    
    // 处理换行符
    const formattedOriginalCode = result.originalCode.replace(/\\n/g, '\n');
    const formattedOptimizedCode = result.optimizedCode.replace(/\\n/g, '\n');
    console.log('格式化后的原始代码:');
    console.log(formattedOriginalCode);
    console.log('格式化后的优化代码:');
    console.log(formattedOptimizedCode);
  } else {
    console.error('未找到JSON代码块');
  }
} catch (error) {
  console.error('JSON解析失败:', error);
  console.log('原始响应:', response);
}

关键点说明:

  1. 大模型直接返回JSON: 通过明确的提示词要求,大模型会直接返回JSON格式的字符串
  2. 错误处理: 用 try-catch 处理JSON解析失败的情况
  • 确保返回的是有效的JSON格式
  • 减少解析错误的概率

提示词使用技巧

高级提示词技术能解决基础提示词效果不好的问题,比如模型理解不准确、输出格式不统一、回答质量不稳定、逻辑性不够强等。这些技术让模型输出更准确、更有用。

Chain of Thought (思维链)

思维链就是让模型像人一样,一步一步地思考问题,而不是直接给出答案。这样做的好处是思路更清晰,逻辑更完整,不容易遗漏,结果更可靠。适用场景包括复杂问题分析、需要详细推理、要求逻辑完整、避免跳跃性思维等。

typescript 复制代码
const chainOfThoughtPrompt = `
请按照以下步骤分析这个购物决策问题:

1. 首先明确购物需求
2. 列出可选的产品选项
3. 分析每个选项的优缺点
4. 考虑预算和实际需求
5. 做出最终选择并说明理由

题目:小明想买一台笔记本电脑,预算5000元,主要用于办公和学习,偶尔看看视频。请帮他分析选择。

请按照上述步骤分析,每一步都要详细说明。
`;

使用思维链的技巧是步骤要具体明确,每一步都要有要求,避免过于复杂的步骤,适合需要分析的问题。

Few-Shot Learning (少样本学习)

少样本学习就是给模型几个例子,让它学会做类似的事情。这样做的好处是模型理解更准确,输出格式更统一,减少提示词长度,提高成功率。适用场景包括分类任务、格式转换、风格模仿、标准化输出等。

typescript 复制代码
const fewShotPrompt = `
请根据以下示例,完成类似的文本分类任务:

示例1:
输入:这部电影真的很棒,演员表演出色,剧情引人入胜。
输出:正面评价

示例2:
输入:剧情拖沓,演员演技一般,特效也很差。
输出:负面评价

现在请分类以下文本:
输入:这部电影还可以,不算太差但也没什么亮点。
输出:
`;

使用少样本学习的技巧是例子要典型,格式要一致,例子数量适中(2-5个),避免过于复杂的例子。


Function Call

Function Call就是让大模型能够"打电话"给你的程序,调用你预先写好的函数。就像你告诉朋友"帮我查一下北京的天气",朋友不会查,但会告诉你"我可以帮你查,需要知道具体日期"。 Function Call让模型知道它能调用什么函数,以及怎么调用。这样做的好处是模型能做更多事情,可以获取实时数据,能执行具体操作,扩展性更强。

适用场景 工具调用: 需要执行特定功能的场景 API集成: 与外部服务交互 工作流自动化: 需要按步骤执行的任务 参数验证: 对输入参数有严格要求

typescript 复制代码
// 定义可调用的函数
const availableFunctions = {
  getWeather: {
    description: "获取指定城市的天气信息",
    parameters: {
      type: "object",
      properties: {
        city: {
          type: "string",
          description: "城市名称"
        },
        date: {
          type: "string",
          description: "日期,格式:YYYY-MM-DD"
        }
      },
      required: ["city"]
    }
  },
  calculateDistance: {
    description: "计算两个城市之间的距离",
    parameters: {
      type: "object",
      properties: {
        city1: {
          type: "string",
          description: "起始城市"
        },
        city2: {
          type: "string",
          description: "目标城市"
        }
      },
      required: ["city1", "city2"]
    }
  }
};

// 调用大模型并处理 Function Call
async function callLLMWithFunctions(prompt: string, functions: any) {
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
    },
    body: JSON.stringify({
      model: 'gpt-3.5-turbo',
      messages: [
        {
          role: 'user',
          content: '帮我查询北京今天的天气'
        }
      ],
      functions: functions,
      function_call: 'auto'
    })
  });

  const data = await response.json();
  const message = data.choices[0].message;
  
  // 检查是否需要调用函数
  if (message.function_call) {
    const functionName = message.function_call.name;
    const functionArgs = JSON.parse(message.function_call.arguments);
    
    // 执行对应的函数
    const result = await executeFunction(functionName, functionArgs);
    
    // 将函数结果发送回模型
    return await sendFunctionResult(message, result);
  }
  
  return message.content;
}

// 执行函数的示例
async function executeFunction(functionName: string, args: any) {
  switch (functionName) {
    case 'getWeather':
      return await getWeatherData(args.city, args.date);
    case 'calculateDistance':
      return await calculateCityDistance(args.city1, args.city2);
    default:
      throw new Error(`未知函数: ${functionName}`);
  }
}

Tool Call

Tool Call是Function Call的升级版本,让大模型能够调用多个工具,并且可以并行执行。

Function Call与MCP协议

技术演进关系

Function Call和MCP协议代表了AI工具调用技术的不同发展阶段,它们既有技术继承关系,又有根本性的差异。

发展时间线
scss 复制代码
Function Call (2023年) → Tool Call (2024年) → MCP (2024年11月)
  • Function Call:OpenAI在2023年推出的基础功能
  • Tool Call:OpenAI在2024年对Function Call的升级
  • MCP:Anthropic在2024年11月推出的标准化协议
技术继承关系
scss 复制代码
Function Call (基础概念)
    ↓
Tool Call (功能扩展)
    ↓
MCP (协议标准化)

📊 功能对比分析

1. Function Call (OpenAI基础版)
typescript 复制代码
// OpenAI Function Call示例
const functions = [
  {
    name: "get_weather",
    description: "获取天气信息",
    parameters: {
      type: "object",
      properties: {
        city: { type: "string" }
      },
      required: ["city"]
    }
  }
];

// 调用方式
const response = await openai.chat.completions.create({
  model: "gpt-4",
  messages: [{ role: "user", content: "北京天气怎么样?" }],
  functions: functions,
  function_call: "auto"
});

特点:

  • 只能调用一个函数
  • 模型自动选择函数
  • 简单的参数传递
  • 基础的工具调用能力
2. Tool Call (OpenAI升级版)
typescript 复制代码
// OpenAI Tool Call示例
const tools = [
  {
    type: "function",
    function: {
      name: "get_weather",
      description: "获取天气信息",
      parameters: {
        type: "object",
        properties: {
          city: { type: "string" }
        },
        required: ["city"]
      }
    }
  },
  {
    type: "function",
    function: {
      name: "get_news",
      description: "获取新闻",
      parameters: {
        type: "object",
        properties: {
          topic: { type: "string" }
        },
        required: ["topic"]
      }
    }
  }
];

// 调用方式
const response = await openai.chat.completions.create({
  model: "gpt-4",
  messages: [{ role: "user", content: "北京天气和科技新闻" }],
  tools: tools,
  tool_choice: "auto"
});

特点:

  • 支持多个工具并行调用
  • 更灵活的工具选择
  • 增强的参数验证
  • 更好的错误处理
3. MCP (Anthropic标准化协议)
typescript 复制代码
// MCP协议示例
const mcpRequest = {
  jsonrpc: "2.0",
  id: 1,
  method: "tools/call",
  params: {
    name: "get_weather",
    arguments: {
      city: "北京"
    }
  }
};

// 通过MCP客户端调用
const response = await mcpClient.callTool("get_weather", { city: "北京" });

特点:

  • 完全标准化的协议
  • 支持多种传输方式
  • 丰富的工具类型
  • 完整的生态系统

🔄 技术架构对比

1. Function Call架构
javascript 复制代码
用户 → OpenAI API → Function → 结果
2. Tool Call架构
复制代码
用户 → OpenAI API → Tools → 并行执行 → 结果
3. MCP架构
复制代码
用户 → AI模型 → MCP客户端 → MCP服务器 → 工具 → 结果

核心差异分析

1. 标准化程度

Function Call/Tool Call:

  • OpenAI专有技术
  • 绑定OpenAI生态系统
  • 协议不开放
  • 厂商锁定风险

MCP:

  • 完全开源标准
  • 厂商无关
  • 协议开放透明
  • 避免厂商锁定
2. 功能范围

Function Call/Tool Call:

  • 仅支持函数调用
  • 功能相对简单
  • 扩展性有限
  • 主要针对OpenAI模型

MCP:

  • 支持工具、资源、提示词
  • 功能更加丰富
  • 高度可扩展
  • 支持多种AI模型
3. 生态系统

Function Call/Tool Call:

  • OpenAI控制
  • 工具相对集中
  • 集成成本较高
  • 依赖OpenAI基础设施

MCP:

  • 社区驱动
  • 工具生态丰富
  • 集成成本较低
  • 独立的基础设施

实际使用关系

1. 可以同时使用
typescript 复制代码
class HybridAIAgent {
  private openaiClient: OpenAIClient;
  private mcpClient: MCPClient;
  
  async handleUserRequest(userInput: string) {
    // 1. 使用OpenAI Tool Call处理简单任务
    if (this.isSimpleTask(userInput)) {
      return await this.handleWithOpenAI(userInput);
    }
    
    // 2. 使用MCP处理复杂任务
    if (this.isComplexTask(userInput)) {
      return await this.handleWithMCP(userInput);
    }
    
    // 3. 混合使用
    return await this.handleHybrid(userInput);
  }
  
  private async handleHybrid(userInput: string) {
    // 先用OpenAI分析意图
    const intent = await this.openaiClient.analyzeIntent(userInput);
    
    // 再用MCP执行具体任务
    const result = await this.mcpClient.callTool(intent.toolName, intent.params);
    
    // 最后用OpenAI生成回复
    return await this.openaiClient.generateResponse(result);
  }
}
1. 使用Function Call/Tool Call的场景
  • 项目完全基于OpenAI生态
  • 只需要简单的函数调用
  • 对标准化要求不高
  • 快速原型开发
  • 短期项目需求
2. 使用MCP的场景
  • 需要跨平台兼容性
  • 复杂的工具集成需求
  • 长期项目维护
  • 开源生态建设
  • 避免厂商锁定
  • 多模型支持需求
3. 混合使用的场景
  • 渐进式迁移
  • 多模型支持
  • 复杂业务场景

📝 总结

Function Call和MCP协议的关系可以概括为:

  1. 技术演进:Function Call → Tool Call → MCP
  2. 标准化程度:专有技术 → 开放标准
  3. 功能范围:简单函数调用 → 完整工具生态
  4. 使用方式:可以独立使用,也可以混合使用
  5. 发展方向:MCP代表了更开放、更标准化的未来

建议:新项目优先考虑MCP,现有OpenAI项目可以逐步迁移,或者采用混合架构来获得最佳效果。MCP的开源特性和标准化设计使其成为AI工具集成的长期发展方向。

简单示例
typescript 复制代码
// 定义两个简单的工具
const tools = [
  {
    type: "function",
    function: {
      name: "getWeather",
      description: "获取天气信息",
      parameters: {
        type: "object",
        properties: {
          city: { type: "string", description: "城市名称" }
        },
        required: ["city"]
      }
    }
  },
  {
    type: "function",
    function: {
      name: "getNews",
      description: "获取新闻",
      parameters: {
        type: "object",
        properties: {
          topic: { type: "string", description: "新闻主题" }
        },
        required: ["topic"]
      }
    }
  }
];

// 工具执行函数
async function executeTool(toolName: string, args: any) {
  switch (toolName) {
    case 'getWeather':
      // 模拟天气API调用
      return {
        city: args.city,
        temperature: '25°C',
        condition: '晴天',
        humidity: '60%'
      };
    
    case 'getNews':
      // 模拟新闻API调用
      return {
        topic: args.topic,
        news: [
          { title: `${args.topic}相关新闻1`, summary: '这是第一条新闻' },
          { title: `${args.topic}相关新闻2`, summary: '这是第二条新闻' }
        ]
      };
    
    default:
      throw new Error(`未知工具: ${toolName}`);
  }
}

// 调用API
const response = await fetch('https://api.openai.com/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
  },
  body: JSON.stringify({
    model: 'gpt-4',
    messages: [{ role: 'user', content: '北京今天天气怎么样?有什么科技新闻?' }],
    tools: tools,
    tool_choice: 'auto'
  })
});

const data = await response.json();
const message = data.choices[0].message;

// 检查是否有工具调用
if (message.tool_calls) {
  console.log('模型要调用工具:', message.tool_calls);
  
  // 执行所有工具调用
  const toolResults = await Promise.all(
    message.tool_calls.map(async (toolCall) => {
      const toolName = toolCall.function.name;
      const toolArgs = JSON.parse(toolCall.function.arguments);
      
      console.log(`执行工具: ${toolName},参数:`, toolArgs);
      
      const result = await executeTool(toolName, toolArgs);
      
      return {
        tool_call_id: toolCall.id,
        role: 'tool',
        content: JSON.stringify(result)
      };
    })
  );
  
  // 将结果发送回模型获取最终回答
  const finalResponse = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
    },
    body: JSON.stringify({
      model: 'gpt-4',
      messages: [
        { role: 'user', content: '北京今天天气怎么样?有什么科技新闻?' },
        message,
        ...toolResults
      ]
    })
  });
  
  const finalData = await finalResponse.json();
  console.log('最终回答:', finalData.choices[0].message.content);
}

深入理解MCP服务

在本节中,我们将学习如何开发一个MCP服务,从MCP开发视角,来看如何搭建一个完整的AI智能体应用

📋 MCP协议介绍

MCP,即模型上下文协议(Model Context Protocol),是 Anthropic Claude 的一个开源开放协议,旨在建立 AI 模型和开发环境之间的统一上下文交互,通过提供标准化的上下文信息访问,使 AI 模型能够更好地理解和处理代码。就像给它们之间搭建了一座桥梁,使得开发者可以通过一套标准将 AI 应用和数据源连接起来

MCP能做什么?

  • 调用工具:执行具体的任务(创建文档、发送邮件、查询数据)

  • 读取资源:获取各种信息(文档内容、图片、数据库记录)

  • 使用模板:基于预设的提示词生成内容(写报告、做总结)

  • 标准化:所有MCP工具都遵循相同的通信规则

  • JSON格式:使用人类可读的JSON格式进行数据交换

  • RPC风格:采用请求-响应的通信模式,就像远程调用函数一样

  • 类型安全:通过JSON Schema定义参数格式,确保数据正确性

核心组件架构

MCP系统有三个主要角色:

  1. MCP服务器:提供各种工具和资源的"商店"
  2. MCP客户端:AI智能体用来连接服务器的"连接器"
  3. MCP Host端:宿主运行环境。通常是指mcp的应用,比如AI智能体"
graph TD subgraph "应用程序主机进程 (Application Host Process)" A[主机 - Host] --> B[客户端1 - Client 1] A --> C[客户端2 - Client 2] A --> D[客户端3 - Client 3] end subgraph "本地机器 (Local Machine)" E[服务器1 - 文件 & Git] --> F[本地资源A - 文件系统] G[服务器2 - 数据库] --> H[本地资源B - 数据库] end subgraph "互联网 (Internet)" I[服务器3 - 外部API] --> J[远程资源C - 云服务] end B --> E C --> G D --> I F -.->|读写访问| E H -.->|读写访问| G J -.->|读写访问| I style A fill:#ffeb3b style B fill:#e1f5fe style C fill:#e1f5fe style D fill:#e1f5fe style E fill:#c8e6c9 style G fill:#c8e6c9 style I fill:#ffccbc style F fill:#f3e5f5 style H fill:#f3e5f5 style J fill:#f3e5f5

架构说明:

  1. 主机 (Host):AI智能体的核心控制器,负责:

    • 分析用户意图
    • 选择合适的客户端
    • 协调各个组件的交互
  2. 客户端 (Clients):不同类型的MCP客户端,包括:

    • 客户端1:连接本地文件系统和Git服务
    • 客户端2:连接本地数据库服务
    • 客户端3:连接外部互联网服务
  3. MCP服务端: 开放的API能力对外提供,比如

    • 文件务:管理文件操作
    • 数据库服务:处理数据存储和查询
  4. 互联网服务

    • 外部API服务:集成第三方云服务和在线资源
  5. 资源层

    • 本地资源:文件系统、数据库等本地存储
    • 远程资源:云数据库、在线文件存储等远程服务

通信流程

sequenceDiagram participant AI as AI智能体 participant MCP as MCP客户端 participant Server as MCP服务器 participant Tool as 具体工具 AI->>MCP: 用户请求 MCP->>Server: 1. 获取工具列表 Server-->>MCP: 返回可用工具 MCP->>Server: 2. 调用指定工具 Server->>Tool: 执行工具功能 Tool-->>Server: 返回执行结果 Server-->>MCP: 返回工具结果 MCP-->>AI: 生成友好回复
消息格式示例

1. 获取提示词模板列表

请求:

json 复制代码
{
  "jsonrpc": "2.0",
  "id": 6,
  "method": "prompts/list",
  "params": {}
}

响应:

json 复制代码
{
  "jsonrpc": "2.0",
  "id": 6,
  "result": [
    {
      "name": "weather_report",
      "description": "生成天气报告",
      "arguments": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string",
            "description": "城市名称"
          },
          "format": {
            "type": "string",
            "description": "报告格式",
            "enum": ["简洁", "详细"]
          }
        },
        "required": ["city"]
      }
    }
  ]
}

2. 执行提示词模板

请求:

json 复制代码
{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "prompts/execute",
  "params": {
    "name": "weather_report",
    "arguments": {
      "city": "北京",
      "format": "详细"
    }
  }
}

响应:

json 复制代码
{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "北京今日天气报告:\n\n当前温度:25°C\n天气状况:晴天\n湿度:60%\n风力:微风\n能见度:10km\n气压:1013hPa\n\n天气适宜外出活动,建议适当防晒。"
      }
    ]
  }
}

错误响应示例

当工具不存在时的错误响应:

json 复制代码
{
  "jsonrpc": "2.0",
  "id": 3,
  "error": {
    "code": -1001,
    "message": "工具不存在",
    "data": {
      "toolName": "get_weather"
    }
  }
}
完整的MCP交互流程示例

步骤1:建立连接

json 复制代码
// 客户端发送初始化请求
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "tools": {},
      "resources": {},
      "prompts": {}
    },
    "clientInfo": {
      "name": "ai-coder",
      "version": "1.0.0"
    }
  }
}

// 服务器响应
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "tools": {
        "listChanged": true
      },
      "resources": {
        "listChanged": true
      },
      "prompts": {
        "listChanged": true
      }
    },
    "serverInfo": {
      "name": "weather-service",
      "version": "1.0.0"
    }
  }
}

步骤2:获取可用工具

json 复制代码
// 客户端请求工具列表
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list",
  "params": {}
}

// 服务器返回工具列表
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": [
    {
      "name": "get_weather",
      "description": "获取指定城市的天气信息",
      "inputSchema": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string",
            "description": "城市名称"
          }
        },
        "required": ["city"]
      }
    },
    {
      "name": "get_forecast",
      "description": "获取指定城市的天气预报",
      "inputSchema": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string",
            "description": "城市名称"
          },
          "days": {
            "type": "integer",
            "description": "预报天数",
            "minimum": 1,
            "maximum": 7
          }
        },
        "required": ["city"]
      }
    }
  ]
}

步骤3:调用具体工具

json 复制代码
// 客户端调用天气工具
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "city": "北京"
    }
  }
}

// 服务器返回天气数据
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "city": "北京",
    "temperature": "25°C",
    "condition": "晴天",
    "humidity": "60%",
    "wind": "微风",
    "visibility": "10km",
    "pressure": "1013hPa"
  }
}

步骤4:获取资源信息

json 复制代码
// 客户端请求资源列表
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "resources/list",
  "params": {}
}

// 服务器返回资源列表
{
  "jsonrpc": "2.0",
  "id": 4,
  "result": [
    {
      "uri": "mcp://weather-service/current/beijing",
      "name": "北京当前天气",
      "description": "北京地区的实时天气信息",
      "mimeType": "application/json"
    },
    {
      "uri": "mcp://weather-service/forecast/beijing",
      "name": "北京天气预报",
      "description": "北京地区的7天天气预报",
      "mimeType": "application/json"
    }
  ]
}

步骤5:读取具体资源

json 复制代码
// 客户端读取天气资源
{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "resources/read",
  "params": {
    "uri": "mcp://weather-service/current/beijing"
  }
}

// 服务器返回资源内容
{
  "jsonrpc": "2.0",
  "id": 5,
  "result": {
    "contents": [
      {
        "uri": "mcp://weather-service/current/beijing",
        "mimeType": "application/json",
        "text": "{\"city\":\"北京\",\"temperature\":\"25°C\",\"condition\":\"晴天\"}"
      }
    ]
  }
}
🛠️ MCP Server工具类型

MCP支持三种主要类型:

1. 工具 (Tools)
  • 作用:执行具体的操作
  • 例子:创建文档、发送邮件、查询天气
  • 特点:有输入参数,返回执行结果
2. 资源 (Resources)
  • 作用:提供各种信息
  • 例子:文档内容、图片、数据库记录
  • 特点:可以读取,但不能修改
3. 提示词模板 (Prompts)
  • 作用:指导AI生成特定类型的内容
  • 例子:写技术文档、做数据分析报告
  • 特点:基于模板生成,格式统一
💡 实际应用场景

场景1:智能客服

  • 用户问:"我的订单什么时候发货?"
  • AI通过MCP调用订单查询工具
  • 返回具体的发货时间和物流信息

场景2:技术文档助手

  • 用户说:"帮我写一个React教程"
  • AI通过MCP调用文档生成模板
  • 生成结构化的React学习教程
🔧 开发MCP服务器的简单步骤
第1步:定义你的工具
typescript 复制代码
// 定义一个简单的天气查询工具
const weatherTool = {
  name: "get_weather",
  description: "查询指定城市的天气信息",
  inputSchema: {
    type: "object",
    properties: {
      city: { 
        type: "string", 
        description: "城市名称" 
      }
    },
    required: ["city"]
  }
};
第2步:实现工具功能
typescript 复制代码
// 实现天气查询功能
async function getWeather(city: string) {
  // 模拟天气数据
  const weatherData = {
    '北京': { temperature: '25°C', condition: '晴天', humidity: '60%' },
    '上海': { temperature: '28°C', condition: '多云', humidity: '70%' },
    '广州': { temperature: '30°C', condition: '小雨', humidity: '80%' }
  };
  
  return weatherData[city] || { temperature: '未知', condition: '未知', humidity: '未知' };
}
第3步:创建简单的MCP服务器
typescript 复制代码
import express from 'express';

const app = express();
app.use(express.json());

// 存储工具
const tools = new Map();

// 注册工具
function registerTool(name: string, handler: Function, schema: any) {
  tools.set(name, { handler, schema });
}

// 注册天气工具
registerTool('get_weather', getWeather, weatherTool.inputSchema);

// MCP工具列表接口
app.get('/tools', (req, res) => {
  const toolsList = Array.from(tools.keys()).map(name => ({
    name,
    description: tools.get(name).schema.description || '',
    inputSchema: tools.get(name).schema
  }));
  res.json(toolsList);
});

// MCP工具调用接口
app.post('/tools/call', async (req, res) => {
  try {
    const { name, arguments: args } = req.body;
    
    if (!tools.has(name)) {
      return res.status(400).json({ error: `工具不存在: ${name}` });
    }
    
    const tool = tools.get(name);
    const result = await tool.handler(args.city);
    
    res.json({
      content: [
        {
          type: 'text',
          text: `${args.city}天气:温度${result.temperature},${result.condition},湿度${result.humidity}`
        }
      ]
    });
    
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 启动服务器
app.listen(3000, () => {
  console.log('MCP服务器已启动: http://localhost:3000');
  console.log('可用工具:', Array.from(tools.keys()));
});
📚 学习建议

初学者路径

  1. 先理解MCP的基本概念(工具、资源、模板)
  2. 学习如何调用现成的MCP工具
  3. 尝试开发一个简单的MCP工具
  4. 逐步构建完整的MCP服务器

进阶方向

  1. 深入学习MCP协议规范
  2. 开发复杂的工具和资源
  3. 优化性能和安全性
  4. 参与MCP生态建设
MCP client端

现在,我们来实现一个简单的MCP客户端,用于连接MCP服务器并调用其中的工具。

typescript 复制代码
// MCP客户端配置
interface MCPClientConfig {
  serverUrl: string;
  apiKey: string;
  timeout?: number;
}

// MCP工具调用请求
interface MCPToolCallRequest {
  toolName: string;
  parameters: Record<string, any>;
}

// MCP客户端类
class MCPClient {
  private config: MCPClientConfig;
  
  constructor(config: MCPClientConfig) {
    this.config = config;
  }
  
  // 调用MCP工具的方法
  async callTool(toolCallRequest: MCPToolCallRequest): Promise<any> {
    try {
      const response = await fetch(`${this.config.serverUrl}/api/tool/call`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(toolCallRequest),
        timeout: this.config.timeout || 30000
      });
      
      if (!response.ok) {
        throw new Error(`MCP调用失败: ${response.status} ${response.statusText}`);
      }
      
      return await response.json();
    } catch (error) {
      console.error('MCP工具调用错误:', error);
      throw error;
    }
  }
  
  // 列出可用的工具
  async listAvailableTools(): Promise<any> {
    try {
      const response = await fetch(`${this.config.serverUrl}/api/tools`, {
        method: 'GET',
        headers: {
          'Authorization': `Bearer ${this.config.apiKey}`
        }
      });
      
      return await response.json();
    } catch (error) {
      console.error('获取工具列表失败:', error);
      throw error;
    }
  }
}

// 使用MCP客户端处理请求
async function handleWithMCPClient(userQuery: string, toolName: string): Promise<string> {
  // 创建MCP客户端实例
  const mcpClient = new MCPClient({
    serverUrl: 'https://mcp-server.example.com', // 替换为实际的MCP服务器地址
  });
  
  try {
    // 解析用户查询,提取参数
    const parameters = extractParameters(userQuery, toolName);
    
    // 调用MCP工具
    const result = await mcpClient.callTool({
      toolName: toolName,
      parameters: parameters,
  
    });
    
    // 格式化结果为友好的回复
    return formatMCPResult(result, toolName);
  } catch (error) {
    console.error('MCP处理失败:', error);
    // 失败时回退到直接使用大模型
    return await callLLM(userQuery);
  }
}

// 从用户查询中提取参数
function extractParameters(userQuery: string, toolName: string): Record<string, any> {
  const params: Record<string, any> = {};
  
  // 简单的参数提取逻辑
  switch (toolName) {
    case 'weather_tool':
      // 提取城市名
      const cityMatch = userQuery.match(/(北京|上海|广州|深圳|杭州|成都|武汉|西安|重庆|天津)/);
      if (cityMatch) {
        params.city = cityMatch[0];
      } else {
        params.city = '北京'; // 默认值
      }
      break;
      
    case 'news_tool':
      // 提取新闻主题
      const topicMatch = userQuery.match(/(科技|体育|娱乐|财经|政治)/);
      if (topicMatch) {
        params.topic = topicMatch[0];
      } else {
        params.topic = '科技'; // 默认值
      }
      break;
  }
  
  return params;
}

// 格式化MCP结果
function formatMCPResult(result: any, toolName: string): string {
  switch (toolName) {
    case 'weather_tool':
      return `\n当前${result.city}的天气情况:\n- 温度:${result.temperature}\n- 天气状况:${result.condition}\n- 湿度:${result.humidity}\n- 风力:${result.wind}`;
      
    case 'news_tool':
      let newsSummary = `\n最新${result.topic}新闻:\n`;
      result.news.forEach((item: any, index: number) => {
        newsSummary += `${index + 1}. ${item.title}\n   ${item.summary}\n`;
      });
      return newsSummary;
      
    default:
      return JSON.stringify(result, null, 2);
  }
}
完整智能体示例

下面是一个整合了以上所有功能的完整智能体示例:

typescript 复制代码
// 完整的智能体应用
class SimpleAIAgent {
  private mcpClient: MCPClient;
  
  constructor(mcpConfig: MCPClientConfig) {
    this.mcpClient = new MCPClient(mcpConfig);
  }
  
  // 启动智能体
  async start() {
    console.log('AI智能体已启动,等待用户输入...');
    
    // 这里可以添加实际的用户界面或API接口
    // 为了演示,我们使用几个测试问题
    const testQueries = [
      '北京今天天气怎么样?',
      '有什么最新的科技新闻?',
      '如何用JavaScript实现一个简单的计算器?',
      '你能给我讲个笑话吗?'
    ];
    
    // 处理每个测试问题
    for (const query of testQueries) {
      console.log(`\n用户问题: ${query}`);
      const response = await this.handleUserQuery(query);
      console.log(`智能体回复: ${response}`);
    }
  }
  
  // 处理用户查询
  async handleUserQuery(userQuery: string): Promise<string> {
    // 1. 分析用户意图
    const intent = await analyzeUserIntent(userQuery);
    console.log(`识别到的意图: ${intent}`);
    
    // 2. 根据意图选择处理方式
    switch (intent) {
      case 'weather_query':
        return await this.handleWithMCPClient(userQuery, 'weather_tool');
      case 'news_query':
        return await this.handleWithMCPClient(userQuery, 'news_tool');
      case 'code_help':
        return await handleCodeHelp(userQuery);
      default:
        // 直接使用大模型回复
        return await callLLM(userQuery);
    }
  }
  
  // 使用MCP客户端处理请求
  private async handleWithMCPClient(userQuery: string, toolName: string): Promise<string> {
    try {
      // 解析参数
      const parameters = extractParameters(userQuery, toolName);
      
      // 调用MCP工具
      const result = await this.mcpClient.callTool({
        toolName: toolName,
        parameters: parameters,
      });
      
      // 格式化结果
      return formatMCPResult(result, toolName);
    } catch (error) {
      console.error('MCP处理失败,回退到直接调用大模型:', error);
      return await callLLM(userQuery);
    }
  }
}

// 使用示例
async function runExample() {
  // 创建并启动智能体
  const agent = new SimpleAIAgent({
    serverUrl: 'https://mcp-server.example.com',
    apiKey: 'your-mcp-api-key'
  });
  
  await agent.start();
}

// 运行示例
runExample().catch(console.error);

ai智能体工作流程

主流程图

graph TD A[用户发送消息] --> B[意图分析 LLM调用#1] B --> C[意图分类结果] C --> D[意图处理] D --> E{意图类型} E -->|代码操作| F[代码处理 LLM调用#2] E -->|代码帮助| G[帮助处理 LLM调用#2] E -->|工具调用| H[工具选择 LLM调用#2] E -->|一般对话| I[对话处理 LLM调用#2] H --> H1[解析工具选择结果] H1 --> H2{工具类型判断} H2 -->|prompt:模板名| H3[Prompt模板执行] H2 -->|tool:工具名| H4[MCP工具执行] H3 --> H5[Prompt执行结果] H4 --> H6[MCP工具执行结果] H5 --> H7[结果解释 LLM调用#3] H6 --> H7 H7 --> H8[生成最终回复] F --> F1[代码变更生成] F1 --> F2[Function Call解析] F2 --> F3[代码操作响应] G --> L[生成响应] H8 --> L I --> L F3 --> L L --> M[渲染结果] style A fill:#e1f5fe style M fill:#c8e6c9 style B fill:#fff3e0 style F fill:#fff3e0 style G fill:#fff3e0 style H fill:#fff3e0 style I fill:#fff3e0 style H7 fill:#fff3e0 style F1 fill:#fff3e0
  • 分层调用: 不同阶段使用不同的 LLM 调用策略
  • 上下文传递: 每次调用都会传递相关的上下文信息
  • 工具类型区分: 明确区分Prompt模板和MCP工具的执行方式
  • 结果缓存: 避免重复的 LLM 调用
  • 降级处理: 当 LLM 调用失败时的备选方案

总结

通过本教程,我们学习了如何构建一个完整的AI智能体系统,包括:

  1. 基础概念: 模型调用、提示词编写、Function Call、Tool Call等
  2. MCP协议: 协议规范、服务器端实现、客户端开发
  3. 意图分析: 使用大模型进行智能意图识别
  4. 工具调用: 集成MCP工具和Prompt模板
  5. 流程设计: 多次LLM调用的优化策略
  6. 错误处理: 完善的降级和容错机制
相关推荐
瓦香钵钵鸡6 小时前
机器学习通关秘籍|Day 05:过拟合和欠拟合、正则化、岭回归、拉索回归、逻辑回归、Kmeans聚类
人工智能·机器学习·回归·逻辑回归·k-means·过拟合
技术与健康6 小时前
【系列12】端侧AI:构建与部署高效的本地化AI模型 第11章:边缘设备与IoT部署
人工智能·物联网
Ronin-Lotus7 小时前
深度学习篇---ShuffleNet网络结构
人工智能·深度学习
yueyuebaobaoxinx7 小时前
AI + 机器人:当大语言模型赋予机械 “思考能力”,未来工厂将迎来怎样变革?
人工智能·语言模型·机器人
OEC小胖胖7 小时前
构建单页应用:React Router v6 核心概念与实战
前端·react.js·前端框架·web
ss2737 小时前
手写MyBatis第46弹:多插件责任链模式的实现原理与执行顺序奥秘--MyBatis插件架构深度解析
前端·javascript·html
scott1985127 小时前
世界模型的典型框架与分类
人工智能·计算机视觉·生成式·世界模型
VIP_CQCRE7 小时前
身份证识别及信息核验 API 对接说明
java·前端·数据库
LeeZhao@7 小时前
【项目】多模态RAG必备神器—olmOCR重塑PDF文本提取格局
人工智能·语言模型·自然语言处理·pdf·aigc