Skip to content

Tool System

AceGuru-mjh edited this page Oct 1, 2026 · 4 revisions

工具系统 v2 / v3

🔧 能力 · 🏠 首页 › Tool-System

Home Version Kotlin Modules Tools License

Tool-System typing

📑 本页目录

工具是 LLM 触达系统能力的唯一出口。v2 回答"工具是什么"(schema / 元数据 / 风险), v3 回答"工具在真实 Agent 循环里如何不死、不炸、可观测"。 本文是 docs/tool-system-v2.md 与 docs/tool-system-v3.md 的 Wiki 化整理。

0. 一眼看懂

flowchart LR
    M["模型 tool_call"] --> R{"ToolRegistry 查找"}
    R -->|未命中| S["ToolSuggester:Levenshtein 相近建议<br/>(不再倾倒全量 id 清单)"]
    R -->|命中| P["v3 EnhancedToolExecutor 管线"]
    P --> P1["环境门 ToolEnvironmentGate(fail-open)"]
    P1 --> P2["风险门 RiskAwareToolGate / selfGated 跳过重复弹窗"]
    P2 --> P3["schema 校验 ToolSchema(声明即校验)"]
    P3 --> P4["限流 ToolRateLimiter(令牌桶,超限即拒)"]
    P4 --> P5["熔断 ToolCircuitBreaker"]
    P5 --> P6["超时 + 重试 ToolRunPolicy(全抖动指数退避)"]
    P6 --> T["真实工具实现"]
    T --> X["ToolResult(成 / 结构化错误码)"]
    P -.每次尝试.-> TR["ToolTraceRecorder:一个 ToolTraceSpan"]
    P -.聚合.-> UT["ToolUsageTracker:次数/成败/耗时"]
Loading

设计三原则(继承 v2):

  1. 全部可选注入 —— 不接 v3 组件时行为与 v2 逐字节一致,既有测试零迁移;
  2. 错误即内容 —— 所有拒绝(限流/熔断/环境/超时)返回模型可读的修复指引,而不是抛异常中断循环;
  3. fail-open —— 环境遥测缺失或过期时放行,绝不让推断性门控硬禁用工具。

1. v2:结构化工具契约

1.1 五个曾经的缺口(为什么必须改)

# 缺口 症状
1 schema 只是文档 parametersSchema 是手写 JSON 字符串,运行时零校验,模型靠猜参数
2 失败没有形状 工具返回裸字符串,失败靠 "Error: " 前缀识别,引擎分不清"参数错了改了再试"和"权限拒绝永远别试"
3 未知工具错误倾倒全量清单 打错工具名 → 错误信息塞进 40+ id 墙 → 模型继续猜
4 风险不分级 shell_execute 有命令级确认,app_uninstall / delete_file 什么都不问
5 元数据不存在 prompt 里是字母序平铺长列表(40+ 行噪音),也没有"哪个工具在被用/总在失败"的观测面

1.2 核心基建(9 个文件)

文件 职责
ToolMetadata.kt 类别(16 类)/ 风险(LOW·MEDIUM·HIGH,3 级)/ 标签 —— 按 id 推断,v1 工具零迁移
ToolResult.kt ToolErrorCode(9 个)+ 结构化结果 + StructuredAgentTool 契约
ToolSchema.kt schema DSL:声明即校验(单一真源) + v1 JSON 宽松导入
ToolArguments.kt 强类型参数读取器:缺参/类型错 → 带字段名的结构化错误
ToolExecutionGate.kt 执行门接口 + ToolPermissionManager 会话状态机
ToolUsageTracker.kt 每工具次数 / 成败 / 耗时 / 最近错误(无锁统计)
ToolSuggester.kt Levenshtein 相近建议(修复"倾倒全量 id")
ToolRegistry.kt 注册表加固 + DefaultToolExecutor v2 管线
SafeAgentTool.kt 元数据透传包装器(不吞流式,有专门回归测试)

1.3 关键设计决策

  • 字符串协议保持一等公民:ToolResult.render() 产出引擎已在解析的 "Error: …" 字符串,下游零改动;结构化信息是增量而非替代;
  • 接口新增全部带默认实现:AgentTool.metadata 默认按 id 推断;ToolRegistry.register(tool, policy) 默认 REPLACE(v1 语义),测试里的 FakeToolRegistry / StubAgentTool 一行未改仍能编译;
  • DSL 声明即校验:toolSchema { string("path", required = true) } 同时是渲染源与校验规则,结构上不可能漂移;v1 手写 schema 经 fromRendered 宽松导入,解析失败静默跳过(绝不误伤);
  • 校验对未知字段宽容:模型爱塞垃圾字段,拒绝只会造成重试循环,只强制声明过的约束;
  • selfGated 跳过双重弹窗:shell_execute 自带命令级确认(CommandPermissionGate),工具级再弹一次是骚扰,故在 ToolPermissionManager.selfGatedToolIds 预置放行;
  • 交互闭环留在 app 层:core 不依赖引擎问题桥(依赖方向约束),由 app 的 RiskAwareToolGate 注入 UserQuestionGateway 回调驱动同一状态机;headless/测试注入自己的 confirm 即可复用。

1.4 ToolErrorCode(9 个)

INVALID_ARGUMENT · PERMISSION_DENIED · NOT_FOUND · TIMEOUT · RATE_LIMITED · CIRCUIT_OPEN · EXECUTION_FAILED · UNSUPPORTED · UNKNOWN

含义:INVALID_ARGUMENT 提示"Fix the arguments and retry";PERMISSION_DENIED 提示 "换方案,勿重试" —— 这正是 v1 表达不出来的差别。

2. v3:执行硬化八层

v3 的动机来自对 7 个业界方案的源码级调研(MCP spec 2025-06-18 · LangGraph ToolNode · Anthropic computer-use · OpenAI CU · AutoGPT · LangGraph · AndroidWorld):

# 缺口 业界基准 v2 现状
1 工具级超时 MCP 规范 MUST;AndroidWorld 全工具 30s 无 —— http_request 挂死 = Agent 循环挂死
2 瞬态失败重试 LangGraph wrap_tool_call 指数退避 无 —— 一次网络抖动 = 一步失败
3 速率限制 成熟平台标配 无 —— 模型失败循环可刷 50 次/分钟
4 熔断器 Hystrix 形态 无 —— 已损坏工具持续烧 token
5 逐调用追踪 LangSmith / NodeExecutionStats 仅聚合计数(ToolUsageTracker)
6 批量顺序执行 Anthropic CU actions[],首错即停 无 —— 每步一次 LLM 往返
7 组合动作(自进化) Mobile-Agent-E Shortcut 无
8 环境态门控 Mobile-Agent 键盘门控 无 —— 键盘未弹出时 input_text 必败
9 MCP 注解词汇 readOnly/destructive/idempotent/openWorld 四 hints 仅 LOW/MEDIUM/HIGH 粗粒度

另外修了存量 bug:ToolModule 中 MCP 三工具被重复注册三次(REPLACE 静默互踩)。

2.1 ToolAnnotations(MCP 四 hints + sensitiveAction)

ToolAnnotations(readOnlyHint, destructiveHint, idempotentHint, openWorldHint, sensitiveAction)
  • retrySafe 派生:readOnly || idempotent —— 自动重试同一 payload 的安全充要条件 (重放已成功的破坏性操作 = 数据丢失);
  • 零迁移推断:infer(id, risk) 覆盖全部存量 id 族(与引擎 T76 幂等注册表同源口径),显式声明优先;
  • 挂在 ToolMetadata.annotations(带默认值,data class 向后兼容)。

2.2 ToolRunPolicy + RetryClassifier + ToolRateLimiter

预设 超时 重试 限流
quickRead 30s 1 次 120 rpm
network 90s 2 次 30 rpm
mutating 60s 不盲重试 —
legacy v1/v2 语义 — —
  • 全抖动指数退避(AWS 风格):delay = random(0 .. base * 2^attempt),避免恢复中后端的惊群;
  • RetryClassifier:权限 / 沙箱 / 参数类错误判为终态(重发同 payload 无意义);超时 / IO / 执行错误可重试;
  • ToolRateLimiter:连续回填令牌桶(capacity = 每分钟配额,tokens/ms 回填),超限立即拒绝而非排队 —— 失控循环要立刻看见错误。

2.3 ToolCircuitBreaker

CLOSED ──N 连败──▶ OPEN ──冷却结束──▶ HALF_OPEN(单探测)
                                        ├── 成功 ─▶ CLOSED
                                        └── 失败 ─▶ OPEN(冷却 2× 加宽,上限 maxCooldownMs)

纯惰性状态机(无定时器线程),check() 即求值。Deny 文案带"上次错误 + 探测倒计时 + 勿立即重试"指引。

2.4 ToolTraceRecorder + ToolCallIds

  • 每次尝试一个 ToolTraceSpan(callId, toolId, durationMs, outcome, attempt, argsDigest, errorSlug);
  • args 摘要而非原文(长度 / 可打印字符计数)—— 诊断导出不含用户粘贴的机密;
  • 环形有界缓冲(默认 200,app 配 300)+ 监听器分发 + JSON 导出 + 直方图报告;
  • 幂等完成:同一 handle 二次 complete 不产生幻影 span;
  • 与 ToolUsageTracker 并存:tracker 答"多常/多快",tracer 答"具体哪次、什么参数、第几轮重试"。

2.5 ToolBatchRunner(Anthropic CU 批量语义)

  • 严格顺序、首错即停,未执行步骤统一返回契约原文 "Not executed: an earlier action in this turn failed.";
  • {n} 步间引用:整引用 / 模板内插值,头截断 4KB 防爆炸;前向 / 越界引用在执行前拒绝(调用计数为零);
  • 整批墙钟预算默认 120s(上限 600s);步骤上限 16;
  • 模型入口 tool_batch_run(steps 支持内联数组或字符串编码两种形态)。

2.6 Shortcut 组合动作(Mobile-Agent-E)

  • ShortcutDefinition:id(shortcut.* 命名空间)/ 前置条件 / 声明参数 / 步骤模板({param} 占位符 + {0} 步间引用);
  • 解析期全量校验:未知工具、未声明占位符、死参数(声明未使用)、超步数全部字段级拒绝;
  • JSON 感知替换:字符串参数按 JSON string content 转义,数值/布尔裸插入 —— 渲染出的步骤参数永远是合法 JSON;
  • 风险继承:编译工具的风险 = 所含步骤最坏风险(含 app_uninstall 的快捷方式必为 HIGH);
  • 模型入口三件套:shortcut_define(定义 + 热注册为一等工具)/ shortcut_list(清单 + 挖掘建议)/ shortcut_run(显式执行兜底);
  • ShortcutSuggester:从 ToolTraceRecorder 的成功相邻 bigram 中挖掘高频序列(≥3 次),向模型建议打包成 Shortcut。

2.7 ToolEnvironmentState / ToolEnvironmentGate

环境遥测(如 IME 是否弹出、当前前台包名)缺失或过期 → fail-open 放行; 命中风险场景 → 返回结构化错误 + 修复指引(例如"请先打开输入框")。

3. 工具清单(按模块分组)

Note

数量随版本演进;README 当前口径为 109 个。CI 有工具 ID 唯一性门禁(ci.yml), 撞 id 会直接红。以下分组为 Registry 注册基线,动态安装的 Skill 经 SkillToolAdapter 注册为运行时工具(如 web_scrape)。

3.1 core:tool-registry(纯 JVM 为主)

