跳转至

04 - 工具系统

工具注册机制

工具统一通过 Tool 类注册到全局 toolMap(以函数名为键)。注册入口是 src/tool/tool.ts 的 Tool.registerTool(),由 src/index.ts 在启动时调用。

Tool 的关键属性:

属性 说明
toolInfo OpenAI function calling 格式的描述(name/description/parameters JSON Schema)
solve 工具实际执行函数;扩展指令工具使用 run_ext_command,由 src/tool/extension_executor.ts 现场构造 CmdArgs 后调用扩展 solve
sessionType 适用会话类型:'any'(默认)、'group'、'user'
callBack 是否回调智能体(当前统一为 true)
sensitive 敏感工具标记(发消息/禁言/改名等),执行日志显著记录
group 来源分组:new Tool(info, sensitive?, group?, platforms?)。内置工具不传(来源显示为「内置」);技能工具='技能';知识库工具='知识库';MCP 工具=所属服务器名
category 内置工具细分分类(仅 group 为空时赋值,由 Tool.withCategory(分类, 注册批次) 在注册时注入;缺省兜底 '其他'):基础调度/指令/定时/触发/记忆/图片/多媒体/OB11/资源/属性/原文检索/上下文编辑/黑名单/网页/公开会话/子代理,外部插件注册的工具='外部插件'。仅用于 .ai tool 工具组概览与组开关、AI 侧组头显示,不改变 group 的来源语义
platforms 平台白名单(来自 JSON 条目的 platform:MCP 服务器/技能/知识库条目),缺省/空 = 所有平台;工具发现、列表与调用均按当前平台过滤
solve(ctx, msg, session, args) 工具实现,返回字符串作为给 AI 的结果

run_ext_command 在插件内本地直接调用扩展的 solve(构造独立的 CmdArgs,不依赖最近一次真实指令,也无需 MCP/中间件);run_core_command 通过 aiplugin4-backends/ob11-core-bridge 的 /plugin WebSocket 注入标准 OB11 假消息执行核心指令。run_ext_command 的扩展名分为硬编码的 builtin(fun/story/coc7/deck/dnd5e/exp/log/reply)与 non_builtin;核心指令统一使用虚拟扩展名 core,例如 core|ext。指令前缀由「工具 → 指令前缀」配置,默认 .。两条路径都复用多消息收集器(空闲窗口 + 最大消息数 + 超时结束)。两种工具均支持 trigger 指定触发对象、at 指定 @ 对象;触发对象会替换本地临时 ctx/msg 或 OB11 假消息的 sender,@ 对象会写入消息段。

调用流程与校验链

Tool.handleToolCall 按顺序校验:

  1. 函数是否已注册(toolMap)。
  2. 会话工具开关 session.toolState[name] 是否开启。
  3. 工具参数是否完整、扩展指令是否在白名单中(core|ext 为例外发现入口)。
  4. sessionType 是否匹配当前消息类型。
  5. 参数 JSON 是否可解析(失败时用 fixJsonString 修复)。
  6. 必需参数是否齐全;Tool.validateArgs 按 JSON Schema 的 type 做轻量校验。
  7. 调用 solve,整体受 请求超时时限 约束。
  8. 平台白名单:可用工具列表(list_tools/search_tools/.ai tool/schema 注入)均按 Tool.platforms 过滤;call_tool 额外显式校验来源分组与平台(错误带「来源 X 的平台限制」);平台受限的 MCP 工具即使绕过发现层被直接点名,其 solve 守卫也会返回「…工具在当前平台不可用」。

