feat(agents): Phase 8.4-10.5 built-in plugins, bundled skills, coordinator
This commit is contained in:
173
development-doc/plan/tool-update/README.md
Normal file
173
development-doc/plan/tool-update/README.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Jarvis Tools 升级计划索引
|
||||
|
||||
本目录用于存放 Jarvis 工具系统的分阶段升级规划文档。
|
||||
|
||||
## 文档说明
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `README.md` | 总览、阶段关系、实施顺序 |
|
||||
| `phase-t-0-current-state.md` | 当前现状、问题、目标架构、VCPToolBox 借鉴 |
|
||||
| `phase-t-1-manifest-system.md` | Manifest 驱动系统 |
|
||||
| `phase-t-2-tool-registry.md` | 工具注册中心 |
|
||||
| `phase-t-3-tool-implementation.md` | 核心工具实现 |
|
||||
| `phase-t-4-advanced.md` | 高级特性(多运行时/Agent协作) |
|
||||
| `checklist.md` | 执行清单 |
|
||||
|
||||
## 推荐阅读顺序
|
||||
|
||||
1. 先读 `phase-t-0-current-state.md`
|
||||
2. 再按顺序阅读 phase t-1 ~ t-4
|
||||
3. 实施时严格按阶段推进
|
||||
4. 参考 `checklist.md` 进行任务追踪
|
||||
|
||||
---
|
||||
|
||||
## 总体升级原则
|
||||
|
||||
1. **Manifest 驱动** - 声明式工具定义,热插拔
|
||||
2. **标准契约** - 统一的调用格式和返回结构
|
||||
3. **多运行时** - 支持 Python/JS/原生
|
||||
4. **类型安全** - Pydantic Schema 验证
|
||||
5. **可观测性** - 调用日志、耗时统计
|
||||
|
||||
---
|
||||
|
||||
## 阶段总览图
|
||||
|
||||
```
|
||||
T.0 ──────────────────────────────────────────────────────────────┐
|
||||
│ 现状与目标 │
|
||||
│ - 当前工具系统分析 │
|
||||
│ - 短板识别 │
|
||||
│ - VCPToolBox 工具系统借鉴 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
T.1 ──────────────────────────────────────────────────────────────┐
|
||||
│ Manifest 驱动系统 │
|
||||
│ - 工具 manifest 定义 │
|
||||
│ - 标准化契约 │
|
||||
│ - Schema 验证 │
|
||||
│ │
|
||||
│ 核心文件: tools/manifests/, tools/schemas/ │
|
||||
│ 工作量: 3 天 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
T.2 ──────────────────────────────────────────────────────────────┐
|
||||
│ 工具注册中心 │
|
||||
│ - 工具发现机制 │
|
||||
│ - 动态注册 │
|
||||
│ - 工具描述生成 │
|
||||
│ │
|
||||
│ 核心文件: tools/registry.py │
|
||||
│ 依赖: T.1 │
|
||||
│ 工作量: 2 天 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
T.3 ──────────────────────────────────────────────────────────────┐
|
||||
│ 核心工具实现 │
|
||||
│ - 文件操作工具 │
|
||||
│ - 搜索工具 │
|
||||
│ - 网页抓取工具 │
|
||||
│ - 任务管理工具 │
|
||||
│ │
|
||||
│ 核心文件: tools/implementations/ │
|
||||
│ 依赖: T.2 │
|
||||
│ 工作量: 5 天 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
T.4 ──────────────────────────────────────────────────────────────┐
|
||||
│ 高级特性 │
|
||||
│ - 多运行时支持 │
|
||||
│ - Agent 间协作 │
|
||||
│ - 定时任务 │
|
||||
│ │
|
||||
│ 核心文件: tools/runtime/, agents/tools/ │
|
||||
│ 依赖: T.3 │
|
||||
│ 工作量: 4 天 │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## VCPToolBox 工具系统核心借鉴
|
||||
|
||||
| 借鉴点 | 实现位置 | 难度 |
|
||||
|--------|---------|------|
|
||||
| Manifest 驱动 | T.1 | 🟢 低 |
|
||||
| 标准化契约 | T.1 | 🟢 低 |
|
||||
| configSchema 配置 | T.1 | 🟢 低 |
|
||||
| 工具注册中心 | T.2 | 🟡 中 |
|
||||
| 动态发现 | T.2 | 🟡 中 |
|
||||
| 文件操作工具 | T.3 | 🟢 低 |
|
||||
| 搜索工具 | T.3 | 🟡 中 |
|
||||
| 网页抓取 | T.3 | 🟡 中 |
|
||||
| 多运行时支持 | T.4 | 🟡 中 |
|
||||
| Agent 间协作 | T.4 | 🟡 中 |
|
||||
|
||||
---
|
||||
|
||||
## 实施顺序
|
||||
|
||||
```
|
||||
T.0 → T.1 → T.2 → T.3 → T.4
|
||||
│ │ │ │ │
|
||||
│ │ │ │ └── 多运行时/Agent协作
|
||||
│ │ │ └── 核心工具
|
||||
│ │ └── 注册中心
|
||||
│ └── Manifest系统
|
||||
└── 现状与目标
|
||||
```
|
||||
|
||||
**注意:** T.1 是基础,后续阶段都依赖 T.1。
|
||||
|
||||
---
|
||||
|
||||
## 文件变更追踪
|
||||
|
||||
| Phase | 新增文件 | 修改文件 |
|
||||
|-------|---------|---------|
|
||||
| T.1 | `tools/manifests/*.yaml`, `tools/schemas/` | `pyproject.toml` |
|
||||
| T.2 | `tools/registry.py`, `tools/base.py` | `services/agent_service.py` |
|
||||
| T.3 | `tools/implementations/*.py` | `tools/registry.py` |
|
||||
| T.4 | `tools/runtime/`, `agents/tools/` | `agents/graph.py` |
|
||||
|
||||
---
|
||||
|
||||
## 与 Agent Phase 1-5 的关系
|
||||
|
||||
| Agent Phase | Tools 协作内容 |
|
||||
|-------------|---------------|
|
||||
| Phase 1 | Task Schema 追踪工具调用 |
|
||||
| Phase 2 | 工具可委托给执行 Agent |
|
||||
| Phase 3 | 动态选择最优工具 |
|
||||
| Phase 4 | 工具调用可视化 |
|
||||
| Phase 5 | 多 Agent 工具协作 |
|
||||
| **Phase T** | **工具系统升级,与 Phase 1-5 协同** |
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
| 注意事项 | 说明 |
|
||||
|---------|------|
|
||||
| T.1 是基础 | T.2-T.4 都依赖 T.1 的 Manifest 系统 |
|
||||
| 兼容性优先 | 保持现有 Agent 工具调用方式 |
|
||||
| 安全第一 | 严格权限控制,防止滥用 |
|
||||
| 测试优先 | 每个工具都要配套测试 |
|
||||
|
||||
---
|
||||
|
||||
## 总工作量
|
||||
|
||||
| Phase | 工作量 |
|
||||
|-------|--------|
|
||||
| T.1 | 3 天 |
|
||||
| T.2 | 2 天 |
|
||||
| T.3 | 5 天 |
|
||||
| T.4 | 4 天 |
|
||||
| **总计** | **14 天** |
|
||||
251
development-doc/plan/tool-update/checklist.md
Normal file
251
development-doc/plan/tool-update/checklist.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# Tools 升级执行清单
|
||||
|
||||
本清单用于追踪 Tools 升级计划的执行进度。
|
||||
|
||||
---
|
||||
|
||||
## 总进度
|
||||
|
||||
| Phase | 名称 | 状态 | 工作量 |
|
||||
|-------|------|------|--------|
|
||||
| T.0 | 现状与目标 | ✅ 完成 | - |
|
||||
| T.1 | Manifest 驱动系统 | ⬜ 待开始 | 3 天 |
|
||||
| T.2 | 工具注册中心 | ⬜ 待开始 | 2 天 |
|
||||
| T.3 | 核心工具实现 | ⬜ 待开始 | 5 天 |
|
||||
| T.4 | 高级特性 | ⬜ 待开始 | 4 天 |
|
||||
| **总计** | | | **14 天** |
|
||||
|
||||
---
|
||||
|
||||
## Phase T.1:Manifest 驱动系统
|
||||
|
||||
### 目标
|
||||
建立 Jarvis 的 Manifest 驱动工具系统,定义标准化工件声明。
|
||||
|
||||
### 任务清单
|
||||
|
||||
#### Schema 定义
|
||||
- [ ] 创建 `tools/schemas/manifest.py`
|
||||
- [ ] 定义 `ToolManifest` Schema
|
||||
- [ ] 定义 `ToolType` 枚举
|
||||
- [ ] 定义 `RuntimeType` 枚举
|
||||
- [ ] 定义 `InvocationCommand` Schema
|
||||
- [ ] 创建 `tools/schemas/tool_call.py`
|
||||
- [ ] 定义 `ToolCallRequest` Schema
|
||||
- [ ] 定义 `ToolCallResponse` Schema
|
||||
- [ ] 定义 `ToolExecutionLog` Schema
|
||||
|
||||
#### 验证器
|
||||
- [ ] 创建 `tools/schemas/validator.py`
|
||||
- [ ] 实现 `validate_manifest` 函数
|
||||
- [ ] 实现 `validate_tool_call` 函数
|
||||
- [ ] 实现错误类
|
||||
|
||||
#### 配置系统
|
||||
- [ ] 创建 `tools/configs/loader.py`
|
||||
- [ ] 实现 `ConfigLoader` 类
|
||||
- [ ] 实现配置缓存
|
||||
- [ ] 实现配置重载
|
||||
|
||||
#### Manifest 文件
|
||||
- [ ] 创建 `tools/manifests/file_operator.yaml`
|
||||
- [ ] 创建 `tools/manifests/web_search.yaml`
|
||||
- [ ] 创建其他工具 Manifest
|
||||
|
||||
#### 测试
|
||||
- [ ] 单元测试
|
||||
|
||||
### 产出文件
|
||||
- `tools/schemas/manifest.py`
|
||||
- `tools/schemas/tool_call.py`
|
||||
- `tools/schemas/validator.py`
|
||||
- `tools/configs/loader.py`
|
||||
- `tools/manifests/*.yaml`
|
||||
|
||||
### 验收
|
||||
- [ ] Schema 验证正常工作
|
||||
- [ ] 配置加载器正常工作
|
||||
- [ ] Manifest 文件格式正确
|
||||
- [ ] 单元测试通过
|
||||
|
||||
---
|
||||
|
||||
## Phase T.2:工具注册中心
|
||||
|
||||
### 目标
|
||||
实现工具的动态注册、发现和管理。
|
||||
|
||||
### 任务清单
|
||||
|
||||
#### 注册中心
|
||||
- [ ] 创建 `tools/registry.py`
|
||||
- [ ] 实现 `ToolMetadata` dataclass
|
||||
- [ ] 实现 `ToolRegistry` 类
|
||||
- [ ] 实现注册/注销方法
|
||||
- [ ] 实现查询方法
|
||||
- [ ] 实现统计方法
|
||||
|
||||
#### 工具发现
|
||||
- [ ] 创建 `tools/discovery.py`
|
||||
- [ ] 实现 `ToolDiscovery` 类
|
||||
- [ ] 实现自动发现
|
||||
- [ ] 实现热重载
|
||||
|
||||
#### 描述生成
|
||||
- [ ] 创建 `tools/description.py`
|
||||
- [ ] 实现 AI 友好描述生成
|
||||
- [ ] 实现工具列表生成
|
||||
|
||||
#### 权限控制
|
||||
- [ ] 创建 `tools/permissions.py`
|
||||
- [ ] 实现 `ToolPermission` 枚举
|
||||
- [ ] 实现 `ToolPermissionChecker` 类
|
||||
|
||||
#### LangChain 集成
|
||||
- [ ] 创建 `tools/langchain_adapter.py`
|
||||
- [ ] 实现适配器
|
||||
- [ ] 集成到 Agent
|
||||
|
||||
#### 测试
|
||||
- [ ] 单元测试
|
||||
|
||||
### 产出文件
|
||||
- `tools/registry.py`
|
||||
- `tools/discovery.py`
|
||||
- `tools/description.py`
|
||||
- `tools/permissions.py`
|
||||
- `tools/langchain_adapter.py`
|
||||
|
||||
### 验收
|
||||
- [ ] 注册中心正常工作
|
||||
- [ ] 工具发现正常工作
|
||||
- [ ] 权限检查正常工作
|
||||
- [ ] LangChain 适配器正常工作
|
||||
- [ ] 单元测试通过
|
||||
|
||||
---
|
||||
|
||||
## Phase T.3:核心工具实现
|
||||
|
||||
### 目标
|
||||
实现文件操作、搜索、网页抓取等核心工具。
|
||||
|
||||
### 任务清单
|
||||
|
||||
#### 文件操作工具
|
||||
- [ ] 创建 `tools/implementations/file_operator.py`
|
||||
- [ ] 实现 `FileOperator` 类
|
||||
- [ ] 实现 read_file
|
||||
- [ ] 实现 write_file
|
||||
- [ ] 实现 list_directory
|
||||
- [ ] 实现 search_files
|
||||
- [ ] 实现路径安全检查
|
||||
- [ ] 实现多格式支持(PDF/DOCX/XLSX)
|
||||
|
||||
#### 搜索工具
|
||||
- [ ] 创建 `tools/implementations/web_search.py`
|
||||
- [ ] 实现 `WebSearch` 类
|
||||
- [ ] 实现 search 方法
|
||||
- [ ] 实现 deep_search 方法
|
||||
|
||||
#### 网页抓取工具
|
||||
- [ ] 创建 `tools/implementations/web_fetch.py`
|
||||
- [ ] 实现 `WebFetch` 类
|
||||
- [ ] 实现 fetch 方法
|
||||
- [ ] 实现 screenshot 方法
|
||||
|
||||
#### 任务管理工具
|
||||
- [ ] 创建 `tools/implementations/task_manager.py`
|
||||
- [ ] 实现 `TaskManager` 类
|
||||
- [ ] 实现任务 CRUD
|
||||
|
||||
#### Manifest 绑定
|
||||
- [ ] 更新 Manifest 文件
|
||||
- [ ] 注册到工具中心
|
||||
|
||||
#### 测试
|
||||
- [ ] 单元测试
|
||||
|
||||
### 产出文件
|
||||
- `tools/implementations/file_operator.py`
|
||||
- `tools/implementations/web_search.py`
|
||||
- `tools/implementations/web_fetch.py`
|
||||
- `tools/implementations/task_manager.py`
|
||||
|
||||
### 验收
|
||||
- [ ] 文件操作工具正常工作
|
||||
- [ ] 搜索工具正常工作
|
||||
- [ ] 网页抓取工具正常工作
|
||||
- [ ] 任务管理工具正常工作
|
||||
- [ ] 单元测试通过
|
||||
|
||||
---
|
||||
|
||||
## Phase T.4:高级特性
|
||||
|
||||
### 目标
|
||||
实现多运行时支持、Agent 协作和定时任务。
|
||||
|
||||
### 任务清单
|
||||
|
||||
#### 运行时系统
|
||||
- [ ] 创建 `tools/runtime/base.py`
|
||||
- [ ] 定义 `BaseRuntime` 抽象基类
|
||||
- [ ] 创建 `tools/runtime/python_runtime.py`
|
||||
- [ ] 创建 `tools/runtime/js_runtime.py`
|
||||
- [ ] 创建 `tools/runtime/native_runtime.py`
|
||||
- [ ] 创建 `tools/runtime/manager.py`
|
||||
|
||||
#### Agent 协作
|
||||
- [ ] 创建 `agents/tools/collaboration.py`
|
||||
- [ ] 定义 `CollaborationMessage`
|
||||
- [ ] 实现 `CollaborationProtocol` 类
|
||||
- [ ] 实现请求/响应机制
|
||||
|
||||
#### 定时任务
|
||||
- [ ] 创建 `tools/scheduler.py`
|
||||
- [ ] 实现 `ScheduledTask`
|
||||
- [ ] 实现 `ToolScheduler` 类
|
||||
- [ ] 实现多种调度类型
|
||||
|
||||
#### 测试
|
||||
- [ ] 单元测试
|
||||
|
||||
### 产出文件
|
||||
- `tools/runtime/*.py`
|
||||
- `agents/tools/collaboration.py`
|
||||
- `tools/scheduler.py`
|
||||
|
||||
### 验收
|
||||
- [ ] 多运行时正常工作
|
||||
- [ ] Agent 协作正常工作
|
||||
- [ ] 定时任务正常工作
|
||||
- [ ] 单元测试通过
|
||||
|
||||
---
|
||||
|
||||
## 完成标准
|
||||
|
||||
- [ ] 所有 Phase T.1-T.4 任务完成
|
||||
- [ ] 所有单元测试通过
|
||||
- [ ] API 文档更新完成
|
||||
- [ ] 部署验证通过
|
||||
|
||||
---
|
||||
|
||||
## 风险与注意事项
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|------|------|----------|
|
||||
| 多运行时复杂性 | 开发成本增加 | 优先实现 Python 运行时 |
|
||||
| JS 运行时依赖 Node | 部署环境要求 | 提供降级方案 |
|
||||
| 定时任务精度 | 任务延迟 | 使用专业调度库 |
|
||||
| 安全漏洞 | 系统风险 | 严格权限控制 |
|
||||
|
||||
---
|
||||
|
||||
## 更新日志
|
||||
|
||||
| 日期 | Phase | 变更内容 |
|
||||
|------|-------|----------|
|
||||
| 2026-04-04 | T.0 | 创建文档 |
|
||||
192
development-doc/plan/tool-update/phase-t-0-current-state.md
Normal file
192
development-doc/plan/tool-update/phase-t-0-current-state.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# Phase T.0:Tools 现状与目标
|
||||
|
||||
日期:2026-04-04
|
||||
状态:已完成
|
||||
借鉴来源:VCPToolBox Plugin 系统
|
||||
|
||||
---
|
||||
|
||||
## 1. 本阶段目的
|
||||
|
||||
本文件用于统一背景认知,明确:
|
||||
|
||||
- Jarvis 当前工具系统处于什么水平
|
||||
- 主要短板是什么
|
||||
- 为什么要升级
|
||||
- 升级后的目标形态是什么
|
||||
- VCPToolBox 给我们什么启发
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前 Jarvis Tools 架构
|
||||
|
||||
### 2.1 核心流程
|
||||
|
||||
```
|
||||
Agent 决策 → 工具调用 → LLM API → 返回结果
|
||||
```
|
||||
|
||||
### 2.2 当前工具实现
|
||||
|
||||
Jarvis 当前的工具以 LangChain Tools 形式存在:
|
||||
|
||||
```python
|
||||
# agents/tools/
|
||||
├── base.py # 工具基类
|
||||
├── file_tools.py # 文件操作
|
||||
├── search_tools.py # 搜索
|
||||
└── web_tools.py # 网页相关
|
||||
```
|
||||
|
||||
### 2.3 当前工具列表
|
||||
|
||||
| 工具 | 功能 | 实现方式 |
|
||||
|------|------|---------|
|
||||
| `read_file` | 读取文件 | Python |
|
||||
| `write_file` | 写入文件 | Python |
|
||||
| `run_python` | 执行代码 | Python |
|
||||
| `search_knowledge` | 知识库检索 | LangChain |
|
||||
| `search_web` | 联网搜索 | API |
|
||||
|
||||
### 2.4 当前问题
|
||||
|
||||
| 问题 | 影响 |
|
||||
|------|------|
|
||||
| 硬编码工具 | 新增工具需改代码 |
|
||||
| 无 manifest | 无法热插拔 |
|
||||
| 无标准化契约 | 调用格式不统一 |
|
||||
| 无配置分离 | 敏感信息易泄露 |
|
||||
| 无类型安全 | 验证缺失 |
|
||||
| 无权限控制 | 安全隐患 |
|
||||
|
||||
---
|
||||
|
||||
## 3. VCPToolBox 工具系统分析
|
||||
|
||||
### 3.1 六大插件类型
|
||||
|
||||
```javascript
|
||||
const PLUGIN_TYPES = {
|
||||
static: "静态占位符,自动注入系统提示词",
|
||||
synchronous: "同步执行,stdio 协议",
|
||||
asynchronous: "异步执行,后台处理",
|
||||
service: "持续运行服务",
|
||||
hybridservice: "混合服务(Agent间通讯)",
|
||||
messagePreprocessor: "消息预处理"
|
||||
};
|
||||
```
|
||||
|
||||
### 3.2 Manifest 标准契约
|
||||
|
||||
```javascript
|
||||
{
|
||||
"manifestVersion": "1.0.0",
|
||||
"name": "PluginName",
|
||||
"displayName": "中文显示名",
|
||||
"description": "功能描述",
|
||||
"pluginType": "synchronous",
|
||||
"version": "1.0.0",
|
||||
|
||||
"entryPoint": {
|
||||
"type": "nodejs",
|
||||
"command": "node Plugin.js",
|
||||
"timeout": 300000
|
||||
},
|
||||
|
||||
"communication": {
|
||||
"protocol": "stdio",
|
||||
"timeout": 300000
|
||||
},
|
||||
|
||||
"configSchema": {
|
||||
"API_KEY": {
|
||||
"type": "string",
|
||||
"description": "API 密钥"
|
||||
}
|
||||
},
|
||||
|
||||
"capabilities": {
|
||||
"invocationCommands": [
|
||||
{
|
||||
"commandIdentifier": "CommandName",
|
||||
"description": "详细描述+调用格式示例",
|
||||
"example": "调用示例"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 核心设计理念
|
||||
|
||||
1. **声明式** - 工具通过 manifest 声明,框架自动发现
|
||||
2. **标准化** - 统一的调用协议(stdio/websocket)
|
||||
3. **可配置** - configSchema 声明配置项
|
||||
4. **可观测** - 调用日志、超时控制
|
||||
5. **安全隔离** - 沙箱执行、权限控制
|
||||
|
||||
### 3.4 关键文件
|
||||
|
||||
| 文件 | 作用 |
|
||||
|------|------|
|
||||
| `Plugin.js` | 插件加载与执行引擎 |
|
||||
| `plugin-manifest.json` | 插件声明契约 |
|
||||
| `config.env` | 插件配置 |
|
||||
| `routes/` | API 路由层 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 目标架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Tool Manifests │
|
||||
│ - YAML/JSON 声明式定义 │
|
||||
│ - 版本管理 │
|
||||
│ - Schema 验证 │
|
||||
└─────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────┴───────────────────────────────────┐
|
||||
│ Tool Registry │
|
||||
│ - 动态发现 │
|
||||
│ - 权限控制 │
|
||||
│ - 调用统计 │
|
||||
└─────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────┴───────────────────────────────────┐
|
||||
│ Tool Executor │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Python RT │ │ JS RT │ │ Native RT │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
└─────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────┴───────────────────────────────────┐
|
||||
│ Tool Output │
|
||||
│ - 类型化返回 │
|
||||
│ - 错误处理 │
|
||||
│ - 调用日志 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 借鉴点映射
|
||||
|
||||
| VCPToolBox 借鉴点 | Jarvis 实现位置 | 优先级 |
|
||||
|-------------------|---------------|--------|
|
||||
| Manifest 驱动 | `tools/manifests/` | 🟢 高 |
|
||||
| 标准化契约 | `tools/schemas/` | 🟢 高 |
|
||||
| configSchema | `tools/base.py` | 🟢 高 |
|
||||
| 工具注册中心 | `tools/registry.py` | 🟡 中 |
|
||||
| 动态发现 | `tools/discovery.py` | 🟡 中 |
|
||||
| 调用日志 | `tools/logging.py` | 🟡 中 |
|
||||
| 超时控制 | `tools/executor.py` | 🟡 中 |
|
||||
| 多运行时 | `tools/runtime/` | 🟡 中 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 本阶段产出要求
|
||||
|
||||
- [x] 团队对 Jarvis 当前工具系统和目标方向达成一致
|
||||
- [x] VCPToolBox 工具系统借鉴点已映射到具体 Phase
|
||||
- [x] 后续 phase 文档能够在这个认知基础上展开
|
||||
484
development-doc/plan/tool-update/phase-t-1-manifest-system.md
Normal file
484
development-doc/plan/tool-update/phase-t-1-manifest-system.md
Normal file
@@ -0,0 +1,484 @@
|
||||
# Phase T.1:Manifest 驱动系统
|
||||
|
||||
日期:2026-04-04
|
||||
状态:待开始
|
||||
依赖:T.0(已完成)
|
||||
|
||||
---
|
||||
|
||||
## 1. 本阶段目的
|
||||
|
||||
建立 Jarvis 的 Manifest 驱动工具系统:
|
||||
|
||||
- 定义工具 Manifest Schema
|
||||
- 实现 Schema 验证
|
||||
- 创建核心工具的 Manifest 文件
|
||||
- 实现配置分离
|
||||
|
||||
---
|
||||
|
||||
## 2. Manifest Schema 设计
|
||||
|
||||
### 2.1 目录结构
|
||||
|
||||
```
|
||||
backend/app/tools/
|
||||
├── manifests/ # 工具 manifest
|
||||
│ ├── file_operator.yaml
|
||||
│ ├── search.yaml
|
||||
│ └── web_fetch.yaml
|
||||
├── schemas/ # Schema 定义
|
||||
│ ├── __init__.py
|
||||
│ ├── manifest.py # Manifest Schema
|
||||
│ ├── tool_call.py # 工具调用 Schema
|
||||
│ └── config.py # 配置 Schema
|
||||
└── configs/ # 配置分离
|
||||
└── .tool.example # 工具配置模板
|
||||
```
|
||||
|
||||
### 2.2 ToolManifest Schema
|
||||
|
||||
```python
|
||||
# tools/schemas/manifest.py
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List, Dict, Any
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ToolType(str, Enum):
|
||||
"""工具类型"""
|
||||
SYNC = "sync" # 同步执行
|
||||
ASYNC = "async" # 异步执行
|
||||
SERVICE = "service" # 持续服务
|
||||
|
||||
|
||||
class RuntimeType(str, Enum):
|
||||
"""运行时类型"""
|
||||
PYTHON = "python"
|
||||
JAVASCRIPT = "javascript"
|
||||
NATIVE = "native"
|
||||
|
||||
|
||||
class InvocationCommand(BaseModel):
|
||||
"""调用命令定义"""
|
||||
name: str = Field(..., description="命令名称")
|
||||
description: str = Field(..., description="命令描述(给 AI 看)")
|
||||
parameters: Optional[Dict[str, Any]] = Field(
|
||||
default=None,
|
||||
description="参数 JSON Schema"
|
||||
)
|
||||
required: Optional[List[str]] = Field(
|
||||
default=None,
|
||||
description="必需参数列表"
|
||||
)
|
||||
example: Optional[str] = Field(
|
||||
default=None,
|
||||
description="调用示例"
|
||||
)
|
||||
|
||||
|
||||
class ToolManifest(BaseModel):
|
||||
"""工具 Manifest"""
|
||||
manifest_version: str = Field(
|
||||
default="1.0.0",
|
||||
description="Manifest 版本"
|
||||
)
|
||||
name: str = Field(..., description="工具名称(英文,唯一)")
|
||||
display_name: str = Field(..., description="显示名称(中文)")
|
||||
description: str = Field(..., description="工具描述")
|
||||
author: Optional[str] = Field(default=None, description="作者")
|
||||
version: str = Field(default="1.0.0", description="版本号")
|
||||
|
||||
# 执行配置
|
||||
type: ToolType = Field(default=ToolType.SYNC, description="工具类型")
|
||||
runtime: RuntimeType = Field(default=RuntimeType.PYTHON, description="运行时")
|
||||
entry: str = Field(..., description="执行入口(文件路径或命令)")
|
||||
timeout: int = Field(default=30000, description="超时时间(毫秒)")
|
||||
|
||||
# 配置
|
||||
config_schema: Optional[Dict[str, Any]] = Field(
|
||||
default=None,
|
||||
description="配置项 Schema"
|
||||
)
|
||||
|
||||
# 能力
|
||||
commands: List[InvocationCommand] = Field(
|
||||
default_factory=list,
|
||||
description="可用命令列表"
|
||||
)
|
||||
|
||||
# 元数据
|
||||
tags: Optional[List[str]] = Field(default=None, description="标签")
|
||||
dependencies: Optional[List[str]] = Field(default=None, description="依赖工具")
|
||||
enabled: bool = Field(default=True, description="是否启用")
|
||||
|
||||
class Config:
|
||||
use_enum_values = True
|
||||
```
|
||||
|
||||
### 2.3 ToolCall Schema
|
||||
|
||||
```python
|
||||
# tools/schemas/tool_call.py
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, Dict, Any, List
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class ToolCallRequest(BaseModel):
|
||||
"""工具调用请求"""
|
||||
tool_name: str = Field(..., description="工具名称")
|
||||
command: str = Field(..., description="命令名称")
|
||||
parameters: Dict[str, Any] = Field(default_factory=dict, description="参数")
|
||||
timeout: Optional[int] = Field(default=None, description="超时时间")
|
||||
context: Optional[Dict[str, Any]] = Field(
|
||||
default=None,
|
||||
description="上下文信息"
|
||||
)
|
||||
|
||||
|
||||
class ToolCallResponse(BaseModel):
|
||||
"""工具调用响应"""
|
||||
status: str = Field(..., description="状态: success/error")
|
||||
result: Optional[Any] = Field(default=None, description="执行结果")
|
||||
error: Optional[str] = Field(default=None, description="错误信息")
|
||||
message: Optional[str] = Field(default=None, description="AI 友好消息")
|
||||
base64: Optional[str] = Field(default=None, description="Base64 数据")
|
||||
duration_ms: Optional[int] = Field(default=None, description="执行耗时")
|
||||
timestamp: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
|
||||
class ToolExecutionLog(BaseModel):
|
||||
"""工具执行日志"""
|
||||
id: str
|
||||
tool_name: str
|
||||
command: str
|
||||
parameters: Dict[str, Any]
|
||||
status: str
|
||||
duration_ms: int
|
||||
error: Optional[str]
|
||||
user_id: Optional[str]
|
||||
agent_id: Optional[str]
|
||||
created_at: datetime
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Manifest 示例
|
||||
|
||||
### 3.1 file_operator.yaml
|
||||
|
||||
```yaml
|
||||
manifest_version: "1.0.0"
|
||||
name: file_operator
|
||||
display_name: 文件操作器
|
||||
description: 强大的文件系统操作工具,支持读写、搜索、下载等功能
|
||||
author: Jarvis
|
||||
version: "1.0.0"
|
||||
|
||||
type: sync
|
||||
runtime: python
|
||||
entry: tools/implementations/file_operator.py
|
||||
timeout: 30000
|
||||
|
||||
config_schema:
|
||||
allowed_directories:
|
||||
type: string
|
||||
description: 允许操作的目录列表,逗号分隔
|
||||
default: ""
|
||||
max_file_size:
|
||||
type: integer
|
||||
description: 最大文件大小(字节)
|
||||
default: 10485760
|
||||
|
||||
commands:
|
||||
- name: read_file
|
||||
description: |
|
||||
读取指定路径文件的内容。支持 PDF、DOCX、XLSX 等格式自动解析。
|
||||
参数:
|
||||
- filePath (必需): 文件绝对路径
|
||||
- encoding (可选): 编码格式,默认 utf8
|
||||
parameters:
|
||||
type: object
|
||||
properties:
|
||||
filePath:
|
||||
type: string
|
||||
description: 文件绝对路径
|
||||
encoding:
|
||||
type: string
|
||||
default: utf8
|
||||
required: [filePath]
|
||||
|
||||
- name: write_file
|
||||
description: |
|
||||
将内容写入文件。如果文件存在,自动创建新文件避免覆盖。
|
||||
参数:
|
||||
- filePath (必需): 文件绝对路径
|
||||
- content (必需): 文件内容
|
||||
parameters:
|
||||
type: object
|
||||
properties:
|
||||
filePath:
|
||||
type: string
|
||||
content:
|
||||
type: string
|
||||
required: [filePath, content]
|
||||
|
||||
- name: list_directory
|
||||
description: |
|
||||
列出目录内容。
|
||||
参数:
|
||||
- directoryPath (必需): 目录绝对路径
|
||||
- showHidden (可选): 是否显示隐藏文件
|
||||
parameters:
|
||||
type: object
|
||||
properties:
|
||||
directoryPath:
|
||||
type: string
|
||||
showHidden:
|
||||
type: boolean
|
||||
default: false
|
||||
required: [directoryPath]
|
||||
|
||||
- name: search_files
|
||||
description: |
|
||||
递归搜索匹配模式的文件。
|
||||
参数:
|
||||
- searchPath (必需): 搜索起始目录
|
||||
- pattern (必需): 文件模式,如 *.txt
|
||||
parameters:
|
||||
type: object
|
||||
properties:
|
||||
searchPath:
|
||||
type: string
|
||||
pattern:
|
||||
type: string
|
||||
required: [searchPath, pattern]
|
||||
|
||||
tags: [file, system, essential]
|
||||
enabled: true
|
||||
```
|
||||
|
||||
### 3.2 search.yaml
|
||||
|
||||
```yaml
|
||||
manifest_version: "1.0.0"
|
||||
name: web_search
|
||||
display_name: 联网搜索
|
||||
description: 语义级并发搜索引擎,支持多源搜索和结果聚合
|
||||
author: Jarvis
|
||||
version: "1.0.0"
|
||||
|
||||
type: sync
|
||||
runtime: python
|
||||
entry: tools/implementations/web_search.py
|
||||
timeout: 60000
|
||||
|
||||
config_schema:
|
||||
api_key:
|
||||
type: string
|
||||
description: 搜索引擎 API 密钥
|
||||
required: true
|
||||
max_results:
|
||||
type: integer
|
||||
description: 最大返回结果数
|
||||
default: 10
|
||||
|
||||
commands:
|
||||
- name: search
|
||||
description: |
|
||||
执行语义级搜索。
|
||||
参数:
|
||||
- query (必需): 搜索关键词
|
||||
- max_results (可选): 最大结果数
|
||||
- sources (可选): 搜索源列表
|
||||
parameters:
|
||||
type: object
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
max_results:
|
||||
type: integer
|
||||
default: 10
|
||||
sources:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
required: [query]
|
||||
|
||||
- name: deep_search
|
||||
description: |
|
||||
深度搜索,带摘要生成。
|
||||
参数:
|
||||
- query (必需): 研究主题
|
||||
- keywords (必需): 关键词列表
|
||||
parameters:
|
||||
type: object
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
keywords:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
required: [query, keywords]
|
||||
|
||||
tags: [search, web, research]
|
||||
enabled: true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Schema 验证
|
||||
|
||||
### 4.1 验证器
|
||||
|
||||
```python
|
||||
# tools/schemas/validator.py
|
||||
from pydantic import ValidationError
|
||||
from tools.schemas.manifest import ToolManifest
|
||||
|
||||
|
||||
def validate_manifest(data: dict) -> ToolManifest:
|
||||
"""验证 Manifest 数据"""
|
||||
try:
|
||||
return ToolManifest(**data)
|
||||
except ValidationError as e:
|
||||
raise ManifestValidationError(str(e))
|
||||
|
||||
|
||||
def validate_tool_call(data: dict) -> ToolCallRequest:
|
||||
"""验证工具调用请求"""
|
||||
from tools.schemas.tool_call import ToolCallRequest
|
||||
try:
|
||||
return ToolCallRequest(**data)
|
||||
except ValidationError as e:
|
||||
raise ToolCallValidationError(str(e))
|
||||
|
||||
|
||||
class ManifestValidationError(Exception):
|
||||
"""Manifest 验证错误"""
|
||||
pass
|
||||
|
||||
|
||||
class ToolCallValidationError(Exception):
|
||||
"""工具调用验证错误"""
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 配置分离
|
||||
|
||||
### 5.1 工具配置模板
|
||||
|
||||
```yaml
|
||||
# tools/configs/.tool.example
|
||||
# 文件操作器
|
||||
file_operator:
|
||||
allowed_directories: ""
|
||||
max_file_size: 10485760
|
||||
|
||||
# 联网搜索
|
||||
web_search:
|
||||
api_key: ""
|
||||
max_results: 10
|
||||
```
|
||||
|
||||
### 5.2 配置加载器
|
||||
|
||||
```python
|
||||
# tools/configs/loader.py
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any
|
||||
|
||||
|
||||
class ConfigLoader:
|
||||
"""工具配置加载器"""
|
||||
|
||||
def __init__(self, config_dir: Path):
|
||||
self.config_dir = config_dir
|
||||
self._cache: Dict[str, Any] = {}
|
||||
|
||||
def load(self, tool_name: str) -> Dict[str, Any]:
|
||||
"""加载指定工具的配置"""
|
||||
if tool_name in self._cache:
|
||||
return self._cache[tool_name]
|
||||
|
||||
config_file = self.config_dir / f"{tool_name}.yaml"
|
||||
if not config_file.exists():
|
||||
return {}
|
||||
|
||||
with open(config_file) as f:
|
||||
config = yaml.safe_load(f) or {}
|
||||
|
||||
self._cache[tool_name] = config
|
||||
return config
|
||||
|
||||
def reload(self, tool_name: str) -> Dict[str, Any]:
|
||||
"""重新加载配置"""
|
||||
if tool_name in self._cache:
|
||||
del self._cache[tool_name]
|
||||
return self.load(tool_name)
|
||||
|
||||
def get(self, tool_name: str, key: str, default: Any = None) -> Any:
|
||||
"""获取配置项"""
|
||||
config = self.load(tool_name)
|
||||
return config.get(key, default)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 实现步骤
|
||||
|
||||
| 步骤 | 任务 | 优先级 |
|
||||
|------|------|--------|
|
||||
| 1 | 创建目录结构 | 🟢 高 |
|
||||
| 2 | 实现 ToolManifest Schema | 🟢 高 |
|
||||
| 3 | 实现 ToolCall Schema | 🟢 高 |
|
||||
| 4 | 实现 Schema 验证器 | 🟢 高 |
|
||||
| 5 | 创建配置加载器 | 🟢 高 |
|
||||
| 6 | 创建 file_operator.yaml | 🟢 高 |
|
||||
| 7 | 创建 search.yaml | 🟡 中 |
|
||||
| 8 | 创建其他工具 Manifest | 🟡 中 |
|
||||
| 9 | 单元测试 | 🟡 中 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 核心文件变更
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `tools/__init__.py` | 模块初始化 |
|
||||
| `tools/schemas/__init__.py` | Schema 导出 |
|
||||
| `tools/schemas/manifest.py` | 新增 |
|
||||
| `tools/schemas/tool_call.py` | 新增 |
|
||||
| `tools/schemas/validator.py` | 新增 |
|
||||
| `tools/configs/loader.py` | 新增 |
|
||||
| `tools/manifests/file_operator.yaml` | 新增 |
|
||||
| `tools/manifests/search.yaml` | 新增 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 工作量估算
|
||||
|
||||
| 任务 | 工作量 |
|
||||
|------|--------|
|
||||
| Schema 定义 | 1 天 |
|
||||
| 验证器 | 0.5 天 |
|
||||
| 配置加载器 | 0.5 天 |
|
||||
| Manifest 文件 | 0.5 天 |
|
||||
| 单元测试 | 0.5 天 |
|
||||
| **总计** | **3 天** |
|
||||
|
||||
---
|
||||
|
||||
## 9. 验收标准
|
||||
|
||||
- [ ] ToolManifest Schema 可正确验证 Manifest
|
||||
- [ ] ToolCall Schema 可正确验证调用请求
|
||||
- [ ] 配置加载器可正确加载配置
|
||||
- [ ] Manifest 文件格式正确
|
||||
- [ ] Schema 验证器可捕获错误
|
||||
- [ ] 单元测试覆盖核心逻辑
|
||||
476
development-doc/plan/tool-update/phase-t-2-tool-registry.md
Normal file
476
development-doc/plan/tool-update/phase-t-2-tool-registry.md
Normal file
@@ -0,0 +1,476 @@
|
||||
# Phase T.2:工具注册中心
|
||||
|
||||
日期:2026-04-04
|
||||
状态:待开始
|
||||
依赖:T.1(待完成)
|
||||
|
||||
---
|
||||
|
||||
## 1. 本阶段目的
|
||||
|
||||
建立 Jarvis 的工具注册中心:
|
||||
|
||||
- 工具动态发现
|
||||
- 工具注册与管理
|
||||
- 工具描述生成
|
||||
- 调用统计与监控
|
||||
|
||||
---
|
||||
|
||||
## 2. 工具注册中心架构
|
||||
|
||||
### 2.1 核心类
|
||||
|
||||
```python
|
||||
# tools/registry.py
|
||||
from typing import Dict, List, Optional, Callable
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
import asyncio
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToolMetadata:
|
||||
"""工具元数据"""
|
||||
name: str
|
||||
display_name: str
|
||||
description: str
|
||||
version: str
|
||||
author: Optional[str] = None
|
||||
tags: List[str] = field(default_factory=list)
|
||||
dependencies: List[str] = field(default_factory=list)
|
||||
enabled: bool = True
|
||||
registered_at: datetime = field(default_factory=datetime.utcnow)
|
||||
|
||||
# 统计
|
||||
call_count: int = 0
|
||||
error_count: int = 0
|
||||
total_duration_ms: int = 0
|
||||
|
||||
@property
|
||||
def avg_duration_ms(self) -> int:
|
||||
if self.call_count == 0:
|
||||
return 0
|
||||
return self.total_duration_ms // self.call_count
|
||||
|
||||
@property
|
||||
def error_rate(self) -> float:
|
||||
if self.call_count == 0:
|
||||
return 0.0
|
||||
return self.error_count / self.call_count
|
||||
|
||||
|
||||
class ToolRegistry:
|
||||
"""工具注册中心"""
|
||||
|
||||
def __init__(self):
|
||||
self._tools: Dict[str, ToolMetadata] = {}
|
||||
self._executors: Dict[str, Callable] = {}
|
||||
self._configs: Dict[str, dict] = {}
|
||||
self._lock = asyncio.Lock()
|
||||
|
||||
# === 注册方法 ===
|
||||
|
||||
async def register(
|
||||
self,
|
||||
manifest_path: str,
|
||||
executor: Callable,
|
||||
config: Optional[dict] = None,
|
||||
) -> ToolMetadata:
|
||||
"""注册工具"""
|
||||
from tools.schemas.validator import validate_manifest
|
||||
import yaml
|
||||
|
||||
with open(manifest_path) as f:
|
||||
data = yaml.safe_load(f)
|
||||
|
||||
manifest = validate_manifest(data)
|
||||
|
||||
metadata = ToolMetadata(
|
||||
name=manifest.name,
|
||||
display_name=manifest.display_name,
|
||||
description=manifest.description,
|
||||
version=manifest.version,
|
||||
author=manifest.author,
|
||||
tags=manifest.tags or [],
|
||||
dependencies=manifest.dependencies or [],
|
||||
enabled=manifest.enabled,
|
||||
)
|
||||
|
||||
async with self._lock:
|
||||
self._tools[manifest.name] = metadata
|
||||
self._executors[manifest.name] = executor
|
||||
if config:
|
||||
self._configs[manifest.name] = config
|
||||
|
||||
return metadata
|
||||
|
||||
async def unregister(self, name: str) -> bool:
|
||||
"""注销工具"""
|
||||
async with self._lock:
|
||||
if name in self._tools:
|
||||
del self._tools[name]
|
||||
del self._executors[name]
|
||||
self._configs.pop(name, None)
|
||||
return True
|
||||
return False
|
||||
|
||||
async def enable(self, name: str) -> None:
|
||||
"""启用工具"""
|
||||
async with self._lock:
|
||||
if name in self._tools:
|
||||
self._tools[name].enabled = True
|
||||
|
||||
async def disable(self, name: str) -> None:
|
||||
"""禁用工具"""
|
||||
async with self._lock:
|
||||
if name in self._tools:
|
||||
self._tools[name].enabled = False
|
||||
|
||||
# === 查询方法 ===
|
||||
|
||||
async def get(self, name: str) -> Optional[ToolMetadata]:
|
||||
"""获取工具元数据"""
|
||||
return self._tools.get(name)
|
||||
|
||||
async def get_executor(self, name: str) -> Optional[Callable]:
|
||||
"""获取工具执行器"""
|
||||
return self._executors.get(name)
|
||||
|
||||
async def get_config(self, name: str) -> dict:
|
||||
"""获取工具配置"""
|
||||
return self._configs.get(name, {})
|
||||
|
||||
async def list_all(self) -> List[ToolMetadata]:
|
||||
"""列出所有工具"""
|
||||
return list(self._tools.values())
|
||||
|
||||
async def list_enabled(self) -> List[ToolMetadata]:
|
||||
"""列出已启用的工具"""
|
||||
return [t for t in self._tools.values() if t.enabled]
|
||||
|
||||
async def list_by_tag(self, tag: str) -> List[ToolMetadata]:
|
||||
"""按标签筛选工具"""
|
||||
return [t for t in self._tools.values() if tag in t.tags]
|
||||
|
||||
async def search(self, query: str) -> List[ToolMetadata]:
|
||||
"""搜索工具"""
|
||||
query = query.lower()
|
||||
return [
|
||||
t for t in self._tools.values()
|
||||
if query in t.name.lower()
|
||||
or query in t.description.lower()
|
||||
or query in t.display_name.lower()
|
||||
]
|
||||
|
||||
# === 统计方法 ===
|
||||
|
||||
async def record_call(
|
||||
self,
|
||||
name: str,
|
||||
duration_ms: int,
|
||||
error: bool = False,
|
||||
) -> None:
|
||||
"""记录调用"""
|
||||
async with self._lock:
|
||||
if name in self._tools:
|
||||
tool = self._tools[name]
|
||||
tool.call_count += 1
|
||||
tool.total_duration_ms += duration_ms
|
||||
if error:
|
||||
tool.error_count += 1
|
||||
|
||||
async def get_stats(self) -> dict:
|
||||
"""获取统计信息"""
|
||||
tools = list(self._tools.values())
|
||||
return {
|
||||
"total_tools": len(tools),
|
||||
"enabled_tools": sum(1 for t in tools if t.enabled),
|
||||
"total_calls": sum(t.call_count for t in tools),
|
||||
"total_errors": sum(t.error_count for t in tools),
|
||||
"avg_error_rate": sum(t.error_rate for t in tools) / len(tools) if tools else 0,
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 工具发现机制
|
||||
|
||||
### 3.1 自动发现
|
||||
|
||||
```python
|
||||
# tools/discovery.py
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
|
||||
class ToolDiscovery:
|
||||
"""工具自动发现"""
|
||||
|
||||
def __init__(self, manifest_dir: Path):
|
||||
self.manifest_dir = manifest_dir
|
||||
|
||||
def discover(self) -> List[Path]:
|
||||
"""发现所有 Manifest 文件"""
|
||||
manifests = list(self.manifest_dir.glob("**/*.yaml"))
|
||||
manifests.extend(self.manifest_dir.glob("**/*.yml"))
|
||||
manifests.extend(self.manifest_dir.glob("**/*.json"))
|
||||
return manifests
|
||||
|
||||
def discover_by_tag(self, tag: str) -> List[Path]:
|
||||
"""按标签发现"""
|
||||
# 读取所有 manifest,筛选标签
|
||||
pass
|
||||
|
||||
async def hot_reload(self, registry: ToolRegistry) -> None:
|
||||
"""热重载工具"""
|
||||
for manifest_path in self.discover():
|
||||
# 重新注册
|
||||
pass
|
||||
```
|
||||
|
||||
### 3.2 启动时注册
|
||||
|
||||
```python
|
||||
# tools/loader.py
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
async def load_all_tools(registry: ToolRegistry) -> None:
|
||||
"""加载所有工具"""
|
||||
manifest_dir = Path(__file__).parent / "manifests"
|
||||
|
||||
discovery = ToolDiscovery(manifest_dir)
|
||||
|
||||
for manifest_path in discovery.discover():
|
||||
tool_name = manifest_path.stem
|
||||
|
||||
# 加载 executor
|
||||
executor = load_executor(manifest_path)
|
||||
|
||||
# 加载配置
|
||||
config = load_config(tool_name)
|
||||
|
||||
# 注册
|
||||
await registry.register(manifest_path, executor, config)
|
||||
|
||||
|
||||
def load_executor(manifest_path: Path) -> Callable:
|
||||
"""加载工具执行器"""
|
||||
import yaml
|
||||
|
||||
with open(manifest_path) as f:
|
||||
manifest = yaml.safe_load(f)
|
||||
|
||||
# 根据运行时类型加载
|
||||
runtime = manifest.get("runtime", "python")
|
||||
|
||||
if runtime == "python":
|
||||
return load_python_executor(manifest)
|
||||
elif runtime == "javascript":
|
||||
return load_js_executor(manifest)
|
||||
else:
|
||||
return load_native_executor(manifest)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 工具描述生成
|
||||
|
||||
### 4.1 AI 友好的工具描述
|
||||
|
||||
```python
|
||||
# tools/description.py
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
def generate_tool_description(manifest: dict) -> str:
|
||||
"""生成 AI 友好的工具描述"""
|
||||
lines = [
|
||||
f"## {manifest['display_name']}",
|
||||
f"{manifest['description']}",
|
||||
"",
|
||||
"### 可用命令:",
|
||||
]
|
||||
|
||||
for cmd in manifest.get("commands", []):
|
||||
lines.append(f"#### {cmd['name']}")
|
||||
lines.append(cmd["description"])
|
||||
lines.append("")
|
||||
|
||||
if cmd.get("example"):
|
||||
lines.append("**示例:**")
|
||||
lines.append(f"```\n{cmd['example']}\n```")
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def generate_tools_for_llm(registry: ToolRegistry) -> str:
|
||||
"""生成给 LLM 的工具列表"""
|
||||
tools = registry.list_enabled()
|
||||
|
||||
sections = ["## 可用工具\n"]
|
||||
|
||||
for tool in tools:
|
||||
manifest = load_manifest(tool.name)
|
||||
sections.append(generate_tool_description(manifest))
|
||||
sections.append("\n---\n")
|
||||
|
||||
return "\n".join(sections)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 权限控制
|
||||
|
||||
### 5.1 工具权限
|
||||
|
||||
```python
|
||||
# tools/permissions.py
|
||||
from enum import Enum
|
||||
from typing import Set
|
||||
|
||||
|
||||
class ToolPermission(str, Enum):
|
||||
"""工具权限"""
|
||||
EXECUTE = "tool:execute"
|
||||
CONFIGURE = "tool:configure"
|
||||
ENABLE = "tool:enable"
|
||||
DISABLE = "tool:disable"
|
||||
|
||||
|
||||
class ToolPermissionChecker:
|
||||
"""工具权限检查"""
|
||||
|
||||
def __init__(self):
|
||||
self._user_permissions: Dict[str, Set[ToolPermission]] = {}
|
||||
self._tool_roles: Dict[str, Set[str]] = {} # tool_name -> required_roles
|
||||
|
||||
def set_user_permissions(
|
||||
self,
|
||||
user_id: str,
|
||||
permissions: Set[ToolPermission],
|
||||
) -> None:
|
||||
"""设置用户权限"""
|
||||
self._user_permissions[user_id] = permissions
|
||||
|
||||
def set_tool_roles(
|
||||
self,
|
||||
tool_name: str,
|
||||
required_roles: Set[str],
|
||||
) -> None:
|
||||
"""设置工具所需角色"""
|
||||
self._tool_roles[tool_name] = required_roles
|
||||
|
||||
def can_execute(self, user_id: str, tool_name: str) -> bool:
|
||||
"""检查用户是否可以执行工具"""
|
||||
# 检查全局权限
|
||||
if ToolPermission.EXECUTE in self._user_permissions.get(user_id, set()):
|
||||
return True
|
||||
|
||||
# 检查工具特定角色
|
||||
required_roles = self._tool_roles.get(tool_name, set())
|
||||
if not required_roles:
|
||||
return True
|
||||
|
||||
# TODO: 检查用户是否有所需角色
|
||||
return False
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 集成到 Agent
|
||||
|
||||
### 6.1 LangChain 集成
|
||||
|
||||
```python
|
||||
# tools/langchain_adapter.py
|
||||
from typing import List, Optional
|
||||
from langchain.tools import BaseTool
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class LangChainToolAdapter:
|
||||
"""LangChain 工具适配器"""
|
||||
|
||||
def __init__(self, registry: ToolRegistry):
|
||||
self.registry = registry
|
||||
|
||||
def to_langchain_tools(self) -> List[BaseTool]:
|
||||
"""转换为 LangChain 工具"""
|
||||
tools = []
|
||||
|
||||
for metadata in self.registry.list_enabled():
|
||||
executor = self.registry.get_executor(metadata.name)
|
||||
config = self.registry.get_config(metadata.name)
|
||||
|
||||
tool = self._create_tool(metadata, executor, config)
|
||||
tools.append(tool)
|
||||
|
||||
return tools
|
||||
|
||||
def _create_tool(
|
||||
self,
|
||||
metadata: ToolMetadata,
|
||||
executor: Callable,
|
||||
config: dict,
|
||||
) -> BaseTool:
|
||||
"""创建单个 LangChain 工具"""
|
||||
# 根据 manifest 创建工具
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 实现步骤
|
||||
|
||||
| 步骤 | 任务 | 优先级 |
|
||||
|------|------|--------|
|
||||
| 1 | 实现 ToolRegistry 类 | 🟢 高 |
|
||||
| 2 | 实现 ToolDiscovery | 🟢 高 |
|
||||
| 3 | 实现工具描述生成 | 🟡 中 |
|
||||
| 4 | 实现权限检查 | 🟡 中 |
|
||||
| 5 | 实现 LangChain 适配器 | 🟡 中 |
|
||||
| 6 | 集成到 Agent | 🟢 高 |
|
||||
| 7 | 单元测试 | 🟡 中 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 核心文件变更
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `tools/registry.py` | 新增 |
|
||||
| `tools/discovery.py` | 新增 |
|
||||
| `tools/description.py` | 新增 |
|
||||
| `tools/permissions.py` | 新增 |
|
||||
| `tools/langchain_adapter.py` | 新增 |
|
||||
| `tools/__init__.py` | 更新导出 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 工作量估算
|
||||
|
||||
| 任务 | 工作量 |
|
||||
|------|--------|
|
||||
| ToolRegistry | 0.5 天 |
|
||||
| ToolDiscovery | 0.5 天 |
|
||||
| 描述生成 | 0.3 天 |
|
||||
| 权限检查 | 0.3 天 |
|
||||
| LangChain 适配 | 0.3 天 |
|
||||
| 集成 | 0.5 天 |
|
||||
| 单元测试 | 0.5 天 |
|
||||
| **总计** | **2.5 天** |
|
||||
|
||||
---
|
||||
|
||||
## 10. 验收标准
|
||||
|
||||
- [ ] ToolRegistry 可正确注册/注销工具
|
||||
- [ ] ToolDiscovery 可发现所有 Manifest
|
||||
- [ ] 工具描述生成正确
|
||||
- [ ] 权限检查正常工作
|
||||
- [ ] LangChain 工具可正常转换
|
||||
- [ ] Agent 可使用注册的工具
|
||||
- [ ] 单元测试通过
|
||||
@@ -0,0 +1,586 @@
|
||||
# Phase T.3:核心工具实现
|
||||
|
||||
日期:2026-04-04
|
||||
状态:待开始
|
||||
依赖:T.2(待完成)
|
||||
|
||||
---
|
||||
|
||||
## 1. 本阶段目的
|
||||
|
||||
实现 Jarvis 的核心工具:
|
||||
|
||||
- 文件操作工具
|
||||
- 搜索工具
|
||||
- 网页抓取工具
|
||||
- 任务管理工具
|
||||
|
||||
---
|
||||
|
||||
## 2. 文件操作工具
|
||||
|
||||
### 2.1 实现
|
||||
|
||||
```python
|
||||
# tools/implementations/file_operator.py
|
||||
import os
|
||||
import shutil
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Dict, Any
|
||||
|
||||
|
||||
class FileOperator:
|
||||
"""文件操作工具"""
|
||||
|
||||
def __init__(self, config: dict):
|
||||
self.allowed_dirs = self._parse_allowed_dirs(
|
||||
config.get("allowed_directories", "")
|
||||
)
|
||||
self.max_file_size = config.get("max_file_size", 10 * 1024 * 1024)
|
||||
|
||||
def _parse_allowed_dirs(self, dirs_str: str) -> Optional[List[str]]:
|
||||
"""解析允许目录"""
|
||||
if not dirs_str:
|
||||
return None
|
||||
return [d.strip() for d in dirs_str.split(",") if d.strip()]
|
||||
|
||||
def _check_path(self, path: str) -> bool:
|
||||
"""检查路径是否允许"""
|
||||
if not self.allowed_dirs:
|
||||
return True
|
||||
resolved = Path(path).resolve()
|
||||
return any(
|
||||
str(resolved).startswith(allowed)
|
||||
for allowed in self.allowed_dirs
|
||||
)
|
||||
|
||||
async def read_file(
|
||||
self,
|
||||
filePath: str,
|
||||
encoding: str = "utf-8",
|
||||
) -> Dict[str, Any]:
|
||||
"""读取文件"""
|
||||
if not self._check_path(filePath):
|
||||
return {"status": "error", "error": "路径不在允许范围内"}
|
||||
|
||||
path = Path(filePath)
|
||||
|
||||
if not path.exists():
|
||||
return {"status": "error", "error": "文件不存在"}
|
||||
|
||||
if path.stat().st_size > self.max_file_size:
|
||||
return {"status": "error", "error": "文件过大"}
|
||||
|
||||
# 根据扩展名处理
|
||||
suffix = path.suffix.lower()
|
||||
if suffix in [".pdf", ".docx", ".xlsx", ".xls", ".csv"]:
|
||||
return await self._read_binary_file(path)
|
||||
|
||||
try:
|
||||
content = path.read_text(encoding=encoding)
|
||||
return {"status": "success", "result": content}
|
||||
except Exception as e:
|
||||
return {"status": "error", "error": str(e)}
|
||||
|
||||
async def _read_binary_file(self, path: Path) -> Dict[str, Any]:
|
||||
"""读取二进制文件"""
|
||||
suffix = path.suffix.lower()
|
||||
|
||||
if suffix == ".pdf":
|
||||
return await self._read_pdf(path)
|
||||
elif suffix in [".docx", ".doc"]:
|
||||
return await self._read_docx(path)
|
||||
elif suffix in [".xlsx", ".xls"]:
|
||||
return await self._read_xlsx(path)
|
||||
elif suffix == ".csv":
|
||||
return await self._read_csv(path)
|
||||
|
||||
return {"status": "error", "error": "不支持的文件格式"}
|
||||
|
||||
async def write_file(
|
||||
self,
|
||||
filePath: str,
|
||||
content: str,
|
||||
) -> Dict[str, Any]:
|
||||
"""写入文件"""
|
||||
if not self._check_path(filePath):
|
||||
return {"status": "error", "error": "路径不在允许范围内"}
|
||||
|
||||
path = Path(filePath)
|
||||
|
||||
# 如果文件存在,自动创建新文件名
|
||||
if path.exists():
|
||||
path = self._get_unique_path(path)
|
||||
|
||||
try:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(content, encoding="utf-8")
|
||||
return {
|
||||
"status": "success",
|
||||
"result": f"文件已保存: {path.name}",
|
||||
"path": str(path),
|
||||
}
|
||||
except Exception as e:
|
||||
return {"status": "error", "error": str(e)}
|
||||
|
||||
def _get_unique_path(self, path: Path) -> Path:
|
||||
"""获取唯一路径"""
|
||||
if not path.exists():
|
||||
return path
|
||||
|
||||
stem = path.stem
|
||||
suffix = path.suffix
|
||||
parent = path.parent
|
||||
counter = 1
|
||||
|
||||
while True:
|
||||
new_path = parent / f"{stem}({counter}){suffix}"
|
||||
if not new_path.exists():
|
||||
return new_path
|
||||
counter += 1
|
||||
|
||||
async def list_directory(
|
||||
self,
|
||||
directoryPath: str,
|
||||
showHidden: bool = False,
|
||||
) -> Dict[str, Any]:
|
||||
"""列出目录"""
|
||||
if not self._check_path(directoryPath):
|
||||
return {"status": "error", "error": "路径不在允许范围内"}
|
||||
|
||||
path = Path(directoryPath)
|
||||
|
||||
if not path.exists():
|
||||
return {"status": "error", "error": "目录不存在"}
|
||||
|
||||
if not path.is_dir():
|
||||
return {"status": "error", "error": "不是目录"}
|
||||
|
||||
items = []
|
||||
for item in path.iterdir():
|
||||
if not showHidden and item.name.startswith("."):
|
||||
continue
|
||||
items.append({
|
||||
"name": item.name,
|
||||
"type": "directory" if item.is_dir() else "file",
|
||||
"size": item.stat().st_size if item.is_file() else None,
|
||||
})
|
||||
|
||||
return {"status": "success", "result": items}
|
||||
|
||||
async def search_files(
|
||||
self,
|
||||
searchPath: str,
|
||||
pattern: str,
|
||||
**options,
|
||||
) -> Dict[str, Any]:
|
||||
"""搜索文件"""
|
||||
import fnmatch
|
||||
|
||||
if not self._check_path(searchPath):
|
||||
return {"status": "error", "error": "路径不在允许范围内"}
|
||||
|
||||
path = Path(searchPath)
|
||||
if not path.exists():
|
||||
return {"status": "error", "error": "路径不存在"}
|
||||
|
||||
case_sensitive = options.get("caseSensitive", False)
|
||||
file_type = options.get("fileType", "all")
|
||||
include_hidden = options.get("includeHidden", False)
|
||||
|
||||
results = []
|
||||
for item in path.rglob("*"):
|
||||
if not include_hidden and item.name.startswith("."):
|
||||
continue
|
||||
|
||||
if not fnmatch.fnmatch(item.name, pattern):
|
||||
continue
|
||||
|
||||
if file_type == "file" and item.is_dir():
|
||||
continue
|
||||
if file_type == "directory" and item.is_file():
|
||||
continue
|
||||
|
||||
results.append(str(item))
|
||||
|
||||
return {"status": "success", "result": results[:100]} # 限制结果数
|
||||
```
|
||||
|
||||
### 2.2 Manifest 绑定
|
||||
|
||||
```python
|
||||
# tools/implementations/__init__.py
|
||||
from tools.implementations.file_operator import FileOperator
|
||||
|
||||
|
||||
def create_file_operator_executor(config: dict):
|
||||
"""创建文件操作执行器"""
|
||||
operator = FileOperator(config)
|
||||
|
||||
async def execute(command: str, parameters: dict) -> dict:
|
||||
if command == "read_file":
|
||||
return await operator.read_file(**parameters)
|
||||
elif command == "write_file":
|
||||
return await operator.write_file(**parameters)
|
||||
elif command == "list_directory":
|
||||
return await operator.list_directory(**parameters)
|
||||
elif command == "search_files":
|
||||
return await operator.search_files(**parameters)
|
||||
else:
|
||||
return {"status": "error", "error": f"未知命令: {command}"}
|
||||
|
||||
return execute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 搜索工具
|
||||
|
||||
### 3.1 实现
|
||||
|
||||
```python
|
||||
# tools/implementations/web_search.py
|
||||
import asyncio
|
||||
from typing import Dict, Any, List, Optional
|
||||
|
||||
|
||||
class WebSearch:
|
||||
"""联网搜索工具"""
|
||||
|
||||
def __init__(self, config: dict):
|
||||
self.api_key = config.get("api_key")
|
||||
self.max_results = config.get("max_results", 10)
|
||||
|
||||
async def search(
|
||||
self,
|
||||
query: str,
|
||||
max_results: Optional[int] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""执行搜索"""
|
||||
try:
|
||||
# 实现搜索逻辑
|
||||
results = await self._do_search(
|
||||
query,
|
||||
max_results or self.max_results,
|
||||
)
|
||||
return {"status": "success", "result": results}
|
||||
except Exception as e:
|
||||
return {"status": "error", "error": str(e)}
|
||||
|
||||
async def _do_search(self, query: str, limit: int) -> List[dict]:
|
||||
"""实际搜索"""
|
||||
# TODO: 接入搜索 API
|
||||
return []
|
||||
|
||||
async def deep_search(
|
||||
self,
|
||||
query: str,
|
||||
keywords: List[str],
|
||||
) -> Dict[str, Any]:
|
||||
"""深度搜索"""
|
||||
try:
|
||||
# 并发执行多个搜索
|
||||
tasks = [
|
||||
self._do_search(kw, 5)
|
||||
for kw in [query] + keywords
|
||||
]
|
||||
results = await asyncio.gather(*tasks)
|
||||
|
||||
# 聚合结果
|
||||
aggregated = self._aggregate_results(results)
|
||||
|
||||
return {"status": "success", "result": aggregated}
|
||||
except Exception as e:
|
||||
return {"status": "error", "error": str(e)}
|
||||
|
||||
def _aggregate_results(self, results: List[List[dict]]) -> dict:
|
||||
"""聚合搜索结果"""
|
||||
# TODO: 实现结果聚合
|
||||
return {"summary": "聚合结果", "sources": []}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 网页抓取工具
|
||||
|
||||
### 4.1 实现
|
||||
|
||||
```python
|
||||
# tools/implementations/web_fetch.py
|
||||
import asyncio
|
||||
from typing import Dict, Any, Optional
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class FetchResult:
|
||||
"""抓取结果"""
|
||||
url: str
|
||||
title: Optional[str]
|
||||
content: str
|
||||
images: List[str]
|
||||
links: List[str]
|
||||
status: int
|
||||
|
||||
|
||||
class WebFetch:
|
||||
"""网页抓取工具"""
|
||||
|
||||
def __init__(self, config: dict):
|
||||
self.timeout = config.get("timeout", 30)
|
||||
self.user_agent = config.get(
|
||||
"user_agent",
|
||||
"Mozilla/5.0 (compatible; Jarvis/1.0)"
|
||||
)
|
||||
|
||||
async def fetch(
|
||||
self,
|
||||
url: str,
|
||||
include_images: bool = True,
|
||||
) -> Dict[str, Any]:
|
||||
"""抓取网页"""
|
||||
try:
|
||||
result = await self._do_fetch(url, include_images)
|
||||
return {
|
||||
"status": "success",
|
||||
"result": {
|
||||
"url": result.url,
|
||||
"title": result.title,
|
||||
"content": result.content,
|
||||
"images": result.images,
|
||||
"status": result.status,
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
return {"status": "error", "error": str(e)}
|
||||
|
||||
async def _do_fetch(
|
||||
self,
|
||||
url: str,
|
||||
include_images: bool,
|
||||
) -> FetchResult:
|
||||
"""实际抓取"""
|
||||
import httpx
|
||||
|
||||
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||||
response = await client.get(
|
||||
url,
|
||||
headers={"User-Agent": self.user_agent},
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
# TODO: 解析 HTML 提取内容
|
||||
return FetchResult(
|
||||
url=url,
|
||||
title=None,
|
||||
content=response.text,
|
||||
images=[],
|
||||
links=[],
|
||||
status=response.status_code,
|
||||
)
|
||||
|
||||
async def screenshot(
|
||||
self,
|
||||
url: str,
|
||||
) -> Dict[str, Any]:
|
||||
"""截取网页截图"""
|
||||
# TODO: 接入截图服务
|
||||
return {"status": "error", "error": "未实现"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 任务管理工具
|
||||
|
||||
### 5.1 实现
|
||||
|
||||
```python
|
||||
# tools/implementations/task_manager.py
|
||||
from typing import Dict, Any, List, Optional
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class TaskStatus(str, Enum):
|
||||
PENDING = "pending"
|
||||
RUNNING = "running"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
@dataclass
|
||||
class Task:
|
||||
"""任务"""
|
||||
id: str
|
||||
name: str
|
||||
description: str
|
||||
status: TaskStatus = TaskStatus.PENDING
|
||||
created_at: datetime = field(default_factory=datetime.utcnow)
|
||||
scheduled_at: Optional[datetime] = None
|
||||
result: Optional[Any] = None
|
||||
error: Optional[str] = None
|
||||
|
||||
|
||||
class TaskManager:
|
||||
"""任务管理工具"""
|
||||
|
||||
def __init__(self, config: dict):
|
||||
self._tasks: Dict[str, Task] = {}
|
||||
|
||||
async def create_task(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
scheduled_at: Optional[datetime] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""创建任务"""
|
||||
import uuid
|
||||
|
||||
task_id = str(uuid.uuid4())[:8]
|
||||
task = Task(
|
||||
id=task_id,
|
||||
name=name,
|
||||
description=description,
|
||||
scheduled_at=scheduled_at,
|
||||
)
|
||||
self._tasks[task_id] = task
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"result": {
|
||||
"id": task_id,
|
||||
"name": task.name,
|
||||
"status": task.status.value,
|
||||
}
|
||||
}
|
||||
|
||||
async def list_tasks(
|
||||
self,
|
||||
status: Optional[str] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""列出任务"""
|
||||
tasks = list(self._tasks.values())
|
||||
|
||||
if status:
|
||||
tasks = [t for t in tasks if t.status.value == status]
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"result": [
|
||||
{
|
||||
"id": t.id,
|
||||
"name": t.name,
|
||||
"status": t.status.value,
|
||||
"created_at": t.created_at.isoformat(),
|
||||
}
|
||||
for t in tasks
|
||||
]
|
||||
}
|
||||
|
||||
async def get_task(self, task_id: str) -> Dict[str, Any]:
|
||||
"""获取任务"""
|
||||
task = self._tasks.get(task_id)
|
||||
if not task:
|
||||
return {"status": "error", "error": "任务不存在"}
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"result": {
|
||||
"id": task.id,
|
||||
"name": task.name,
|
||||
"description": task.description,
|
||||
"status": task.status.value,
|
||||
"result": task.result,
|
||||
"error": task.error,
|
||||
}
|
||||
}
|
||||
|
||||
async def complete_task(
|
||||
self,
|
||||
task_id: str,
|
||||
result: Any,
|
||||
) -> Dict[str, Any]:
|
||||
"""完成任务"""
|
||||
task = self._tasks.get(task_id)
|
||||
if not task:
|
||||
return {"status": "error", "error": "任务不存在"}
|
||||
|
||||
task.status = TaskStatus.COMPLETED
|
||||
task.result = result
|
||||
|
||||
return {"status": "success"}
|
||||
|
||||
async def fail_task(
|
||||
self,
|
||||
task_id: str,
|
||||
error: str,
|
||||
) -> Dict[str, Any]:
|
||||
"""标记任务失败"""
|
||||
task = self._tasks.get(task_id)
|
||||
if not task:
|
||||
return {"status": "error", "error": "任务不存在"}
|
||||
|
||||
task.status = TaskStatus.FAILED
|
||||
task.error = error
|
||||
|
||||
return {"status": "success"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 实现步骤
|
||||
|
||||
| 步骤 | 任务 | 优先级 |
|
||||
|------|------|--------|
|
||||
| 1 | 实现 FileOperator | 🟢 高 |
|
||||
| 2 | 实现 WebSearch | 🟡 中 |
|
||||
| 3 | 实现 WebFetch | 🟡 中 |
|
||||
| 4 | 实现 TaskManager | 🟡 中 |
|
||||
| 5 | 创建 Manifest 文件 | 🟢 高 |
|
||||
| 6 | 注册到工具中心 | 🟢 高 |
|
||||
| 7 | 单元测试 | 🟡 中 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 核心文件变更
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `tools/implementations/__init__.py` | 新增 |
|
||||
| `tools/implementations/file_operator.py` | 新增 |
|
||||
| `tools/implementations/web_search.py` | 新增 |
|
||||
| `tools/implementations/web_fetch.py` | 新增 |
|
||||
| `tools/implementations/task_manager.py` | 新增 |
|
||||
| `tools/manifests/file_operator.yaml` | 更新 |
|
||||
| `tools/manifests/web_search.yaml` | 新增 |
|
||||
| `tools/manifests/web_fetch.yaml` | 新增 |
|
||||
| `tools/manifests/task_manager.yaml` | 新增 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 工作量估算
|
||||
|
||||
| 任务 | 工作量 |
|
||||
|------|--------|
|
||||
| FileOperator | 1.5 天 |
|
||||
| WebSearch | 1 天 |
|
||||
| WebFetch | 1 天 |
|
||||
| TaskManager | 0.5 天 |
|
||||
| Manifest + 注册 | 0.5 天 |
|
||||
| 单元测试 | 0.5 天 |
|
||||
| **总计** | **5 天** |
|
||||
|
||||
---
|
||||
|
||||
## 9. 验收标准
|
||||
|
||||
- [ ] FileOperator 可正确读写文件
|
||||
- [ ] FileOperator 支持多种格式解析
|
||||
- [ ] FileOperator 路径安全检查正常
|
||||
- [ ] WebSearch 可执行搜索
|
||||
- [ ] WebFetch 可抓取网页
|
||||
- [ ] TaskManager 可管理任务
|
||||
- [ ] 所有工具注册到工具中心
|
||||
- [ ] 单元测试通过
|
||||
642
development-doc/plan/tool-update/phase-t-4-advanced.md
Normal file
642
development-doc/plan/tool-update/phase-t-4-advanced.md
Normal file
@@ -0,0 +1,642 @@
|
||||
# Phase T.4:高级特性
|
||||
|
||||
日期:2026-04-04
|
||||
状态:待开始
|
||||
依赖:T.3(待完成)
|
||||
|
||||
---
|
||||
|
||||
## 1. 本阶段目的
|
||||
|
||||
实现 Jarvis 工具系统的高级特性:
|
||||
|
||||
- 多运行时支持(Python/JS/原生)
|
||||
- Agent 间协作
|
||||
- 定时任务
|
||||
|
||||
---
|
||||
|
||||
## 2. 多运行时支持
|
||||
|
||||
### 2.1 运行时架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Runtime Manager │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||
│ │ Python │ │ JS │ │ Native │ │ WASM │ │
|
||||
│ │ Runtime │ │ Runtime │ │ Runtime │ │ Runtime │ │
|
||||
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
|
||||
│ │ │ │ │ │
|
||||
│ └─────────────┴──────┬──────┴─────────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────┴────────┐ │
|
||||
│ │ Tool Executor │ │
|
||||
│ │ (统一接口) │ │
|
||||
│ └─────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 运行时基类
|
||||
|
||||
```python
|
||||
# tools/runtime/base.py
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
|
||||
class BaseRuntime(ABC):
|
||||
"""运行时基类"""
|
||||
|
||||
@abstractmethod
|
||||
async def execute(
|
||||
self,
|
||||
entry: str,
|
||||
command: str,
|
||||
parameters: Dict[str, Any],
|
||||
timeout: int,
|
||||
) -> Dict[str, Any]:
|
||||
"""执行工具"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def validate(self, entry: str) -> bool:
|
||||
"""验证工具是否可用"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_name(self) -> str:
|
||||
"""获取运行时名称"""
|
||||
pass
|
||||
```
|
||||
|
||||
### 2.3 Python 运行时
|
||||
|
||||
```python
|
||||
# tools/runtime/python_runtime.py
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from tools.runtime.base import BaseRuntime
|
||||
|
||||
|
||||
class PythonRuntime(BaseRuntime):
|
||||
"""Python 运行时"""
|
||||
|
||||
def __init__(self):
|
||||
self._executors: Dict[str, Callable] = {}
|
||||
|
||||
def get_name(self) -> str:
|
||||
return "python"
|
||||
|
||||
async def validate(self, entry: str) -> bool:
|
||||
path = Path(entry)
|
||||
return path.exists() and path.suffix == ".py"
|
||||
|
||||
async def execute(
|
||||
self,
|
||||
entry: str,
|
||||
command: str,
|
||||
parameters: Dict[str, Any],
|
||||
timeout: int,
|
||||
) -> Dict[str, Any]:
|
||||
# 动态加载并执行
|
||||
# 或者通过 subprocess 调用
|
||||
pass
|
||||
```
|
||||
|
||||
### 2.4 JavaScript 运行时
|
||||
|
||||
```python
|
||||
# tools/runtime/js_runtime.py
|
||||
import asyncio
|
||||
import subprocess
|
||||
import json
|
||||
from tools.runtime.base import BaseRuntime
|
||||
|
||||
|
||||
class JavaScriptRuntime(BaseRuntime):
|
||||
"""JavaScript 运行时"""
|
||||
|
||||
def __init__(self):
|
||||
self.node_path = "node" # 可配置
|
||||
|
||||
def get_name(self) -> str:
|
||||
return "javascript"
|
||||
|
||||
async def validate(self, entry: str) -> bool:
|
||||
# 检查 node 是否可用
|
||||
try:
|
||||
result = await asyncio.create_subprocess_exec(
|
||||
self.node_path, "--version",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
)
|
||||
return result.returncode == 0
|
||||
except:
|
||||
return False
|
||||
|
||||
async def execute(
|
||||
self,
|
||||
entry: str,
|
||||
command: str,
|
||||
parameters: Dict[str, Any],
|
||||
timeout: int,
|
||||
) -> Dict[str, Any]:
|
||||
# 通过 stdio 调用 Node.js 脚本
|
||||
input_data = json.dumps({
|
||||
"command": command,
|
||||
"parameters": parameters,
|
||||
})
|
||||
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
self.node_path, entry,
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
|
||||
stdout, stderr = await asyncio.wait_for(
|
||||
process.communicate(input=input_data.encode()),
|
||||
timeout=timeout / 1000,
|
||||
)
|
||||
|
||||
if process.returncode != 0:
|
||||
return {
|
||||
"status": "error",
|
||||
"error": stderr.decode(),
|
||||
}
|
||||
|
||||
return json.loads(stdout.decode())
|
||||
```
|
||||
|
||||
### 2.5 原生运行时
|
||||
|
||||
```python
|
||||
# tools/runtime/native_runtime.py
|
||||
import asyncio
|
||||
import subprocess
|
||||
from tools.runtime.base import BaseRuntime
|
||||
|
||||
|
||||
class NativeRuntime(BaseRuntime):
|
||||
"""原生二进制运行时"""
|
||||
|
||||
def get_name(self) -> str:
|
||||
return "native"
|
||||
|
||||
async def validate(self, entry: str) -> bool:
|
||||
from pathlib import Path
|
||||
path = Path(entry)
|
||||
return path.exists() and path.stat().st_mode & 0o111
|
||||
|
||||
async def execute(
|
||||
self,
|
||||
entry: str,
|
||||
command: str,
|
||||
parameters: Dict[str, Any],
|
||||
timeout: int,
|
||||
) -> Dict[str, Any]:
|
||||
# 调用原生可执行文件
|
||||
args = [entry, command] + self._format_args(parameters)
|
||||
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*args,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
|
||||
stdout, stderr = await asyncio.wait_for(
|
||||
process.communicate(),
|
||||
timeout=timeout / 1000,
|
||||
)
|
||||
|
||||
if process.returncode != 0:
|
||||
return {
|
||||
"status": "error",
|
||||
"error": stderr.decode(),
|
||||
}
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"result": stdout.decode(),
|
||||
}
|
||||
|
||||
def _format_args(self, parameters: Dict[str, Any]) -> list:
|
||||
"""格式化参数"""
|
||||
args = []
|
||||
for key, value in parameters.items():
|
||||
args.extend([f"--{key}", str(value)])
|
||||
return args
|
||||
```
|
||||
|
||||
### 2.6 运行时管理器
|
||||
|
||||
```python
|
||||
# tools/runtime/manager.py
|
||||
from tools.runtime.base import BaseRuntime
|
||||
from tools.runtime.python_runtime import PythonRuntime
|
||||
from tools.runtime.js_runtime import JavaScriptRuntime
|
||||
from tools.runtime.native_runtime import NativeRuntime
|
||||
|
||||
|
||||
class RuntimeManager:
|
||||
"""运行时管理器"""
|
||||
|
||||
def __init__(self):
|
||||
self._runtimes: Dict[str, BaseRuntime] = {
|
||||
"python": PythonRuntime(),
|
||||
"javascript": JavaScriptRuntime(),
|
||||
"native": NativeRuntime(),
|
||||
}
|
||||
|
||||
def get_runtime(self, name: str) -> BaseRuntime:
|
||||
return self._runtimes.get(name)
|
||||
|
||||
async def execute(
|
||||
self,
|
||||
runtime_name: str,
|
||||
entry: str,
|
||||
command: str,
|
||||
parameters: Dict[str, Any],
|
||||
timeout: int,
|
||||
) -> Dict[str, Any]:
|
||||
runtime = self.get_runtime(runtime_name)
|
||||
if not runtime:
|
||||
return {
|
||||
"status": "error",
|
||||
"error": f"未知运行时: {runtime_name}",
|
||||
}
|
||||
|
||||
return await runtime.execute(entry, command, parameters, timeout)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Agent 间协作
|
||||
|
||||
### 3.1 协作协议
|
||||
|
||||
```python
|
||||
# agents/tools/collaboration.py
|
||||
from typing import Dict, Any, Optional, List
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class MessageType(str, Enum):
|
||||
REQUEST = "request" # 请求协作
|
||||
RESPONSE = "response" # 响应结果
|
||||
PROGRESS = "progress" # 进度更新
|
||||
CANCEL = "cancel" # 取消请求
|
||||
|
||||
|
||||
@dataclass
|
||||
class CollaborationMessage:
|
||||
"""协作消息"""
|
||||
id: str
|
||||
type: MessageType
|
||||
from_agent: str
|
||||
to_agent: str
|
||||
content: Any
|
||||
metadata: Dict[str, Any]
|
||||
timestamp: datetime = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.timestamp is None:
|
||||
self.timestamp = datetime.utcnow()
|
||||
|
||||
|
||||
class CollaborationProtocol:
|
||||
"""Agent 协作协议"""
|
||||
|
||||
def __init__(self):
|
||||
self._pending_requests: Dict[str, CollaborationMessage] = {}
|
||||
self._handlers: Dict[str, callable] = {}
|
||||
|
||||
def register_handler(self, tool_name: str, handler: callable) -> None:
|
||||
"""注册工具处理器"""
|
||||
self._handlers[tool_name] = handler
|
||||
|
||||
async def request_collaboration(
|
||||
self,
|
||||
from_agent: str,
|
||||
to_agent: str,
|
||||
tool_name: str,
|
||||
parameters: Dict[str, Any],
|
||||
timeout: int = 30000,
|
||||
) -> Dict[str, Any]:
|
||||
"""请求协作"""
|
||||
import uuid
|
||||
|
||||
request_id = str(uuid.uuid4())
|
||||
|
||||
message = CollaborationMessage(
|
||||
id=request_id,
|
||||
type=MessageType.REQUEST,
|
||||
from_agent=from_agent,
|
||||
to_agent=to_agent,
|
||||
content={
|
||||
"tool": tool_name,
|
||||
"parameters": parameters,
|
||||
},
|
||||
metadata={"timeout": timeout},
|
||||
)
|
||||
|
||||
self._pending_requests[request_id] = message
|
||||
|
||||
# 发送请求
|
||||
await self._send_message(message)
|
||||
|
||||
# 等待响应
|
||||
try:
|
||||
response = await self._wait_for_response(
|
||||
request_id,
|
||||
timeout,
|
||||
)
|
||||
return response
|
||||
except TimeoutError:
|
||||
return {
|
||||
"status": "error",
|
||||
"error": "协作请求超时",
|
||||
}
|
||||
|
||||
async def handle_request(
|
||||
self,
|
||||
message: CollaborationMessage,
|
||||
) -> CollaborationMessage:
|
||||
"""处理协作请求"""
|
||||
tool_name = message.content["tool"]
|
||||
parameters = message.content["parameters"]
|
||||
|
||||
handler = self._handlers.get(tool_name)
|
||||
if not handler:
|
||||
return CollaborationMessage(
|
||||
id=str(uuid.uuid4()),
|
||||
type=MessageType.RESPONSE,
|
||||
from_agent=message.to_agent,
|
||||
to_agent=message.from_agent,
|
||||
content={
|
||||
"status": "error",
|
||||
"error": f"未知工具: {tool_name}",
|
||||
},
|
||||
metadata={},
|
||||
)
|
||||
|
||||
try:
|
||||
result = await handler(**parameters)
|
||||
return CollaborationMessage(
|
||||
id=str(uuid.uuid4()),
|
||||
type=MessageType.RESPONSE,
|
||||
from_agent=message.to_agent,
|
||||
to_agent=message.from_agent,
|
||||
content={"status": "success", "result": result},
|
||||
metadata={},
|
||||
)
|
||||
except Exception as e:
|
||||
return CollaborationMessage(
|
||||
id=str(uuid.uuid4()),
|
||||
type=MessageType.RESPONSE,
|
||||
from_agent=message.to_agent,
|
||||
to_agent=message.from_agent,
|
||||
content={"status": "error", "error": str(e)},
|
||||
metadata={},
|
||||
)
|
||||
|
||||
async def _send_message(self, message: CollaborationMessage) -> None:
|
||||
"""发送消息"""
|
||||
# TODO: 实现消息发送(WebSocket/消息队列)
|
||||
pass
|
||||
|
||||
async def _wait_for_response(
|
||||
self,
|
||||
request_id: str,
|
||||
timeout: int,
|
||||
) -> Dict[str, Any]:
|
||||
"""等待响应"""
|
||||
# TODO: 实现等待逻辑
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 定时任务
|
||||
|
||||
### 4.1 定时任务服务
|
||||
|
||||
```python
|
||||
# tools/scheduler.py
|
||||
import asyncio
|
||||
from typing import Dict, Any, Callable, Optional
|
||||
from datetime import datetime, timedelta
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ScheduleType(str, Enum):
|
||||
ONCE = "once" # 单次
|
||||
INTERVAL = "interval" # 间隔
|
||||
CRON = "cron" # Cron 表达式
|
||||
|
||||
|
||||
@dataclass
|
||||
class ScheduledTask:
|
||||
"""定时任务"""
|
||||
id: str
|
||||
name: str
|
||||
schedule_type: ScheduleType
|
||||
schedule_value: str # 时间/间隔/cron
|
||||
tool_name: str
|
||||
parameters: Dict[str, Any]
|
||||
enabled: bool = True
|
||||
last_run: Optional[datetime] = None
|
||||
next_run: Optional[datetime] = None
|
||||
run_count: int = 0
|
||||
callback: Optional[Callable] = field(default=None)
|
||||
|
||||
|
||||
class ToolScheduler:
|
||||
"""工具定时调度器"""
|
||||
|
||||
def __init__(self):
|
||||
self._tasks: Dict[str, ScheduledTask] = {}
|
||||
self._running = False
|
||||
self._loop_task = None
|
||||
|
||||
async def schedule(
|
||||
self,
|
||||
name: str,
|
||||
schedule_type: ScheduleType,
|
||||
schedule_value: str,
|
||||
tool_name: str,
|
||||
parameters: Dict[str, Any],
|
||||
callback: Optional[Callable] = None,
|
||||
) -> str:
|
||||
"""创建定时任务"""
|
||||
import uuid
|
||||
|
||||
task_id = str(uuid.uuid4())[:8]
|
||||
|
||||
task = ScheduledTask(
|
||||
id=task_id,
|
||||
name=name,
|
||||
schedule_type=schedule_type,
|
||||
schedule_value=schedule_value,
|
||||
tool_name=tool_name,
|
||||
parameters=parameters,
|
||||
callback=callback,
|
||||
)
|
||||
|
||||
task.next_run = self._calculate_next_run(task)
|
||||
self._tasks[task_id] = task
|
||||
|
||||
# 启动调度器
|
||||
if not self._running:
|
||||
await self.start()
|
||||
|
||||
return task_id
|
||||
|
||||
def _calculate_next_run(self, task: ScheduledTask) -> datetime:
|
||||
"""计算下次运行时间"""
|
||||
now = datetime.utcnow()
|
||||
|
||||
if task.schedule_type == ScheduleType.ONCE:
|
||||
return datetime.fromisoformat(task.schedule_value)
|
||||
|
||||
elif task.schedule_type == ScheduleType.INTERVAL:
|
||||
seconds = int(task.schedule_value)
|
||||
return now + timedelta(seconds=seconds)
|
||||
|
||||
elif task.schedule_type == ScheduleType.CRON:
|
||||
# 解析 cron 表达式
|
||||
# TODO: 实现 cron 解析
|
||||
return now + timedelta(hours=1)
|
||||
|
||||
return now
|
||||
|
||||
async def start(self) -> None:
|
||||
"""启动调度器"""
|
||||
self._running = True
|
||||
self._loop_task = asyncio.create_task(self._run_loop())
|
||||
|
||||
async def stop(self) -> None:
|
||||
"""停止调度器"""
|
||||
self._running = False
|
||||
if self._loop_task:
|
||||
self._loop_task.cancel()
|
||||
|
||||
async def _run_loop(self) -> None:
|
||||
"""调度循环"""
|
||||
while self._running:
|
||||
now = datetime.utcnow()
|
||||
|
||||
for task in self._tasks.values():
|
||||
if not task.enabled:
|
||||
continue
|
||||
|
||||
if task.next_run and task.next_run <= now:
|
||||
await self._execute_task(task)
|
||||
|
||||
await asyncio.sleep(1) # 每秒检查一次
|
||||
|
||||
async def _execute_task(self, task: ScheduledTask) -> None:
|
||||
"""执行任务"""
|
||||
# 调用工具
|
||||
executor = get_executor(task.tool_name)
|
||||
result = await executor(
|
||||
command=task.parameters.get("command"),
|
||||
parameters=task.parameters,
|
||||
)
|
||||
|
||||
# 更新状态
|
||||
task.last_run = datetime.utcnow()
|
||||
task.run_count += 1
|
||||
|
||||
# 计算下次运行
|
||||
if task.schedule_type != ScheduleType.ONCE:
|
||||
task.next_run = self._calculate_next_run(task)
|
||||
else:
|
||||
task.enabled = False
|
||||
|
||||
# 调用回调
|
||||
if task.callback:
|
||||
await task.callback(task, result)
|
||||
|
||||
async def cancel(self, task_id: str) -> bool:
|
||||
"""取消任务"""
|
||||
if task_id in self._tasks:
|
||||
del self._tasks[task_id]
|
||||
return True
|
||||
return False
|
||||
|
||||
async def list_tasks(self) -> list:
|
||||
"""列出所有任务"""
|
||||
return [
|
||||
{
|
||||
"id": t.id,
|
||||
"name": t.name,
|
||||
"type": t.schedule_type.value,
|
||||
"enabled": t.enabled,
|
||||
"next_run": t.next_run.isoformat() if t.next_run else None,
|
||||
"run_count": t.run_count,
|
||||
}
|
||||
for t in self._tasks.values()
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 实现步骤
|
||||
|
||||
| 步骤 | 任务 | 优先级 |
|
||||
|------|------|--------|
|
||||
| 1 | 实现运行时基类 | 🟢 高 |
|
||||
| 2 | 实现 Python 运行时 | 🟢 高 |
|
||||
| 3 | 实现 JS 运行时 | 🟡 中 |
|
||||
| 4 | 实现原生运行时 | 🟡 中 |
|
||||
| 5 | 实现运行时管理器 | 🟢 高 |
|
||||
| 6 | 实现协作协议 | 🟡 中 |
|
||||
| 7 | 实现定时调度器 | 🟡 中 |
|
||||
| 8 | 单元测试 | 🟡 中 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 核心文件变更
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `tools/runtime/__init__.py` | 新增 |
|
||||
| `tools/runtime/base.py` | 新增 |
|
||||
| `tools/runtime/python_runtime.py` | 新增 |
|
||||
| `tools/runtime/js_runtime.py` | 新增 |
|
||||
| `tools/runtime/native_runtime.py` | 新增 |
|
||||
| `tools/runtime/manager.py` | 新增 |
|
||||
| `agents/tools/collaboration.py` | 新增 |
|
||||
| `tools/scheduler.py` | 新增 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 工作量估算
|
||||
|
||||
| 任务 | 工作量 |
|
||||
|------|--------|
|
||||
| 运行时基类 | 0.5 天 |
|
||||
| Python 运行时 | 0.5 天 |
|
||||
| JS 运行时 | 0.5 天 |
|
||||
| 原生运行时 | 0.5 天 |
|
||||
| 运行时管理器 | 0.5 天 |
|
||||
| 协作协议 | 1 天 |
|
||||
| 定时调度器 | 0.5 天 |
|
||||
| 单元测试 | 0.5 天 |
|
||||
| **总计** | **4 天** |
|
||||
|
||||
---
|
||||
|
||||
## 8. 验收标准
|
||||
|
||||
- [ ] Python 运行时可正常执行工具
|
||||
- [ ] JS 运行时可通过 stdio 调用
|
||||
- [ ] 原生运行时可执行二进制
|
||||
- [ ] 运行时管理器正确路由
|
||||
- [ ] 协作协议可正常请求/响应
|
||||
- [ ] 定时调度器可按计划执行任务
|
||||
- [ ] 单元测试通过
|
||||
Reference in New Issue
Block a user