update at 2025-10-15 15:07:45
This commit is contained in:
512
docs/mcp-tool-calling-example.md
Normal file
512
docs/mcp-tool-calling-example.md
Normal file
@@ -0,0 +1,512 @@
|
||||
# MCP 工具调用完整示例
|
||||
|
||||
## 概述
|
||||
|
||||
本文档展示 Cherry Studio 架构风格的 MCP 工具调用流程,通过"发布小红书文章"的实际例子,详细说明 AI 如何理解用户意图、生成内容、并自动调用 MCP 工具。
|
||||
|
||||
## 实现架构
|
||||
|
||||
### 核心流程
|
||||
|
||||
```
|
||||
用户输入
|
||||
↓
|
||||
获取 MCP 工具 (带服务器名称前缀)
|
||||
↓
|
||||
添加 System Prompt (指导 AI 使用工具)
|
||||
↓
|
||||
AI 理解意图 + 生成内容
|
||||
↓
|
||||
AI 调用工具 (OpenAI Function Calling)
|
||||
↓
|
||||
解析工具名称 (serverName__toolName)
|
||||
↓
|
||||
执行 MCP 工具
|
||||
↓
|
||||
工具结果返回
|
||||
↓
|
||||
AI 生成友好回复
|
||||
```
|
||||
|
||||
### 关键创新点
|
||||
|
||||
1. **工具名称前缀**: `serverName__toolName` 格式避免多服务器工具名冲突
|
||||
2. **System Prompt**: 详细的工具使用指南,让 AI 理解如何创作和调用
|
||||
3. **参数自动注入**: AI 根据用户意图自动生成完整参数
|
||||
4. **多轮对话**: 支持工具结果继续对话
|
||||
|
||||
## 完整示例:发布小红书文章
|
||||
|
||||
### 用户输入
|
||||
|
||||
```
|
||||
用户: 帮我发布小红书文章,内容是:如何制作一道酸菜鱼
|
||||
```
|
||||
|
||||
### 步骤 1: 获取 MCP 工具
|
||||
|
||||
假设连接了名为 `xiaohongshu` 的 MCP 服务器,提供以下工具:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "public_content",
|
||||
"description": "发布内容到小红书平台",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "文章标题,吸引眼球且相关"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "文章正文,Markdown 格式"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"description": "标签列表,3-5个",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "分类,如美食、生活、旅游等"
|
||||
}
|
||||
},
|
||||
"required": ["title", "content", "tags", "category"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 2: 转换为 OpenAI 格式(带前缀)
|
||||
|
||||
```typescript
|
||||
// chatService.ts - convertToolsToOpenAIFormat()
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'xiaohongshu__public_content', // 添加服务器前缀
|
||||
description: '发布内容到小红书平台',
|
||||
parameters: { ...inputSchema }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 3: 生成 System Prompt
|
||||
|
||||
```typescript
|
||||
// chatService.ts - createSystemPromptWithTools()
|
||||
你是一个智能助手,可以使用以下工具完成任务:
|
||||
|
||||
• xiaohongshu__public_content
|
||||
描述: 发布内容到小红书平台
|
||||
参数:
|
||||
- title [必填]: 文章标题,吸引眼球且相关
|
||||
- content [必填]: 文章正文,Markdown 格式
|
||||
- tags [必填]: 标签列表,3-5个
|
||||
- category [必填]: 分类,如美食、生活、旅游等
|
||||
|
||||
使用指南:
|
||||
1. 当用户需要完成某个任务时,请分析哪个工具最合适
|
||||
2. 如果需要发布内容(如文章、笔记等),请根据用户意图创作完整的内容
|
||||
3. 为内容生成合适的标题、正文、标签等所有必需参数
|
||||
4. 自动调用相应工具,将生成的内容作为参数传递
|
||||
5. 根据工具执行结果,给用户友好的反馈
|
||||
|
||||
注意事项:
|
||||
- 保持内容质量和平台特色
|
||||
- 标签要相关且有吸引力
|
||||
- 分类要准确
|
||||
- 如果工具执行失败,给出明确的错误说明和建议
|
||||
|
||||
当前连接的 MCP 服务器: xiaohongshu
|
||||
```
|
||||
|
||||
### 步骤 4: 发送请求到 LLM
|
||||
|
||||
```typescript
|
||||
// modelServiceManager.ts - sendChatRequestStream()
|
||||
const request = {
|
||||
model: 'gpt-4',
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: '你是一个智能助手,可以使用以下工具...' // System Prompt
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: '帮我发布小红书文章,内容是:如何制作一道酸菜鱼'
|
||||
}
|
||||
],
|
||||
tools: [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'xiaohongshu__public_content',
|
||||
description: '发布内容到小红书平台',
|
||||
parameters: { ... }
|
||||
}
|
||||
}
|
||||
],
|
||||
tool_choice: 'auto',
|
||||
stream: true
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 5: AI 理解 + 生成内容 + 调用工具
|
||||
|
||||
AI 响应(SSE 流式返回):
|
||||
|
||||
```json
|
||||
// 第一部分:AI 思考过程(可选)
|
||||
{
|
||||
"choices": [{
|
||||
"delta": {
|
||||
"content": "好的,我来帮你创作一篇关于酸菜鱼制作的小红书文章并发布。"
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
// 第二部分:工具调用
|
||||
{
|
||||
"choices": [{
|
||||
"delta": {
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_abc123",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "xiaohongshu__public_content",
|
||||
"arguments": {
|
||||
"title": "🐟 超详细!家常酸菜鱼做法,10分钟学会!",
|
||||
"content": "# 酸菜鱼制作教程\n\n## 所需食材\n- 草鱼1条(约1.5kg)\n- 酸菜200g\n- 姜片、蒜瓣适量...\n\n## 制作步骤\n\n### 1. 处理鱼肉\n...",
|
||||
"tags": ["美食教程", "酸菜鱼", "家常菜", "川菜", "烹饪技巧"],
|
||||
"category": "美食"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"finish_reason": "tool_calls"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 6: 解析工具名称
|
||||
|
||||
```typescript
|
||||
// chatService.ts - executeToolCalls()
|
||||
const fullFunctionName = 'xiaohongshu__public_content'
|
||||
const parts = fullFunctionName.split('__')
|
||||
|
||||
if (parts.length !== 2) {
|
||||
console.error('工具名称格式错误')
|
||||
return
|
||||
}
|
||||
|
||||
const [serverName, toolName] = parts
|
||||
// serverName = 'xiaohongshu'
|
||||
// toolName = 'public_content'
|
||||
```
|
||||
|
||||
### 步骤 7: 执行 MCP 工具
|
||||
|
||||
```typescript
|
||||
// MCPClientService.ts - callTool()
|
||||
const result = await mcpClient.callTool(
|
||||
'xiaohongshu', // serverId
|
||||
'public_content', // toolName (不带前缀)
|
||||
{
|
||||
title: '🐟 超详细!家常酸菜鱼做法,10分钟学会!',
|
||||
content: '# 酸菜鱼制作教程\n\n## 所需食材...',
|
||||
tags: ['美食教程', '酸菜鱼', '家常菜', '川菜', '烹饪技巧'],
|
||||
category: '美食'
|
||||
}
|
||||
)
|
||||
|
||||
// MCP Server 响应:
|
||||
{
|
||||
"success": true,
|
||||
"article_id": "xhs_2024_001",
|
||||
"url": "https://www.xiaohongshu.com/discovery/item/xhs_2024_001",
|
||||
"views": 0,
|
||||
"likes": 0
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 8: 工具结果返回 AI
|
||||
|
||||
```typescript
|
||||
// chatService.ts - 继续对话
|
||||
const messages = [
|
||||
{
|
||||
role: 'system',
|
||||
content: '...' // System Prompt
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: '帮我发布小红书文章,内容是:如何制作一道酸菜鱼'
|
||||
},
|
||||
{
|
||||
role: 'assistant',
|
||||
tool_calls: [{
|
||||
id: 'call_abc123',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'xiaohongshu__public_content',
|
||||
arguments: '{"title":"🐟 超详细!家常酸菜鱼做法,10分钟学会!",...}'
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
role: 'tool',
|
||||
tool_call_id: 'call_abc123',
|
||||
name: 'xiaohongshu__public_content', // 保持原名称(带前缀)
|
||||
content: JSON.stringify({
|
||||
success: true,
|
||||
article_id: 'xhs_2024_001',
|
||||
url: 'https://www.xiaohongshu.com/discovery/item/xhs_2024_001'
|
||||
})
|
||||
}
|
||||
]
|
||||
|
||||
// 再次调用 LLM
|
||||
```
|
||||
|
||||
### 步骤 9: AI 生成友好回复
|
||||
|
||||
```json
|
||||
{
|
||||
"choices": [{
|
||||
"delta": {
|
||||
"content": "✅ 文章已成功发布到小红书!\n\n📝 标题:🐟 超详细!家常酸菜鱼做法,10分钟学会!\n🔗 链接:https://www.xiaohongshu.com/discovery/item/xhs_2024_001\n\n你的酸菜鱼教程已经上线啦!记得定期查看浏览和点赞数据哦~ 🎉"
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
## 关键代码实现
|
||||
|
||||
### 1. System Prompt 生成 (chatService.ts)
|
||||
|
||||
```typescript
|
||||
private createSystemPromptWithTools(tools: any[], serverName: string): string {
|
||||
const toolDescriptions = tools.map(tool => {
|
||||
const func = tool.function
|
||||
const params = func.parameters?.properties || {}
|
||||
const required = func.parameters?.required || []
|
||||
|
||||
const paramDesc = Object.entries(params).map(([name, schema]: [string, any]) => {
|
||||
const isRequired = required.includes(name)
|
||||
const requiredMark = isRequired ? '[必填]' : '[可选]'
|
||||
return ` - ${name} ${requiredMark}: ${schema.description || schema.type}`
|
||||
}).join('\n')
|
||||
|
||||
return `• ${func.name}\n 描述: ${func.description}\n 参数:\n${paramDesc || ' 无参数'}`
|
||||
}).join('\n\n')
|
||||
|
||||
return `你是一个智能助手,可以使用以下工具完成任务:
|
||||
|
||||
${toolDescriptions}
|
||||
|
||||
使用指南:
|
||||
1. 当用户需要完成某个任务时,请分析哪个工具最合适
|
||||
2. 如果需要发布内容(如文章、笔记等),请根据用户意图创作完整的内容
|
||||
3. 为内容生成合适的标题、正文、标签等所有必需参数
|
||||
4. 自动调用相应工具,将生成的内容作为参数传递
|
||||
5. 根据工具执行结果,给用户友好的反馈
|
||||
|
||||
注意事项:
|
||||
- 保持内容质量和平台特色
|
||||
- 标签要相关且有吸引力
|
||||
- 分类要准确
|
||||
- 如果工具执行失败,给出明确的错误说明和建议
|
||||
|
||||
当前连接的 MCP 服务器: ${serverName}`
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 工具名称转换 (chatService.ts)
|
||||
|
||||
```typescript
|
||||
private convertToolsToOpenAIFormat(mcpTools: any[], serverName: string): any[] {
|
||||
return mcpTools.map(tool => ({
|
||||
type: 'function',
|
||||
function: {
|
||||
name: `${serverName}__${tool.name}`, // 添加服务器前缀
|
||||
description: tool.description || '',
|
||||
parameters: tool.inputSchema || {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
required: []
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 工具名称解析 (chatService.ts)
|
||||
|
||||
```typescript
|
||||
private async executeToolCalls(
|
||||
conversation: Conversation,
|
||||
toolCalls: any[],
|
||||
model: string | undefined,
|
||||
onChunk: (chunk: string) => void,
|
||||
mcpServerId: string
|
||||
): Promise<void> {
|
||||
for (const toolCall of toolCalls) {
|
||||
const fullFunctionName = toolCall.function.name
|
||||
const args = JSON.parse(toolCall.function.arguments)
|
||||
|
||||
console.log('🔧 执行工具调用:', {
|
||||
fullName: fullFunctionName,
|
||||
id: toolCall.id,
|
||||
arguments: args
|
||||
})
|
||||
|
||||
// 解析 serverName__toolName 格式
|
||||
const parts = fullFunctionName.split('__')
|
||||
if (parts.length !== 2) {
|
||||
console.error('❌ 工具名称格式错误,应为 serverName__toolName:', fullFunctionName)
|
||||
continue
|
||||
}
|
||||
|
||||
const toolName = parts[1]
|
||||
console.log('🎯 提取工具名称:', toolName)
|
||||
|
||||
try {
|
||||
// 调用 MCP 工具(使用不带前缀的工具名)
|
||||
const result = await this.mcpClient.callTool(
|
||||
mcpServerId,
|
||||
toolName, // 使用原始工具名
|
||||
args
|
||||
)
|
||||
|
||||
// 添加工具结果到消息历史(使用完整名称)
|
||||
const toolResultMessage: Message = {
|
||||
id: Date.now().toString(),
|
||||
role: 'tool',
|
||||
content: JSON.stringify(result),
|
||||
timestamp: new Date(),
|
||||
status: 'success',
|
||||
toolCallId: toolCall.id,
|
||||
toolName: fullFunctionName // 保持完整名称
|
||||
}
|
||||
|
||||
conversation.messages.push(toolResultMessage)
|
||||
this.saveConversations()
|
||||
|
||||
// 继续对话
|
||||
await this.callModelStream(conversation, model, onChunk, mcpServerId)
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 工具执行失败:', error)
|
||||
// 错误处理...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 测试场景
|
||||
|
||||
### 场景 1: 发布文章
|
||||
|
||||
```
|
||||
用户: 帮我发布一篇关于"春季穿搭指南"的小红书笔记
|
||||
|
||||
AI 处理:
|
||||
1. 识别需要使用 xiaohongshu__public_content 工具
|
||||
2. 创作完整文章(标题、正文、标签、分类)
|
||||
3. 调用工具发布
|
||||
4. 返回发布结果和链接
|
||||
```
|
||||
|
||||
### 场景 2: 多工具选择
|
||||
|
||||
假设有多个 MCP 服务器:
|
||||
|
||||
```
|
||||
- xiaohongshu__public_content (发布小红书)
|
||||
- weibo__post_status (发布微博)
|
||||
- notion__create_page (创建 Notion 页面)
|
||||
```
|
||||
|
||||
```
|
||||
用户: 帮我把这篇文章同时发到小红书和微博
|
||||
|
||||
AI 处理:
|
||||
1. 理解需要两个工具
|
||||
2. 为小红书创作合适格式的内容
|
||||
3. 为微博创作合适格式的内容(字数限制)
|
||||
4. 依次调用两个工具
|
||||
5. 返回两个平台的发布结果
|
||||
```
|
||||
|
||||
### 场景 3: 错误处理
|
||||
|
||||
```
|
||||
用户: 发布文章到小红书,标题是"测试"
|
||||
|
||||
AI 处理:
|
||||
1. 识别内容不完整
|
||||
2. 提示用户补充正文内容
|
||||
3. 等待用户补充后再调用工具
|
||||
```
|
||||
|
||||
## 优势总结
|
||||
|
||||
### 1. **智能参数生成**
|
||||
- AI 自动创作内容,无需用户逐一填写参数
|
||||
- 符合平台特色(小红书风格 vs 微博风格)
|
||||
|
||||
### 2. **工具名称隔离**
|
||||
- `serverName__toolName` 避免多服务器冲突
|
||||
- 清晰的工具来源
|
||||
|
||||
### 3. **友好的用户体验**
|
||||
- 自然语言输入:"帮我发布..."
|
||||
- 自动处理所有技术细节
|
||||
- 结果友好呈现
|
||||
|
||||
### 4. **可扩展性**
|
||||
- 轻松添加新 MCP 服务器
|
||||
- 支持任意数量和类型的工具
|
||||
- System Prompt 自动生成
|
||||
|
||||
### 5. **多轮对话支持**
|
||||
- 工具结果自动传回 AI
|
||||
- 可以追问、修改、重试
|
||||
|
||||
## 对比 Cherry Studio
|
||||
|
||||
| 特性 | mcp-client-vue | Cherry Studio |
|
||||
|------|---------------|---------------|
|
||||
| 工具名称格式 | ✅ `serverName__toolName` | ✅ `serverName__toolName` |
|
||||
| System Prompt | ✅ 自动生成 | ✅ 自动生成 |
|
||||
| 参数自动注入 | ✅ AI 生成 | ✅ AI 生成 |
|
||||
| 多轮对话 | ✅ 完整支持 | ✅ 完整支持 |
|
||||
| 流式响应 | ✅ SSE 真流式 | ✅ 真流式 |
|
||||
| 错误处理 | ✅ 完善 | ✅ 完善 |
|
||||
| UI 界面 | Vue 3 + Naive UI | Electron + React |
|
||||
|
||||
## 下一步优化
|
||||
|
||||
1. **批量工具调用**: 同时调用多个工具
|
||||
2. **工具调用历史**: 记录和展示工具调用日志
|
||||
3. **工具执行超时**: 防止长时间阻塞
|
||||
4. **工具权限控制**: 敏感操作需要用户确认
|
||||
5. **工具调用缓存**: 避免重复调用
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `/web/src/services/chatService.ts` - 核心服务
|
||||
- `/web/src/services/modelServiceManager.ts` - 模型管理
|
||||
- `/web/src/services/MCPClientService.ts` - MCP 客户端
|
||||
- `/web/src/components/Chat/ChatLayout.vue` - UI 组件
|
||||
|
||||
---
|
||||
|
||||
**版本**: v1.0.2+
|
||||
**更新时间**: 2024-01
|
||||
**作者**: MCP Client Vue Team
|
||||
Reference in New Issue
Block a user