485 lines
12 KiB
Markdown
485 lines
12 KiB
Markdown
|
|
# 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 验证器可捕获错误
|
|||
|
|
- [ ] 单元测试覆盖核心逻辑
|