Doubao MCP Agent
by taffy123d
README.md
# ToolKit本地技能Agent
一个基于 MCP (Model Context Protocol) 协议的本地技能助手,支持计算器、天气查询等自定义技能,提供 Web 界面和 API 接口。
## 项目结构
```
..
├── .env # 大模型 API 配置
├── chat_history.db # SQLite 对话历史数据库(自动生成)
├── index.html # 前端 Web 界面
├── main.py # 主入口(命令行界面)
├── mcp_server.py # MCP 服务端(核心)
├── server.py # Flask 后端服务
├── requirements.txt # 依赖清单
├── README.md # 项目说明
├── tree.txt # 目录结构
├── client/ # 客户端目录
│ ├── doubao_mcp_client.py # 豆包 API 客户端
│ └── __init__.py
├── config/ # 配置目录
│ ├── settings.py # 全局配置
│ └── __init__.py
└── skills/ # 技能实现目录
├── calculator.py # 计算器技能
├── weather.py # 天气查询技能
├── web_search/ # 网络搜索技能目录
│ └── web_search.py # DuckDuckGo搜索实现
| └── SKILL.md # skill描述
| └── _init_.py
└── __init__.py
```
## 技术栈
**后端框架**:Python + Flask 构建 Web 服务,提供 RESTful API 与 SSE 流式输出接口
**AI 协议与模型调用**:基于 OpenAI 兼容 SDK 对接大模型 API,支持豆包等 OpenAI 格式模型接入
**核心协议**:MCP(Model Context Protocol)实现工具调用标准化,统一技能注册与调度
**异步架构**:asyncio 异步处理 + 线程池隔离,解决 Flask 同步环境下异步调用阻塞问题
**数据持久化**:SQLite 实现多会话对话上下文存储,支持会话管理与历史加载
**技能插件化**:模块化技能系统,支持计算器、天气、网络搜索等可插拔工具扩展
**前端**:原生 HTML/JS 实现 Web 交互界面,支持 Markdown 渲染、流式打字效果、思维链展示
**工程化** :api变量配置(.env)、依赖管理(uv/pip)、错误重试与降级机制、工具调用缓存
## 核心功能
✅ **稳定的异步处理** - 修复了Flask路由中直接使用asyncio.run()的问题,使用线程池执行异步函数
✅ **对话历史持久化** - 使用 SQLite 存储对话历史,服务重启不丢失,支持多会话管理
✅ **工具调用容错** - 自动重试机制,工具调用失败时降级到模型直接回答
✅ **MCP 工具缓存** - 首次获取工具列表后缓存,减少重复初始化开销
✅ **流式输出** - 实现了完整的SSE流式接口,支持逐字输出体验
✅ **工具调用提示** - 当调用技能时,会显示"【调用了工具:{工具名称}】"的提示信息
✅ **多端支持** - 提供Web界面和命令行界面两种交互方式
✅ **丰富的技能** - 内置计算器、天气查询和网络搜索技能
✅ **技能管理** - 前端可视化技能管理,可自由开关技能
✅ **Markdown渲染** - 支持Markdown格式的回复,支持代码高亮、表格、列表、数学公式等
✅ **思维链展示** - 可折叠的AI思考过程展示,便于理解推理逻辑
✅ **多会话管理** - 支持创建多个独立对话,每个对话独立保存历史
✅ **历史对话加载** - 切换会话时自动加载历史对话,完整记录交互过程
## 环境要求
- Python 3.11+
- openaiSDK(api)
- uv 包管理工具(推荐)或 pip
## 安装
### 方法一:使用 uv 包管理工具(推荐)
1. **安装 uv**
```bash
# Windows
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
irm https://astral.sh/uv/install.ps1 | iex
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
```
2. **克隆项目**
```bash
git clone https://github.com/taffy123d/Doubao-MCP-agent
cd <项目目录>
```
3. **创建虚拟环境**
```bash
uv venv
```
4. **安装依赖**
```bash
uv sync
```
### 方法二:使用 pip
1. **克隆项目**
```bash
git clone https://github.com/taffy123d/Doubao-MCP-agent
cd <项目目录>
```
2. **创建虚拟环境**
```bash
python -m venv venv
```
3. **激活虚拟环境**
```bash
# Windows
venv\Scripts\activate
# macOS / Linux
source venv/bin/activate
```
4. **安装依赖**
```bash
pip install -r requirements.txt
```
## 配置
- ##### 在前端配置api密钥
- ##### 或在 `.env` 文件中填写 API 密钥:
```env
# OpenAI 兼容格式的 API 配置
OPENAI_API_KEY=你的API密钥
OPENAI_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
OPENAI_MODEL=你的模型ID
```
## 运行
### 方法一:完整启动(推荐)
```bash
uv run server.py
#或者
python server.py
```
- 前端访问:`http://localhost:5000`
- API 接口:`http://localhost:5000/api/*`
### 方法二:命令行界面
```bash
uv run main.py
#或者
python main.py
```
- 直接在终端中进行对话
- 支持多轮对话和历史记录
- 输入 `clear` 或 `清除历史` 可以清除对话历史
- 输入 `exit`、`quit` 或 `退出` 可以退出程序
## API 接口
| 接口 | 方法 | 说明 |
|------|------|------|
| `/` | GET | 前端页面 |
| `/api/health` | GET | 健康检查 |
| `/api/tools` | GET | 获取技能列表 |
| `/api/config` | GET | 获取配置 |
| `/api/config` | POST | 保存配置 |
| `/api/test-connection` | POST | 测试 API 连接 |
| `/api/chat` | POST | 聊天(支持对话历史) |
| `/api/chat/stream` | POST | 流式聊天(SSE) |
| `/api/chat/clear` | POST | 清除对话历史 |
| `/api/sessions` | GET | 获取所有会话列表 |
| `/api/sessions/<id>` | DELETE | 删除指定会话 |
| `/api/sessions/<id>/history` | GET | 获取会话历史记录 |
### API 请求示例
#### 聊天接口
```bash
curl -X POST http://localhost:5000/api/chat \
-H "Content-Type: application/json" \
-d '{
"api_key": "你的API密钥",
"model": "你的模型ID",
"base_url": "https://ark.cn-beijing.volces.com/api/v3",
"message": "北京天气",
"session_id": "default"
}'
```
#### 流式聊天接口
```bash
curl -X POST http://localhost:5000/api/chat/stream \
-H "Content-Type: application/json" \
-d '{
"api_key": "你的API密钥",
"model": "你的模型ID",
"base_url": "https://ark.cn-beijing.volces.com/api/v3",
"message": "北京天气",
"session_id": "default"
}'
```
#### 清除历史接口
```bash
curl -X POST http://localhost:5000/api/chat/clear \
-H "Content-Type: application/json" \
-d '{
"session_id": "default"
}'
```
## 如何使用
### Web 界面
1. **配置 API**
- 在左侧配置面板填写 API Key 和 Endpoint ID
- 点击「测试」按钮验证连接
2. **聊天**
- 在输入框中输入问题
- 支持的技能:
- 计算器:`计算 123+456`
- 天气查询:`北京天气`
- 网络搜索:`搜索 最新AI新闻`
3. **技能管理**
- 点击左侧「🔧 技能管理」展开面板
- 查看所有可用技能及其描述
- 点击开关按钮启用/禁用技能
- 只有启用的技能才会被调用
4. **多会话管理**
- 点击左侧「💬 对话管理」展开面板
- 点击「➕ 新建对话」创建新会话
- 点击会话列表项切换到对应对话
- 点击🗑️删除不需要的对话
- 每个会话独立保存历史记录
5. **查看结果**
- 系统会自动调用相应的技能并返回结果
- 支持Markdown格式的回复(代码高亮、表格、列表等)
- 可点击「🧠 思考过程」查看AI的推理逻辑
- 支持多轮对话
### 命令行界面
1. **运行程序**
```bash
python main.py
```
2. **输入问题**
- 直接在终端中输入你的问题
- 支持的技能:
- 计算器:`计算 123+456`
- 天气查询:`北京天气`
3. **查看结果**
- 系统会自动调用相应的技能并返回结果
- 支持多轮对话
- 输入 `clear` 或 `清除历史` 可以清除对话历史
## 如何增加新技能
### 步骤 1:创建技能文件
在 `skills/` 目录下创建新的技能文件,例如 `my_skill.py`:
```python
"""我的自定义技能"""
from mcp.server.fastmcp import FastMCP
def register_my_skill(mcp: FastMCP):
"""注册技能到 MCP 服务"""
@mcp.tool()
def my_skill(param1: str, param2: int = 1) -> str:
"""
我的自定义技能描述
示例:my_skill(param1="值", param2=2)
Args:
param1: 参数1描述
param2: 参数2描述(默认值)
Returns:
技能执行结果
"""
try:
# 技能逻辑实现
result = f"处理结果: {param1} - {param2}"
return result
except Exception as e:
return f"处理失败: {str(e)}"
```
### 步骤 2:注册技能
编辑 `skills/__init__.py`,添加新技能的注册函数:
```python
from .calculator import register_calculator_tool
from .weather import register_weather_tool
from .my_skill import register_my_skill
__all__ = [
"register_calculator_tool",
"register_weather_tool",
"register_my_skill"
]
```
### 步骤 3:更新 MCP 服务
编辑 `mcp_server.py`,添加新技能的注册:
```python
from skills import register_calculator_tool, register_weather_tool, register_my_skill
# 注册所有技能工具
register_calculator_tool(mcp)
register_weather_tool(mcp)
register_my_skill(mcp) # 添加这一行
```
### 步骤 4:重启服务
重新启动 MCP 服务和后端服务,新技能即可使用。
## 技能开发规范
1. **文件命名**:使用小写字母和下划线
2. **函数命名**:`register_xxx_tool` 格式
3. **工具装饰器**:使用 `@mcp.tool()` 装饰
4. **文档字符串**:包含功能描述、示例和参数说明
5. **错误处理**:捕获异常并返回友好提示
6. **参数类型**:使用类型注解
## 如何创建复杂 Skill(带 SKILL.md)
对于功能较复杂的技能,建议创建独立的 skill 目录,包含技能实现和 SKILL.md 描述文件。
### 目录结构
```
skills/
└── my_complex_skill/ # skill 目录
├── __init__.py # 导出配置(必选)
├── my_skill.py # 技能实现(必选)
└── SKILL.md # skill 描述文档(必选)
```
### 步骤 1:创建 skill 目录和实现文件
在 `skills/` 目录下创建新的 skill 目录,例如 `skills/my_complex_skill/`
#### 1.1 创建技能实现文件 `my_skill.py`
```python
"""我的复杂技能实现"""
from mcp.server.fastmcp import FastMCP
from duckduckgo_search import AsyncDuckDuckGoSearcher # 示例依赖
def register_my_complex_skill(mcp: FastMCP):
"""注册复杂技能到 MCP 服务"""
@mcp.tool()
async def my_complex_skill(query: str, limit: int = 5) -> str:
"""
我的复杂技能描述
Args:
query: 查询关键词
limit: 返回结果数量,默认5
Returns:
格式化的搜索结果
"""
try:
async with AsyncDuckDuckGoSearcher() as searcher:
results = await searcher.atext(query, max_results=limit)
# 处理并返回结果
return f"找到 {len(results)} 条结果..."
except Exception as e:
return f"搜索失败: {str(e)}"
```
#### 1.2 创建 `__init__.py` 导出配置
```python
"""my_complex_skill - 我的复杂技能"""
from .my_skill import register_my_complex_skill
__all__ = ["register_my_complex_skill"]
```
#### 1.3 创建 `SKILL.md` 描述文档
```markdown
# 我的复杂技能
## 功能描述
一句话描述技能功能...
## 使用场景
### ✅ 适用场景
- 场景1
- 场景2
## 参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| query | string | 是 | - | 查询关键词 |
## 使用示例
```python
# 示例1
my_complex_skill(query="关键词")
```
## 返回结果格式
- 结果1:xxx
- 结果2:xxx
## 异常处理
| 错误类型 | 处理方式 |
|----------|----------|
| 网络错误 | 返回友好的错误提示 |
## 注意事项
1. 注意事项1
2. 注意事项2
```
### 步骤 2:更新 skills/__init__.py
```python
from .calculator import register_calculator_tool
from .weather import register_weather_tool
from .web_search import register_web_search_tool
from .my_complex_skill import register_my_complex_skill # 新增
__all__ = [
"register_calculator_tool",
"register_weather_tool",
"register_web_search_tool",
"register_my_complex_skill" # 新增
]
```
### 步骤 3:更新 mcp_server.py
```python
from skills import (
register_calculator_tool,
register_weather_tool,
register_web_search_tool,
register_my_complex_skill # 新增
)
# 注册所有技能工具
register_calculator_tool(mcp)
register_weather_tool(mcp)
register_web_search_tool(mcp)
register_my_complex_skill(mcp) # 新增
```
### 步骤 4:安装额外依赖(如需要)
如果新 skill 需要额外的 Python 包,在使用 `uv add` 导入或`requirements.txt` 中添加:
```text
uv add 包名称
或
包名称 >=版本号 #requirements.txt
```
然后运行:
```bash
uv sync
# 或
pip install 包名称
```
### 步骤 5:重启服务
重新启动服务,新技能即可使用。
### SKILL.md 规范
| 字段 | 必填 | 说明 |
|------|------|------|
| # 标题 | 是 | 技能名称 |
| ## 功能描述 | 是 | 一句话说明技能作用 |
| ## 使用场景 | 建议 | 列出适用场景 |
| ## 参数说明 | 建议 | 表格形式说明参数 |
| ## 使用示例 | 建议 | 代码和对话示例 |
| ## 返回结果格式 | 建议 | 说明返回内容结构 |
| ## 异常处理 | 建议 | 错误处理方式 |
| ## 注意事项 | 建议 | 使用注意点 |
## 示例技能
### 计算器技能
- **功能**:支持加减乘除、括号、幂运算
- **调用**:`计算 (10+5)*2`
### 天气查询技能
- **功能**:查询城市天气和预报
- **调用**:`上海天气` 或 `北京天气 3天`
### 网络搜索技能
- **功能**:使用DuckDuckGo搜索最新资讯
- **调用**:`搜索 Python最新版本` 或 `搜索 今天科技新闻`
- **依赖**:`ddgs` 库(pip install duckduckgo-search)
## 技术亮点
1. **异步处理优化** - 使用线程池执行异步函数,避免了每次请求创建新事件循环的问题
2. **对话历史持久化** - 基于 SQLite 的持久化存储,服务重启不丢失,支持多会话隔离
3. **工具调用容错** - 失败自动重试 2 次,降级到模型直接回答,提升鲁棒性
4. **MCP 工具缓存** - 减少重复初始化开销,提升响应速度
5. **流式输出实现** - 完整的 SSE 流式接口,提供更好的用户体验
6. **工具调用提示** - 清晰的工具调用提示,提升用户体验
7. **多端支持** - 同时提供 Web 界面和命令行界面
8. **技能管理系统** - 前端可视化技能管理,支持灵活开关
9. **Markdown渲染** - 完整的Markdown支持,包括代码高亮、表格等
10. **思维链展示** - 可折叠的AI推理过程展示
11. **多会话管理** - 完整的会话创建、切换、删除功能
12. **历史对话加载** - 自动加载和展示会话历史
## 注意事项
1. **API 密钥安全**:不要将 API 密钥提交到版本控制
2. **技能安全性**:避免在技能中执行危险操作
3. **性能优化**:对于耗时操作,考虑使用异步处理
4. **错误处理**:确保技能能优雅处理异常情况
## 故障排除
- **连接失败**:检查 API 密钥和网络连接
- **技能不响应**:检查 MCP 服务是否正常运行
- **前端不显示**:检查浏览器控制台是否有错误
- **流式接口问题**:确保网络连接稳定,避免中途断开
- **数据库错误**:检查 `chat_history.db` 文件权限,确保可读写
## 数据存储
项目使用 SQLite 数据库持久化对话历史:
- **数据库文件**:`chat_history.db`(项目根目录,首次运行自动生成)
- **表结构**:
```sql
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL, -- 会话ID,支持多会话隔离
role TEXT NOT NULL, -- 角色(user/assistant/tool)
content TEXT NOT NULL, -- 消息内容
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
```
- **查询历史**:使用 SQLite 工具或命令行查看
```bash
sqlite3 chat_history.db "SELECT * FROM messages ORDER BY timestamp DESC LIMIT 10;"
```
## 扩展建议
1. **更多技能**:添加翻译、股票查询、新闻等技能
2. **多语言支持**:添加多语言界面
3. **部署优化**:使用 Docker 容器化部署
4. **技能市场**:创建技能市场,支持用户分享和下载技能
5. **模型切换**:支持切换不同的大语言模型
## 更新日志
### 2026-03-29 重大更新
#### API 调用方式升级
- **httpx → OpenAI SDK**:所有 API 调用从 `httpx` 直接 HTTP 请求改为 `openai>=1.0.0` SDK 方式
- **配置字段重命名**:
- `DOUBAO_API_KEY` → `OPENAI_API_KEY`
- `DOUBAO_ENDPOINT_ID` → `OPENAI_MODEL`
- `DOUBAO_BASE_URL` → `OPENAI_BASE_URL`(去掉了 `/chat/completions` 后缀)
#### 工具调用优化
- **Schema 清理**:自动移除 `title`、`default` 等豆包 API 不支持的字段
- **Description 清理**:压缩多余空白字符,优化格式
- **消息转换**:添加 `_msg_to_dict()` 函数,正确处理 OpenAI SDK 返回的 `ChatCompletionMessage` 对象
- **第二次调用**:修复工具调用后二次请求的消息格式问题
#### Bug 修复
- ✅ 修复 "Object of type ChatCompletionMessage is not JSON serializable" 错误
- ✅ 修复消息历史保存时的类型转换问题
- ✅ 添加详细的异常堆栈跟踪,便于调试
#### 架构改进
- 添加 `_msg_to_dict()` 辅助函数,统一消息格式转换
- 添加 API 类型检测(讯飞 API 自动跳过 tools 参数)
- 优化 `chat()` 路由的异常处理和日志输出
---
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues