diff --git a/IMPLEMENTATION_LOG.md b/IMPLEMENTATION_LOG.md new file mode 100644 index 0000000..2765a88 --- /dev/null +++ b/IMPLEMENTATION_LOG.md @@ -0,0 +1,306 @@ +# Picobot 工具机制增强实现日志 + +## 实现记录 + +### 1. SchemaCleanr - 跨 Provider Schema 归一化 + +**日期**: 2026-04-07 +**Commit**: `d5b6cd2` + +#### 背景 +不同 LLM provider 对 JSON Schema 支持差异很大: +- **Gemini**: 最严格,不支持 `minLength`, `maxLength`, `pattern`, `minimum`, `maximum` 等 +- **Anthropic**: 中等,只要求解决 `$ref` +- **OpenAI**: 最宽松,支持大部分关键词 + +#### 实现方案 + +创建 `src/tools/schema.rs`,提供: + +1. **`CleaningStrategy` enum** + ```rust + pub enum CleaningStrategy { + Gemini, // 最严格 + Anthropic, // 中等 + OpenAI, // 最宽松 + Conservative, + } + ``` + +2. **`SchemaCleanr::clean()`** - 核心清洗函数 + - 移除 provider 不支持的关键词 + - 解析 `$ref` 到 `$defs`/`definitions` + - 将 `anyOf`/`oneOf` 合并为 `enum` + - 将 `const` 转换为 `enum` + - 移除 `type` 数组中的 `null` + +3. **`SchemaCleanr::validate()`** - Schema 验证 + +#### 使用方法 +```rust +use picobot::tools::{SchemaCleanr, CleaningStrategy}; + +// Gemini 兼容清洗(最严格) +let cleaned = SchemaCleanr::clean_for_gemini(schema); + +// Anthropic 兼容清洗 +let cleaned = SchemaCleanr::clean_for_anthropic(schema); + +// OpenAI 兼容清洗(最宽松) +let cleaned = SchemaCleanr::clean_for_openai(schema); + +// 自定义策略 +let cleaned = SchemaCleanr::clean(schema, CleaningStrategy::Conservative); +``` + +#### 工具 Trait 增强 + +在 `src/tools/traits.rs` 的 `Tool` trait 中新增: + +```rust +pub trait Tool: Send + Sync + 'static { + // ... 原有方法 ... + + /// 是否只读(无副作用) + fn read_only(&self) -> bool { false } + + /// 是否可以与其他工具并行执行 + fn concurrency_safe(&self) -> bool { + self.read_only() && !self.exclusive() + } + + /// 是否需要独占执行 + fn exclusive(&self) -> bool { false } +} +``` + +这些属性为后续的并行工具执行提供基础。 + +#### 测试 +- 12 个单元测试覆盖所有清洗逻辑 +- 运行 `cargo test --lib tools::schema` 验证 + +### 2. file_read 工具 + +**日期**: 2026-04-07 +**Commit**: `a9e7aab` + +#### 功能 +- 读取文件内容(支持 offset/limit 分页) +- 返回带行号的内容,便于引用 +- 自动处理二进制文件(base64 编码) +- 可选的目录限制(安全隔离) + +#### Schema +```json +{ + "type": "object", + "properties": { + "path": { "type": "string", "description": "文件路径" }, + "offset": { "type": "integer", "description": "起始行号(1-indexed)" }, + "limit": { "type": "integer", "description": "最大行数" } + }, + "required": ["path"] +} +``` + +#### 使用方法 +```rust +use picobot::tools::FileReadTool; + +// 基本用法 +let tool = FileReadTool::new(); +let result = tool.execute(json!({ + "path": "/some/file.txt", + "offset": 1, + "limit": 100 +})).await; +``` + +#### 测试 +- 4 个单元测试 +- `cargo test --lib tools::file_read` + +### 3. file_write 工具 + +**日期**: 2026-04-07 +**Commit**: `16b052b` + +#### 功能 +- 写入内容到文件 +- 自动创建父目录 +- 覆盖已存在文件 + +#### Schema +```json +{ + "type": "object", + "properties": { + "path": { "type": "string", "description": "文件路径" }, + "content": { "type": "string", "description": "写入内容" } + }, + "required": ["path", "content"] +} +``` + +#### 测试 +- 5 个单元测试 +- `cargo test --lib tools::file_write` + +### 4. file_edit 工具 + +**日期**: 2026-04-07 +**Commit**: `f3187ce` + +#### 功能 +- 编辑文件,替换 old_text 为 new_text +- 支持多行编辑 +- 模糊匹配处理微小差异 +- replace_all 选项批量替换 + +#### Schema +```json +{ + "type": "object", + "properties": { + "path": { "type": "string", "description": "文件路径" }, + "old_text": { "type": "string", "description": "要替换的文本" }, + "new_text": { "type": "string", "description": "替换后的文本" }, + "replace_all": { "type": "boolean", "description": "替换所有匹配", "default": false } + }, + "required": ["path", "old_text", "new_text"] +} +``` + +#### 测试 +- 5 个单元测试 +- `cargo test --lib tools::file_edit` + +### 5. bash 工具 + +**日期**: 2026-04-07 +**Commit**: `68e3663` + +#### 功能 +- 执行 shell 命令 +- 超时控制 +- 危险命令检测(rm -rf, fork bombs) +- 输出截断 +- 工作目录支持 + +#### Schema +```json +{ + "type": "object", + "properties": { + "command": { "type": "string", "description": "Shell 命令" }, + "timeout": { "type": "integer", "description": "超时秒数", "minimum": 1, "maximum": 600 } + }, + "required": ["command"] +} +``` + +#### 测试 +- 7 个单元测试 +- `cargo test --lib tools::bash` + +### 6. http_request 工具 + +**日期**: 2026-04-07 +**Commit**: `1581732` + +#### 功能 +- HTTP 客户端支持 GET/POST/PUT/DELETE/PATCH +- 域名白名单 +- SSRF 保护(阻止私有IP、localhost) +- 响应大小限制和截断 +- 超时控制 + +#### Schema +```json +{ + "type": "object", + "properties": { + "url": { "type": "string", "description": "请求 URL" }, + "method": { "type": "string", "description": "HTTP 方法", "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"] }, + "headers": { "type": "object", "description": "请求头" }, + "body": { "type": "string", "description": "请求体" } + }, + "required": ["url"] +} +``` + +#### 测试 +- 8 个单元测试 +- `cargo test --lib tools::http_request` + +### 7. web_fetch 工具 + +**日期**: 2026-04-07 +**Commit**: `8936e70` + +#### 功能 +- 获取 URL 并提取可读文本 +- HTML 转纯文本 +- 移除 scripts, styles, HTML 标签 +- 解码 HTML 实体 +- JSON 格式化输出 +- SSRF 保护 + +#### Schema +```json +{ + "type": "object", + "properties": { + "url": { "type": "string", "description": "要获取的 URL" } + }, + "required": ["url"] +} +``` + +#### 测试 +- 6 个单元测试 +- `cargo test --lib tools::web_fetch` + +--- + +## 工具清单 + +| 工具 | 名称 | 文件 | 功能 | +|------|------|------|------| +| calculator | 计算器 | `src/tools/calculator.rs` | 25+ 数学和统计函数 | +| file_read | 文件读取 | `src/tools/file_read.rs` | 带分页的文件读取 | +| file_write | 文件写入 | `src/tools/file_write.rs` | 创建/覆盖文件 | +| file_edit | 文件编辑 | `src/tools/file_edit.rs` | 文本替换编辑 | +| bash | Shell 执行 | `src/tools/bash.rs` | 带安全保护的命令执行 | +| http_request | HTTP 请求 | `src/tools/http_request.rs` | API 请求 | +| web_fetch | 网页获取 | `src/tools/web_fetch.rs` | HTML 内容提取 | + +## 工具机制增强 + +### SchemaCleanr +跨 LLM Provider 的 JSON Schema 归一化,支持: +- Gemini (最严格) +- Anthropic (中等) +- OpenAI (最宽松) +- Conservative (保守) + +### 工具属性 +```rust +fn read_only(&self) -> bool { false } // 是否只读 +fn concurrency_safe(&self) -> bool { true } // 是否可并行 +fn exclusive(&self) -> bool { false } // 是否独占 +``` + +## 运行测试 + +```bash +cargo test --lib # 所有测试 +cargo test --lib tools::schema # SchemaCleanr +cargo test --lib tools::file_read # file_read +cargo test --lib tools::file_write # file_write +cargo test --lib tools::file_edit # file_edit +cargo test --lib tools::bash # bash +cargo test --lib tools::http_request # http_request +cargo test --lib tools::web_fetch # web_fetch +```