docs: 工具调用系统介绍文档
- 17个工具完整列表(共享插件 + AI-Core专属) - 注册机制 + ToolExecutor接口 - 对话中完整调用流程(含LLM API请求/响应示例) - 工具调用循环(最多5轮) - 后台思考白名单机制 - 超时异步处理 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,183 @@
|
|||||||
|
# Cyrene 工具调用系统
|
||||||
|
|
||||||
|
> **日期**:2026-06-23
|
||||||
|
> **适用范围**:对话 + 后台自主思考
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
昔涟可以调用 16 个工具来完成任务——从搜索互联网、控制智能家居,到创建提醒、检索知识库。
|
||||||
|
|
||||||
|
工具调用的核心机制是 **OpenAI Function Calling 协议**:每次 LLM 请求时,将所有可用工具的名称、描述、参数格式一并发送。LLM 自行判断是否需要调用工具,返回工具名和参数,由服务端执行后把结果送回 LLM 生成最终回复。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注册机制
|
||||||
|
|
||||||
|
### 启动时注册
|
||||||
|
|
||||||
|
`ai-core/cmd/main.go` 在服务启动时将全部工具注册到 `ToolRegistry`:
|
||||||
|
|
||||||
|
```
|
||||||
|
共享插件 (pkg/plugins)
|
||||||
|
calculator 数学表达式求值
|
||||||
|
datetime 日期时间计算/格式化
|
||||||
|
text 文本处理(统计/截断)
|
||||||
|
crypto 哈希/加解密
|
||||||
|
random 随机数/字符串
|
||||||
|
markdown Markdown 渲染
|
||||||
|
json JSON 解析/查询
|
||||||
|
file 文件读写/列表
|
||||||
|
http HTTP 请求
|
||||||
|
web_search SearXNG 网络搜索
|
||||||
|
web_fetch 网页内容提取
|
||||||
|
|
||||||
|
AI-Core 专属 (internal/tools)
|
||||||
|
iot_query IoT 设备状态查询
|
||||||
|
iot_control IoT 设备操控
|
||||||
|
knowledge_search 知识库 RAG 检索
|
||||||
|
knowledge_ingest 知识库文档导入
|
||||||
|
reminder_create 创建提醒
|
||||||
|
reminder_list 列出提醒
|
||||||
|
reminder_delete 删除提醒
|
||||||
|
```
|
||||||
|
|
||||||
|
### 工具定义
|
||||||
|
|
||||||
|
每个工具实现 `ToolExecutor` 接口:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type ToolExecutor interface {
|
||||||
|
Execute(ctx context.Context, args map[string]interface{}) (*ToolResult, error)
|
||||||
|
Definition() ToolDefinition
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToolDefinition struct {
|
||||||
|
Name string // 工具名,LLM 用它来选择
|
||||||
|
Description string // 工具用途描述,LLM 据此判断是否需要调用
|
||||||
|
Parameters map[string]interface{} // JSON Schema 参数定义
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`Description` 就是 LLM 的"说明书"——它通过阅读描述来判断当前对话是否需要调用这个工具。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 对话中的调用流程
|
||||||
|
|
||||||
|
```
|
||||||
|
用户: "帮我查一下今天天气"
|
||||||
|
|
||||||
|
Synthesizer.Synthesize()
|
||||||
|
│
|
||||||
|
├─ 1. buildOpenAITools()
|
||||||
|
│ 从 ToolRegistry 取出全部工具的定义
|
||||||
|
│ 转换为 OpenAI Function Calling 格式
|
||||||
|
│
|
||||||
|
├─ 2. ChatWithTools(messages, tools)
|
||||||
|
│ POST LLM API:
|
||||||
|
│ {
|
||||||
|
│ "model": "deepseek-v4-flash",
|
||||||
|
│ "messages": [
|
||||||
|
│ {"role":"system","content":"你是昔涟..."},
|
||||||
|
│ {"role":"user","content":"帮我查一下今天天气"}
|
||||||
|
│ ],
|
||||||
|
│ "tools": [
|
||||||
|
│ {
|
||||||
|
│ "type": "function",
|
||||||
|
│ "function": {
|
||||||
|
│ "name": "web_search",
|
||||||
|
│ "description": "搜索互联网获取实时信息...",
|
||||||
|
│ "parameters": {
|
||||||
|
│ "type": "object",
|
||||||
|
│ "properties": {
|
||||||
|
│ "query": {"type": "string", "description": "搜索关键词"}
|
||||||
|
│ },
|
||||||
|
│ "required": ["query"]
|
||||||
|
│ }
|
||||||
|
│ }
|
||||||
|
│ },
|
||||||
|
│ // ... 其余 15 个工具
|
||||||
|
│ ],
|
||||||
|
│ "tool_choice": "auto"
|
||||||
|
│ }
|
||||||
|
│
|
||||||
|
│ LLM 判断: 用户要查天气 → 需要 web_search
|
||||||
|
│ 返回:
|
||||||
|
│ {
|
||||||
|
│ "choices": [{
|
||||||
|
│ "delta": {
|
||||||
|
│ "tool_calls": [{
|
||||||
|
│ "id": "call_abc123",
|
||||||
|
│ "function": {
|
||||||
|
│ "name": "web_search",
|
||||||
|
│ "arguments": "{\"query\":\"今天天气\"}"
|
||||||
|
│ }
|
||||||
|
│ }]
|
||||||
|
│ }
|
||||||
|
│ }]
|
||||||
|
│ }
|
||||||
|
│
|
||||||
|
├─ 3. ToolRegistry.Execute("web_search", {query: "今天天气"})
|
||||||
|
│ → 发送 HTTP 请求到 SearXNG
|
||||||
|
│ → 返回: {success: true, data: "晴转多云 22°C..."}
|
||||||
|
│
|
||||||
|
├─ 4. 工具结果追加到对话历史
|
||||||
|
│ messages += {
|
||||||
|
│ role: "tool",
|
||||||
|
│ tool_call_id: "call_abc123",
|
||||||
|
│ content: "{\"success\":true,\"data\":\"晴转多云 22°C...\"}"
|
||||||
|
│ }
|
||||||
|
│
|
||||||
|
└─ 5. 再次 ChatWithTools
|
||||||
|
LLM 读取工具结果 → 生成最终回复:
|
||||||
|
"今天晴转多云,22°C♪ 很适合出门走走呢~"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 工具调用循环
|
||||||
|
|
||||||
|
LLM 可以在一轮对话中**多次调用工具**,最多 **5 轮**:
|
||||||
|
|
||||||
|
```
|
||||||
|
第 1 轮: LLM → web_search("今天天气")
|
||||||
|
第 2 轮: LLM → web_fetch("https://weather.com/detail")
|
||||||
|
第 3 轮: LLM 综合结果 → 生成回复
|
||||||
|
```
|
||||||
|
|
||||||
|
每轮 LLM 都可以选择继续调工具或直接回复。达到 5 轮上限后强制生成回复。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 后台思考中的工具调用
|
||||||
|
|
||||||
|
后台思考器(Thinker)使用相同的工具注册中心,但通过 **`AutonomousToolPolicy`** 白名单控制:
|
||||||
|
|
||||||
|
```go
|
||||||
|
AllowedTools: []string{
|
||||||
|
"iot_query", "iot_control", "memory_search", "web_search",
|
||||||
|
"calculator", "datetime", "web_fetch",
|
||||||
|
"reminder_create", "reminder_list", "reminder_delete",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
思考循环中只有白名单内的工具可被调用,防止自主思考执行高风险操作。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 工具超时与异步
|
||||||
|
|
||||||
|
每个工具调用有 **8 秒超时**。超时的工具转入后台异步执行,结果在下一轮对话中返回。LLM 会收到提示:"该工具正在后台运行,你可以继续聊天"。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 如何添加新工具
|
||||||
|
|
||||||
|
1. 实现 `ToolExecutor` 接口(`Definition()` + `Execute()`)
|
||||||
|
2. 在 `main.go` 中 `toolRegistry.Register(wrapTool(...))`
|
||||||
|
3. 如需在思考中可用,加入 `AutonomousToolPolicy.AllowedTools`
|
||||||
|
|
||||||
|
工具注册后,LLM 会在下次请求时自动感知到新工具——无需修改 prompt。
|
||||||
Reference in New Issue
Block a user