# 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。