Files
JARVIS/development-doc/plan/tool-update/phase-t-1-manifest-system.md

12 KiB
Raw Blame History

Phase T.1Manifest 驱动系统

日期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

# 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

# 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

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

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 验证器

# 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 工具配置模板

# tools/configs/.tool.example
# 文件操作器
file_operator:
  allowed_directories: ""
  max_file_size: 10485760

# 联网搜索
web_search:
  api_key: ""
  max_results: 10

5.2 配置加载器

# 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 验证器可捕获错误
  • 单元测试覆盖核心逻辑