Files
Mofox-Core/docs/plugins/tool-system.md
2025-06-15 23:30:53 +08:00

495 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🔧 工具系统详解
## 📖 什么是工具系统
工具系统是MaiBot的信息获取能力扩展组件**专门用于在Focus模式下扩宽麦麦能够获得的信息量**。如果说Action组件功能五花八门可以拓展麦麦能做的事情那么Tool就是在某个过程中拓宽了麦麦能够获得的信息量。
### 🎯 工具系统的特点
- 🔍 **信息获取增强**:扩展麦麦获取外部信息的能力
- 🎯 **Focus模式专用**:仅在专注聊天模式下工作,必须开启工具处理器
- 📊 **数据丰富**:帮助麦麦获得更多背景信息和实时数据
- 🔌 **插件式架构**:支持独立开发和注册新工具
-**自动发现**:工具会被系统自动识别和注册
### 🆚 Tool vs Action vs Command 区别
| 特征 | Action | Command | Tool |
|-----|-------|---------|------|
| **主要用途** | 扩展麦麦行为能力 | 响应用户指令 | 扩展麦麦信息获取 |
| **适用模式** | 所有模式 | 所有模式 | 仅Focus模式 |
| **触发方式** | 麦麦智能决策 | 用户主动触发 | LLM根据需要调用 |
| **目标** | 让麦麦做更多事情 | 提供具体功能 | 让麦麦知道更多信息 |
| **使用场景** | 增强交互体验 | 功能服务 | 信息查询和分析 |
## 🏗️ 工具基本结构
### 必要组件
每个工具必须继承 `BaseTool` 基类并实现以下属性和方法:
```python
from src.tools.tool_can_use.base_tool import BaseTool, register_tool
class MyTool(BaseTool):
# 工具名称,必须唯一
name = "my_tool"
# 工具描述告诉LLM这个工具的用途
description = "这个工具用于获取特定类型的信息"
# 参数定义遵循JSONSchema格式
parameters = {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "查询参数"
},
"limit": {
"type": "integer",
"description": "结果数量限制"
}
},
"required": ["query"]
}
async def execute(self, function_args, message_txt=""):
"""执行工具逻辑"""
# 实现工具功能
result = f"查询结果: {function_args.get('query')}"
return {
"name": self.name,
"content": result
}
# 注册工具
register_tool(MyTool)
```
### 属性说明
| 属性 | 类型 | 说明 |
|-----|------|------|
| `name` | str | 工具的唯一标识名称 |
| `description` | str | 工具功能描述帮助LLM理解用途 |
| `parameters` | dict | JSONSchema格式的参数定义 |
### 方法说明
| 方法 | 参数 | 返回值 | 说明 |
|-----|------|--------|------|
| `execute` | `function_args`, `message_txt` | `dict` | 执行工具核心逻辑 |
## 🔄 自动注册机制
工具系统采用自动发现和注册机制:
1. **文件扫描**:系统自动遍历 `tool_can_use` 目录中的所有Python文件
2. **类识别**:寻找继承自 `BaseTool` 的工具类
3. **自动注册**:调用 `register_tool()` 的工具会被注册到系统中
4. **即用即加载**:工具在需要时被实例化和调用
### 注册流程
```python
# 1. 创建工具类
class WeatherTool(BaseTool):
name = "weather_query"
description = "查询指定城市的天气信息"
# ...
# 2. 注册工具(在文件末尾)
register_tool(WeatherTool)
# 3. 系统自动发现(无需手动操作)
# discover_tools() 函数会自动完成注册
```
## 🎨 完整工具示例
### 天气查询工具
```python
from src.tools.tool_can_use.base_tool import BaseTool, register_tool
import aiohttp
import json
class WeatherTool(BaseTool):
"""天气查询工具 - 获取指定城市的实时天气信息"""
name = "weather_query"
description = "查询指定城市的实时天气信息,包括温度、湿度、天气状况等"
parameters = {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "要查询天气的城市名称,如:北京、上海、纽约"
},
"country": {
"type": "string",
"description": "国家代码CN、US可选参数"
}
},
"required": ["city"]
}
async def execute(self, function_args, message_txt=""):
"""执行天气查询"""
try:
city = function_args.get("city")
country = function_args.get("country", "")
# 构建查询参数
location = f"{city},{country}" if country else city
# 调用天气API示例
weather_data = await self._fetch_weather(location)
# 格式化结果
result = self._format_weather_data(weather_data)
return {
"name": self.name,
"content": result
}
except Exception as e:
return {
"name": self.name,
"content": f"天气查询失败: {str(e)}"
}
async def _fetch_weather(self, location: str) -> dict:
"""获取天气数据"""
# 这里是示例实际需要接入真实的天气API
api_url = f"http://api.weather.com/v1/current?q={location}"
async with aiohttp.ClientSession() as session:
async with session.get(api_url) as response:
return await response.json()
def _format_weather_data(self, data: dict) -> str:
"""格式化天气数据"""
if not data:
return "暂无天气数据"
# 提取关键信息
city = data.get("location", {}).get("name", "未知城市")
temp = data.get("current", {}).get("temp_c", "未知")
condition = data.get("current", {}).get("condition", {}).get("text", "未知")
humidity = data.get("current", {}).get("humidity", "未知")
# 格式化输出
return f"""
🌤️ {city} 实时天气
━━━━━━━━━━━━━━━━━━
🌡️ 温度: {temp}°C
☁️ 天气: {condition}
💧 湿度: {humidity}%
━━━━━━━━━━━━━━━━━━
""".strip()
# 注册工具
register_tool(WeatherTool)
```
### 知识查询工具
```python
from src.tools.tool_can_use.base_tool import BaseTool, register_tool
class KnowledgeSearchTool(BaseTool):
"""知识搜索工具 - 查询百科知识和专业信息"""
name = "knowledge_search"
description = "搜索百科知识、专业术语解释、历史事件等信息"
parameters = {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "要搜索的知识关键词或问题"
},
"category": {
"type": "string",
"description": "知识分类science(科学)、history(历史)、technology(技术)、general(通用)等",
"enum": ["science", "history", "technology", "general"]
},
"language": {
"type": "string",
"description": "结果语言zh(中文)、en(英文)",
"enum": ["zh", "en"]
}
},
"required": ["query"]
}
async def execute(self, function_args, message_txt=""):
"""执行知识搜索"""
try:
query = function_args.get("query")
category = function_args.get("category", "general")
language = function_args.get("language", "zh")
# 执行搜索逻辑
search_results = await self._search_knowledge(query, category, language)
# 格式化结果
result = self._format_search_results(query, search_results)
return {
"name": self.name,
"content": result
}
except Exception as e:
return {
"name": self.name,
"content": f"知识搜索失败: {str(e)}"
}
async def _search_knowledge(self, query: str, category: str, language: str) -> list:
"""执行知识搜索"""
# 这里实现实际的搜索逻辑
# 可以对接维基百科API、百度百科API等
# 示例返回数据
return [
{
"title": f"{query}的定义",
"summary": f"关于{query}的详细解释...",
"source": "Wikipedia"
}
]
def _format_search_results(self, query: str, results: list) -> str:
"""格式化搜索结果"""
if not results:
return f"未找到关于 '{query}' 的相关信息"
formatted_text = f"📚 关于 '{query}' 的搜索结果:\n\n"
for i, result in enumerate(results[:3], 1): # 限制显示前3条
title = result.get("title", "无标题")
summary = result.get("summary", "无摘要")
source = result.get("source", "未知来源")
formatted_text += f"{i}. **{title}**\n"
formatted_text += f" {summary}\n"
formatted_text += f" 📖 来源: {source}\n\n"
return formatted_text.strip()
# 注册工具
register_tool(KnowledgeSearchTool)
```
## 📊 工具开发步骤
### 1. 创建工具文件
`src/tools/tool_can_use/` 目录下创建新的Python文件
```bash
# 例如创建 my_new_tool.py
touch src/tools/tool_can_use/my_new_tool.py
```
### 2. 实现工具类
```python
from src.tools.tool_can_use.base_tool import BaseTool, register_tool
class MyNewTool(BaseTool):
name = "my_new_tool"
description = "新工具的功能描述"
parameters = {
"type": "object",
"properties": {
# 定义参数
},
"required": []
}
async def execute(self, function_args, message_txt=""):
# 实现工具逻辑
return {
"name": self.name,
"content": "执行结果"
}
register_tool(MyNewTool)
```
### 3. 测试工具
创建测试文件验证工具功能:
```python
import asyncio
from my_new_tool import MyNewTool
async def test_tool():
tool = MyNewTool()
result = await tool.execute({"param": "value"})
print(result)
asyncio.run(test_tool())
```
### 4. 系统集成
工具创建完成后,系统会自动发现和注册,无需额外配置。
## ⚙️ 工具处理器配置
### 启用工具处理器
工具系统仅在Focus模式下工作需要确保工具处理器已启用
```python
# 在Focus模式配置中
focus_config = {
"enable_tool_processor": True, # 必须启用
"tool_timeout": 30, # 工具执行超时时间(秒)
"max_tools_per_message": 3 # 单次消息最大工具调用数
}
```
### 工具使用流程
1. **用户发送消息**在Focus模式下发送需要信息查询的消息
2. **LLM判断需求**:麦麦分析消息,判断是否需要使用工具获取信息
3. **选择工具**:根据需求选择合适的工具
4. **调用工具**:执行工具获取信息
5. **整合回复**:将工具获取的信息整合到回复中
### 使用示例
```python
# 用户消息示例
"今天北京的天气怎么样?"
# 系统处理流程:
# 1. 麦麦识别这是天气查询需求
# 2. 调用 weather_query 工具
# 3. 获取北京天气信息
# 4. 整合信息生成回复
# 最终回复:
"根据最新天气数据北京今天晴天温度22°C湿度45%,适合外出活动。"
```
## 🚨 注意事项和限制
### 当前限制
1. **模式限制**仅在Focus模式下可用
2. **独立开发**:需要单独编写,暂未完全融入插件系统
3. **适用范围**:主要适用于信息获取场景
4. **配置要求**:必须开启工具处理器
### 未来改进
工具系统在之后可能会面临以下修改:
1. **插件系统融合**:更好地集成到插件系统中
2. **模式扩展**:可能扩展到其他聊天模式
3. **配置简化**:简化配置和部署流程
4. **性能优化**:提升工具调用效率
### 开发建议
1. **功能专一**:每个工具专注单一功能
2. **参数明确**:清晰定义工具参数和用途
3. **错误处理**:完善的异常处理和错误反馈
4. **性能考虑**:避免长时间阻塞操作
5. **信息准确**:确保获取信息的准确性和时效性
## 🎯 最佳实践
### 1. 工具命名规范
```python
# ✅ 好的命名
name = "weather_query" # 清晰表达功能
name = "knowledge_search" # 描述性强
name = "stock_price_check" # 功能明确
# ❌ 避免的命名
name = "tool1" # 无意义
name = "wq" # 过于简短
name = "weather_and_news" # 功能过于复杂
```
### 2. 描述规范
```python
# ✅ 好的描述
description = "查询指定城市的实时天气信息,包括温度、湿度、天气状况"
# ❌ 避免的描述
description = "天气" # 过于简单
description = "获取信息" # 不够具体
```
### 3. 参数设计
```python
# ✅ 合理的参数设计
parameters = {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如:北京、上海"
},
"unit": {
"type": "string",
"description": "温度单位celsius(摄氏度) 或 fahrenheit(华氏度)",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["city"]
}
# ❌ 避免的参数设计
parameters = {
"type": "object",
"properties": {
"data": {
"type": "string",
"description": "数据" # 描述不清晰
}
}
}
```
### 4. 结果格式化
```python
# ✅ 良好的结果格式
def _format_result(self, data):
return f"""
🔍 查询结果
━━━━━━━━━━━━
📊 数据: {data['value']}
📅 时间: {data['timestamp']}
📝 说明: {data['description']}
━━━━━━━━━━━━
""".strip()
# ❌ 避免的结果格式
def _format_result(self, data):
return str(data) # 直接返回原始数据
```
---
🎉 **工具系统为麦麦提供了强大的信息获取能力!合理使用工具可以让麦麦变得更加智能和博学。**