From caa9bfced59124908b8009cc004c4d0aa7a1957b Mon Sep 17 00:00:00 2001 From: AskaEth Date: Tue, 23 Jun 2026 12:53:23 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=B7=A5=E5=85=B7=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E4=BB=8B=E7=BB=8D=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 17个工具完整列表(共享插件 + AI-Core专属) - 注册机制 + ToolExecutor接口 - 对话中完整调用流程(含LLM API请求/响应示例) - 工具调用循环(最多5轮) - 后台思考白名单机制 - 超时异步处理 Co-Authored-By: Claude --- docs/api/backend-services/tool-system.md | 183 +++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 docs/api/backend-services/tool-system.md diff --git a/docs/api/backend-services/tool-system.md b/docs/api/backend-services/tool-system.md new file mode 100644 index 0000000..a310e69 --- /dev/null +++ b/docs/api/backend-services/tool-system.md @@ -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。