类别 工具
🖥️ Shell shell_execute(三级权限链 + 流式输出)
📁 文件 read_file write_file edit_file list_files glob_files search_files copy_move_file delete_file
🌐 网络 web_fetch web_search http_request download_file(流式进度)
🧠 文件记忆 memorize recall forget
📱 应用 app_list app_launch app_install app_uninstall app_force_stop app_info
⚙️ 系统 get_device_info get_set_settings control_media clipboard get_time logcat
🖱️ UI 自动化 ui_tap ui_swipe ui_dump screenshot input_text
🧮 实用 calculate text_transform get_location notification_read
🧩 技能 skill_search skill_install skill_create skill_list skill_uninstall
🛰️ MCP mcp_connect mcp_list mcp_call
🧱 结构化 v2(15) regex_extract regex_replace text_diff json_path xml_extract csv_query base_convert unit_convert duration_convert string_distance random_generate uuid_generate file_hash datetime cron_next(全部纯 JVM / 离线 / 确定性)
⚡ 执行硬化 v3(7) wait(≤300s 可取消等待)· json_transform(jq 风格七操作管线)· version_compare(SemVer 排序)· tool_batch_run · shortcut_define / shortcut_list / shortcut_run

结构化 v2 工具的代表性实现文件:RegexTools JsonPathTool TextDiffTool XmlExtractTool UnitConvertTool JsonTransformTool WaitTool VersionCompareTool ShortcutTools …

3.2 :app(需要 Android / Activity 上下文)

类别 工具
🌐 浏览器(15) browser_navigate browser_click browser_input browser_scroll browser_select browser_screenshot browser_snapshot browser_toggle browser_show browser_download_list browser_file_upload browser_date_input browser_context_summary browser_network_log browser_debug_dump
🐙 GitHub(7) github_get_user github_list_repos github_read_file github_write_file github_create_issue github_list_issues github_search_code(配置 PAT 后才注册)

3.3 platform:terminal(18 个 terminal.*)

terminal.create / run / write / observe / snapshot / wait / resize / signal / close / workspaces / backends / linux_bootstrap / linux_status / linux_packages / linux_network / ubuntu_install,以及 T82 新增的 terminal.ubuntu.ensure / terminal.ubuntu.status。 详见 终端运行时。

3.4 platform:cs-mem(3 个记忆召回)

工具 用途
memory_search_nodes 按文本/角色搜索语义节点(支持迁移别名解析)
memory_recent_episodes 近期情景回顾
memory_recall_macro 宏技能召回

4. 如何新增一个工具

flowchart TD
    A["写实现:继承 BaseTool / StructuredAgentTool"] --> B["toolSchema { … } 声明参数(同时是渲染与校验)"]
    B --> C["ToolMetadata:类别 / 风险 / 标注(annotations)"]
    C --> D["arguments 校验:缺参/类型错 → INVALID_ARGUMENT(带字段名)"]
    D --> E["异常兜底:EXECUTION_FAILED,绝不冒泡炸掉 agent 循环"]
    E --> F["注册:ToolRegistry.register(tool, policy)"]
    F --> G{"ID 唯一?(CI 门禁)"}
    G -->|否| H["改名 / 合并重复实现"]
    G -->|是| I["补测试:happy path + 参数错误 + 权限/环境拒绝"]
Loading

清单自查:

  • id 全局唯一(CI 强制);
  • 需要 Android API 的实现必须在 platform:* 或 :app,并在 di/ToolModule 注入;
  • 破坏性操作声明 destructiveHint / 风险 ≥ MEDIUM,App 侧会走 RiskAwareToolGate;
  • 幂等信息声明正确(决定是否允许重试);
  • 有流式输出的能力实现 StreamingTool,并确认 SafeAgentTool 包装不吞流式;
  • 失败错误信息是模型可执行的指引,而不是"出错了"。

5. MCP 接入

  • McpManager + McpClient + McpHttpTransport / McpStdioTransport + McpConfigImport 实现 Model Context Protocol 客户端;
  • 三个入口工具:mcp_connect / mcp_list / mcp_call;
  • 不要在 ToolModule 里重复注册(历史 bug:三工具被注册三次,REPLACE 静默互踩)。

6. 相关页面

footer

🏠 返回首页 · 📚 文档索引 · ❓ FAQ · 🔧 故障排查 · 🗺️ 路线图 · 🐛 提 Issue

Android Guru Agent · v1.4.4 · Kotlin 2.0.21 · Compose · PRoot · Room

Clone this wiki locally