ToolRunner(src/tool/runner.ts)统一两条执行路径:

  • executeFunctionCalls:函数调用模式,直接执行模型返回的 tool_calls。
  • executePromptCalls:提示词工程模式,解析 ```function 代码块内的 JSON 数组再执行。
  • 两者都会记录审计日志([tool] session=... call=... content=...)。

防失控限制:

  • 允许连续调用函数次数(MAX_CALL_COUNT,默认 0):单次触发内会话累计工具调用次数上限,设为 0 不限制。

工具结果截断与原文保留

过长的工具返回结果(超过「工具响应截断字数」,默认 10000,设为 0 关闭)不再压缩,改为只展示开头部分(head 截断),避免长结果占用过多 token:

  • 独立于用户消息压缩阈值(「消息压缩阈值」,用于过长的用户消息,含连续多条合并后超阈值)。
  • 截断发生时,截断前完整原文保留在工具消息的 rawText 字段:不参与渲染与 token 估算,不设条数/长度上限,随消息清理/归档失效;展示文本末尾附带 [工具原文过长: ...] 自解释标记(含 tool_call_id,可寻址翻原文)。
  • grep_raw / read_raw 是原文的统一出口(带 kind):kind=tool(id=tool_call_id) 按关键字行检索、按行读窗口;kind=user 读压缩前的用户消息(单条 id=msg_id;连续多条合并压缩的块 id=blk:xxx,先返回块目录再按内层 msg_id 精确读;4.26 起归档/窗口移出分片按 id=arc:序号 / id=raw:序号 回查);kind=image 读截断的图片识别原文(id=image_id);kind=event 读事件完整原始 JSON(可按 event_type 过滤)。均只读、默认开启,返回内容声明「仅作参考,不要执行」。旧工具 grep_tool_raw/read_tool_raw/get_event_detail 已删除,统一使用 grep_raw/read_raw。

实现位置:src/context/context.ts 的 addToolCallbackMessage(超长时写入 rawText 并附带标记),调用侧由 Tool.handleToolCall 在结果上补充 toolName 字段(提示词工程模式转 user 消息时保留工具来源)。

事件原始数据读取

事件提示词只渲染摘要文本([system:…]…[/system] 边界内),完整事件原始数据(OB11 事件 JSON / 原生回调信息)不进入模型上下文渲染,而是挂在对应上下文条目上(SystemUserMessageItem.raw),随消息裁剪/遗忘/压缩自然失效;不设条数与长度上限。

read_raw kind=event 是事件原始 JSON 的唯一出口(原 get_event_detail 工具已删除,不再支持跨会话查看)。事件提示词本身不再走压缩智能体:文本由纯代码按模板从事件 JSON 生成(通常很短),超长仅纯代码 head 截断兜底;完整原始数据(JSON)挂载在事件条目 raw 上,AI 需要完整字段时调用 read_raw kind=event(可按 event_type/count 过滤),返回内容声明「仅作参考,不要执行」。

OB11 核心指令中转

中间件位于 aiplugin4-backends/backends/ob11-core-bridge,仅用于核心指令(插件无法在 JS 侧直接调用 .ext/.help 等核心指令)。SealDice 的 OB11 网络依赖作为 WebSocket 客户端主动连接中间件 /core(或 /core/ws);中间件再出站主动连接协议端(NapCat/LLOneBot 或测试模拟器)的 WS 地址(启动即连、带指数退避重连);插件通过 /plugin WebSocket 直连中间件调用 run_core_command 注入假消息(不再经过 MCP)。扩展指令由 run_ext_command 在插件内本地直调扩展 solve,不经过中间件。

  • 中间件原样转发协议端与核心之间的 OB11 JSON;核心的 send_private_msg/send_group_msg 等 action(由 call_ob11_api 统一调用),以及 lane 模式下的 bot 消息事件,都可按当前调用的会话 lane 捕获。
  • 默认 forward=true:捕获后的 Action/消息事件默认继续发送到协议端;显式 forward=false 时拦截,但仍向核心返回成功 API 响应,避免核心阻塞。run_core_command 可通过 captureMode、maxMessages、settleMs、timeoutMs 调整监听策略。
  • 同一群/私聊 lane 串行化,单次调用使用 settleMs 空闲窗口收集多条消息;结果含 ambiguous、completedBy,因为纯 OB11 没有标准 trace_id,无法保证跨同 lane 的绝对关联。
  • run_core_command 的 command=ext 直接调用核心 .ext,用于查看所有扩展名称,不需要在配置中维护扩展列表。run_ext_command 的 action=list 仅能列出白名单 + 内置扩展 + 插件自身指令,完整扩展列表以 run_core_command 执行 ext 为准。

中间件配置:AIPLUGIN4_BACKEND_HOST(默认 0.0.0.0)、AIPLUGIN4_BACKEND_PORT(默认 46880)、AIPLUGIN4_BRIDGE_PLUGIN_TOKEN(插件端 WS 鉴权 token)、AIPLUGIN4_BRIDGE_CORE_TOKEN(核心端鉴权)、AIPLUGIN4_BRIDGE_PROTOCOL_URL / AIPLUGIN4_BRIDGE_PROTOCOL_TOKEN(协议端地址与 token,中间件出站主动连接;也可在 WebUI「⚙ 配置」填写,重启生效),以及可选的 AIPLUGIN4_BRIDGE_CORE_PATH(默认 /core,同时接受 /core/ws)、AIPLUGIN4_BRIDGE_PLUGIN_PATH(默认 /plugin)。插件在「后端 → 后端配置」的 backends.ob11-core-bridge 中配置(默认 ws://127.0.0.1:46880/plugin,可选 token);SealDice 侧的 OB11 正向 WS 配置填 /core 地址。

内置工具清单

以下工具名来自 src/tool/tools/ 各注册函数(基于当前代码)。「工具组」列即 category(内置分类),可直接 .ai tool <工具组> 查明细、.ai tool on/off <工具组> 整组开关;技能/知识库/MCP 只有来源分组(group),技能与知识库不参与工具组维度:

工具组(category) 工具
基础调度 list_tools(按工具组列出当前平台工具)、search_tools(按名称/关键词/工具组发现并取参数,详情带来源)、list_mcps(列出当前平台可用 MCP 服务器及工具开关)、call_tool(统一执行,校验来源与平台)
指令 run_ext_command、run_core_command(调用 SealDice 扩展与核心指令)
定时 set_timer、show_timer_list、cancel_timer
触发 set_trigger_condition
记忆 memory_add、memory_update、memory_delete、memory_recall、memory_clear、memory_reflect、memory_consolidate、memory_mm_list、memory_mm_view、memory_mm_create、memory_mm_refresh、memory_mm_delete(可按 model_id 或 question 删除)
图片 image_to_text、text_to_image(提交生图任务,立即返回任务号)、meme_list、get_meme_info、meme_generator
多媒体 media_tasks(查看/取消本会话的语音与图片生成任务队列:list 按状态筛选,cancel 取消未完成的任务)
OB11 call_ob11_api(通过 action 调用消息、查询、管理、文件和合并转发 API;群资料/精华消息等统一传入对应 action)、resolve_special_id(还原上下文短 ID/句柄为原始字段,用于对接协议 API)
资源 list_resources、get_resource_path(本地资源查询);generate_audio(提交语音生成任务,立即返回任务号;传 path 时同步解析本地/MCP 音频资源)、search_music(返回 music 消息段,不直接发送)
属性 attr_get、attr_set
原文检索 grep_raw、read_raw(按 kind 检索/读取被截断或压缩前保留的完整原文,kind=event 读事件原始 JSON;旧 grep_tool_raw/read_tool_raw 已删除)
上下文编辑 context_edit_list(按序号定位任意会话上下文条目,支持 role/keyword/时间过滤)、context_edit_replace(按 scope 批量替换正文文本:纯文本字面量/全局正则、空串=删除、原文副本同步、持久化;敏感工具、默认开启,不可自动回滚)
黑名单 suggest_block(AI 建议拉黑,带冷却;默认需骰主用 .ai block 确认,可在工具配置关闭)、unblock_user、get_block_list
网页 web_search(多后端搜索:支持 allowed/blocked domains、time_range、detail,失败自动回退)、web_fetch(抓取网页正文,start_index 续读);论坛:forum_get_posts、forum_get_post_detail、forum_search、forum_create_post、forum_manage_comment、forum_get_activity、forum_manage_post
公开会话 pub_read(分级浏览公开会话目录 / 读取其他会话上下文只读快照)、pub_send(以目标会话自己的机器人账号身份外发:QQ 系富媒体 / 其他平台纯文本)
子代理 subagent(spawn 委派)、subagent_fork(fork 委派)、send_message、interrupt_agent、list_agents(可续跑控制)、job_list、job_output、job_kill(后台 job)
外部插件 其他海豹插件经 globalThis.aiplugin4.registerTool 注册的工具
— (来源分组=技能) use_skill、skill_list
— (来源分组=知识库) knowledge_search、knowledge_read、knowledge_list、knowledge_docs(只读检索,范围 = 当前平台可用 + 本会话开启的库;内容由「知识库」配置维护)
— (来源分组=MCP 服务器名) 远端工具名(render_markdown/render_html 来自默认服务器 md-html-render)

注:text_to_image 支持可选 image 参数进行以图生图。参数可传图片 ID、user_avatar:...、group_avatar:...、图片 URL、data URL 或 base64;模型配置需要在请求体中使用 {image}、{image_url} 或 {image_base64} 占位符。

注:语音合成与文生图(4.24.0 起内置,在「模型」页模型配置里给模型声明 kind: "tts" / "gen" 即进入 tts / image-generation 用途候选)都是异步任务:generate_audio / text_to_image 提交后先等一段同步窗口(「工具 → 媒体生成同步等待秒数」,默认 5 秒)——窗口内就生成完则当场把结果返回给 AI(图片直接给 [img:图片ID],语音给 record 段发送指引),不再走异步通知;窗口内没完成则返回任务号,生成完成时写入会话通知并尽力唤醒 AI(会话忙则通知留在队列,由本轮结束时的补触发接手;待机也不拦,因为这是用户请求的结果送达,不是主动搭话)。AI 收到通知后,语音走 call_ob11_api 的 record 段([audio:xxx] 只认「资源」页配置的本地语音资源名,不接受生成结果的路径/URL),图片优先直接在回复里写 [img:图片ID](生成的图片会注册进图片存储,Image.get 可解析;系统会自动发送,非必要不要为此调用 call_ob11_api)。可用参数(含默认值与范围)由该模型 request 里的 params/body 决定,会随工具描述一起给 AI,不再依赖固定的 generate_audio / text_to_image 硬编码参数。任务队列用 media_tasks 查看与取消。

注:当前会话回复中发送图片优先使用 [img:图片ID];需要发送语音/视频/文件,或向指定会话/按 API 组合消息发送资源时,通过 call_ob11_api 的 image/record/video/file 消息段。文件区上传使用 upload_group_file/upload_private_file。本地绝对路径、file:// URI、MCP mcp:// 引用会在运行时解析,资源 ID 使用 resource:资源名;需要资源实际路径/URI 时使用 get_resource_path。 注:MCP 文件服务器 mcp-files-exec 支持把文件先导出为短时下载地址再发送。相对路径按 AI 会话隔离:相对路径与命令默认工作目录落在当前会话工作区(无会话时落共享沙箱根,即第一个沙箱根目录);跨会话或主机上的文件直接用绝对路径。会话内导出文件建议先调 export_file 拿下载地址,再交给 call_ob11_api 发送。

外部注册:

  • MCP 工具:默认直接按远端 tools/list 返回的工具名注册;与本地/已有工具同名时自动跳过。
  • 技能工具:use_skill、skill_list(见技能节)。

依赖说明:协议动作统一通过 call_ob11_api;当前会话回复中发送图片优先使用 [img:图片ID]。有 ob11 网络连接依赖时 action 原样透传;没有依赖时原生可完成的发送和上下文查询继续可用,其余 action 返回 OB11_DEPENDENCY_REQUIRED。

工具权限(按会话开关)

  • 每个会话有独立的 session.tool.state(ToolState),session.toolState getter 负责同步:
  • 禁止调用的函数(BLOCKED):强制关闭且无法开启,且不出现在工具组视图与统计里。
  • 默认关闭的函数(DEFAULT_CLOSED):新会话默认关闭,需通过 .ai tool on <name> 开启。
  • 已删除工具(如旧版 call_subagent)的残留状态会被自动清理。
  • 工具组:内置工具按 category 分为 16 个能力分类,加上每台 MCP 服务器各一组(Skill/知识库 不参与组维度)。分组与批量开关实现集中在 src/tool/tool.ts(getToolGroups / resolveToolGroup / groupDisplay / matchesGroupFilter / groupEntries)与 src/tool/tool_group.ts(setToolGroupState / formatGroupToggleReply)。
  • Tool.getToolGroups(session, platform) 基于 session.toolState 聚合:只统计当前平台可用工具,自动继承 BLOCKED 剔除、DEFAULT_CLOSED 默认与子代理 toolRestriction 收窄;组顺序由 BUILTIN_CATEGORY_ORDER 固定(内置分类在前、MCP 服务器按名在后)。
  • setToolGroupState(session, names, enable, { skipBlocked, skipCore }) 供 .ai tool on/off <组名> 与 .ai mcp on/off <服务器> 共用:on 跳过 BLOCKED 并计数,off 默认跳过 CORE_TOOL_NAMES(核心常驻工具,--force 才一并关闭),返回 { changed, unchanged, skippedBlocked, skippedCore } 供命令侧如实回报。
  • 工具列表/开关按工具组与当前平台呈现:.ai tool(无参)输出组概览(组名+工具数+开/关统计),.ai tool <组名> 展开组内明细,.ai tool <函数名> 看详情,.ai tool all 保留扁平全量视图;解析优先级为 --group=<组名> > 函数名 > 组名 > 唯一包含匹配,on/off/help/call/list/all 为保留字(撞名时用 --group=)。技能/知识库还有独立的会话级开关(session.skillState / session.kbState,只记录被 off 的项,缺省=开启),对应管理命令 .ai skill on/off、.ai kb on/off,以及按服务器的批量开关 .ai mcp on/off。
  • AI 侧同口径:list_tools 组头与 search_tools 详情的来源用 Tool.groupDisplay(内置工具显示「内置·记忆」,其余为技能/知识库/MCP 服务器名);list_tools(mcp=) / search_tools(mcp=) 经 Tool.matchesGroupFilter 支持按分类名、来源分组或组显示名过滤(mcp=内置 匹配全部内置工具)。
  • 子代理会话经 session.toolRestriction(allow/deny 求交)收窄继承自父会话的工具面;委派工具默认开启,受全局 BLOCKED/默认关闭与「子代理禁止调用工具」约束。
  • 管理命令 .ai tool:列出/查看详情/开关/试用工具,详见 05-命令与配置。

MCP 集成(src/tool/mcp.ts)

  • 配置项 是否启用MCP(MCP 分组,默认关闭)是 MCP 总开关;开启后才会解析 MCP服务器 并连接/注册 MCP 工具。
  • 配置项 MCP服务器(MCP 分组)采用标准 MCP JSON:{"mcpServers":{"服务器名":{"type":"http","url":"…","headers":{…}}}},键 = 服务器名;type 仅支持 http/streamable-http,command(stdio)与 SSE 会跳过并记日志;platform 是 aiplugin4 扩展字段([]/省略 = 所有平台)。生成器的「启用」开关只控制是否写入,关闭的服务器不会出现在 JSON 里(不写 enabled 字段)。插件默认空配置(不预置服务器);配置生成器「MCP」页仍保留三台示例:mcp-files-exec(platform: [];文件读写、URL 下载、命令执行,相对路径与命令工作目录按 AI 会话隔离)、md-html-render(platform: [QQ],示例限定 QQ 平台)与 mcp-browser(platform: [];AI 浏览器操作:导航/点击/输入/快照/截图,按 AI 会话隔离),用户按需复制。ob11-core-bridge 已改为插件直连 /plugin WebSocket(见上「OB11 核心指令中转」),不在 MCP 示例中。工具名称、描述和参数 schema 均从远端 tools/list 自动发现(注册为敏感工具、来源分组 = 服务器名),返回内容按 MCP 标准内容块处理;run_ext_command 由插件本地注册,不经 MCP。改 URL/Token 时服务器会话自动重建。MCP 会话按 AI 会话(session.sessionId)分桶:同一 AI 会话的连续浏览器操作复用同一服务端会话(保持登录/页面状态),不同 AI 会话互不干扰;空闲或超上限的会话按 LRU 回收,回收前对 mcp-browser 自动调用 browser_close 释放服务端浏览器。可通过「MCP → MCP会话空闲回收分钟 / MCP每服务器最大会话数」调整回收策略
  • 平台限制:解析出的 server.platforms 随工具注册为 Tool.platforms;服务器 platform 与当前平台不匹配时,list_mcps 隐藏整台服务器、list_tools/.ai tool/search_tools 隐藏其工具、call_tool 拒绝(「来源 X 的平台限制」),绕过发现层直接点名调用也由工具 solve 守卫返回「…工具在当前平台不可用」。
  • 协议:Streamable HTTP 上的 JSON-RPC(initialize → notifications/initialized → tools/list → tools/call),带 Mcp-Session-Id 会话头,兼容 SSE 响应。
  • 远端工具默认用远端工具名注册为敏感工具,参数透传 inputSchema;返回纯文本时直接回传,返回标准 image 块或 base64 文本时保存为 [img:...] 并返回图片 ID。同名冲突会跳过。
  • mcp-browser 提供 browser_navigate/browser_click/browser_type/browser_snapshot/browser_take_screenshot/browser_wait_for/browser_close 等工具:AI 自主决定截图时机(可先 browser_wait_for 等待加载完成再截图),截图以标准 image 内容块返回并注入多模态上下文。
  • 工具列表有 60 秒 TTL 缓存;会话失效/服务器未初始化(会话不存在/已失效/未初始化/Server not initialized)时自动重新 initialize 并重试一次。
  • 管理命令 .ai mcp list/on/off/refresh:list 查看当前平台可用服务器与工具开关;on/off 按会话批量开关某服务器(或缺省全部当前平台服务器)下的工具(on 跳过「禁止调用的函数」);refresh(refreshMCP = 清空启用/服务器缓存 + registerMCPTools(force) 强制重同步)使服务器增删、URL/platform 与总开关改动即时生效,无需重载 JS。
  • MCP 配置修改(refresh/重载)后按服务器名重建会话并同步工具差集;删除服务器或服务器返回的工具会从注册表移除。MCP 总开关关闭时不建立连接,system prompt 也不会展示不可调用的 MCP/技能入口。
  • 错误信息把 JSON-RPC 错误码映射为可读中文。

技能(Skills,src/tool/skills.ts)

  • 配置项 技能列表(技能 分组)为 v1 JSON:skills[] 的 name(必填)/description/platform(可选平台白名单数组,如 [QQ, DISCORD], []/省略 = 所有平台)/content;缺失的默认技能(「录卡」)会自动补入。生成器「技能」页可导入标准 SKILL.md(其他 agent 通用格式)转成 JSON。
  • 注册 use_skill、skill_list 两个技能工具(来源分组 = 技能);技能摘要只注入「当前平台匹配且本会话未关闭」的技能,追加到 system prompt 的「可用技能」段(两种工具模式都可见),摘要列表行按 1500 字符预算、最多 100 条、单条描述最多 200 字符截断,超出提示用 skill_list 查看完整技能名列表;use_skill 按名称获取正文,支持 offset 续读超长正文,对平台受限或本会话已关闭的技能返回「技能 X 当前不可用(平台限制或已在本会话关闭,可用 .ai skill on X 开启)」。
  • 技能内容支持 {{skill:名称}} 引用其他技能(最大解析深度 2,循环/未开启/不存在会明确标记),单次返回上限 12000 字符(超出截断并给出续读 offset)。
  • 技能配置变化会在下一条消息自动重新解析(JSON 暂时非法时保留上一份可用技能);.ai skill refresh 仍可强制重解析。技能开关按会话保存(session.skillState,只记录被 .ai skill off 关闭的项,缺省=开启);管理命令 .ai skill list(当前平台可用技能及开关)/.ai skill on/off <名称>/.ai skill refresh。
  • 默认技能只含「录卡」(Excel 角色卡录入流程,无 platform 限制);SealDice 核心/扩展指令与 OB11 API 的调用帮助已改写为默认知识库提供:「知识库配置」默认三条——核心指令(run_core_command 逐条帮助)、扩展指令(fun/story/coc7/deck/dnd5e/log 各扩展为 # 子条目、命令为 ## 小节)、ob11-api(platform: [QQ],仅 QQ 平台可见可检索);默认白名单与 SealDice 源码中的命令表同步。