File size: 13,316 Bytes
eeeb2b6 96f34e3 eeeb2b6 96f34e3 eeeb2b6 5b13359 eeeb2b6 96f34e3 eeeb2b6 762b1c8 eeeb2b6 96f34e3 5b13359 1f1f955 5b13359 96f34e3 5b13359 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | # AI 工具系统
## Tool 抽象
所有 AI 可调用的工具(Tool)都通过 **`buildTool()`** 框架构建。每个工具定义以下核心属性:
```
Tool<Input, Output, Progress>
```
### 核心字段
| 字段 | 类型 | 说明 |
|------|------|------|
| `name` | `string` | 工具名称,用于 API 调用标识 |
| `aliases` | `string[]` | 可选的别名,用于向后兼容 |
| `searchHint` | `string` | 3-10 词的关键词提示,辅助 ToolSearch 匹配 |
| `inputSchema` | `Zod Schema` | 输入参数的 Zod 校验 schema |
| `inputJSONSchema` | `object` | 可选的 JSON Schema 直接输出(MCP 工具使用) |
| `outputSchema` | `Zod Schema` | 输出参数的 Zod schema |
| `call()` | 函数 | 工具的核心执行逻辑 |
| `description()` | 函数 | 工具的描述字符串 |
| `prompt()` | 函数 | 返回工具在系统提示词中的内容 |
| `isEnabled()` | 函数 | 工具是否启用(默认 true) |
| `isConcurrencySafe()` | 函数 | 是否支持并发执行(默认 false) |
| `isReadOnly()` | 函数 | 是否为只读操作(默认 false) |
| `isDestructive()` | 函数 | 是否为破坏性操作(删除、覆盖、发送;默认 false) |
| `shouldDefer` | `boolean` | 是否延迟加载(配合 ToolSearch 使用) |
| `alwaysLoad` | `boolean` | 是否始终加载(即使在 ToolSearch 模式下) |
| `maxResultSizeChars` | `number` | 结果超过此大小时持久化到磁盘 |
| `strict` | `boolean` | 是否启用工具调用严格模式 |
| `checkPermissions()` | 函数 | 检查用户权限 |
| `validateInput()` | 函数 | 输入值校验逻辑 |
| `renderToolUseMessage()` | 函数 | 渲染工具调用 UI |
| `renderToolResultMessage()` | 函数 | 渲染工具结果 UI |
| `renderToolUseProgressMessage()` | 函数 | 渲染执行进度 UI |
| `userFacingName()` | 函数 | 面向用户显示的名称 |
| `getActivityDescription()` | 函数 | 进度提示文本 |
| `mapToolResultToToolResultBlockParam()` | 函数 | 将结果映射为 API 格式 |
### buildTool() 默认值
`buildTool()` 提供以下安全默认值:
| 方法 | 默认值 |
|------|--------|
| `isEnabled()` | `true` |
| `isConcurrencySafe()` | `false`(默认不支持并发) |
| `isReadOnly()` | `false`(默认可能写入) |
| `isDestructive()` | `false` |
| `checkPermissions()` | `{ behavior: 'allow', updatedInput: input }`(放行) |
| `toAutoClassifierInput()` | `''`(跳过分类器) |
| `userFacingName()` | `name` |
相关文件:`/home/yuki/Code/Agent/Codev/src/Tool.ts`
---
## 注册机制
### getTools() 流程
`src/tools.ts` 的 **`getTools()`** 函数是工具注册的核心入口:
```
getTools(permissionContext) → Tool[]
```
执行流程:
1. **`CLAUDE_CODE_SIMPLE` 模式**:仅返回 BashTool、FileReadTool、FileEditTool(极简模式)
2. **`getAllBaseTools()`**:收集所有内置工具,按 feature flag 和条件编译
3. **`filterToolsByDenyRules()`**:检查 deny rules,过滤被禁止的工具
4. **`REPL 模式过滤`**:当 REPL 启用时,隐藏原始工具(`REPL_ONLY_TOOLS`)
5. **`isEnabled()` 过滤**:逐个检查工具是否启用
### assembleToolPool()
`assembleToolPool()` 是内置工具与 MCP 工具的汇聚函数:
1. 调用 `getTools()` 获取内置工具
2. 通过 `filterToolsByDenyRules()` 过滤 MCP 工具
3. 按名称去重(内置工具优先)
4. 按名称排序(保持 prompt cache 稳定性)
### getMergedTools()
单纯合并内置工具与 MCP 工具(不去重、不排序),用于工具搜索阈值计算等场景。
相关文件:`/home/yuki/Code/Agent/Codev/src/tools.ts`
---
## 执行引擎
### runToolUse()
`src/services/tools/toolExecution.ts` 的 **`runToolUse()`** 是工具调用的入口点,接收 `ToolUseBlock` 并返回异步生成器。
执行生命周期:
```
runToolUse(toolUse, assistantMessage, canUseTool, toolUseContext)
→ AsyncGenerator<MessageUpdateLazy>
```
### 完整生命周期
```
LLM 请求工具调用
│
├─ 1. 查找工具 ── findToolByName()
│ ├─ 找到 → 继续
│ └─ 未找到 → 尝试别名匹配 → 返回"工具不存在"错误
│
├─ 2. 输入校验 ── inputSchema.safeParse(input)
│ ├─ 成功 → 继续
│ └─ 失败 → 返回 Zod 校验错误(含 ToolSearch schema 未发送提示)
│
├─ 3. 自定义校验 ── validateInput()
│ ├─ 通过 → 继续
│ └─ 拒绝 → 返回自定义错误消息
│
├─ 4. PreToolUse Hooks ── runPreToolUseHooks()
│ ├─ 产生消息(进度、附件)
│ ├─ 产生 hookPermissionResult(提前决定权限)
│ ├─ 产生 hookUpdatedInput(修改输入)
│ └─ 产生 stop(钩子要求停止执行)
│
├─ 5. 权限检查 ── resolveHookPermissionDecision()
│ ├─ permission mode 决定交互方式
│ ├─ 权限缓存 / alwaysAllow / alwaysDeny / alwaysAsk
│ ├─ 用户交互弹窗(default 模式)
│ ├─ 自动分类器(auto 模式)
│ └─ 结果: allow / reject / ask
│
├─ 6. 工具执行 ── tool.call()
│ ├─ 执行核心逻辑
│ ├─ 发送进度通知 (onProgress)
│ └─ 返回 ToolResult
│
├─ 7. 结果处理 ── mapToolResultToToolResultBlockParam()
│ ├─ 大结果持久化到磁盘
│ └─ 生成预览
│
├─ 8. PostToolUse Hooks ── runPostToolUseHooks()
│ ├─ MCP 工具: 可修改 toolOutput
│ └─ 非 MCP 工具: 添加附件消息
│
└─ 9. 返回结果 ── 生成 MessageUpdateLazy
├─ 成功: tool_result 消息
├─ 错误: tool_use_error 消息
└─ 中断: tool_result_stop 消息
```
---
## 权限系统
权限系统基于 `permission mode` 决定工具调用是否需要用户确认:
### Permission Mode
| 模式 | 说明 |
|------|------|
| `default` | 默认模式,敏感操作需用户确认 |
| `acceptEdits` | 自动接受文件编辑类操作 |
| `bypassPermissions` | 绕过所有权限检查 |
| `plan` | 规划模式,限制工具使用 |
| `auto` | 自动模式,分类器决定权限 |
### 权限规则覆盖
根据 `ToolPermissionContext`,系统支持:
- **alwaysAllowRules**: 始终允许的规则(按工具名称、文件路径模式、shell 命令前缀)
- **alwaysDenyRules**: 始终拒绝的规则
- **alwaysAskRules**: 始终询问的规则
- **denyRules**: 在工具注册阶段过滤掉的工具
### 权限决定来源(OTel `source` 词汇)
| 来源 | 说明 |
|------|------|
| `user_temporary` | 用户会话级临时允许 |
| `user_permanent` | 用户永久允许(存盘) |
| `user_reject` | 用户拒绝 |
| `hook` | 钩子系统决定 |
| `config` | 配置/预设规则决定 |
---
## 进度通知系统
工具执行过程中通过 `onProgress` 回调发送进度通知,不同类型的工具有不同的进度类型:
| 进度类型 | 说明 | 对应工具 |
|----------|------|----------|
| `BashProgress` | Shell 命令执行进度 | BashTool |
| `AgentProgress` | 子代理执行进度 | AgentTool |
| `MCPProgress` | MCP 工具调用进度 | MCP 工具 |
| `SkillToolProgress` | 技能执行进度 | SkillTool |
| `TaskOutputProgress` | 后台任务输出进度 | TaskOutputTool |
| `WebSearchProgress` | 网络搜索进度 | WebSearchTool |
| `LocationToolProgress` | 地理位置搜索进度 | LocationTool |
| `REPLToolProgress` | REPL 工具进度 | REPLTool |
| `HookProgress` | 钩子执行进度 | Hooks |
相关文件:
- `/home/yuki/Code/Agent/Codev/src/services/tools/toolExecution.ts` — 执行引擎
- `/home/yuki/Code/Agent/Codev/src/types/tools.ts` — 进度类型定义
- `/home/yuki/Code/Agent/Codev/src/hooks/toolPermission/` — 权限钩子
- `/home/yuki/Code/Agent/Codev/src/utils/permissions/` — 权限工具函数
---
## 关键工具详述
### AgentTool
执行子代理任务的核心工具。
- **输入参数**: `description`, `prompt`, `subagent_type`, `model`, `run_in_background`, `name`, `team_name`, `mode`, `isolation`, `cwd`
- **行为**: 启动一个独立的子代理会话,可指定代理类型、模型、隔离方式
- **权限**: 可能需要用户确认
- **进度**: `AgentProgress` 类型
### BashTool
在本地 Shell 中执行命令。
- **输入参数**: `command`, `timeout`, `description`, `run_in_background`, `dangerouslyDisableSandbox`
- **行为**: 执行任意 shell 命令,支持超时控制、后台运行、沙箱模式
- **权限**: 按命令前缀匹配权限规则
- **进度**: 超过 2 秒显示 `BashProgress`
- **自动分类**: search/read/list 命令会折叠显示
### Read / Edit / Write
三大文件操作工具:
- **FileReadTool**: 读取文件内容,支持 PDF、图片、Jupyter Notebook
- **FileEditTool**: 精确替换文件中的字符串,支持 diff 显示和 git diff 跟踪
- **FileWriteTool**: 创建或覆盖写入文件内容,包含文件变更追踪
### WebSearch / WebFetch
#### 双后端架构
WebSearchTool 支持两个搜索后端:
| 后端 | 类型 | 配置 | 特点 |
|------|------|------|------|
| **Tavily** | 云 API | `TAVILY_API_KEY` 环境变量 | 稳定、无需自托管 |
| **SearXNG** | 自托管 | Docker 运行 `verspersearch` | 完全隐私、无 API 成本 |
后端自动选择:若配置了 `TAVILY_API_KEY` 则使用 Tavily,否则回退到本地 SearXNG。
#### WebFetch
抓取 URL 内容并应用 prompt 处理(提取、总结)。支持将结果渲染为 Markdown 格式,包含图片链接。
配置项:
- `JINA_API_KEY` — 可选的 Jina AI API 密钥,用于增强型内容提取
相关文件:
- `/home/yuki/Code/Agent/Codev/src/Tool.ts` — Tool 类型与 buildTool 框架
- `/home/yuki/Code/Agent/Codev/src/tools.ts` — 工具注册与过滤
- `/home/yuki/Code/Agent/Codev/src/services/tools/toolExecution.ts` — 执行引擎
- `/home/yuki/Code/Agent/Codev/src/services/tools/toolHooks.ts` — 工具钩子
---
### LocationTool
地理位置与地图搜索工具。根据地区自动选择地图服务商:
| 地区 | 服务商 | 环境变量 |
|------|--------|----------|
| 中国大陆 | 高德地图 (Amap) | `AMAP_API_KEY` |
| 海外 | Google Maps | `GOOGLE_MAPS_API_KEY` |
**支持的操作:**
| 动作 | 功能 | Amap API | Google API |
|------|------|----------|------------|
| `locate` | IP 地理定位 (无需 location 参数) | — | — |
| `geocode` | 地址→坐标 | 地理编码 | Geocoding |
| `search_places` | 周边/关键词搜索 POI | POI 周边搜索 | Places / Nearby Search |
| `get_directions` | 两点间路线规划 | 驾车/公交路径规划 | Directions |
| `plan_trip` | 多途经点行程规划 | 分段路径拼接 | 多航点 Directions |
**地区自动检测:** 含中文字符或已知中国城市名 → Amap,否则 → Google。支持 `region` 参数强制指定。
**配合搜索工具:** Prompt 层面指导模型在获取地点后,可调用 WebSearchTool 查攻略/评价,或用 WebFetchTool 获取详情页内容。
**搜索结果数量:** 高德搜索默认翻页至全部结果(每页 1000 条,最多 20 页 ≈ 10000 条);Google 搜索使用 `next_page_token` 翻页(最多 60 条,Google 上限)。`locate` 动作优先尝试 Amap/Google WiFi 指纹定位,最后回退到 IP 定位(3 个 IP 服务)。
**输入参数:** `action` (必填), `location` (必填), `destination`, `query`, `radius` (默认 5000m), `mode` (driving/walking/transit/bicycling), `waypoints`, `type` (Amap POI 类型过滤), `region` (强制指定), `language`
相关文件:
- `/home/yuki/Code/Agent/Codev/src/tools/LocationTool/LocationTool.ts` — 主工具逻辑
- `/home/yuki/Code/Agent/Codev/src/tools/LocationTool/prompt.ts` — 提示词
- `/home/yuki/Code/Agent/Codev/src/tools/LocationTool/UI.tsx` — 渲染组件
#### API Key 申请指南
**高德地图 API Key(中国大陆用)**
1. **注册账号** — 打开 [高德开放平台](https://lbs.amap.com/),点击右上角「注册」
2. **创建应用** — 登录后进入控制台 → 「应用管理」→ 「创建新应用」
3. **添加 Key** — 在创建的应用中点击「添加 Key」→ 服务平台选择 **「Web 服务」**
4. **获取 Key** — 创建成功后复制 Key,设为环境变量:
```bash
export AMAP_API_KEY=你的高德Key
```
**Google Maps API Key(海外用)**
1. **创建项目** — 打开 [Google Cloud Console](https://console.cloud.google.com/),创建新项目
2. **启用 API** — 进入「API 和服务」→「库」,搜索并启用以下 API:
- **Geocoding API**(地址 → 坐标)
- **Places API**(地点搜索)
- **Directions API**(路线规划)
3. **创建凭据** — 「API 和服务」→「凭据」→「创建凭据」→「API 密钥」
4. **限制密钥**(强烈建议)— 在凭据页面设置 API 限制,仅允许上面启用的三个 API,避免滥用
5. 设为环境变量:
```bash
export GOOGLE_MAPS_API_KEY=你的GoogleKey
```
|