caa9bfced5
- 17个工具完整列表(共享插件 + AI-Core专属) - 注册机制 + ToolExecutor接口 - 对话中完整调用流程(含LLM API请求/响应示例) - 工具调用循环(最多5轮) - 后台思考白名单机制 - 超时异步处理 Co-Authored-By: Claude <noreply@anthropic.com>
5.4 KiB
5.4 KiB
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 接口:
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 白名单控制:
AllowedTools: []string{
"iot_query", "iot_control", "memory_search", "web_search",
"calculator", "datetime", "web_fetch",
"reminder_create", "reminder_list", "reminder_delete",
...
}
思考循环中只有白名单内的工具可被调用,防止自主思考执行高风险操作。
工具超时与异步
每个工具调用有 8 秒超时。超时的工具转入后台异步执行,结果在下一轮对话中返回。LLM 会收到提示:"该工具正在后台运行,你可以继续聊天"。
如何添加新工具
- 实现
ToolExecutor接口(Definition()+Execute()) - 在
main.go中toolRegistry.Register(wrapTool(...)) - 如需在思考中可用,加入
AutonomousToolPolicy.AllowedTools
工具注册后,LLM 会在下次请求时自动感知到新工具——无需修改 prompt。