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 包管理工具(推荐)
安装 uv
# 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克隆项目
git clone https://github.com/taffy123d/Doubao-MCP-agent cd <项目目录>创建虚拟环境
uv venv安装依赖
uv sync
方法二:使用 pip
克隆项目
git clone https://github.com/taffy123d/Doubao-MCP-agent cd <项目目录>创建虚拟环境
python -m venv venv激活虚拟环境
# Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate安装依赖
pip install -r requirements.txt
配置
在前端配置api密钥
或在
.env文件中填写 API 密钥:
# OpenAI 兼容格式的 API 配置
OPENAI_API_KEY=你的API密钥
OPENAI_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
OPENAI_MODEL=你的模型ID
运行
方法一:完整启动(推荐)
uv run server.py
#或者
python server.py
- 前端访问:
http://localhost:5000 - API 接口:
http://localhost:5000/api/*
方法二:命令行界面
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 请求示例
聊天接口
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"
}'
流式聊天接口
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"
}'
清除历史接口
curl -X POST http://localhost:5000/api/chat/clear \
-H "Content-Type: application/json" \
-d '{
"session_id": "default"
}'
如何使用
Web 界面
配置 API
- 在左侧配置面板填写 API Key 和 Endpoint ID
- 点击「测试」按钮验证连接
聊天
- 在输入框中输入问题
- 支持的技能:
- 计算器:
计算 123+456 - 天气查询:
北京天气 - 网络搜索:
搜索 最新AI新闻
- 计算器:
技能管理
- 点击左侧「🔧 技能管理」展开面板
- 查看所有可用技能及其描述
- 点击开关按钮启用/禁用技能
- 只有启用的技能才会被调用
多会话管理
- 点击左侧「💬 对话管理」展开面板
- 点击「➕ 新建对话」创建新会话
- 点击会话列表项切换到对应对话
- 点击🗑️删除不需要的对话
- 每个会话独立保存历史记录
查看结果
- 系统会自动调用相应的技能并返回结果
- 支持Markdown格式的回复(代码高亮、表格、列表等)
- 可点击「🧠 思考过程」查看AI的推理逻辑
- 支持多轮对话
命令行界面
运行程序
python main.py输入问题
- 直接在终端中输入你的问题
- 支持的技能:
- 计算器:
计算 123+456 - 天气查询:
北京天气
- 计算器:
查看结果
- 系统会自动调用相应的技能并返回结果
- 支持多轮对话
- 输入
clear或清除历史可以清除对话历史
如何增加新技能
步骤 1:创建技能文件
在 skills/ 目录下创建新的技能文件,例如 my_skill.py:
"""我的自定义技能"""
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,添加新技能的注册函数:
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,添加新技能的注册:
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 服务和后端服务,新技能即可使用。
技能开发规范
- 文件命名:使用小写字母和下划线
- 函数命名:
register_xxx_tool格式 - 工具装饰器:使用
@mcp.tool()装饰 - 文档字符串:包含功能描述、示例和参数说明
- 错误处理:捕获异常并返回友好提示
- 参数类型:使用类型注解
如何创建复杂 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
"""我的复杂技能实现"""
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 导出配置
"""my_complex_skill - 我的复杂技能"""
from .my_skill import register_my_complex_skill
__all__ = ["register_my_complex_skill"]
1.3 创建 SKILL.md 描述文档
# 我的复杂技能
## 功能描述
一句话描述技能功能...
## 使用场景
### ✅ 适用场景
- 场景1
- 场景2
## 参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| query | string | 是 | - | 查询关键词 |
## 使用示例
```python
# 示例1
my_complex_skill(query="关键词")
返回结果格式
- 结果1:xxx
- 结果2:xxx
异常处理
| 错误类型 | 处理方式 |
|---|---|
| 网络错误 | 返回友好的错误提示 |
注意事项
- 注意事项1
- 注意事项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
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 中添加:
uv add 包名称
或
包名称 >=版本号 #requirements.txt
然后运行:
uv sync
# 或
pip install 包名称
步骤 5:重启服务
重新启动服务,新技能即可使用。
SKILL.md 规范
| 字段 | 必填 | 说明 |
|---|---|---|
| # 标题 | 是 | 技能名称 |
| ## 功能描述 | 是 | 一句话说明技能作用 |
| ## 使用场景 | 建议 | 列出适用场景 |
| ## 参数说明 | 建议 | 表格形式说明参数 |
| ## 使用示例 | 建议 | 代码和对话示例 |
| ## 返回结果格式 | 建议 | 说明返回内容结构 |
| ## 异常处理 | 建议 | 错误处理方式 |
| ## 注意事项 | 建议 | 使用注意点 |
示例技能
计算器技能
- 功能:支持加减乘除、括号、幂运算
- 调用:
计算 (10+5)*2
天气查询技能
- 功能:查询城市天气和预报
- 调用:
上海天气或北京天气 3天
网络搜索技能
- 功能:使用DuckDuckGo搜索最新资讯
- 调用:
搜索 Python最新版本或搜索 今天科技新闻 - 依赖:
ddgs库(pip install duckduckgo-search)
技术亮点
- 异步处理优化 - 使用线程池执行异步函数,避免了每次请求创建新事件循环的问题
- 对话历史持久化 - 基于 SQLite 的持久化存储,服务重启不丢失,支持多会话隔离
- 工具调用容错 - 失败自动重试 2 次,降级到模型直接回答,提升鲁棒性
- MCP 工具缓存 - 减少重复初始化开销,提升响应速度
- 流式输出实现 - 完整的 SSE 流式接口,提供更好的用户体验
- 工具调用提示 - 清晰的工具调用提示,提升用户体验
- 多端支持 - 同时提供 Web 界面和命令行界面
- 技能管理系统 - 前端可视化技能管理,支持灵活开关
- Markdown渲染 - 完整的Markdown支持,包括代码高亮、表格等
- 思维链展示 - 可折叠的AI推理过程展示
- 多会话管理 - 完整的会话创建、切换、删除功能
- 历史对话加载 - 自动加载和展示会话历史
注意事项
- API 密钥安全:不要将 API 密钥提交到版本控制
- 技能安全性:避免在技能中执行危险操作
- 性能优化:对于耗时操作,考虑使用异步处理
- 错误处理:确保技能能优雅处理异常情况
故障排除
- 连接失败:检查 API 密钥和网络连接
- 技能不响应:检查 MCP 服务是否正常运行
- 前端不显示:检查浏览器控制台是否有错误
- 流式接口问题:确保网络连接稳定,避免中途断开
- 数据库错误:检查
chat_history.db文件权限,确保可读写
数据存储
项目使用 SQLite 数据库持久化对话历史:
- 数据库文件:
chat_history.db(项目根目录,首次运行自动生成) - 表结构:
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 工具或命令行查看
sqlite3 chat_history.db "SELECT * FROM messages ORDER BY timestamp DESC LIMIT 10;"
扩展建议
- 更多技能:添加翻译、股票查询、新闻等技能
- 多语言支持:添加多语言界面
- 部署优化:使用 Docker 容器化部署
- 技能市场:创建技能市场,支持用户分享和下载技能
- 模型切换:支持切换不同的大语言模型
更新日志
2026-03-29 重大更新
API 调用方式升级
- httpx → OpenAI SDK:所有 API 调用从
httpx直接 HTTP 请求改为openai>=1.0.0SDK 方式 - 配置字段重命名:
DOUBAO_API_KEY→OPENAI_API_KEYDOUBAO_ENDPOINT_ID→OPENAI_MODELDOUBAO_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()路由的异常处理和日志输出