feat(agents): Phase 7-10 hook system, plugins, skills, orchestration

Phase 7: Built-in Hooks (audit_log, dangerous_confirmation, security_scan)
Phase 8: Plugin system (PluginManager, PluginSandbox, PluginManifest)
Phase 9: Skills registry (SkillRegistry, local/plugin/MCP loaders)
Phase 10: TeamLeader, RemoteTransport, BackgroundTaskManager
This commit is contained in:
2026-04-04 22:56:27 +08:00
parent e5bd492d74
commit a3fe4d24fc
35 changed files with 8501 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
"""后台任务系统 - Phase 10.4"""
import asyncio
import uuid
from dataclasses import dataclass
from datetime import datetime
from typing import Any
from enum import Enum
class BackgroundTaskStatus(Enum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
@dataclass
class BackgroundTask:
"""后台任务"""
id: str
name: str
status: BackgroundTaskStatus
created_at: datetime
started_at: datetime | None = None
completed_at: datetime | None = None
result: Any = None
error: str | None = None
class BackgroundTaskManager:
"""后台任务管理器"""
def __init__(self):
self._tasks: dict[str, BackgroundTask] = {}
self._.coroutines: dict[str, asyncio.Task] = {}
def submit_task(self, name: str, coro: Any, *args, **kwargs) -> str:
"""提交后台任务
Args:
name: 任务名称
coro: 协程函数
*args: 位置参数
**kwargs: 关键字参数
Returns:
任务 ID
"""
task_id = str(uuid.uuid4())[:8]
# 创建任务记录
self._tasks[task_id] = BackgroundTask(
id=task_id,
name=name,
status=BackgroundTaskStatus.PENDING,
created_at=datetime.now(),
)
# 创建 asyncio task
async def run_task():
self._tasks[task_id].status = BackgroundTaskStatus.RUNNING
self._tasks[task_id].started_at = datetime.now()
try:
result = await coro(*args, **kwargs)
self._tasks[task_id].status = BackgroundTaskStatus.COMPLETED
self._tasks[task_id].result = result
except Exception as e:
self._tasks[task_id].status = BackgroundTaskStatus.FAILED
self._tasks[task_id].error = str(e)
finally:
self._tasks[task_id].completed_at = datetime.now()
if task_id in self._coroutines:
del self._coroutines[task_id]
self._coroutines[task_id] = asyncio.create_task(run_task())
return task_id
def cancel_task(self, task_id: str) -> bool:
"""取消任务
Args:
task_id: 任务 ID
Returns:
是否成功取消
"""
if task_id not in self._tasks:
return False
if task_id in self._coroutines:
self._coroutines[task_id].cancel()
del self._coroutines[task_id]
self._tasks[task_id].status = BackgroundTaskStatus.CANCELLED
self._tasks[task_id].completed_at = datetime.now()
return True
def get_task_status(self, task_id: str) -> BackgroundTask | None:
"""获取任务状态"""
return self._tasks.get(task_id)
def list_tasks(self) -> list[BackgroundTask]:
"""列出所有任务"""
return list(self._tasks.values())
# 全局单例
_manager: BackgroundTaskManager | None = None
def get_background_task_manager() -> BackgroundTaskManager:
"""获取全局后台任务管理器"""
global _manager
if _manager is None:
_manager = BackgroundTaskManager()
return _manager

View File

@@ -0,0 +1,20 @@
"""高级编排系统 - Phase 10"""
from app.agents.team.leader import TeamLeader, TeamTask, TaskStatus
from app.agents.transport.remote import RemoteTransport, StructuredMessage
from app.agents.background.manager import (
BackgroundTaskManager,
BackgroundTask,
get_background_task_manager,
)
__all__ = [
"TeamLeader",
"TeamTask",
"TaskStatus",
"RemoteTransport",
"StructuredMessage",
"BackgroundTaskManager",
"BackgroundTask",
"get_background_task_manager",
]

View File

@@ -0,0 +1,12 @@
"""插件系统 - Phase 8"""
from app.agents.plugins.manager import PluginManager, get_plugin_manager
from app.agents.plugins.manifest import PluginManifest
from app.agents.plugins.sandbox import PluginSandbox
__all__ = [
"PluginManager",
"PluginManifest",
"PluginSandbox",
"get_plugin_manager",
]

View File

@@ -0,0 +1,207 @@
"""插件管理器 - Phase 8.2"""
import importlib.util
import os
import sys
from typing import Any
from app.agents.plugins.manifest import PluginManifest
from app.agents.plugins.sandbox import PluginSandbox
class PluginManager:
"""插件管理器
负责插件的安装、卸载、启用、禁用和生命周期管理。
"""
def __init__(self, plugins_dir: str | None = None):
"""
Args:
plugins_dir: 插件目录None 则使用默认目录
"""
if plugins_dir is None:
plugins_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..", "plugins")
self.plugins_dir = plugins_dir
self._plugins: dict[str, PluginManifest] = {}
self._enabled: dict[str, bool] = {}
self._modules: dict[str, Any] = {}
self._sandbox = PluginSandbox()
def install(self, plugin_path: str) -> bool:
"""安装插件
Args:
plugin_path: 插件目录路径或 manifest.json 所在目录
Returns:
是否安装成功
"""
try:
manifest_path = os.path.join(plugin_path, "manifest.json")
if not os.path.exists(manifest_path):
return False
with open(manifest_path, "r", encoding="utf-8") as f:
import json
data = json.load(f)
manifest = PluginManifest.from_dict(data)
# 验证 manifest
if not self._validate_manifest(manifest, plugin_path):
return False
# 复制插件到 plugins_dir
target_dir = os.path.join(self.plugins_dir, manifest.id)
os.makedirs(os.path.dirname(target_dir), exist_ok=True)
# 保存 manifest
with open(os.path.join(target_dir, "manifest.json"), "w", encoding="utf-8") as f:
json.dump(manifest.to_dict(), f, indent=2, ensure_ascii=False)
# 注册插件
self._plugins[manifest.id] = manifest
self._enabled[manifest.id] = True
return True
except Exception:
return False
def uninstall(self, plugin_id: str) -> bool:
"""卸载插件
Args:
plugin_id: 插件 ID
Returns:
是否卸载成功
"""
if plugin_id not in self._plugins:
return False
# 禁用插件
self.disable(plugin_id)
# 移除模块
if plugin_id in self._modules:
del self._modules[plugin_id]
# 移除插件
del self._plugins[plugin_id]
del self._enabled[plugin_id]
# 删除目录
plugin_dir = os.path.join(self.plugins_dir, plugin_id)
if os.path.exists(plugin_dir):
import shutil
shutil.rmtree(plugin_dir)
return True
def enable(self, plugin_id: str) -> bool:
"""启用插件
Args:
plugin_id: 插件 ID
Returns:
是否启用成功
"""
if plugin_id not in self._plugins:
return False
self._enabled[plugin_id] = True
return True
def disable(self, plugin_id: str) -> bool:
"""禁用插件
Args:
plugin_id: 插件 ID
Returns:
是否禁用成功
"""
if plugin_id not in self._plugins:
return False
self._enabled[plugin_id] = False
return True
def reload(self, plugin_id: str) -> bool:
"""重新加载插件
Args:
plugin_id: 插件 ID
Returns:
是否重新加载成功
"""
if plugin_id not in self._plugins:
return False
# 卸载模块
if plugin_id in self._modules:
del self._modules[plugin_id]
# 重新加载
return self._load_plugin_module(plugin_id)
def list_plugins(self) -> list[PluginManifest]:
"""列出所有插件"""
return list(self._plugins.values())
def get_plugin(self, plugin_id: str) -> PluginManifest | None:
"""获取插件清单"""
return self._plugins.get(plugin_id)
def is_enabled(self, plugin_id: str) -> bool:
"""检查插件是否启用"""
return self._enabled.get(plugin_id, False)
def _validate_manifest(self, manifest: PluginManifest, plugin_path: str) -> bool:
"""验证 manifest"""
# 检查主入口文件是否存在
main_path = os.path.join(plugin_path, manifest.main)
if not os.path.exists(main_path):
return False
return True
def _load_plugin_module(self, plugin_id: str) -> bool:
"""加载插件模块"""
plugin_dir = os.path.join(self.plugins_dir, plugin_id)
manifest = self._plugins.get(plugin_id)
if not manifest:
return False
try:
main_path = os.path.join(plugin_dir, manifest.main)
spec = importlib.util.spec_from_file_location(plugin_id, main_path)
if spec and spec.loader:
module = importlib.util.module_from_spec(spec)
sys.modules[plugin_id] = module
spec.loader.exec_module(module)
self._modules[plugin_id] = module
return True
except Exception:
pass
return False
# 全局单例
_manager: PluginManager | None = None
def get_plugin_manager() -> PluginManager:
"""获取全局插件管理器"""
global _manager
if _manager is None:
_manager = PluginManager()
return _manager

View File

@@ -0,0 +1,73 @@
"""插件清单定义 - Phase 8.1"""
from dataclasses import dataclass, field
from typing import Any
@dataclass
class PluginManifest:
"""插件清单
定义插件的元数据和接口。
"""
id: str # 唯一标识
name: str # 显示名称
version: str # 版本号
description: str # 描述
author: str = "" # 作者
homepage: str = "" # 主页
license: str = "MIT" # 许可证
# 插件类型
plugin_type: str = "tool" # tool, hook, skill, all
# 入口点
main: str = "index.py" # 主入口文件
hooks: list[str] = field(default_factory=list) # 提供的 Hook 列表
tools: list[str] = field(default_factory=list) # 提供的工具列表
skills: list[str] = field(default_factory=list) # 提供的 Skills 列表
# 依赖
dependencies: dict[str, str] = field(default_factory=dict) # pip 依赖
peer_dependencies: dict[str, str] = field(default_factory=dict) # 对等依赖
# 权限要求
permissions: list[str] = field(default_factory=list) # 需要的权限
allowed_paths: list[str] = field(default_factory=list) # 允许访问的路径
denied_paths: list[str] = field(default_factory=list) # 禁止访问的路径
# 网络权限
network_allowed: bool = False # 是否允许网络访问
allowed_hosts: list[str] = field(default_factory=list) # 允许访问的 host
# 配置
config_schema: dict[str, Any] = field(default_factory=dict) # 配置 schema
def to_dict(self) -> dict[str, Any]:
return {
"id": self.id,
"name": self.name,
"version": self.version,
"description": self.description,
"author": self.author,
"homepage": self.homepage,
"license": self.license,
"plugin_type": self.plugin_type,
"main": self.main,
"hooks": self.hooks,
"tools": self.tools,
"skills": self.skills,
"dependencies": self.dependencies,
"peer_dependencies": self.peer_dependencies,
"permissions": self.permissions,
"allowed_paths": self.allowed_paths,
"denied_paths": self.denied_paths,
"network_allowed": self.network_allowed,
"allowed_hosts": self.allowed_hosts,
"config_schema": self.config_schema,
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "PluginManifest":
return cls(**data)

View File

@@ -0,0 +1,111 @@
"""插件沙箱隔离 - Phase 8.3"""
import os
import sys
from typing import Any
class PluginSandbox:
"""插件沙箱
提供插件执行隔离环境。
"""
def __init__(self):
self._allowed_paths: set[str] = set()
self._denied_paths: set[str] = set()
self._network_allowed: bool = False
self._allowed_hosts: set[str] = set()
def set_file_permissions(
self,
allowed_paths: list[str] | None = None,
denied_paths: list[str] | None = None,
) -> None:
"""设置文件访问权限
Args:
allowed_paths: 允许访问的路径列表
denied_paths: 禁止访问的路径列表
"""
self._allowed_paths = set(allowed_paths or [])
self._denied_paths = set(denied_paths or [])
def set_network_permissions(
self, allowed: bool, allowed_hosts: list[str] | None = None
) -> None:
"""设置网络访问权限
Args:
allowed: 是否允许网络访问
allowed_hosts: 允许访问的 host 列表
"""
self._network_allowed = allowed
self._allowed_hosts = set(allowed_hosts or [])
def check_file_access(self, path: str) -> bool:
"""检查文件访问权限
Args:
path: 文件路径
Returns:
是否允许访问
"""
# 如果有允许列表,只允许访问列表中的路径
if self._allowed_paths:
return path in self._allowed_paths or any(
path.startswith(allowed) for allowed in self._allowed_paths
)
# 如果有禁止列表,禁止访问列表中的路径
if self._denied_paths:
return not any(path.startswith(denied) for denied in self._denied_paths)
# 没有限制
return True
def check_network_access(self, host: str) -> bool:
"""检查网络访问权限
Args:
host: 主机地址
Returns:
是否允许访问
"""
if not self._network_allowed:
return False
if self._allowed_hosts:
return host in self._allowed_hosts or any(
host.endswith(allowed) for allowed in self._allowed_hosts
)
return True
def execute_in_sandbox(self, func: Any, *args, **kwargs) -> Any:
"""在沙箱中执行函数
Args:
func: 要执行的函数
*args: 位置参数
**kwargs: 关键字参数
Returns:
函数返回值
"""
# 保存当前状态
old_allowed_paths = self._allowed_paths.copy()
old_denied_paths = self._denied_paths.copy()
old_network_allowed = self._network_allowed
old_allowed_hosts = self._allowed_hosts.copy()
try:
return func(*args, **kwargs)
finally:
# 恢复状态
self._allowed_paths = old_allowed_paths
self._denied_paths = old_denied_paths
self._network_allowed = old_network_allowed
self._allowed_hosts = old_allowed_hosts

View File

@@ -0,0 +1,16 @@
"""Skills 注册表 - Phase 9"""
from app.agents.skills.registry import SkillRegistry, get_skill_registry
from app.agents.skills.metadata import SkillMetadata
from app.agents.skills.loaders.local_loader import LocalSkillLoader
from app.agents.skills.loaders.plugin_loader import PluginSkillLoader
from app.agents.skills.mcp_builder import MCPSkillBuilder
__all__ = [
"SkillRegistry",
"SkillMetadata",
"LocalSkillLoader",
"PluginSkillLoader",
"MCPSkillBuilder",
"get_skill_registry",
]

View File

@@ -0,0 +1,100 @@
"""本地 Skills 加载器 - Phase 9.2"""
import os
import re
from typing import Any
from app.agents.skills.metadata import SkillMetadata
class LocalSkillLoader:
"""本地 Skills 加载器
从 skills_dir 目录加载 SKILL.md 文件。
"""
def __init__(self, skills_dir: str):
self.skills_dir = skills_dir
def load_all(self) -> list[SkillMetadata]:
"""加载所有本地 Skills
Returns:
Skill 元数据列表
"""
skills = []
if not os.path.exists(self.skills_dir):
return skills
for root, dirs, files in os.walk(self.skills_dir):
# 跳过隐藏目录
dirs[:] = [d for d in dirs if not d.startswith(".")]
if "SKILL.md" in files:
skill = self._load_skill_from_dir(root)
if skill:
skills.append(skill)
return skills
def _load_skill_from_dir(self, skill_dir: str) -> SkillMetadata | None:
"""从目录加载 Skill
Args:
skill_dir: Skill 目录
Returns:
Skill 元数据
"""
skill_path = os.path.join(skill_dir, "SKILL.md")
try:
with open(skill_path, "r", encoding="utf-8") as f:
content = f.read()
# 解析 frontmatter
metadata = self._parse_frontmatter(content)
# 获取 Skill 名称(目录名)
name = os.path.basename(skill_dir)
return SkillMetadata(
name=metadata.get("name", name),
description=metadata.get("description", ""),
version=metadata.get("version", "1.0.0"),
author=metadata.get("author", ""),
tags=metadata.get("tags", []),
triggers=metadata.get("triggers", []),
content=content,
source="local",
source_id=skill_dir,
)
except Exception:
return None
def _parse_frontmatter(self, content: str) -> dict[str, Any]:
"""解析 frontmatter"""
metadata = {}
# 匹配 --- 包裹的 frontmatter
match = re.match(r"^---\n(.*?)\n---", content, re.DOTALL)
if match:
frontmatter = match.group(1)
for line in frontmatter.split("\n"):
if ":" in line:
key, value = line.split(":", 1)
key = key.strip()
value = value.strip()
# 处理列表
if value.startswith("[") and value.endswith("]"):
value = [v.strip().strip('"').strip("'") for v in value[1:-1].split(",")]
elif value.lower() in ("true", "false"):
value = value.lower() == "true"
metadata[key] = value
return metadata

View File

@@ -0,0 +1,51 @@
"""插件 Skills 加载器 - Phase 9.2"""
from app.agents.skills.metadata import SkillMetadata
from app.agents.plugins.manager import get_plugin_manager
class PluginSkillLoader:
"""插件 Skills 加载器
从已安装的插件中加载 Skills。
"""
def __init__(self):
self.plugin_manager = get_plugin_manager()
def load_all(self) -> list[SkillMetadata]:
"""从所有已启用的插件加载 Skills
Returns:
Skill 元数据列表
"""
skills = []
for plugin in self.plugin_manager.list_plugins():
if not self.plugin_manager.is_enabled(plugin.id):
continue
# 从插件加载 Skills
plugin_skills = self._load_from_plugin(plugin)
skills.extend(plugin_skills)
return skills
def _load_from_plugin(self, plugin: Any) -> list[SkillMetadata]:
"""从单个插件加载 Skills"""
skills = []
for skill_name in plugin.skills:
skill = SkillMetadata(
name=f"{plugin.id}/{skill_name}",
description=f"Skill from plugin: {plugin.name}",
version=plugin.version,
author=plugin.author,
tags=["plugin", plugin.id],
content=f"# {skill_name}\n\nFrom plugin: {plugin.name}",
source="plugin",
source_id=plugin.id,
)
skills.append(skill)
return skills

View File

@@ -0,0 +1,100 @@
"""MCP Skill Builder - Phase 9.3"""
from typing import Any
from app.agents.skills.metadata import SkillMetadata
class MCPSkillBuilder:
"""MCP Skill Builder
从 MCP 服务器发现和构建 Skills。
"""
def __init__(self):
self._skills: dict[str, SkillMetadata] = {}
def discover_skills_from_mcp(self, mcp_servers: list[dict[str, Any]]) -> list[SkillMetadata]:
"""从 MCP 服务器发现 Skills
Args:
mcp_servers: MCP 服务器配置列表
Returns:
发现的 Skill 元数据列表
"""
skills = []
for server in mcp_servers:
server_skills = self._discover_from_server(server)
skills.extend(server_skills)
return skills
def _discover_from_server(self, server: dict[str, Any]) -> list[SkillMetadata]:
"""从单个 MCP 服务器发现 Skills"""
skills = []
server_name = server.get("name", "unknown")
tools = server.get("tools", [])
# 按工具分组
tool_groups: dict[str, list[str]] = {}
for tool in tools:
group = tool.get("group", "default")
if group not in tool_groups:
tool_groups[group] = []
tool_groups[group].append(tool)
# 为每个组创建一个 Skill
for group_name, group_tools in tool_groups.items():
skill = self._tool_to_skill(group_name, group_tools, server_name)
skills.append(skill)
return skills
def _tool_to_skill(self, group: str, tools: list[dict[str, Any]], server: str) -> SkillMetadata:
"""将 MCP 工具转换为 Skill"""
tool_summaries = []
for tool in tools:
name = tool.get("name", "unknown")
description = tool.get("description", "")
input_schema = tool.get("inputSchema", {})
tool_summaries.append(f"### {name}\n{description}\n\nInput: {input_schema}")
content = f"""# MCP Skill: {group}
来自 MCP 服务器: {server}
## 工具列表
{chr(10).join(tool_summaries)}
## 使用说明
使用这些工具前请确保理解每个工具的输入输出格式。
"""
return SkillMetadata(
name=f"mcp-{server}-{group}",
description=f"MCP skill from {server}: {group}",
version="1.0.0",
tags=["mcp", server, group],
triggers=[group, server],
content=content,
source="mcp",
source_id=f"{server}:{group}",
)
def _group_to_skill(self, group: str, tools: list[str], server: str) -> SkillMetadata:
"""将 MCP 工具组转换为 Skill"""
return SkillMetadata(
name=f"mcp-{server}-{group}",
description=f"MCP skill from {server}: {group}",
version="1.0.0",
tags=["mcp", server, group],
triggers=[group, server],
content=f"# {group}\n\nTools: {', '.join(tools)}",
source="mcp",
source_id=f"{server}:{group}",
)

View File

@@ -0,0 +1,38 @@
"""Skill 元数据定义 - Phase 9.1"""
from dataclasses import dataclass, field
from typing import Any
@dataclass
class SkillMetadata:
"""Skill 元数据"""
name: str # Skill 名称
description: str # 描述
version: str = "1.0.0" # 版本
author: str = "" # 作者
tags: list[str] = field(default_factory=list) # 标签
triggers: list[str] = field(default_factory=list) # 触发关键词
content: str = "" # Skill 内容markdown
source: str = "local" # 来源local, plugin, mcp, bundled
source_id: str = "" # 来源 ID
enabled: bool = True # 是否启用
def to_dict(self) -> dict[str, Any]:
return {
"name": self.name,
"description": self.description,
"version": self.version,
"author": self.author,
"tags": self.tags,
"triggers": self.triggers,
"content": self.content,
"source": self.source,
"source_id": self.source_id,
"enabled": self.enabled,
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "SkillMetadata":
return cls(**data)

View File

@@ -0,0 +1,133 @@
"""Skills 注册表 - Phase 9.1"""
import os
from typing import Any
from app.agents.skills.metadata import SkillMetadata
from app.agents.skills.loaders.local_loader import LocalSkillLoader
class SkillRegistry:
"""Skills 注册表
管理所有 Skills 的注册、发现和加载。
"""
def __init__(self):
self._skills: dict[str, SkillMetadata] = {}
self._loaders: list[Any] = []
def load_all(self, skills_dir: str | None = None) -> int:
"""加载所有 Skills
Args:
skills_dir: Skills 目录None 则使用默认目录
Returns:
加载的 Skill 数量
"""
if skills_dir is None:
skills_dir = os.path.join(
os.path.dirname(__file__), "..", "..", "..", ".claude", "skills"
)
count = 0
# 本地加载器
local_loader = LocalSkillLoader(skills_dir)
local_skills = local_loader.load_all()
for skill in local_skills:
self.register(skill)
count += 1
# 插件加载器
for loader in self._loaders:
try:
external_skills = loader.load_all()
for skill in external_skills:
self.register(skill)
count += 1
except Exception:
pass
return count
def register(self, skill: SkillMetadata) -> None:
"""注册 Skill"""
self._skills[skill.name] = skill
def unregister(self, name: str) -> bool:
"""注销 Skill"""
if name in self._skills:
del self._skills[name]
return True
return False
def get_skill(self, name: str) -> SkillMetadata | None:
"""获取 Skill"""
return self._skills.get(name)
def search(self, query: str) -> list[SkillMetadata]:
"""搜索 Skills
Args:
query: 搜索关键词
Returns:
匹配的 Skills 列表
"""
query_lower = query.lower()
results = []
for skill in self._skills.values():
if not skill.enabled:
continue
# 匹配名称、描述、标签
if (
query_lower in skill.name.lower()
or query_lower in skill.description.lower()
or any(query_lower in tag.lower() for tag in skill.tags)
or any(query_lower in trigger.lower() for trigger in skill.triggers)
):
results.append(skill)
return results
def get_skill_context(self, names: list[str]) -> str:
"""获取 Skill 上下文
Args:
names: Skill 名称列表
Returns:
拼接的 Skill 内容
"""
contexts = []
for name in names:
skill = self._skills.get(name)
if skill and skill.enabled:
contexts.append(f"# {skill.name}\n\n{skill.content}")
return "\n\n---\n\n".join(contexts)
def add_loader(self, loader: Any) -> None:
"""添加加载器"""
self._loaders.append(loader)
def list_all(self) -> list[SkillMetadata]:
"""列出所有 Skills"""
return list(self._skills.values())
# 全局单例
_registry: SkillRegistry | None = None
def get_skill_registry() -> SkillRegistry:
"""获取全局 Skills 注册表"""
global _registry
if _registry is None:
_registry = SkillRegistry()
return _registry

View File

@@ -0,0 +1,121 @@
"""Team 多 Agent 协作 - Phase 10.1"""
from dataclasses import dataclass, field
from typing import Any
from enum import Enum
class TaskStatus(Enum):
PENDING = "pending"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
FAILED = "failed"
@dataclass
class TeamTask:
"""团队任务"""
id: str
description: str
assignee: str | None = None
status: TaskStatus = TaskStatus.PENDING
result: Any = None
error: str | None = None
class TeamLeader:
"""团队领导者
协调多个 Agent 成员执行任务。
"""
def __init__(self, team_id: str, members: list[str]):
"""
Args:
team_id: 团队 ID
members: 成员 ID 列表
"""
self.team_id = team_id
self.members = members
self._tasks: dict[str, TeamTask] = {}
def create_task(self, description: str) -> str:
"""创建任务
Args:
description: 任务描述
Returns:
任务 ID
"""
import uuid
task_id = str(uuid.uuid4())[:8]
self._tasks[task_id] = TeamTask(
id=task_id,
description=description,
)
return task_id
def assign_task(self, task_id: str, member: str) -> bool:
"""分配任务
Args:
task_id: 任务 ID
member: 成员 ID
Returns:
是否成功
"""
if task_id not in self._tasks:
return False
if member not in self.members:
return False
self._tasks[task_id].assignee = member
self._tasks[task_id].status = TaskStatus.IN_PROGRESS
return True
def broadcast_task(self, description: str) -> list[str]:
"""广播任务给所有成员
Args:
description: 任务描述
Returns:
创建的任务 ID 列表
"""
task_ids = []
for member in self.members:
task_id = self.create_task(description)
self.assign_task(task_id, member)
task_ids.append(task_id)
return task_ids
def collect_results(self) -> dict[str, Any]:
"""收集所有任务结果
Returns:
任务 ID -> 结果的映射
"""
return {
task_id: task.result
for task_id, task in self._tasks.items()
if task.status == TaskStatus.COMPLETED
}
def get_team_status(self) -> dict[str, Any]:
"""获取团队状态
Returns:
团队状态摘要
"""
return {
"team_id": self.team_id,
"members": self.members,
"task_count": len(self._tasks),
"completed": sum(1 for t in self._tasks.values() if t.status == TaskStatus.COMPLETED),
"failed": sum(1 for t in self._tasks.values() if t.status == TaskStatus.FAILED),
}

View File

@@ -0,0 +1,11 @@
"""内置 Hook 集合 - Phase 7"""
from app.agents.tools.hooks.builtins.audit_log import AuditLogHook
from app.agents.tools.hooks.builtins.dangerous_confirmation import DangerousConfirmationHook
from app.agents.tools.hooks.builtins.security_scan import SecurityScanHook
__all__ = [
"AuditLogHook",
"DangerousConfirmationHook",
"SecurityScanHook",
]

View File

@@ -0,0 +1,115 @@
"""审计日志 Hook - Phase 7.2
记录所有工具调用到审计日志。
"""
from typing import Any
from app.agents.tools.hooks.types import (
ExecutionContext,
HookResult,
HookType,
)
from app.agents.tools.manifest import ToolCategory
class AuditLogHook:
"""审计日志 Hook
记录所有工具调用的详细信息,包括:
- 调用时间
- 工具名称
- 输入参数
- 执行结果
- 执行时长
- 用户 ID
"""
def __init__(self, log_path: str | None = None):
"""
Args:
log_path: 日志文件路径None 则输出到 stdout
"""
self.log_path = log_path
self._logs: list[dict[str, Any]] = []
async def pre_tool_use(self, context: ExecutionContext) -> HookResult:
"""工具执行前记录"""
log_entry = {
"event": "pre_tool",
"tool_name": context.tool_name,
"input": context.tool_input,
"user_id": context.user_id,
"session_id": context.session_id,
}
self._logs.append(log_entry)
self._write_log(log_entry)
return HookResult(
hook_name="audit_log",
success=True,
continue_execution=True,
)
async def post_tool_use(self, context: ExecutionContext, result: Any) -> HookResult:
"""工具执行后记录"""
log_entry = {
"event": "post_tool",
"tool_name": context.tool_name,
"result": str(result)[:500] if result else None,
"duration_ms": (
(context.end_time - context.start_time) * 1000
if context.start_time and context.end_time
else None
),
}
self._logs.append(log_entry)
self._write_log(log_entry)
return HookResult(
hook_name="audit_log",
success=True,
continue_execution=True,
modified_output=result,
)
async def tool_error(self, context: ExecutionContext, error: Exception) -> HookResult:
"""工具出错时记录"""
log_entry = {
"event": "tool_error",
"tool_name": context.tool_name,
"error": str(error),
"error_type": type(error).__name__,
}
self._logs.append(log_entry)
self._write_log(log_entry)
return HookResult(
hook_name="audit_log",
success=False,
continue_execution=True,
error=str(error),
)
def _write_log(self, entry: dict[str, Any]) -> None:
"""写入日志"""
import json
import datetime
entry["timestamp"] = datetime.datetime.now().isoformat()
if self.log_path:
try:
with open(self.log_path, "a", encoding="utf-8") as f:
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
except Exception:
# 日志写入失败不影响主流程
pass
else:
# 输出到 stdout
print(f"[AUDIT] {json.dumps(entry, ensure_ascii=False)}")
def get_logs(self) -> list[dict[str, Any]]:
"""获取所有日志"""
return self._logs.copy()
def clear_logs(self) -> None:
"""清空日志"""
self._logs.clear()

View File

@@ -0,0 +1,142 @@
"""危险操作确认 Hook - Phase 7.2
对危险操作要求用户确认。
"""
from typing import Any
from app.agents.tools.hooks.types import (
ExecutionContext,
HookResult,
)
from app.agents.tools.manifest import SideEffectScope
# 危险操作关键词
DANGEROUS_PATTERNS = [
# 文件操作
"delete",
"remove",
"rm ",
"rmdir",
"unlink",
"format",
"truncate",
# 系统操作
"shutdown",
"reboot",
"kill",
"pkill",
"sudo",
"chmod",
"chown",
# 数据操作
"drop",
"truncate",
"delete from",
"delete.*where",
"insert into.*select",
"update.*set",
# 网络操作
"curl",
"wget",
"nc ",
"netcat",
"ssh ",
"scp ",
"sftp ",
# 环境变量
"export.*secret",
"export.*key",
"export.*token",
]
class DangerousConfirmationHook:
"""危险操作确认 Hook
检查工具调用是否包含危险操作,如是则要求确认。
"""
def __init__(self, auto_block: bool = False):
"""
Args:
auto_block: True 表示自动拦截危险操作False 表示仅警告
"""
self.auto_block = auto_block
self._pending_confirmations: dict[str, bool] = {}
async def pre_tool_use(self, context: ExecutionContext) -> HookResult:
"""检查是否为危险操作"""
is_dangerous = self._check_dangerous(context.tool_name, context.tool_input)
if is_dangerous:
if self.auto_block:
return HookResult(
hook_name="dangerous_confirmation",
success=False,
continue_execution=False,
error=f"危险操作被自动拦截: {context.tool_name}",
metadata={"dangerous": True, "auto_blocked": True},
)
else:
# 标记需要确认
context.metadata["requires_confirmation"] = True
context.metadata["dangerous_operation"] = True
return HookResult(
hook_name="dangerous_confirmation",
success=True,
continue_execution=True,
metadata={"dangerous": True, "requires_confirmation": True},
)
return HookResult(
hook_name="dangerous_confirmation",
success=True,
continue_execution=True,
)
def _check_dangerous(self, tool_name: str, tool_input: dict[str, Any]) -> bool:
"""检查是否为危险操作"""
# 检查工具名称
dangerous_tools = [
"delete",
"remove",
"drop",
"truncate",
"kill",
"shutdown",
"reboot",
"bash",
"powershell",
"shell",
]
if tool_name.lower() in dangerous_tools:
return True
# 检查输入参数
input_str = str(tool_input).lower()
for pattern in DANGEROUS_PATTERNS:
if pattern.lower() in input_str:
return True
return False
def confirm(self, session_id: str, confirmed: bool) -> None:
"""确认危险操作
Args:
session_id: 会话 ID
confirmed: True 表示用户确认False 表示取消
"""
self._pending_confirmations[session_id] = confirmed
def is_confirmed(self, session_id: str) -> bool:
"""检查是否已确认"""
return self._pending_confirmations.get(session_id, False)
def clear_confirmation(self, session_id: str) -> None:
"""清除确认状态"""
self._pending_confirmations.pop(session_id, None)

View File

@@ -0,0 +1,183 @@
"""安全扫描 Hook - Phase 7.2
扫描工具调用和结果中的敏感信息。
"""
import re
from typing import Any
from app.agents.tools.hooks.types import (
ExecutionContext,
HookResult,
)
# 敏感信息模式
SENSITIVE_PATTERNS = {
"api_key": [
r"api[_-]?key['\"]?\s*[:=]\s*['\"]?[a-zA-Z0-9_\-]{20,}",
r"apikey['\"]?\s*[:=]\s*['\"]?[a-zA-Z0-9_\-]{20,}",
],
"password": [
r"password['\"]?\s*[:=]\s*['\"]?[^\s'\"]{8,}",
r"passwd['\"]?\s*[:=]\s*['\"]?[^\s'\"]{8,}",
r"secret['\"]?\s*[:=]\s*['\"]?[a-zA-Z0-9_\-]{20,}",
],
"token": [
r"token['\"]?\s*[:=]\s*['\"]?[a-zA-Z0-9_\-\.]{20,}",
r"bearer\s+[a-zA-Z0-9_\-\.]+",
r"ghp_[a-zA-Z0-9]{36}",
r"sk-[a-zA-Z0-9]{48}",
],
"private_key": [
r"-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----",
r"-----END (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----",
],
"ip_address": [
r"\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b",
],
"email": [
r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
],
}
class SecurityScanHook:
"""安全扫描 Hook
扫描工具输入和输出中的敏感信息,进行脱敏处理。
"""
def __init__(
self,
redact: bool = True,
block_on_detect: bool = False,
):
"""
Args:
redact: 是否对敏感信息进行脱敏
block_on_detect: 检测到敏感信息时是否阻止执行
"""
self.redact = redact
self.block_on_detect = block_on_detect
self._compiled_patterns = {
name: [re.compile(p, re.IGNORECASE) for p in patterns]
for name, patterns in SENSITIVE_PATTERNS.items()
}
async def pre_tool_use(self, context: ExecutionContext) -> HookResult:
"""扫描输入参数"""
detected = self._scan_dict(context.tool_input)
if detected:
context.metadata["security_detected"] = detected
if self.block_on_detect:
return HookResult(
hook_name="security_scan",
success=False,
continue_execution=False,
error=f"检测到敏感信息: {', '.join(detected.keys())}",
metadata={"detected": detected, "blocked": True},
)
if self.redact:
redacted_input = self._redact_dict(context.tool_input.copy())
return HookResult(
hook_name="security_scan",
success=True,
continue_execution=True,
modified_input=redacted_input,
metadata={"detected": detected, "redacted": True},
)
return HookResult(
hook_name="security_scan",
success=True,
continue_execution=True,
)
async def post_tool_use(self, context: ExecutionContext, result: Any) -> HookResult:
"""扫描输出结果"""
if isinstance(result, dict):
detected = self._scan_dict(result)
if detected:
context.metadata["security_detected_output"] = detected
if self.redact:
redacted_result = self._redact_dict(result.copy())
return HookResult(
hook_name="security_scan",
success=True,
continue_execution=True,
modified_output=redacted_result,
metadata={"detected": detected, "redacted": True},
)
elif isinstance(result, str):
detected = self._scan_string(result)
if detected:
context.metadata["security_detected_output"] = detected
if self.redact:
redacted_result = self._redact_string(result)
return HookResult(
hook_name="security_scan",
success=True,
continue_execution=True,
modified_output=redacted_result,
metadata={"detected": detected, "redacted": True},
)
return HookResult(
hook_name="security_scan",
success=True,
continue_execution=True,
modified_output=result,
)
def _scan_dict(self, data: dict[str, Any]) -> dict[str, list[str]]:
"""扫描字典中的敏感信息"""
result: dict[str, list[str]] = {}
for key, value in data.items():
if isinstance(value, str):
found = self._scan_string(value)
if found:
result[key] = found
return result
def _scan_string(self, text: str) -> list[str]:
"""扫描字符串中的敏感信息"""
found_types = []
for name, patterns in self._compiled_patterns.items():
for pattern in patterns:
if pattern.search(text):
if name not in found_types:
found_types.append(name)
break
return found_types
def _redact_dict(self, data: dict[str, Any]) -> dict[str, Any]:
"""脱敏字典中的敏感信息"""
for key, value in data.items():
if isinstance(value, str):
data[key] = self._redact_string(value)
elif isinstance(value, dict):
data[key] = self._redact_dict(value)
elif isinstance(value, list):
data[key] = [self._redact_string(v) if isinstance(v, str) else v for v in value]
return data
def _redact_string(self, text: str) -> str:
"""脱敏字符串中的敏感信息"""
for name, patterns in self._compiled_patterns.items():
for pattern in patterns:
text = pattern.sub(f"[REDACTED:{name}]", text)
return text

View File

@@ -0,0 +1,105 @@
"""Hook 配置持久化 - Phase 7.3"""
import json
import os
from dataclasses import asdict, dataclass
from typing import Any
from app.agents.tools.hooks.manager import get_hook_manager
@dataclass
class HookConfigEntry:
"""Hook 配置条目"""
name: str
hook_type: str
enabled: bool
tool_names: list[str] | None = None
categories: list[str] | None = None
priority: int = 0
class HookConfigPersistence:
"""Hook 配置持久化"""
def __init__(self, config_path: str | None = None):
"""
Args:
config_path: 配置文件路径None 则使用默认路径
"""
if config_path is None:
config_path = os.path.join(
os.path.dirname(__file__), "..", "..", "..", "..", "config", "hooks.json"
)
self.config_path = config_path
def load_config(self) -> list[HookConfigEntry]:
"""从文件加载 Hook 配置"""
if not os.path.exists(self.config_path):
return []
try:
with open(self.config_path, "r", encoding="utf-8") as f:
data = json.load(f)
return [HookConfigEntry(**entry) for entry in data]
except Exception:
return []
def save_config(self, entries: list[HookConfigEntry]) -> bool:
"""保存 Hook 配置到文件"""
try:
os.makedirs(os.path.dirname(self.config_path), exist_ok=True)
with open(self.config_path, "w", encoding="utf-8") as f:
json.dump([asdict(e) for e in entries], f, indent=2, ensure_ascii=False)
return True
except Exception:
return False
def apply_config(self) -> int:
"""应用配置到 HookManager
Returns:
应用的 Hook 数量
"""
from app.agents.tools.hooks.types import HookType
manager = get_hook_manager()
entries = self.load_config()
count = 0
for entry in entries:
if entry.enabled:
from app.agents.tools.hooks.types import HookDefinition, HookTrigger
trigger = HookTrigger(
tool_names=entry.tool_names,
categories=entry.categories,
)
# 创建空的 handler只是注册配置
hook_def = HookDefinition(
name=entry.name,
hook_type=HookType(entry.hook_type),
trigger=trigger,
handler=lambda ctx, *args: ctx,
priority=entry.priority,
enabled=True,
)
manager.register(hook_def)
count += 1
return count
# 全局单例
_persistence: HookConfigPersistence | None = None
def get_hook_config_persistence() -> HookConfigPersistence:
"""获取全局 Hook 配置持久化实例"""
global _persistence
if _persistence is None:
_persistence = HookConfigPersistence()
return _persistence

View File

@@ -0,0 +1,113 @@
"""远程传输层 - Phase 10.2"""
import asyncio
import json
from typing import Any
from dataclasses import dataclass
@dataclass
class StructuredMessage:
"""结构化消息"""
type: str # response, event, tool_call, error
data: dict[str, Any]
session_id: str | None = None
class RemoteTransport:
"""远程传输层
处理与远程 Agent 的通信。
"""
def __init__(self):
self._connections: dict[str, Any] = {}
self._handlers: dict[str, Any] = {}
async def send_response(self, session_id: str, response: dict[str, Any]) -> bool:
"""发送响应
Args:
session_id: 会话 ID
response: 响应数据
Returns:
是否发送成功
"""
message = StructuredMessage(
type="response",
data=response,
session_id=session_id,
)
return await self._send(session_id, message)
async def send_event(self, session_id: str, event: dict[str, Any]) -> bool:
"""发送事件
Args:
session_id: 会话 ID
event: 事件数据
Returns:
是否发送成功
"""
message = StructuredMessage(
type="event",
data=event,
session_id=session_id,
)
return await self._send(session_id, message)
async def send_tool_call(self, session_id: str, tool_call: dict[str, Any]) -> bool:
"""发送工具调用
Args:
session_id: 会话 ID
tool_call: 工具调用数据
Returns:
是否发送成功
"""
message = StructuredMessage(
type="tool_call",
data=tool_call,
session_id=session_id,
)
return await self._send(session_id, message)
async def _send(self, session_id: str, message: StructuredMessage) -> bool:
"""内部发送方法"""
if session_id not in self._connections:
return False
try:
connection = self._connections[session_id]
if hasattr(connection, "send"):
await connection.send(json.dumps(message.__dict__))
return True
except Exception:
pass
return False
def register_handler(self, event_type: str, handler: Any) -> None:
"""注册消息处理器
Args:
event_type: 事件类型
handler: 处理函数
"""
self._handlers[event_type] = handler
async def handle_message(self, session_id: str, message: dict[str, Any]) -> None:
"""处理收到的消息
Args:
session_id: 会话 ID
message: 消息数据
"""
msg_type = message.get("type")
handler = self._handlers.get(msg_type)
if handler:
await handler(session_id, message.get("data"))

View File

@@ -0,0 +1,282 @@
# Jarvis Agents 升级计划索引
本目录用于存放 Jarvis Agents 2.0 的分阶段规划文档,同时也用于记录**当前代码真实落地状态**。
## 文档说明
| 文件 | 说明 |
|------|------|
| `README.md` | 总览、阶段关系、实施顺序、当前状态 |
| `phase-0-current-state-and-target.md` | 当前现状、问题、目标架构、ADR |
| `phase-1-safe-foundation.md` | 基础设施加固阶段 |
| `phase-2-controlled-collaboration.md` | 受控协作阶段 |
| `phase-3-dynamic-collaboration.md` | 动态协作阶段 |
| `phase-4-visibility-and-isolation.md` | 可视化与隔离执行阶段 |
| `phase-5-advanced-features.md` | 高级特性(可选) |
| `phase-6-tool-system-refactoring.md` | 工具系统重构 |
| `phase-7-hook-interception-layer.md` | Hook 拦截层 |
| `phase-8-plugin-ecosystem.md` | 插件生态 |
| `phase-9-skills-registry.md` | Skills 注册表 |
| `phase-10-advanced-orchestration.md` | 高级编排 |
| `phase-r-rag-upgrade.md` | RAG 系统升级专项(VCPToolBox 借鉴) |
---
## 当前总体状态2026-04-04
当前 Jarvis agent runtime 不再是“Phase 2/3/4 纯草案”,而是已经具备以下现实状态:
### 78 → 90 成熟度标尺
| 分数 | 含义 | 当前状态 |
|------|------|----------|
| 75 | 受控协作基线task/event/verifier/collaboration/dynamic guardrail 已稳定 | 已达到 |
| 85 | visibility + verification 基线phase/checkpoint、topology、evidence、runtime summary、operator 调试入口可用 | 基本达到 |
| 90 | isolation runtime + cost governance + operator surface会话/工作区隔离、成本阈值治理、前端可运营面板闭环 | 已达到 |
| 95+ | full sandbox / persistence / realtime UI / advanced memory | 明确延后 |
| Phase | 当前状态 | 说明 |
|------|------|------|
| Phase 1 | 已落地 | verifier、task/event schema、基础执行模式已存在 |
| Phase 2 | 已实现基线 | collaboration mode、task decomposition、owner、result collection、verifier 收尾已运行 |
| Phase 3 | 已实现受限基线 | parent/root/depth、spawn policy、budget、interrupt/recovery、事件链路已存在 |
| Phase 4 | 已完成 90 分闭环 | visibility API、isolation runtime MVP、cost governance MVP、operator/debug surface 已落地 |
| Phase 5 | 未开始 | 保留为 full sandbox / persistence / realtime push 等可选增强 |
| Phase 6 | 待开始 | 工具系统重构(对标 claw-code |
| Phase 7 | 待开始 | Hook 拦截层 |
| Phase 8 | 待开始 | 插件生态 |
| Phase 9 | 待开始 | Skills 注册表 |
| Phase 10 | 待开始 | 高级编排 |
| Phase R | 部分推进 | RAG 升级按专项继续推进 |
### 本次新增落地
本次补齐了一个此前缺失但非常关键的层:
- runtime 显式 phase model
- runtime checkpoint model
- phase / checkpoint history 持久化
- phase / checkpoint event trace
- 对应自动化测试
新增后,当前 runtime 已可显式追踪:
- `current_phase`
- `phase_history`
- `current_checkpoint`
- `checkpoint_history`
并且会进入这些显式阶段:
- `phase_0_bootstrap`
- `phase_1_routing`
- `phase_2_controlled_collaboration`
- `phase_3_dynamic_collaboration`
- `phase_4_visibility_and_verification`
---
## 推荐阅读顺序
1. 先读 `phase-0-current-state-and-target.md`
2. 再读 `phase-2-controlled-collaboration.md`
3. 再读 `phase-3-dynamic-collaboration.md`
4. 最后读 `phase-4-visibility-and-isolation.md`
原因:当前最重要的不是继续写理想化蓝图,而是先理解“代码里已经实现到了哪一步”。
---
## 总体升级原则
1. **保持简单请求路径稳定** - Direct Mode 不受影响
2. **复杂请求才启用协作模式** - Collaboration Mode 按需触发
3. **执行与验证分离** - Verifier 作为独立角色
4. **动态能力必须受约束** - Budget + Permission + Depth
5. **所有升级都要配套测试** - 回归测试优先
6. **优先做显式状态,不先做大拆分** - 先让运行时可观察、可验证,再抽模块
---
## 阶段关系图(按真实状态修订)
```text
Phase 0 ──────────────────────────────────────────────────────────────┐
│ 现状与目标 │
│ - 当前架构分析 │
│ - Demo 借鉴映射 │
│ - ADR 架构决策 │
└────────────────────────────────────────────────────────────────────┘
Phase 1 ──────────────────────────────────────────────────────────────┐
│ 基础设施加固 (Safe Foundation) │
│ - verifier / schema / execution mode 基础 │
│ 状态:已落地 │
└────────────────────────────────────────────────────────────────────┘
Phase 2 ──────────────────────────────────────────────────────────────┐
│ 受控协作 (Controlled Collaboration) │
│ - collaboration mode │
│ - 任务拆解 / owner / 结果回收 / verifier │
│ - 当前已补 phase + checkpoint │
│ 状态:已实现基线 │
└────────────────────────────────────────────────────────────────────┘
Phase 3 ──────────────────────────────────────────────────────────────┐
│ 动态协作 (Dynamic Collaboration) │
│ - parent/root/depth tracking │
│ - spawn policy + budget │
│ - interrupt/recovery │
│ - phase + checkpoint trace │
│ 状态:已实现受限基线 │
└────────────────────────────────────────────────────────────────────┘
Phase 4 ──────────────────────────────────────────────────────────────┐
│ 可视化与隔离 (Visibility + Isolation) │
│ - visibility 查询 API │
│ - continuity snapshot 持久化 │
│ - isolation strategy 设计 │
│ 状态:最小闭环已完成 │
└────────────────────────────────────────────────────────────────────┘
Phase 5 ──────────────────────────────────────────────────────────────┐
│ 高级特性 (Advanced Features) │
│ - full sandbox / persistence / cost monitoring / advanced UI │
│ 状态:规划中,可选 │
└────────────────────────────────────────────────────────────────────┘
Phase 6 ──────────────────────────────────────────────────────────────┐
│ 工具系统重构 (Tool System Refactoring) │
│ - ToolRegistry / HookExecutor / StreamingToolExecutor │
│ - 新增工具集Glob/Grep/LSP/Bash/PowerShell/Cron │
│ 状态:待开始(对标 claw-code tools/
└────────────────────────────────────────────────────────────────────┘
Phase 7 ──────────────────────────────────────────────────────────────┐
│ Hook 拦截层 (Hook Interception Layer) │
│ - PreTool/PostTool Hook 机制 │
│ - 危险操作确认 / 安全扫描 / 审计日志 │
│ 状态:待开始(依赖 Phase 6
└────────────────────────────────────────────────────────────────────┘
Phase 8 ──────────────────────────────────────────────────────────────┐
│ 插件生态 (Plugin Ecosystem) │
│ - PluginManager / 生命周期管理 / 插件市场 │
│ 状态:待开始(依赖 Phase 6, 7
└────────────────────────────────────────────────────────────────────┘
Phase 9 ──────────────────────────────────────────────────────────────┐
│ Skills 注册表 (Skills Registry) │
│ - 动态 Skills 加载 / MCP Skill Builder / Bundled Skills │
│ 状态:待开始(依赖 Phase 6
└────────────────────────────────────────────────────────────────────┘
Phase 10 ─────────────────────────────────────────────────────────────┐
│ 高级编排 (Advanced Orchestration) │
│ - Team Leader / Remote Transport / Session Manager / Background Tasks │
│ 状态:待开始(对标 claw-code assistant/
└────────────────────────────────────────────────────────────────────┘
```
---
## Demo 项目借鉴映射
| Demo项目 | 主要借鉴点 | 对应 Phase |
|---------|-----------|-----------|
| **Swarm-IDE** | Event trace、Dynamic Spawn、拓扑可视化 | Phase 3, 4 |
| **Claude Code CLI** | Coordinator-worker、Verifier 分离、Tool 权限 | Phase 1, 2 |
| **Claw Code** | Runtime 分层、Port Manifest、隔离策略 | Phase 2, 4, 6, 7, 8, 9, 10 |
| **VCPToolBox** | TagMemo V6、多索引、Token 感知分块 | Phase R, Phase 5 |
### Claw Code 详细对照
| Claw Code 组件 | Jarvis Phase | 说明 |
|----------------|-------------|------|
| `tools/` | Phase 6 | 工具注册表、分层执行 |
| `StreamingToolExecutor` | Phase 6 | 流式工具执行 |
| `toolHooks.ts` | Phase 7 | Hook 拦截层 |
| `PluginLifecycle` | Phase 8 | 插件生态 |
| `skills/loadSkillsDir.ts` | Phase 9 | Skills 注册表 |
| `skills/bundledSkills.ts` | Phase 9 | Bundled Skills |
| `assistant/sessionHistory.ts` | Phase 10 | 高级会话管理 |
| `cli/structuredIO.ts` | Phase 10 | 结构化传输 |
| `cli/remoteIO.ts` | Phase 10 | 远程传输 |
---
## 本次代码落点
本次 phase/checkpoint 补强主要修改:
- `backend/app/agents/state.py`
- `backend/app/agents/graph.py`
- `backend/app/agents/schemas/event.py`
- `backend/app/services/agent_service.py`
- `backend/tests/backend/app/agents/test_graph.py`
- `backend/tests/backend/app/services/test_brain_ingestion.py`
### 新增的关键事件
- `agent.phase.changed`
- `agent.checkpoint.recorded`
### 新增的关键持久化字段
- `current_phase`
- `phase_history`
- `current_checkpoint`
- `checkpoint_history`
---
## 当前仍未完成的内容
虽然能力已经明显前进,但下面这些仍属于后续工作:
### 工程结构层
- 独立 `coordinator.py`
- 独立 `message_bus.py`
- 独立 `event_bus.py`
- `dynamic/``recovery/` 目录化拆分
### Claw Code 差距Phase 6-10
- Phase 6: 工具系统重构ToolRegistry/HookExecutor/StreamingToolExecutor
- Phase 7: Hook 拦截层PreTool/PostTool
- Phase 8: 插件生态PluginManager/生命周期/市场)
- Phase 9: Skills 注册表(动态加载/MCP Builder
- Phase 10: 高级编排Team/Remote Transport/Session Manager
### 平台能力层
- full sandbox / persistence / realtime push
- 独立 `coordinator.py` / `message_bus.py` / `event_bus.py`
- 更完整的 operator drilldown 与实时推送
- SSE / WebSocket 实时推送(延后)
- sandbox container 执行器(延后)
---
## 当前阶段结论
目前最准确的说法不是:
> “Jarvis 还在做 agent phase 规划。”
而是:
> “Jarvis 已经具备多阶段 agent runtime 的核心基线,当前工作重点已经从‘是否可行’转向‘如何把已存在能力继续工程化、可视化、隔离化’。”
这也是后续测试、验收和继续升级的正确前提。

View File

@@ -0,0 +1,961 @@
# Jarvis Agents 2.0 升级方案
日期2026-04-03
状态:草案
范围:`backend/app/agents/*`、相关测试,以及后续运行时/UI 扩展
---
## 1. 文档目标
本文档用于明确 Jarvis Agents 2.0 的升级方向。方案基于以下三个 demo 项目的分析结果整理而成:
- `demo/swarm-ide-chore-specs-mvp`
- `demo/claude-code-cli-master`
- `demo/claw-code-main`
本方案的目标不是照搬任意一个项目,而是结合它们各自的优点,让 Jarvis 从当前的:
- 静态分层路由型 agent 系统
逐步升级为:
- 可控的动态协作型 agent 运行时
同时保留 Jarvis 现在已经具备的优势:
- 业务导向明确
- 提醒 / 任务 / 搜索类连续性较强
- 支持多 provider 和 fallback 策略
- 运行边界清晰,容易测试和验证
---
## 2. 当前 Jarvis 的现状
当前核心代码主要集中在:
- `backend/app/agents/graph.py`
- `backend/app/agents/state.py`
- `backend/app/agents/prompts.py`
- `backend/app/agents/registry/*`
- `backend/app/agents/tools/*`
- `backend/tests/backend/app/agents/test_graph.py`
### 2.1 当前优势
Jarvis 目前已经不是简单的“单 prompt + 工具调用”系统,而是一个结构化的业务 agent 运行时,主要优势包括:
1. **有主控层级**
- 存在类似 `master` 的顶层入口
- 已按业务领域拆分出不同 agent / sub-commander
2. **有明确的工具调用策略**
- 支持 native tool calling
- 也支持 JSON fallback
3. **有连续性和澄清机制**
- 支持 clarification
- 支持 pending action / continuity
- 对 reminder / task 这类业务很重要
4. **状态模型较完整**
- 不是纯临时 prompt 拼接
- 已有显式 state 管理
5. **测试基础较好**
- 当前 agent runtime 已有可观测试覆盖
### 2.2 当前短板
虽然 Jarvis 已经具备结构化 agent 系统的雏形,但依旧存在几个明显上限:
1. **本质上仍是静态路由系统**
- 单轮请求通常只会选一条主路径执行
- agent 关系是预先设计好的,不是运行时动态演化的
2. **没有真正的 agent-to-agent 通信原语**
- 现在更多是系统内部调度
- 不是 agent 之间显式发消息、协作、转交任务
3. **没有显式 task/team 运行时**
- 缺少任务对象、所有权、依赖关系、完成状态
4. **缺少独立 verifier 角色**
- 当前执行者通常也是完成判断者
- 缺少“执行”和“验收”分离
5. **可观察性还不够强**
- 系统内部做了很多判断,但外部很难完整看到 agent 协作过程
---
## 3. 三个 demo 项目的借鉴重点
---
## 3.1 Swarm-IDE最值得借鉴的协作形态
关键参考文件:
- `demo/swarm-ide-chore-specs-mvp/README.md`
- `demo/swarm-ide-chore-specs-mvp/backend/src/runtime/agent-runtime.ts`
- `demo/swarm-ide-chore-specs-mvp/backend/src/runtime/event-bus.ts`
- `demo/swarm-ide-chore-specs-mvp/backend/src/runtime/skill-loader.ts`
### 值得借鉴的点
#### 1协作原语非常简单但表达力非常强
Swarm 的核心原语很少,主要就是:
- 创建 agent
- 发送消息
- 建群 / 群聊
- 列出 agent / 群组 / 消息
这种设计的好处是:
- 不需要先写死很多工作流
- 复杂拓扑可以由运行时自然演化
- agent 行为更像“协作中的人”,而不是固定流程节点
#### 2agent 间通信是一等能力
Swarm 不是让系统替 agent 决定所有事情,而是让 agent 自己拥有:
- 创建子 agent 的能力
- 和任意 agent 通信的能力
- 组群协作的能力
这比固定的 master -> domain 路由更灵活。
#### 3人类可以直接介入任意子 agent
这点很关键。多 agent 系统最怕黑箱。一旦用户只能看到最终结果,而看不到中间代理,就很难调试和控制。
#### 4事件流和可观察性很强
Swarm 有清晰的 runtime 事件模型,例如:
- `agent.wakeup`
- `agent.unread`
- `agent.stream`
- `agent.done`
- `agent.error`
这对后续做:
- 调试
- 可视化
- 事件回放
- 协作审计
都非常有帮助。
### 不能直接照搬的点
Swarm 的自由度也意味着风险:
- agent 无限制增殖
- 消息风暴
- token 成本失控
- 系统行为难收敛
所以 Jarvis 应该借鉴它的“机制”,而不是直接复制它的“无限自由”。
---
## 3.2 Claude Code CLI最值得借鉴的平台级编排
关键参考文件:
- `demo/claude-code-cli-master/coordinator/coordinatorMode.ts`
- `demo/claude-code-cli-master/bootstrap/state.ts`
- `demo/claude-code-cli-master/skills/bundled/batch.ts`
### 值得借鉴的点
#### 1coordinator-worker 分层非常清晰
这个项目不是简单地把多个 agent 拼在一起,而是明确区分:
- 协调者 coordinator
- 执行 worker
- 任务生命周期
- 结果汇总
- 后续消息和回收
这正是 Jarvis 从“路由”升级到“编排”时最需要补的能力。
#### 2有明确的 task / team 概念
成熟的多 agent 系统不是只有“谁去做”,而是还需要:
- task id
- owner
- blocked / depends_on
- 完成状态
- worker 生命周期
#### 3验证是独立通道
真正成熟的系统会把:
- 计划
- 执行
- 验证
分开,而不是让执行者自己宣布“我完成了”。
#### 4并行执行强调隔离
对于复杂任务,这种平台会强调:
- 并行 worker
- 各自隔离的工作空间
- 输出清晰归属到某个 worker
这对未来 Jarvis 如果处理更复杂的编码或多步骤任务,价值很高。
---
## 3.3 Claw Code最值得借鉴的 runtime 工程化能力
关键参考文件:
- `demo/claw-code-main/rust/crates/runtime/src/conversation.rs`
- `demo/claw-code-main/rust/crates/tools/src/lib.rs`
- `demo/claw-code-main/rust/crates/plugins/src/lib.rs`
- `demo/claw-code-main/PARITY.md`
### 值得借鉴的点
#### 1运行时分层清晰
Claw 的优势不是协作花样最多,而是运行时拆分很自然:
- runtime loop
- tools
- commands
- plugins
- permission
Jarvis 后续也应该逐步减少 graph 与具体业务工具的强耦合。
#### 2工具注册表可以更强
Jarvis 现在已有 registry 方向但还不够深入。Claw 的启发是:
每个工具不只是名字和描述,还应该有:
- 权限等级
- 副作用范围
- 是否幂等
- 是否可重试
- 是否适合并行
- 是否需要确认
#### 3权限模型显式化
一旦系统支持动态协作,就不能默认所有 worker 都能做所有事。
#### 4插件 / Hook 扩展点应该尽早预留
即使暂时不做插件生态,也应该尽早定义扩展点,否则以后核心 runtime 会越来越难改。
---
## 4. Jarvis 2.0 的总体目标架构
Jarvis 2.0 的目标不是做成“完全自由的蜂群系统”,而是做成:
- **受控的动态协作系统**
### 4.1 核心原则
1. 简单请求继续走当前稳定路径。
2. 复杂请求才进入协作模式。
3. agent 通信必须显式、可观察、可约束。
4. 执行与验证必须分离。
5. 所有危险能力都必须被预算和权限控制。
### 4.2 总体分层
#### 第一层:请求模式选择层
每个请求先判定:
- **直接模式**:继续使用现有 `master -> domain -> sub-commander`
- **协作模式**:进入 coordinator -> task -> delegate -> verify 流程
#### 第二层:协调层
新增 coordinator负责
- 理解请求
- 判断是否需要拆任务
- 生成任务列表
- 分配任务给不同角色
- 回收结构化结果
- 触发 verifier
#### 第三层:工作层
worker agent 负责具体执行,例如:
- schedule 执行
- task 执行
- 检索 / 搜索
- forum 操作
- 分析 / 汇总
- 验证 / 审核
#### 第四层:协作底座
底座层应该提供:
- agent 注册
- agent identity
- task 对象
- message channel
- event stream
- budget / interrupt / recovery
---
## 5. Jarvis 2.0 需要新增的核心概念
---
## 5.1 一等的 agent 通信原语
建议在 Jarvis 内部先引入以下运行时能力:
- `create_agent(role, guidance, parent_id?)`
- `send_agent_message(to_agent_id, content, task_id?)`
- `list_agents()`
- `list_agent_threads(agent_id?)`
- `interrupt_agent(agent_id)`
### 作用
这一步完成后Jarvis 就不再只是“从主图里选路径”,而是具备“让 agent 协作”的基础能力。
### 注意
这些能力可以先作为内部能力,不一定立刻暴露给最终用户。
---
## 5.2 结构化 task 运行时
建议引入任务对象,至少包含:
- `task_id`
- `title`
- `owner_agent_id`
- `status``pending` / `in_progress` / `blocked` / `completed` / `failed`
- `depends_on`
- `evidence`
- `result_summary`
- `created_by`
### 作用
一旦 task 成为系统对象Jarvis 才能真正实现:
- 并行
- 重试
- 显式完成判断
- 进度展示
---
## 5.3 事件总线 Event Bus
建议新增统一事件流,至少包含:
- `agent.created`
- `agent.wakeup`
- `agent.message.sent`
- `agent.message.received`
- `agent.tool.start`
- `agent.tool.result`
- `agent.task.assigned`
- `agent.task.completed`
- `agent.verify.started`
- `agent.verify.completed`
- `agent.error`
### 作用
后续可以用于:
- 调试
- 日志回放
- 可视化
- agent 行为审计
---
## 5.4 verifier 角色
建议新增独立 verifier / reviewer 角色。
### 职责
- 判断是否真的满足用户请求
- 检查是否缺少工具证据
- 识别不完整执行
- 要求重新执行或补充
### 意义
这一步是 Jarvis 从“会调用工具的 assistant”升级为“可靠 agent 系统”的关键一步。
---
## 5.5 工具权限模型
每个工具建议新增如下元数据:
- `permission_class``read` / `write` / `external` / `dangerous`
- `side_effect_scope``none` / `local_state` / `remote_state`
- `safe_for_worker_roles`
- `requires_confirmation`
- `supports_retry`
- `idempotent`
### 意义
动态协作系统必须比静态路由系统有更强的治理能力。
---
## 6. 按代码区域的演进建议
---
## 6.1 `backend/app/agents/state.py`
### 目标
在现有 state 基础上扩展协作信息,而不是推翻重来。
### 建议新增字段
- `agent_id`
- `parent_agent_id`
- `task_id`
- `active_tasks`
- `task_results`
- `message_queue`
- `event_log_refs`
- `verification_status`
- `execution_mode``direct` / `collaboration`
- budget 字段:
- `max_spawn_depth`
- `max_child_agents`
- `max_messages_per_turn`
- `max_parallel_tasks`
### 重点
不要删除当前 continuity / clarification 相关状态,而是在它之上扩展。
---
## 6.2 `backend/app/agents/graph.py`
### 目标
从“纯路由图”升级为“路由 + 编排混合图”。
### 建议新增节点
- `request_mode_selector`
- `coordinator`
- `task_decomposer`
- `delegation_router`
- `worker_runner`
- `verifier`
- `result_synthesizer`
### 推荐行为
#### 简单请求
继续走当前路径,避免影响当前稳定业务。
#### 复杂请求
走以下流程:
1. coordinator 理解请求
2. task_decomposer 产出小任务列表
3. delegation_router 为每个任务分配角色
4. worker_runner 执行任务
5. verifier 检查产出
6. result_synthesizer 汇总最终结果
### 重点
不是替换当前 graph而是在复杂请求上增加新的执行分支。
---
## 6.3 `backend/app/agents/prompts.py`
### 目标
新增更明确的角色 prompt减少职责混乱。
### 需要新增的角色 prompt
- coordinator
- verifier
- critic / reviewer可后续再加
### 协调者 prompt 应强调
- 只有复杂任务才拆分
- 任务拆分要小而清晰
- 不要过度创建 agent
- 输出必须带证据
### verifier prompt 应强调
- 不接受模糊完成
- 检查证据是否充分
- 检查用户意图是否真正满足
---
## 6.4 `backend/app/agents/registry/*`
### 目标
让 registry 不只是描述性的,而是逐渐成为 runtime 驱动的一部分。
### 建议补充
- agent role metadata
- role capability
- role spawn permission
- tool access policy
- task suitability tags
### 建议角色分类
- coordinator
- executor
- scheduler
- retriever
- analyst
- verifier
---
## 6.5 `backend/app/agents/tools/*`
### 目标
保留现有业务工具,但为它们补全运行时治理信息。
### 每个工具建议具备的元数据
- `name`
- `description`
- `permission_class`
- `supports_retry`
- `idempotent`
- `safe_for_parallel_use`
- `returns_structured_evidence`
### 建议新增模块
可以新增:
- `backend/app/agents/tools/collaboration.py`
用于承载协作原语,例如:
- 创建 agent
- 发送内部消息
- claim task
- complete task
- request verification
---
# 7. 升级阶段设计
下面按阶段详细说明,每个阶段都包含:
- 目标
- 解决的问题
- 范围
- 核心改动
- 风险
- 验收标准
- 推荐实施顺序
---
## 阶段一基础设施加固阶段Safe Foundation
### 7.1.1 阶段目标
在不破坏当前稳定业务路径的前提下,为后续多 agent 协作打底。
### 7.1.2 这一阶段要解决的问题
当前 Jarvis 的主要问题不是“不会做业务”,而是:
- 缺 verifier
- 缺 task 对象
- 缺事件流
- 缺工具权限元数据
- graph 不区分直接模式和协作模式
所以第一阶段不是追求炫技,而是先把基础设施补齐。
### 7.1.3 范围
主要涉及:
- `backend/app/agents/state.py`
- `backend/app/agents/graph.py`
- `backend/app/agents/prompts.py`
- `backend/app/agents/registry/models.py`
- `backend/app/agents/registry/builtins.py`
- `backend/app/agents/tools/__init__.py`
- `backend/tests/backend/app/agents/*`
### 7.1.4 核心改动
#### 改动 1新增 `execution_mode`
让系统能区分:
- `direct`
- `collaboration`
#### 改动 2新增 verifier 角色
在不改变主流程的基础上,先插入一个独立 verifier 角色。
#### 改动 3新增事件总线抽象
哪怕先不做 UI也要先定义事件结构和写入方式。
#### 改动 4补工具元数据
给现有工具补权限、幂等、是否适合并行等信息。
#### 改动 5引入 task 数据结构
哪怕暂时只在内部使用,也要先把任务结构定义出来。
### 7.1.5 风险点
1. **改 state 容易影响现有测试**
2. **verifier 插入点选错会影响现有行为**
3. **事件系统如果写得太重,会影响性能和复杂度**
4. **工具元数据定义过度,会让现有实现变复杂**
### 7.1.6 验收标准
满足以下条件才算第一阶段完成:
- 当前 reminder / task / search 主要流程测试仍通过
- 系统可以独立运行 verifier 角色
- 系统可以产生基本事件记录
- 工具权限元数据已存在并有测试
- 不引入动态 agent 创建能力
### 7.1.7 推荐实施顺序
1. 定义 task schema
2. 定义 event schema
3. 扩展 state
4. 扩展 tool metadata
5. 加 verifier prompt
6. 在 graph 中插 verifier 分支
7. 补测试
---
## 阶段二受控协作阶段Controlled Collaboration
### 7.2.1 阶段目标
让 Jarvis 开始具备“拆任务、分配任务、回收结果”的能力,但仍然保持强约束。
### 7.2.2 这一阶段要解决的问题
当前系统的主要限制是:
- 复杂请求只能在一条路径里硬做
- 没法显式表达子任务
- 没法让不同角色协作完成复杂目标
第二阶段的重点,就是把“复杂请求”从“路由”升级为“编排”。
### 7.2.3 范围
主要涉及:
- `backend/app/agents/graph.py`
- `backend/app/agents/prompts.py`
- `backend/app/agents/state.py`
- `backend/app/agents/registry/*`
- 新增协作工具模块
- 新增对应测试
### 7.2.4 核心改动
#### 改动 1新增 coordinator 节点
由 coordinator 判断是否需要拆分请求。
#### 改动 2新增 task decomposition
复杂请求要能拆出 2~4 个清晰的子任务。
#### 改动 3新增 worker assignment
不同子任务按角色分配给不同 worker 逻辑。
#### 改动 4新增内部消息传递
先做最小版本的 agent-to-agent 通信,不必一开始就做复杂群聊。
#### 改动 5完成后必须走 verifier
worker 的产出不直接作为最终结论,必须先过 verifier。
### 7.2.5 风险点
1. **任务拆得太细会增加系统复杂度**
2. **任务拆得太粗又达不到协作收益**
3. **内部通信如果没有预算,会引发回路**
4. **worker 边界不清会导致职责重叠**
### 7.2.6 验收标准
- 复杂请求可以被拆成 2~4 个子任务
- 每个子任务有明确 owner
- worker 输出带结构化 evidence
- verifier 可以拒绝不完整结果
- final result 基于任务结果汇总,而不是某个单一 worker 的主观结论
### 7.2.7 推荐实施顺序
1. 增加 coordinator prompt
2. 增加 task decomposition schema
3. 增加 delegation router
4. 增加最小通信原语
5. 增加 verifier 回收
6. 补整套协作测试
---
## 阶段三动态协作阶段Dynamic Collaboration
### 7.3.1 阶段目标
在受控前提下,让 Jarvis 具备更接近 Swarm 的动态协作能力,但不是完全放飞。
### 7.3.2 这一阶段要解决的问题
第二阶段虽然已经能做任务拆分,但仍然偏“平台帮你分好工”。
第三阶段要解决的问题是:
- worker 能否在必要时请求新的协作
- 是否支持 parent / child agent 关系
- 是否支持更完整的消息通路
- 是否支持更强的中间态可观察性
### 7.3.3 范围
主要涉及:
- `backend/app/agents/state.py`
- `backend/app/agents/graph.py`
- 新增 collaboration runtime 模块
- 新增 message / thread / event 相关抽象
- 未来可能涉及前端可视化接口
### 7.3.4 核心改动
#### 改动 1支持 parent / child agent tracking
让系统知道哪个 agent 是谁创建的。
#### 改动 2支持有限动态创建 agent
注意:必须有限制,不允许无限递归创建。
#### 改动 3支持有限的 agent 消息线程
先支持最小内部线程即可,不一定马上做群聊 UI。
#### 改动 4增强事件流
把协作链路完整记录下来。
#### 改动 5支持 interrupt / recovery
多 agent 系统没有中断与恢复,后面会很难维护。
### 7.3.5 风险点
1. **agent 增殖风险**
2. **消息风暴风险**
3. **token 成本和延迟上升**
4. **调试复杂度上升**
5. **过度动态化破坏当前稳定路径**
### 7.3.6 验收标准
- parent / child agent 关系可追踪
- 系统支持受限动态创建 agent
- agent 间通信链路可记录
- 可中断运行中的协作
- 所有动态协作都受预算限制
### 7.3.7 推荐实施顺序
1. 建 parent / child state
2. 建 spawn budget / message budget
3. 实现受限 `create_agent`
4. 实现内部消息线程
5. 实现 interrupt / recovery
6. 加事件回放和调试日志
---
## 阶段四可视化与隔离执行阶段Visibility + Isolation
### 7.4.1 阶段目标
把多 agent 系统从“后台能跑”升级为“可看、可控、可调试、可隔离”。
### 7.4.2 这一阶段要解决的问题
多 agent 系统发展到一定程度后,纯日志已经不够。你会需要:
- 谁创建了谁
- 谁在做什么
- 谁给谁发了什么
- 哪个任务卡住了
- 某个 agent 为啥没完成
### 7.4.3 范围
这个阶段可能跨:
- backend runtime
- event stream API
- frontend 调试面板 / graph 面板
- 编码任务场景下的隔离执行策略
### 7.4.4 核心改动
#### 改动 1协作链路可视化
至少包括:
- 当前 agent 列表
- parent / child 关系
- task 状态
- message 流向
#### 改动 2agent 历史和工具证据可见
支持查看某个 agent
- 历史消息
- 工具调用
- 工具结果
- verifier 结论
#### 改动 3隔离执行能力
如果以后 Jarvis 要处理更复杂的 coding 任务,可以考虑:
- worker 级隔离目录
- worktree
- 独立 session state
### 7.4.5 风险点
1. **UI 一旦做太早,会分散后端核心升级精力**
2. **事件量上升后,展示层会有性能压力**
3. **隔离执行会提升工程复杂度**
### 7.4.6 验收标准
- 可以看到基本 agent 拓扑
- 可以看到任务流转和关键事件
- 可以查看某个 agent 的执行证据
- 隔离执行至少有设计方案,最好有最小实现
### 7.4.7 推荐实施顺序
1. 先把 event schema 固化
2. 再做 event stream API
3. 再做最小调试页面
4. 最后做隔离执行策略
---
## 8. 必须长期坚持的治理规则
Jarvis 不应该变成“无限自由蜂群”,而应该是“带预算的动态协作系统”。
因此必须长期保留这些约束:
1. **spawn budget**:每次请求最多允许创建多少 agent
2. **message budget**:每个任务 / 每轮最多允许多少消息
3. **max depth**:代理树最大深度
4. **verifier gate**:复杂任务必须经 verifier 才能宣布完成
5. **tool permission policy**:不同角色拥有不同工具权限
6. **interrupt / cancel 路径**:长任务必须可中断
7. **structured evidence**:没有证据就不算完成
---
## 9. 推荐优先级
如果近期只能做少量升级,推荐优先级如下:
1. verifier 角色
2. event bus / 可观察性
3. 结构化 task runtime
4. coordinator for complex requests
5. 内部 agent 通信原语
6. registry 元数据增强
7. 隔离执行能力
---
## 10. 最现实的近期落地顺序
建议按下面顺序推进,而不是同时铺开:
### 第一步
先做 verifier 的 state、prompt、graph 插入点设计。
### 第二步
定义 task schema 和 event schema。
### 第三步
给工具补元数据和权限标签。
### 第四步
在 graph 中加入 coordinator 只处理复杂请求。
### 第五步
引入最小可用的内部协作原语。
### 第六步
补一轮完整测试,确保旧路径不坏、新路径可控。
---
## 11. 最终建议
Jarvis 2.0 不应该单独模仿某一个 demo而应该做这样的融合
-**Swarm-IDE** 学:动态通信原语、可观察性、协作拓扑
-**Claude Code CLI**coordinator / task / verifier 的平台化编排
-**Claw Code**runtime 分层、工具注册表、权限模型
一句话总结升级方向:
> 让 Jarvis 从“静态层级路由系统”升级为“受控的动态协作运行时”。
并且必须坚持一个原则:
> 简单请求保持当前稳定路径,复杂请求才启用协作编排能力。
---
## 12. 建议后续继续拆分的文档
建议在这份总方案之后,再继续补这些中文文档:
1. `development-doc/jarvis-agents-phase-a-design.md`
2. `development-doc/jarvis-agent-event-schema.md`
3. `development-doc/jarvis-agent-task-schema.md`
4. `development-doc/jarvis-agent-role-permissions.md`
5. `development-doc/jarvis-verifier-integration-plan.md`
如果继续往下做,最建议先写的是:
- **Phase A 详细设计文档**
因为它最贴近当前代码,也最容易直接转成开发任务。

View File

@@ -0,0 +1,398 @@
# Jarvis Agents 2 天融合改造计划
日期2026-04-03
状态:草案
目标:在 **2 天内** 完成一版最小可落地的 Jarvis Agents 2.0 融合改造方案,不追求一步到位,只做最有价值、最能落地、风险最低的部分。
---
## 1. 这 2 天要完成什么
这 2 天不做完整的 swarm 化改造,也不做复杂 UI。
这 2 天的核心目标只有 4 个:
1. 把升级方向真正落到当前 Jarvis 代码结构上
2. 先完成 **Phase 1 的最小闭环**
3.**Phase 2 的受控协作** 预埋接口
4. 保证当前 reminder / task / search 主路径不被破坏
换句话说:
> 这 2 天的重点不是“把终态做完”,而是“把融合路径打通”。
---
## 2. 融合原则
融合这套方案时,必须遵守下面 5 个原则:
### 原则 1不推翻当前 graph
当前 `backend/app/agents/graph.py` 是现有稳定主链,不能直接大改成全新框架。
策略:
- 保留现有 direct path
- 只新增最小的 collaboration 入口
### 原则 2先补底座再谈动态协作
先做:
- verifier
- task schema
- event schema
- tool metadata
后做:
- coordinator
- agent message
- 动态 create_agent
### 原则 3先做内部能力不急着做前台可视化
即使未来要做 Swarm 风格可观察性,也不应该在这 2 天里优先做 UI。
先把:
- 事件结构
- 任务结构
- 协作状态
定义清楚。
### 原则 4优先保障现有业务稳定
本次融合不能影响当前已有优势:
- continuity
- clarification
- native tool / fallback
- schedule / task / search 主流程
### 原则 5每一步都要可测试
只要动到:
- state
- graph
- tools
- prompts
就必须补测试,不允许只写方案不验证。
---
## 3. 第 1 天计划:补底座,完成最小 Phase 1
第 1 天目标:
> 让 Jarvis 拥有 verifier、task schema、event schema、tool metadata 这些基础设施,但不引入复杂动态协作。
---
### 3.1 第 1 天工作目标
完成以下结果:
1. state 能表达 direct / collaboration 两种模式
2. verifier 成为独立角色
3. task schema 初版可用
4. event schema 初版可用
5. tool metadata 初版可用
6. 现有主路径测试不回退
---
### 3.2 第 1 天建议改动文件
重点落在这些文件:
- `backend/app/agents/state.py`
- `backend/app/agents/graph.py`
- `backend/app/agents/prompts.py`
- `backend/app/agents/registry/models.py`
- `backend/app/agents/registry/builtins.py`
- `backend/app/agents/tools/__init__.py`
- `backend/tests/backend/app/agents/test_graph.py`
- 新增若干 schema / tests 文件
---
### 3.3 第 1 天详细任务分解
#### 任务 1定义最小 task schema
建议新增最小任务结构,字段控制在必要范围:
- `task_id`
- `title`
- `status`
- `owner_agent_id`
- `evidence`
- `result_summary`
目的:
- 不马上实现完整 task runtime
- 先让 graph 和 verifier 有统一结构可依赖
#### 任务 2定义最小 event schema
建议先定义最小事件:
- `agent.tool.start`
- `agent.tool.result`
- `agent.verify.started`
- `agent.verify.completed`
- `agent.error`
目的:
- 先建立可观察性的“格式标准”
- 后面要不要落 DB / log / stream可以再扩展
#### 任务 3扩展 state
给现有 state 增加:
- `execution_mode`
- `verification_status`
- `active_tasks`
- `task_results`
- 预算字段占位
注意:
- 不要破坏现有 continuity 字段
- 尽量以新增字段为主
#### 任务 4新增 verifier prompt
`prompts.py` 中新增 verifier 的职责说明:
- 不判断“写得像不像完成”
- 只判断“是否真正满足请求 + 是否有证据”
#### 任务 5graph 中插入 verifier 分支
第一天不做复杂 coordinator。
只做:
- 在适当复杂输出后增加 verifier 节点
- direct path 仍保持主导
#### 任务 6给 tools 增加 metadata
当前工具补充:
- `permission_class`
- `side_effect_scope`
- `supports_retry`
- `idempotent`
- `safe_for_parallel_use`
#### 任务 7补测试
至少补下面几类测试:
- state 扩展兼容性
- verifier 执行路径
- tool metadata 存在性
- event schema 生成正确性
- 主流程无回退
---
### 3.4 第 1 天验收标准
第 1 天结束时必须满足:
1. 当前 reminder/task/search 流程测试继续通过
2. verifier 已成为独立角色,而不是写在注释里的计划
3. 有 task schema 和 event schema 初版
4. 每个关键工具已有 metadata
5. 当前系统还没有开始无限动态 agent
---
### 3.5 第 1 天风险控制
1. 不做动态 create_agent
2. 不做 message bus 全量落地
3. 不做 UI
4. 不做大规模 registry 重构
5. 不改当前主路径的核心业务判断逻辑
---
## 4. 第 2 天计划:引入最小协作能力,完成 Phase 2 的雏形
第 2 天目标:
> 在第 1 天底座稳定的基础上,引入最小的 coordinator + task decomposition + worker assignment 能力,让 Jarvis 开始具备“受控协作”的雏形。
---
### 4.1 第 2 天工作目标
完成以下结果:
1. graph 能识别复杂请求并切到 collaboration mode
2. coordinator 能做最小任务拆分
3. worker assignment 能按角色分发任务
4. worker 输出能回收到统一 task result 结构里
5. verifier 能对协作结果验收
---
### 4.2 第 2 天建议改动文件
重点还是这些:
- `backend/app/agents/graph.py`
- `backend/app/agents/prompts.py`
- `backend/app/agents/state.py`
- `backend/app/agents/registry/*`
- `backend/app/agents/tools/*`
- `backend/tests/backend/app/agents/*`
如有必要,可新增:
- `backend/app/agents/tools/collaboration.py`
---
### 4.3 第 2 天详细任务分解
#### 任务 1增加 request_mode_selector
判断当前请求是:
- direct mode
- collaboration mode
判定标准先做简单版,例如:
- 是否明显是多步骤任务
- 是否跨多个领域
- 是否需要多个角色协作
#### 任务 2增加 coordinator prompt
coordinator 只负责:
- 理解任务
- 决定是否拆分
- 输出小任务列表
限制:
- 最多拆 2~4 个任务
- 不允许无限递归拆分
#### 任务 3增加最小 task decomposition
输出结构建议包含:
- `task_id`
- `title`
- `role`
- `goal`
- `expected_evidence`
#### 任务 4增加 worker assignment
先不要做 agent-to-agent 自由通信。
由系统分配即可:
- schedule 类给 scheduler
- retrieval 类给 retriever
- analysis 类给 analyst
- execution 类给 executor
#### 任务 5增加 task result 回收
每个 worker 返回统一结果:
- `task_id`
- `status`
- `summary`
- `evidence`
- `next_action`(可选)
#### 任务 6verifier 统一验收
协作模式最终必须经过 verifier
- 看任务是否完成
- 看证据是否足够
- 看是否仍需补做某个子任务
#### 任务 7补协作测试
至少补:
- 多任务拆分测试
- 角色分配测试
- task result 汇总测试
- verifier 拒绝不完整结果测试
---
### 4.4 第 2 天验收标准
第 2 天结束时必须满足:
1. graph 能区分 direct / collaboration
2. 简单请求仍然走旧路径
3. 复杂请求可以被拆分成多个子任务
4. 子任务可以按角色执行
5. verifier 能拦住不完整结果
6. 结果汇总不是单点硬编码,而是基于 task result
---
### 4.5 第 2 天风险控制
1. 先不做真正动态 create_agent
2. 先不做无限 message channel
3. 先不做 parent/child agent tree
4. 先不做可视化 UI
5. 先保证协作模式只是“受控编排”,不是“自由蜂群”
---
## 5. 2 天之后的融合状态
如果这 2 天按计划完成Jarvis 会到达一个很关键的中间状态:
### 已具备的能力
- direct / collaboration 双模式
- verifier 独立角色
- task schema
- event schema
- tool metadata
- coordinator 雏形
- 最小任务拆分与角色分配
- 协作结果结构化回收
### 还没做的部分
- 动态 create_agent
- parent / child agent 树
- 内部消息线程
- 可视化协作面板
- 隔离执行 / worktree
这意味着:
> 2 天之后Jarvis 还不是终态,但已经完成了“从静态路由走向协作运行时”的第一轮关键融合。
---
## 6. 2 天后的下一步建议
2 天融合完成后,下一步最合理的顺序是:
1.`Phase 3` 详细设计
2. 设计受限 `create_agent`
3. 设计内部消息线程模型
4. 设计 parent / child state
5. 最后再考虑可视化与隔离执行
---
## 7. 一句话结论
这 2 天不要想着“做完 Jarvis 2.0”,而应该明确目标:
> 第 1 天补底座,第 2 天接编排,把最关键的融合路径打通。
只要这条路径打通,后面无论你更偏向 Swarm、Claude Code CLI 还是 Claw 的方向,都能继续演进。

View File

@@ -0,0 +1,314 @@
# Jarvis Agents 8 天工作计划(可勾选执行版)
日期2026-04-03
状态:执行清单
适用范围:基于 `phase-0` ~ `phase-5` 及现有融合方案整理
借鉴来源Claude Code CLI、Swarm-IDE、Claw Code、VCPToolBox
---
## 使用说明
- 完成前使用 `- [ ]`
- 完成后改成 `- [x]`
- Day 2 默认依赖 Day 1 的核心底座完成后再推进
- Day 3 默认依赖 Day 2 的最小协作闭环完成后再推进
- Day 4 默认依赖 Day 3 的动态协作完成后再推进
- Day 5 默认依赖 Day 4 的可见性 API 完成后再推进
- Day 6 默认依赖 Day 5 的隔离执行完成后再推进
- Day 7 默认依赖 Day 6 的成本监控完成后再推进
---
## Day 1补底座完成 Phase 1 最小闭环
Day 1 目标:先把 Jarvis 从"只有静态路由"补成"有任务结构、有事件结构、有 verifier、有工具治理信息"的可扩展底座,同时不破坏当前 direct 主路径。
- [x] 新增最小 `task schema`
改造内容:新增 `backend/app/agents/schemas/task.py`,统一 `task_id``title``status``owner_agent_id``evidence``result_summary`,并补 `role``goal``expected_evidence``created_at``updated_at`;状态固定为 `pending``in_progress``completed``failed``blocked`
- [x] 新增最小 `event schema`
改造内容:新增 `backend/app/agents/schemas/event.py`,统一 `event_id``event_type``timestamp``conversation_id``agent_id``sub_commander_id``task_id``payload``severity`;首批事件类型覆盖 `agent.tool.start``agent.tool.result``agent.verify.started``agent.verify.completed``agent.error`
- [x] 扩展 `backend/app/agents/state.py` 的运行时字段
改造内容:新增 `execution_mode``verification_status``verification_summary``verification_evidence``active_tasks``task_results``event_trace``budget_state`;默认值保持兼容 `initial_state()`,不替换现有 `pending_tasks``completed_tasks``tool_calls`
- [x] 扩展 capability / tool metadata 模型
改造内容:在 `backend/app/agents/registry/models.py` 增加 `permission_class``side_effect_scope``supports_retry``idempotent``safe_for_parallel_use``requires_confirmation`;至少先固化 `read` / `write` / `external``none` / `local_state` / `db_write` / `network` 两组枚举语义。
- [x] 回填 builtin tools 的静态 metadata
改造内容:在 `backend/app/agents/registry/builtins.py` 和需要的 `backend/app/agents/tools/__init__.py` 中,把 search / retrieval 类工具标成偏 `read`create / update 类工具标成偏 `write`,外部检索类工具标成 `external`,并补充是否可重试、是否幂等、是否适合并行等标记。
- [x] 新增 verifier 角色定义
改造内容:在 `backend/app/agents/prompts.py` 增加 verifier prompt明确 verifier 只负责验收,不负责重新规划;验收点聚焦"是否真正满足请求""是否有明确证据""是否把失败伪装成成功"。
- [x] 落地 verifier 模块
改造内容:新增 `backend/app/agents/verifier.py`,支持 `passed``failed``skipped` 三类最小结论,先服务于工具调用后的复杂输出,知识检索结果和分析型汇总输出,不接管纯闲聊路径。
- [x]`backend/app/agents/graph.py` 接入最小 event trace 与 verifier helper
改造内容:给 `_execute_tool_calls()` 增加 tool start / result / error 事件写入;给收尾阶段增加 verifier helper 调用;给 `_run_sub_commander()` 增加 task result 摘要写入,但暂时不重构主图为完整协作编排图。
- [x] 补 Phase 1 单元测试与回归测试
改造内容:新增 `backend/tests/backend/app/agents/test_agent_schemas.py``backend/tests/backend/app/agents/test_verifier.py`,并扩展 `test_graph.py`,覆盖 state 兼容性、schema 合法性、tool metadata 存在性、verifier 判定、主流程不回退。
- [x] 完成 Day 1 验收
改造内容:确认 reminder / task / search 主流程继续通过;确认 verifier 已能独立运行;确认 event schema 与 task schema 已落代码;确认 direct 仍是默认主路径;确认未引入动态 `create_agent`、message bus 全链路和 UI。
---
## Day 2引入最小协作能力完成 Phase 2 雏形
Day 2 目标:在 Day 1 底座稳定的基础上,给 Jarvis 增加"复杂请求可拆分、可分配、可回收、可验收"的最小受控协作能力,但仍然不进入自由 swarm。
- [x] 增加 `request_mode_selector`
改造内容:在 `backend/app/agents/graph.py` 中增加 direct / collaboration 模式选择逻辑;简单请求继续走旧路径,只有明显多步骤、跨领域、需要多角色配合的请求才进入 collaboration mode。
- [x] 新增 coordinator prompt
改造内容:在 `backend/app/agents/prompts.py` 中定义 coordinator 角色,职责限定为"判断是否拆解""输出 2~4 个清晰子任务""分配角色建议""汇总任务结果";明确禁止无限递归拆分。
- [x] 新增最小 task decomposition 结构
改造内容:基于 Day 1 的 task schema 扩展最小拆分结构,至少输出 `task_id``title``role``goal``expected_evidence`,让复杂请求能以结构化任务列表进入后续执行。
- [x] 增加 role -> existing agent assignment
改造内容:先复用当前已有 top-level agent不新增独立 worker runtime把 schedule 类任务映射给 `schedule_planner`retrieval 类任务映射给 `librarian`analysis 类任务映射给 `analyst`execution 类任务映射给 `executor`
- [x] 建立统一 task result 回收结构
改造内容:约束每个角色统一返回 `task_id``status``summary``evidence``next_action`(可选),并把结果写回 `task_results`,避免最终结果继续依赖单点硬编码拼接。
- [x] 让 verifier 强制参与协作结果收尾
改造内容:在 collaboration mode 下,所有复杂请求返回前都必须经过 verifierverifier 有权拒绝证据不足、结果不完整,子任务未闭环的响应。
- [x] 补 Phase 2 协作测试与回归测试
改造内容覆盖复杂请求拆分测试、角色分配测试、task result 汇总测试、verifier 拒绝不完整结果测试,并再次确认 direct 模式原有流程不回退。
- [x] 完成 Day 2 验收
改造内容:确认 graph 已能区分 direct / collaboration确认复杂请求可拆成 2~4 个子任务;确认每个子任务有 owner 和 evidence确认最终答案基于 task result 汇总;确认系统仍未进入无限动态 agent 模式。
---
## Day 3引入受限动态协作能力完成 Phase 3 最小闭环
Day 3 目标:在 Day 2 已具备最小协作编排能力的基础上,让 Jarvis 获得"可追踪、可中断、可恢复、受预算约束"的动态协作 runtime但依然不进入无限自由 swarm。
当前实现状态2026-04-03Day 3 最小闭环已基本落地。`backend/app/agents/state.py` 已补齐协作树、thread/message、interrupt/recovery、budget 相关 runtime 字段;`backend/app/agents/graph.py` 已接入受限 child agent 创建、message trace、spawn budget guardrail、interrupt / recovery 最小闭环与协作结果回收;`backend/app/agents/registry/*` 已补齐 spawn role policy 并接入 graph 校验。
- [x] 扩展 `backend/app/agents/state.py` 记录协作树基础字段
当前状态:`state.py` 已补齐 `agent_id``parent_agent_id``root_agent_id``collaboration_depth``spawned_agent_ids``interrupted_tasks``recovery_points``message_trace` 等 Day 3 runtime 字段,并由 `initial_state()` 完成兼容初始化。
- [x] 定义动态协作 budget state
当前状态:已新增 `CollaborationBudget` schema并在 graph 中通过 `budget_state` / `collaboration_budget_history` 落地 `max_spawn_depth``max_child_agents``max_messages_per_thread``max_messages_per_turn``max_parallel_collaborators``recovery_attempt_limit` 等 guardrail metadata。
- [x] 增加受限 `create_agent` 运行时原语
改造内容:新增最小动态创建能力,仅允许在 collaboration mode 下、由受限角色、在 budget 允许时创建 child agent创建过程会记录 parent / child 关系,并在受限时转入 interrupt / recovery 回退路径。
- [x] 增加 agent spawn permission / role policy
改造内容:已在 `backend/app/agents/registry/*` 中补齐角色 spawn policy并通过 registry indexes 接入 `graph.py` 的运行时权限校验,禁止任意角色无限派生。
- [x] 新增最小 message / thread schema
改造内容:已补齐 `message_id``thread_id``from_agent_id``to_agent_id``task_id``reply_to_message_id``message_type``content_summary``created_at` 等结构,支持 `task_request``task_update``handoff``verification_request``verification_feedback``interrupt_notice`
- [x]`backend/app/agents/graph.py` 接入受限动态协作分支
改造内容coordinator / worker 在满足条件时可以请求受限协作graph 已接入 child agent 创建、message thread 写入、spawn budget 校验与回收逻辑,简单请求仍优先走 direct 路径。
- [x] 扩展 event trace 覆盖动态协作生命周期
改造内容event trace 已覆盖 `agent.created``agent.spawn.blocked``agent.message.sent``agent.message.received``agent.interrupt.requested``agent.interrupt.completed``agent.recovery.started``agent.recovery.completed` 等关键事件。
- [x] 增加 interrupt / recovery 最小闭环
改造内容:已支持中断协作任务、记录中断点,并基于 `task_id` / `thread_id` / budget 进行最小恢复路径记录与回退。
- [x] 增加 Day 3 测试与回归验证
改造内容:补充 parent / child tracking、spawn role policy、message thread、interrupt / recovery、动态协作事件记录等测试并继续确认 direct 主路径不回退。
- [x] 完成 Day 3 验收
改造内容:系统已支持受限动态创建 agent协作树和 message thread 可追踪interrupt / recovery 可跑最小闭环,动态能力受 budget 与 role policy 约束,且仍不是自由蜂群式协作。
---
## Day 4引入可见性 API完成 Phase 4 可视化方向
Day 4 目标:在 Phase 1-3 已具备协作 runtime 的基础上,让 Jarvis 获得"可看、可查、可调试"的可见性 API为后续复杂任务调试和执行打下基础。
当前实现状态2026-04-04Phase 1-3 最小闭环已基本落地Day 4 后端可见性最小闭环已完成。可见性 API 直接读取 continuity snapshot 中保存的 runtime state`event_trace``message_trace``active_tasks``task_results``task_hierarchy``verification_*``tool_outcomes`),并已补 focused API 测试。
- [x] 固化可见性数据源并增加 events 查询 API
改造内容:已在 `backend/app/routers/agent.py` 暴露 `GET /api/agents/visibility/events`,支持按 `conversation_id` / `thread_id` / `agent_id` / `event_type` 过滤 `event_trace`,并支持分页与时间范围查询。
- [x] 新增协作链路拓扑查询 API
改造内容:已新增 `GET /api/agents/visibility/topology`,基于 state 中的 `spawned_agent_ids``task_hierarchy``root_agent_id``active_tasks``task_results` 构建协作拓扑视图,返回 agent 节点、父子边与 task 摘要。
- [x] 新增 task 执行证据查询 API
改造内容:已新增 `GET /api/agents/visibility/tasks/{task_id}/evidence`,基于 state 中的 `task_results``tool_outcomes``verification_*` 返回指定 task 的执行证据链。
- [x] 新增 message thread 查询 API
改造内容:已新增 `GET /api/agents/visibility/threads/{thread_id}/messages`,基于 `message_trace` 返回指定 thread 内所有消息的方向、摘要、时间和关联 task。
- [x] 新增 verifier 结果查询 API
改造内容:已新增 `GET /api/agents/visibility/verifier`,基于 `verification_status``verification_summary``verification_evidence` 返回当前协作会话的验收结论和证据。
- [x] 补 Day 4 可见性 API 测试
改造内容:已新增 `backend/tests/backend/app/agents/test_visibility_api.py`,覆盖 event filter / pagination、topology 构建、evidence 查询、thread 消息重建、verifier 查询、非法 datetime 参数校验等场景。
- [x] 完成 Day 4 验收
改造内容:已确认 visibility API 可查询事件、拓扑、task evidence、thread 消息与 verifier 结果;并已确认原有 reminder / task / search 主路径不在 Day 4 范围内被改坏。
---
## Day 5升级 operator/debug surface已完成
Day 5 目标:把 Day 4 的只读可见性 API 真正接到前端 Agents 页面,形成最小 operator/debug surface。
- [x] 接入 runtime summary API 到前端 `agentApi`
改造内容:在 `frontend/src/api/agent.ts` 增加 runtime summary 类型与 `getRuntimeSummary()` 查询方法。
- [x] 在 Agents 页面展示 phase/checkpoint/verifier/isolation/cost 摘要
改造内容:在 `frontend/src/pages/agents/index.vue``useAgentsPage.ts` 中加入 runtime summary HUD展示 execution mode、phase、checkpoint、verifier、isolation、token/cost、task/node 统计。
- [x] 让 Agents 页面使用当前会话 `conversation_id`
改造内容:复用 `frontend/src/stores/conversation.ts``currentConversationId`,不再使用伪造的 `latest` 占位值。
- [x] 修复 Agents 页面关键乱码与兜底文案
改造内容修复配置抽屉、状态文案、master task 文案等可见乱码,并为未选会话场景提供清晰提示。
- [x] 补前端运行时面板测试
改造内容:补 `frontend/src/pages/agents/agentsPage.test.ts`,覆盖 runtime summary 渲染、无会话提示、会话 ID 传递。
- [x] 完成 Day 5 验收
改造内容:确认 Agents 页面已经是可用的 operator/debug 入口,而不是只显示静态演示图。
---
## Day 6推进 isolation runtime MVP90 分主线)
Day 6 目标:把 Day 4 的 isolation 设计从文档推进到最小运行时闭环,只做 `none / session / worktree` 三层。
当前实现状态2026-04-04Day 6 已落地 isolation runtime MVP。后端已新增 `strategy_selector.py``session_isolation.py``worktree_isolation.py`,并在 `graph.py` 中接入基于任务语义与 tool metadata 的 `none / session / worktree` 选择逻辑;隔离 metadata 会进入 state、event trace、task evidence 与 runtime summaryAgents 页面也可展示 workspace / isolation 状态。
- [x] 实现 IsolationStrategySelector
改造内容:新增 `backend/app/agents/isolation/strategy_selector.py`,根据任务类型与 tool metadata 自动选择 `none / session / worktree`
当前状态:已新增 `backend/app/agents/isolation/strategy_selector.py`可基于用户请求语义、role 与 capability metadata 自动选择 `none / session / worktree`
- [x] 实现 Session 隔离
改造内容:新增 `backend/app/agents/isolation/session_isolation.py`,支持上下文隔离、中间态隔离与 evidence 回传。
当前状态:已新增 `backend/app/agents/isolation/session_isolation.py`,会生成独立 session isolation metadata并把 parent conversation / role / sub commander / capability 信息写回 runtime state。
- [x] 实现 Worktree 隔离
改造内容:新增 `backend/app/agents/isolation/worktree_isolation.py`,基于 git worktree 创建独立工作目录,回传 workspace/branch/cleanup metadata。
当前状态:已新增 `backend/app/agents/isolation/worktree_isolation.py`,支持基于 git worktree 创建独立工作目录,回传 branch / repo_root / cleanup_status 等 metadata创建失败时可回退到 session isolation。
- [x] 集成隔离策略到 graph
改造内容:在 `backend/app/agents/graph.py` 中接入策略选择与 evidence 输出,不做自动 merge-back。
当前状态:`backend/app/agents/graph.py` 已接入 isolation selector / executor运行时会记录 `agent.isolation.selected` / `agent.isolation.fallback` 事件,并把 isolation metadata 写入 evidence 与 task result。
- [x] 补 Day 6 隔离测试
改造内容:新增隔离策略与 metadata 传播测试,覆盖 session/worktree 选择和 runtime summary 展示。
当前状态:已在 `backend/tests/backend/app/agents/test_graph.py` 中补充 isolation selector / worktree fallback / runtime cost 联动测试;`test_visibility_api.py` 继续覆盖 runtime summary 中的 isolation 暴露。
- [x] 完成 Day 6 验收
改造内容:确认高副作用任务可进入 worktree低副作用任务保持 direct/session 路径,主流程无回退。
当前状态:高副作用、代码/仓库语义请求可进入 worktree普通状态写入或分析路径保持 session / direct主流程回归测试已通过 `test_graph.py`
---
## Day 7推进 cost governance MVP90 分主线)
Day 7 目标:把 token/cost 从静态估算升级为会话级可治理能力。
当前实现状态2026-04-04Day 7 已从“静态展示”推进到最小 cost governance 闭环。`graph.py` 已稳定累计 `input_tokens` / `output_tokens` / `estimated_cost`,并按 conversation / child agent 维度写入 `cost_by_agent`budget threshold 会触发 `agent.cost.updated` / `agent.cost.warning` 事件,后端也已暴露 conversation scoped cost API。
- [x] 固化 runtime token 字段写入
改造内容:在 graph / service 层稳定记录 `input_tokens``output_tokens``estimated_cost``budget_warning`
当前状态:`backend/app/agents/graph.py` 已在每次 LLM 响应后提取 usage metadata并稳定写入 `input_tokens``output_tokens``estimated_cost``budget_warning`
- [x] 集成成本累计到 conversation / child agent 维度
改造内容:把协作 run 的 token/cost 汇总到 conversation summary并保留子 agent 维度的来源信息。
当前状态state 中已新增 `cost_by_agent``cost_thresholds`,并可通过 `/api/agents/visibility/cost` 返回 conversation 总量与 child agent 分摊。
- [x] 增加 budget threshold 治理逻辑
改造内容:支持阈值警告、超额提示和 runtime summary 暴露。
当前状态:已新增默认 cost threshold 与 state override 机制,超阈值会写入 `budget_warning` 并产生 warning 事件runtime summary 与前端 HUD 均可见。
- [x] 新增成本查询 API
改造内容:在现有 visibility surface 上补 conversation scoped cost 查询,而不是另起一套孤立接口。
当前状态:已新增 `GET /api/agents/visibility/cost`,返回 conversation scoped total / thresholds / by_agent breakdown。
- [x] 补 Day 7 测试
改造内容覆盖成本累计、阈值预警、runtime summary 成本字段。
当前状态:`backend/tests/backend/app/agents/test_graph.py` 已覆盖 runtime usage 写入与 threshold warning`test_visibility_api.py` 已补 cost summary 返回结构断言。
- [x] 完成 Day 7 验收
改造内容:确认 cost 不再只是“可估算”,而是“可观察、可告警、可治理”。
当前状态Day 7 当前已达到“可观察、可告警、可按会话/agent 查询”,仍未做到更高级的跨会话预算策略与持久化治理。
---
## Day 890 分收口与工具治理增强
Day 8 目标:把现有 runtime 补成真正可运营的 90 分闭环,并明确下一批值得升级的工具能力。
当前实现状态2026-04-04Day 8 已完成 90 分主线收口。Agents 页面已从首屏 summary 升级为可查看 recent events、topology 节点摘要、verifier evidence、cost by agent 与 tool governance 的 operator/debug surface后端也补齐了 `/visibility/tools` 能力分层查询。
- [x] 增强 topology / recent events operator surface
改造内容:在 Agents 页面增加 recent events、拓扑摘要、verifier 证据入口,形成更完整的调试视图。
当前状态:前端 `frontend/src/pages/agents/index.vue``useAgentsPage.ts` 已展示 recent events、topology 节点摘要与 verifier evidence 入口。
- [x] 对工具能力做治理分层
改造内容:基于 `permission_class``side_effect_scope``requires_confirmation``safe_for_parallel_use` 做工具分级和后续 UI 展示规划。
当前状态:后端已新增 `GET /api/agents/visibility/tools`,基于 `permission_class``side_effect_scope``requires_confirmation``safe_for_parallel_use` 返回 tool governance 视图;前端已做 operator 展示。
- [x] 识别值得升级/新增的工具能力
改造内容:优先考虑 `worktree manager``cost inspector``runtime event drilldown``tool policy explorer`,暂不做 TagMemo / AgentDream。
当前状态operator surface 已显式暴露 `worktree_manager``cost_inspector``runtime_event_drilldown``tool_policy_explorer` 四项下一批 upgrade candidates。
- [x] 完成 90 分结项回归
改造内容:统一回归 direct / collaboration / runtime summary / isolation / cost 关键路径,并更新 README / daily / checklist 结论。
当前状态:已完成 `backend/tests/backend/app/agents/test_graph.py` 回归、`frontend/src/pages/agents/agentsPage.test.ts` 前端回归,以及不依赖 pytest tmpdir 的 visibility API 手工验证README / checklist 已同步更新。
---
## 这 8 天明确不做
- 不做无限自由的动态 `create_agent`
- 不做无限层级的 parent / child agent tree
- 不做任意 agent 任意建群 / 广播
- 不做内部消息线程的复杂长期态治理
- 不做完整可视化调试面板(只做首屏 summary / HUD不做完整实时 drilldown UI
- 不做 Full Sandbox 完整实现(只做设计方案)
- 不做自由蜂群式协作
- 不做 Persistence数据库持久化
- 不做 Multi-turn Memory跨会话记忆
- 不做 Plugin System插件系统
- 不做 TagMemo仿生记忆系统
- 不做 AgentDream仿生梦境系统
---
## 8 天结束后的预期状态
- [x] 已具备 `direct` / `collaboration` 双模式入口
- [x] 已具备 verifier 独立验收层
- [x] 已具备 task schema / event schema / tool metadata 底座
- [x] 已具备 coordinator 雏形、任务拆分、角色分配、结果回收
- [x] 已具备受限动态协作 runtime 的最小实现闭环
- [x] 当前 reminder / task / search 主路径无明显回退
- [x] 已具备可见性 API 基础events、topology、evidence、thread、verifier、runtime-summary
- [x] 已具备前端 Agents operator/debug 首屏
- [x] 已具备 isolation strategy selector + session/worktree executor 的最小运行时闭环
- [x] 已具备 conversation / child agent 维度的 cost governance 最小闭环
- [ ] 尚未具备 full sandbox / persistence / realtime push
- [x] 90 分主线已明确为 isolation + cost + operator surface而不是 TagMemo / AgentDream
---
## 后续可选特性(按需实施)
| 特性 | 预估时间 | 触发条件 |
|------|---------|---------|
| AgentDream仿生梦境系统 | 1天 | Day 7完成后 |
| Persistence持久化 | 2-3天 | 有审计需求时 |
| Advanced UI完整前端面板 | 3-5天 | 有前端资源时 |
| Full Sandbox完整沙箱 | 3-5天 | 有安全需求时 |
| Plugin System插件系统 | 2-3天 | 有社区需求时 |
---
##

View File

@@ -0,0 +1,322 @@
# Phase 0当前现状与目标架构
日期2026-04-03
状态:已更新
---
## 1. 本阶段目的
本文件不涉及具体代码实施,而是用于统一背景认知,明确:
- Jarvis 当前 agent 架构处于什么水平
- 主要短板是什么
- 为什么要升级
- 升级后的目标形态是什么
- 三个 demo 项目分别给我们什么启发
- 关键架构决策记录(ADR)
---
## 2. 当前 Jarvis 架构现状
当前核心代码集中在:
- `backend/app/agents/graph.py`
- `backend/app/agents/state.py`
- `backend/app/agents/prompts.py`
- `backend/app/agents/registry/*`
- `backend/app/agents/tools/*`
- `backend/tests/backend/app/agents/test_graph.py`
### 当前架构精确描述
```
┌─────────────────────────────────────────────────────────────┐
│ Master Node │
│ (意图识别 + 路由分发) │
└─────────────────┬───────────────────────────────────────────┘
┌─────────┴─────────┬───────────┬───────────┬─────────┐
▼ ▼ ▼ ▼ │
Schedule_Planner Executor Librarian Analyst │
(规划分析) (执行操作) (知识检索) (数据分析) │
│ │ │ │ │
└───────────────────┴───────────┴───────────┴─────────┘
┌───────────┴───────────┐
│ Tool Executor │
│ (统一工具执行层) │
└───────────────────────┘
```
### 当前优势
1. ✅ LangGraph状态机结构清晰
2. ✅ 已有Master + 4个角色(sub_commander)的层级设计
3. ✅ 支持native tool calling与fallback
4. ✅ 有continuity / clarification / pending action机制
5. ✅ ReAct模式(思考→行动→观察循环)
6. ✅ 有一定测试基础
7. ✅ 流式输出支持
### 当前短板
> 注意:下面列的是**相对目标架构仍然不足的部分**,不是说这些能力完全不存在。
| 短板 | 严重程度 | 当前真实状态 |
|------|----------|------|
| Agent间直接通信原语仍不独立 | 🔴 高 | 当前以 `message_trace` / coordinator 主路径为主,尚未独立 MessageBus |
| worker 自主再委派能力仍受限 | 🔴 高 | 已有受控 spawn但不是自由 swarm |
| EventBus / Coordinator 仍未工程拆分 | 🟡 中 | 能力在 `graph.py` 内可运行,但模块边界仍偏集中 |
| 可观察性仍缺少实时推送与前端面板 | 🟡 中 | 已有 event/phase/checkpoint/visibility API尚无完整实时 UI |
| 隔离执行仍未完整落地 | 🟡 中 | isolation strategy 已设计worktree / sandbox runtime 未完成 |
| 工具治理仍可继续细化 | 🟢 低 | 已有基础 metadata 与权限语义,但尚未到完整产品化治理 |
---
## 3. 为什么需要升级
如果 Jarvis 继续只依赖当前静态路由架构,会遇到以下问题:
1. **复杂请求只能硬塞进单路径执行** → 可靠性低
2. **无法优雅地拆分和回收多步任务** → 只能串行处理
3. **执行与验收耦合** → 工具失败可能被误报为成功
4. **系统内部过程不透明** → 后续难调试
5. **无法支持真实的多Agent协作** → 协作能力上限低
---
## 4. 四个 Demo 的启发总结
### 4.1 Swarm-IDE 给我们的启发
**重点学习**
| Swarm-IDE特性 | 我们的借鉴点 | 实现位置 |
|--------------|-------------|----------|
| `create`/`send` 协作原语 | 定义Agent通信接口 | Phase 2 |
| Agent间通信作为一等能力 | MessageBus设计 | Phase 2 |
| 人类可以直接介入sub-agent | 调试/干预能力 | Phase 4 |
| Runtime event bus | 统一事件流 | Phase 3 |
| WeChat式群聊模式 | Agent群组通信 | Phase 3+ |
| 实时拓扑可视化 | 可视化调试面板 | Phase 4 |
**核心文件参考**`swarm-ide-chore-specs-mvp/backend/src/runtime/agent-runtime.ts`
### 4.2 Claude Code CLI 给我们的启发
**重点学习**
| Claude Code CLI特性 | 我们的借鉴点 | 实现位置 |
|-------------------|-------------|----------|
| Coordinator-worker编排模式 | Phase 2的coordinator | Phase 2 |
| Task/team生命周期管理 | Task schema + lifecycle | Phase 1 |
| 计划-执行-验证分离 | Verifier独立角色 | Phase 1 |
| 并行执行时的隔离策略 | 预算控制 | Phase 3 |
| 工具注册与权限控制 | Tool metadata | Phase 1 |
**核心文件参考**`claude-code-cli-master/src/tools.ts`
### 4.3 Claw Code 给我们的启发
**重点学习**
| Claw Code特性 | 我们的借鉴点 | 实现位置 |
|-------------|-------------|----------|
| Runtime分层 | 抽象runtime接口 | Phase 3 |
| Port manifest模式 | 子系统边界定义 | Phase 2 |
| 显式权限模型 | Tool permission_class | Phase 1 |
| Plugin/hook扩展点 | Registry扩展机制 | Phase 3 |
**核心文件参考**`claw-code-main/src/port_manifest.py`
### 4.4 VCPToolBox 给我们的启发
**重点学习**
| VCPToolBox特性 | 我们的借鉴点 | 实现位置 |
|-------------|-------------|----------|
| VCP协议 | 模型无关的工具调用格式 | Phase 2 |
| 6类型插件系统 | Tool类型分类(SYNC/ASYNC/SERVICE) | Phase 1 |
| TagMemo记忆 | 仿生遗忘曲线 + 重要性权重 | Phase 5 |
| AgentDream | AI睡眠时整理记忆 | Phase 5 |
| 分布式架构 | WebSocket星型网络 | Phase 6+ |
**VCPToolBox核心概念**
1. **VCP协议** — 文本格式工具调用协议
```
<<<[TOOL_REQUEST]>>>
tool_name:「始」VSearch「末」,
SearchTopic:「始」AI医疗诊断「末」
<<<[END_TOOL_REQUEST]>>>
```
- 使用中文分隔符「始」「末」
- 支持 `archery:no_reply`(异步)、`river:full`(上下文注入)
2. **TagMemo记忆系统** — 仿生RAG
- LIF神经元模型脉冲传播机制
- Core Tags核心记忆1.2-1.4x权重加成
- 遗忘曲线:不是无限存储,模拟生物遗忘
3. **6类型插件**
- `static`:占位符
- `messagePreprocessor`:消息预处理
- `synchronous`同步stdio
- `asynchronous`:异步回调
- `service`:后台服务
- `hybridservice`:混合模式
4. **AgentDream** — 仿生梦境系统
- 0-7天近期记忆高频共振
- 7-90天中期记忆弱共振
- >90天长期记忆遗忘边界
**核心文件参考**
- `demo/VCPToolBox-main/Plugin.js`
- `demo/VCPToolBox-main/KnowledgeBaseManager.js`
- `demo/VCPToolBox-main/modules/messageProcessor.js`
---
## 5. 关键架构决策记录(ADR)
### ADR-001: 为什么选择LangGraph
**决策**继续使用LangGraph作为状态机基础
**理由**
- ✅ 当前已有投入,结构清晰
- ✅ 状态转移显式化,易于调试
- ✅ 支持conditional edge可做路由
- ✅ 与LangChain生态集成良好
**替代方案考虑**
- 自研状态机:维护成本高,不值得
- Swarm-IDE模式过度动态缺少可预测性
- Actor模型引入复杂度高收益不明显
### ADR-002: 为什么用sub_commander而非独立Agent
**决策**保持当前Master+sub_commander模式不做完全独立的Agent进程
**理由**
- ✅ 同一进程内共享上下文,开销低
- ✅ 状态传递简单直接
- ✅ 与现有LangGraph集成自然
- ✅ 更易保证事务性
**未来可选演进**
- 复杂任务可用worktree隔离Phase 4
- 但默认仍是sub_commander模式
### ADR-003: 为什么先不做无限动态swarm
**决策**:所有动态能力必须受控,不追求"无限自由"
**理由**
- ❌ 无限动态 → token成本不可控
- ❌ 无限动态 → 调试困难
- ❌ 无限动态 → 行为不可预测
**设计原则**
- 简单请求走direct路径当前稳定模式
- 复杂请求进collaboration模式有预算约束
- 动态spawn有深度/数量/角色限制
### ADR-004: Event Schema设计选择
**决策**Phase 1只做内存trace不做持久化
**理由**
- ✅ 快速迭代不用关心数据库schema
- ✅ 测试容易断言
- ✅ Phase 4再做SSE/持久化
**未来演进**
- Phase 4: SSE流式推送
- Phase 5+: 可选数据库持久化
---
## 6. 目标架构
Jarvis 2.0 的目标是:
> 从静态层级路由系统,升级为**受控的动态协作运行时**。
### 目标架构预览
```
┌─────────────────────────────────────────────────────────────┐
│ User Request │
└─────────────────────────┬───────────────────────────────────┘
┌───────────┴───────────┐
│ Execution Mode │
│ Router (新增) │
└───────────┬───────────┘
┌─────────────────┴─────────────────┐
▼ ▼
┌─────────┐ ┌──────────────┐
│ Direct │ │ Collaboration│
│ Mode │ │ Mode │
│(当前路径)│ │ (Phase 2+) │
└────┬────┘ └──────┬───────┘
│ │
▼ ▼
┌─────────┐ ┌──────────────────┐
│ Master │ │ Coordinator │
│ Node │ │ (新增) │
└────┬────┘ └────────┬─────────┘
│ │
▼ ▼
┌─────────┐ ┌──────────────────┐
│ Sub │◄──────────────────►│ MessageBus │
│Commanders│ │ (新增) │
└────┬────┘ └────────┬─────────┘
│ │
│ ┌────────────────────────┤
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌───────────┐
│ Tools │ │ Verifier│ │ Workers │
│ Executor│ │(独立角色)│ │ (动态创建) │
└─────────┘ └─────────┘ └───────────┘
```
### 升级后的基本原则
1. **简单请求继续走当前稳定路径(direct mode)**
2. **复杂请求才进入协作模式(collaboration mode)**
3. **执行与验证分离(Verifier独立)**
4. **动态能力必须有预算限制(spawn/message/depth budget)**
5. **工具能力必须有权限约束(permission_class)**
6. **全程要有可观察事件流(event trace)**
---
## 7. 总体分阶段策略
| Phase | 名称 | 核心目标 | 关键产出 |
|-------|------|----------|----------|
| Phase 1 | 基础设施加固 | 补底座,打基础 | Verifier, Task/Event Schema, Tool Metadata |
| Phase 2 | 受控协作 | 结构化任务编排 | Coordinator, MessageBus, Task Decomposition |
| Phase 3 | 动态协作 | 受限动态能力 | Dynamic Spawn, Thread Model, Interrupt/Recovery |
| Phase 4 | 可视化与隔离 | 可调试,可控制 | Event Stream API, Topology UI, Isolation Strategy |
| Phase 5 | 高级特性(可选) | 产品化能力 | Sandbox, Full UI, Persistence |
---
## 8. 本阶段产出要求
本阶段完成标准:
- [x] 团队对 Jarvis 当前问题和目标方向达成一致
- [x] 关键架构决策已记录
- [x] Demo借鉴点已映射到具体Phase
- [x] 后续 phase 文档能够在这个认知基础上展开
- [x] 不直接在本阶段进行代码改造

View File

@@ -0,0 +1,215 @@
# Phase 1基础设施加固阶段Safe Foundation
日期2026-04-03
状态:已落地,进入增量加固
---
## 1. 阶段目标
在不破坏当前稳定业务路径的前提下,为后续多 agent 协作补齐基础设施。
这一阶段**不追求动态 swarm不追求并行炫技**,重点是:
- 打基础
- 控风险
- 不破坏当前功能
当前代码已经完成这条基础线:
- execution mode 已存在Direct / Collaboration 已可区分
- verifier 收尾逻辑已存在,并参与协作结果验收
- task schema / event schema 已进入运行时
- graph 已具备可验证、可追踪、可持续增强的基础骨架
---
## 2. Demo借鉴映射
| 本Phase改动 | 借鉴来源 | 具体参考点 |
|------------|----------|-----------|
| Tool Metadata | Claude Code CLI | `tools.ts` - JSON Schema验证 + permission控制 |
| Tool 类型分类 | VCPToolBox | 6类型插件系统(SYNC/ASYNC/SERVICE) |
| Event Schema | Swarm-IDE | `event-bus.ts` - 统一事件格式 |
| Task Schema | Claude Code CLI | Task/team生命周期管理 |
| Verifier角色 | Claude Code CLI | 计划-执行-验证分离模式 |
---
## 3. 当前代码中已落地的能力
当前 Jarvis 已经在 `backend/app/agents/graph.py``backend/app/agents/state.py` 与相关 schema 中完成了 Phase 1 的基础闭环。
### 3.1 Execution mode 与基础运行态
当前已具备:
- `execution_mode` 区分 `direct``collaboration`
- `master_node()` 可根据请求复杂度切换主路径
- `initial_state()` 已兼容这些字段的默认值
这意味着 Phase 1 不再只是“给 Phase 2 预留接口”,而是已经成为实际运行时入口的一部分。
### 3.2 Verifier 收尾基线
当前已具备:
- `_verify_collaboration_results()` 负责对协作结果做统一验收
- `verification_status`
- `verification_summary`
- `verification_evidence`
说明执行与验收已经形成最小分离,而不是完全耦合在执行流内部。
### 3.3 Task Schema 已落地
当前 state 与协作链路已围绕结构化 task 信息运行,包括:
- `task_id`
- `role`
- `owner_agent_id`
- `goal`
- `expected_evidence`
- `task_results`
- `task_hierarchy`
- `active_tasks`
这已经满足 Phase 1 “统一任务结构、可供 verifier 读取”的核心目标。
### 3.4 Event Schema 已落地
当前已具备统一事件模型,并用于 `event_trace`。事件类型已覆盖:
- tool / message / spawn / interrupt / recovery 相关事件
- `agent.phase.changed`
- `agent.checkpoint.recorded`
- `agent.error`
因此当前系统已经不是“没有统一 event schema”而是已经进入“事件类型可继续扩展”的状态。
### 3.5 显式 phase / checkpoint 已补齐
在此前基础之上,本次又补齐了:
- `current_phase`
- `phase_history`
- `current_checkpoint`
- `checkpoint_history`
并在运行时中显式记录:
- `phase_0_bootstrap`
- `phase_1_routing`
- `phase_2_controlled_collaboration`
- `phase_3_dynamic_collaboration`
- `phase_4_visibility_and_verification`
以及关键 checkpoint例如
- `bootstrap.initialized`
- `routing.master_entered`
- `collaboration.tasks_planning`
- `collaboration.tasks_ready`
- `collaboration.task_dispatch`
- `collaboration.task_result_collected`
- `collaboration.verification_started`
- `collaboration.completed`
这意味着 Phase 1 的“可观察基础”已经不只是事件 trace还进一步升级成了显式阶段模型。
---
## 4. 当前实现对应关系
| 设计目标 | 当前状态 | 落点 |
|------|------|------|
| Execution mode 基础分流 | 已实现 | `state.py`, `graph.py` |
| Verifier 收尾基线 | 已实现 | `_verify_collaboration_results` |
| Task Schema 基线 | 已实现 | `state.py`, `graph.py` |
| Event Schema 基线 | 已实现 | `schemas/event.py`, `event_trace` |
| Tool Metadata 基础治理 | 已实现基线 | `registry/models.py`, `registry/builtins.py` |
| Phase / Checkpoint 显式化 | 已实现 | `state.py`, `graph.py`, `agent_service.py` |
---
## 5. 本阶段核心改动与后续边界
Phase 1 现在更准确的定义不是“准备做什么”,而是“哪些基础能力已经稳定存在,哪些还要继续工程化”。
### 5.1 已经完成的核心底座
当前已经完成:
- execution mode 基础分流
- verifier 收尾基线
- task / result / evidence 结构化承载
- event schema 与 `event_trace`
- tool metadata 基础治理
- phase / checkpoint 显式建模
- continuity snapshot 持久化与恢复
- 对应自动化测试
### 5.2 这一阶段仍然保持的边界
Phase 1 仍然不等于完整多 agent 平台。它没有承诺:
- 独立 coordinator 模块
- 独立 message bus 模块
- 自由 worker-to-worker 协作
- 完整 sandbox / worktree 执行器
- 前端实时调试面板
这些内容属于 Phase 2 以后逐步增强的工程层,不影响 Phase 1 已经完成验收。
### 5.3 测试与持久化现状
当前已覆盖的关键验证包括:
- 初始 phase / checkpoint 默认值
- phase 切换与 checkpoint 记录行为
- collaboration flow 下的 phase / checkpoint 演进
- fallback 回 direct 路径时的 checkpoint 恢复
- continuity snapshot roundtrip 保留 phase / checkpoint 历史
因此当前不是“概念设计”,而是已经具备回归保护的运行时能力。
---
## 6. 风险点
| 风险 | 当前状态 / 缓解措施 |
|------|----------|
| 运行时逻辑集中在 `graph.py` | 当前可运行,但后续应继续拆分 coordinator / bus / recovery 模块 |
| visibility 已有基线但缺少实时化 | 后续通过 SSE / WebSocket / UI 面板增强 |
| 动态协作仍受严格治理 | 这是当前有意保留的边界,不是缺陷 |
| tool metadata 仍可继续细化 | 后续再补强更细粒度治理即可 |
---
## 7. 当前验收结论
当前 Phase 1 可以按“已落地”验收:
- [x] `state.py` 已承载 direct / collaboration 基础字段
- [x] verifier 已参与关键结果验收
- [x] task schema / event schema 已有代码落点
- [x] builtin tools 已具备基础 metadata 语义
- [x] graph 主流程已有回归测试保护
- [x] phase / checkpoint 已进入显式运行时模型
- [x] continuity snapshot 已可持久化这些状态
- [ ] 尚未完成进一步工程拆分与实时平台化
---
## 8. 本阶段完成后真实结果
完成 Phase 1 之后Jarvis 已经不只是“单路由 agent”。它已经具备
- ✅ 稳定 direct 路径
- ✅ 复杂请求切换到 collaboration 的入口基础
- ✅ 结构化任务与结果回收底座
- ✅ verifier 验收机制
- ✅ 统一事件记录与阶段追踪
- ✅ continuity 状态延续能力
**结论Phase 1 已不是草案,而是 Jarvis 当前多阶段 runtime 的已落地基础层。**

View File

@@ -0,0 +1,637 @@
# Phase 10高级编排Advanced Orchestration
日期2026-04-04
状态:待开始
前置依赖Phase 6-9工具系统、Hook、插件、Skills
Demo参考claw-code-main — assistant/, cli/, structuredIO.ts, remoteIO.ts
---
## 1. 阶段目标
实现**高级 Agent 编排能力**,包括:
- Team 多 Agent 协作
- 远程/结构化传输
- 高级会话管理
- 后台任务系统
这是 claw-code 与 Jarvis 架构差距最大的地方,也是最复杂的功能。
---
## 2. Team 多 Agent 协作
### 2.1 TeamLeader
```python
# backend/app/agents/team/leader.py
@dataclass
class TeamMember:
"""团队成员"""
agent_id: str
role: str
capabilities: list[str]
status: AgentStatus
current_task: str | None = None
@dataclass
class TeamTask:
"""团队任务"""
task_id: str
description: str
parent_id: str | None = None
assignee: str | None = None
status: TaskStatus
dependencies: list[str] = field(default_factory=list)
result: Any = None
class TeamLeader:
"""
团队领导者
负责协调多个 Agent 的协作
"""
def __init__(
self,
team_id: str,
tool_registry: ToolRegistry,
hook_manager: HookManager
):
self.team_id = team_id
self.tool_registry = tool_registry
self.hook_manager = hook_manager
self.members: dict[str, TeamMember] = {}
self.tasks: dict[str, TeamTask] = {}
self.task_queue: asyncio.Queue = asyncio.Queue()
async def create_team(
self,
config: TeamConfig
) -> str:
"""创建团队"""
self.team_id = config.team_id
# 创建团队成员
for member_config in config.members:
member = TeamMember(
agent_id=member_config.agent_id,
role=member_config.role,
capabilities=member_config.capabilities,
status=AgentStatus.IDLE
)
self.members[member.agent_id] = member
return self.team_id
async def assign_task(
self,
task: TeamTask
) -> str:
"""分配任务"""
self.tasks[task.task_id] = task
# 找到最合适的成员
assignee = await self._find_assignee(task)
if assignee:
task.assignee = assignee.agent_id
assignee.current_task = task.task_id
assignee.status = AgentStatus.WORKING
return task.task_id
async def _find_assignee(
self,
task: TeamTask
) -> TeamMember | None:
"""找到最适合执行任务的成员"""
# 过滤掉忙碌的成员
available = [
m for m in self.members.values()
if m.status == AgentStatus.IDLE
]
if not available:
return None
# 按能力匹配
for cap in task.required_capabilities:
matches = [m for m in available if cap in m.capabilities]
if matches:
return matches[0]
return available[0] if available else None
async def broadcast_task(
self,
description: str,
required_capabilities: list[str]
) -> list[str]:
"""广播任务给所有符合条件的成员"""
task_ids = []
for member in self.members.values():
if member.status != AgentStatus.IDLE:
continue
# 检查能力匹配
if not any(cap in member.capabilities for cap in required_capabilities):
continue
# 创建任务
task = TeamTask(
task_id=generate_id(),
description=description,
assignee=member.agent_id,
status=TaskStatus.PENDING
)
await self.assign_task(task)
task_ids.append(task.task_id)
return task_ids
async def collect_results(
self,
task_ids: list[str],
timeout: float = 300
) -> dict[str, Any]:
"""收集任务结果"""
results = {}
start_time = time.time()
while len(results) < len(task_ids):
if time.time() - start_time > timeout:
break
for task_id in task_ids:
if task_id in results:
continue
task = self.tasks.get(task_id)
if task and task.status == TaskStatus.COMPLETED:
results[task_id] = task.result
await asyncio.sleep(0.1)
return results
async def get_team_status(self) -> TeamStatus:
"""获取团队状态"""
return TeamStatus(
team_id=self.team_id,
members={
agent_id: {
"role": m.role,
"status": m.status.value,
"current_task": m.current_task
}
for agent_id, m in self.members.items()
},
active_tasks=sum(1 for t in self.tasks.values() if t.status == TaskStatus.PENDING),
completed_tasks=sum(1 for t in self.tasks.values() if t.status == TaskStatus.COMPLETED)
)
```
---
## 3. 远程传输层
### 3.1 StructuredIO
```python
# backend/app/agents/transport/structured_io.py
class StructuredIO:
"""
结构化 IO
支持结构化的输入输出格式
"""
async def send_response(
self,
channel: str,
data: dict
):
"""发送结构化响应"""
message = {
"channel": channel,
"type": "structured",
"data": data,
"timestamp": datetime.now().isoformat()
}
await self.transport.send(message)
async def send_event(
self,
event_type: str,
payload: dict
):
"""发送事件"""
message = {
"type": "event",
"event": event_type,
"payload": payload,
"timestamp": datetime.now().isoformat()
}
await self.transport.send(message)
async def send_tool_call(
self,
tool_name: str,
arguments: dict,
call_id: str
):
"""发送工具调用"""
message = {
"type": "tool_call",
"tool": tool_name,
"args": arguments,
"call_id": call_id,
"timestamp": datetime.now().isoformat()
}
await self.transport.send(message)
async def receive(self) -> dict:
"""接收消息"""
raw = await self.transport.receive()
return self._parse(raw)
def _parse(self, raw: bytes) -> dict:
"""解析消息"""
# 支持 JSON 和流式格式
pass
```
### 3.2 RemoteTransport
```python
# backend/app/agents/transport/remote.py
class RemoteTransport:
"""
远程传输
支持远程 Agent 通信
"""
def __init__(
self,
endpoint: str,
auth_token: str
):
self.endpoint = endpoint
self.auth_token = auth_token
self.websocket: WebSocket | None = None
async def connect(self):
"""建立连接"""
self.websocket = await websockets.connect(
self.endpoint,
extra_headers={"Authorization": f"Bearer {self.auth_token}"}
)
async def send(self, message: dict):
"""发送消息"""
if not self.websocket:
await self.connect()
await self.websocket.send(json.dumps(message))
async def receive(self) -> dict:
"""接收消息"""
if not self.websocket:
await self.connect()
raw = await self.websocket.recv()
return json.loads(raw)
async def close(self):
"""关闭连接"""
if self.websocket:
await self.websocket.close()
```
---
## 4. 高级会话管理
### 4.1 AgentSession
```python
# backend/app/agents/session/manager.py
@dataclass
class SessionConfig:
"""会话配置"""
session_id: str
user_id: str
parent_session_id: str | None = None
max_rounds: int = 50
max_tokens: int = 100000
timeout: float = 3600
isolation_mode: IsolationMode = IsolationMode.NONE
capabilities: list[str] = field(default_factory=list)
permissions: list[str] = field(default_factory=list)
class AgentSession:
"""
Agent 会话
管理会话的生命周期和状态
"""
def __init__(
self,
config: SessionConfig,
agent_runtime: AgentRuntime
):
self.config = config
self.agent_runtime = agent_runtime
self.state: SessionState = SessionState.INITIALIZING
self.round_count: int = 0
self.token_count: int = 0
self.messages: list[Message] = []
self.tool_calls: list[ToolCall] = []
self.events: list[SessionEvent] = []
async def initialize(self) -> str:
"""初始化会话"""
# 创建会话记录
await self._create_session_record()
# 初始化隔离环境
if self.config.isolation_mode != IsolationMode.NONE:
await self._initialize_isolation()
self.state = SessionState.ACTIVE
return self.config.session_id
async def process_message(
self,
message: str
) -> Response:
"""处理消息"""
if self.state != SessionState.ACTIVE:
raise SessionStateError(f"Session not active: {self.state}")
# 检查轮次限制
self.round_count += 1
if self.round_count > self.config.max_rounds:
raise SessionLimitError("Max rounds exceeded")
# 处理消息
response = await self.agent_runtime.process(
message,
context=self._get_context()
)
# 记录
self.messages.append(Message(role="user", content=message))
self.messages.append(Message(role="assistant", content=response.content))
# 更新 token 计数
self.token_count += response.usage.total_tokens
return response
async def spawn_child_session(
self,
config: SessionConfig
) -> "AgentSession":
"""创建子会话"""
child_config = SessionConfig(
**{
**asdict(config),
"parent_session_id": self.config.session_id
}
)
child_session = AgentSession(
config=child_config,
agent_runtime=self.agent_runtime
)
await child_session.initialize()
return child_session
async def get_session_summary(self) -> SessionSummary:
"""获取会话摘要"""
return SessionSummary(
session_id=self.config.session_id,
user_id=self.config.user_id,
round_count=self.round_count,
token_count=self.token_count,
message_count=len(self.messages),
tool_call_count=len(self.tool_calls),
duration_seconds=(datetime.now() - self.start_time).total_seconds(),
state=self.state.value
)
async def persist(self):
"""持久化会话"""
await self.session_service.save(SessionRecord(
session_id=self.config.session_id,
user_id=self.config.user_id,
state=self.state.value,
round_count=self.round_count,
token_count=self.token_count,
messages=self.messages,
tool_calls=self.tool_calls,
events=self.events
))
```
---
## 5. 后台任务系统
### 5.1 BackgroundTaskManager
```python
# backend/app/agents/background/manager.py
@dataclass
class BackgroundTask:
"""后台任务"""
task_id: str
description: str
agent_config: dict
schedule: str | None = None # cron expression
status: BackgroundTaskStatus
created_at: datetime
started_at: datetime | None = None
completed_at: datetime | None = None
result: Any = None
error: str | None = None
class BackgroundTaskManager:
"""
后台任务管理器
管理长期运行的 Agent 任务
"""
def __init__(
self,
task_store: TaskStore,
agent_factory: AgentFactory
):
self.task_store = task_store
self.agent_factory = agent_factory
self._running_tasks: dict[str, asyncio.Task] = {}
self._scheduler = AsyncIOScheduler()
async def submit_task(
self,
task: BackgroundTask
) -> str:
"""提交后台任务"""
# 保存任务记录
await self.task_store.save(task)
# 如果有定时计划,调度任务
if task.schedule:
self._scheduler.add_job(
self._execute_task,
CronTrigger.from_crontab(task.schedule),
args=[task.task_id]
)
else:
# 立即执行
asyncio.create_task(self._execute_task(task.task_id))
return task.task_id
async def _execute_task(self, task_id: str):
"""执行任务"""
task = await self.task_store.get(task_id)
if not task:
return
self._running_tasks[task_id] = asyncio.current_task()
task.status = BackgroundTaskStatus.RUNNING
task.started_at = datetime.now()
await self.task_store.save(task)
try:
# 创建 Agent
agent = await self.agent_factory.create(task.agent_config)
# 执行
result = await agent.run()
task.status = BackgroundTaskStatus.COMPLETED
task.result = result
except Exception as e:
task.status = BackgroundTaskStatus.FAILED
task.error = str(e)
finally:
task.completed_at = datetime.now()
await self.task_store.save(task)
if task_id in self._running_tasks:
del self._running_tasks[task_id]
async def cancel_task(self, task_id: str) -> bool:
"""取消任务"""
if task_id in self._running_tasks:
self._running_tasks[task_id].cancel()
del self._running_tasks[task_id]
task = await self.task_store.get(task_id)
if task:
task.status = BackgroundTaskStatus.CANCELLED
await self.task_store.save(task)
return True
async def get_task_status(self, task_id: str) -> BackgroundTask | None:
"""获取任务状态"""
return await self.task_store.get(task_id)
async def list_tasks(
self,
user_id: str,
status: BackgroundTaskStatus | None = None
) -> list[BackgroundTask]:
"""列出任务"""
return await self.task_store.list(user_id, status)
```
---
## 6. 文件结构
```
backend/app/agents/
├── team/ # Team 协作
│ ├── __init__.py
│ ├── leader.py # 团队领导
│ ├── member.py # 团队成员
│ └── task.py # 团队任务
├── transport/ # 传输层
│ ├── __init__.py
│ ├── structured_io.py # 结构化 IO
│ ├── remote.py # 远程传输
│ └── websocket.py # WebSocket 传输
├── session/ # 会话管理
│ ├── __init__.py
│ ├── manager.py # 会话管理器
│ ├── context.py # 会话上下文
│ └── persistence.py # 会话持久化
├── background/ # 后台任务
│ ├── __init__.py
│ ├── manager.py # 后台任务管理器
│ ├── scheduler.py # 任务调度
│ └── executor.py # 任务执行器
└── coordinator.py # 协调整合(现有)
```
---
## 7. 验收标准
| 检查点 | 标准 |
|--------|------|
| Team 创建 | 可以创建和管理 Agent 团队 |
| Team 任务分配 | 任务能正确分配给合适的成员 |
| Team 结果收集 | 能收集和聚合多成员的结果 |
| 结构化 IO | 支持结构化的输入输出格式 |
| 远程传输 | 支持远程 Agent 通信 |
| 会话管理 | 支持复杂的会话层级和状态管理 |
| 后台任务 | 支持定时和异步后台任务 |
| 子会话 | 支持从父会话创建子会话 |
---
## 8. Demo 借鉴
| claw-code | Jarvis 对应 |
|-----------|------------|
| `src/assistant/sessionHistory.ts` | `session/manager.py` |
| `src/cli/structuredIO.ts` | `transport/structured_io.py` |
| `src/cli/remoteIO.ts` | `transport/remote.py` |
| `src/cli/transports/*` | `transport/` |
| Team/* tools | `team/leader.py` |
| Background tasks | `background/manager.py` |

View File

@@ -0,0 +1,165 @@
# Phase 2受控协作阶段Controlled Collaboration
日期2026-04-03
状态:已实现基线,进入增量完善
---
## 1. 阶段目标
让 Jarvis 具备复杂请求下的**结构化协作编排能力**,而不是所有请求都走单一路由或单 agent 顺序执行。
Phase 2 的核心不是“无限协作”,而是:
- 复杂请求可切换到 `collaboration` 模式
- 请求会被拆成有 owner 的子任务
- 子任务结果会被结构化回收
- 最终输出必须经过 verifier 收尾
- Direct Mode 继续保持独立稳定
---
## 2. 当前代码中已落地的能力
当前 Jarvis 后端已经在 `backend/app/agents/graph.py` 中实现了 Phase 2 的最小闭环。
### 2.1 Mode 切换
已具备:
- `master_node()` 根据请求复杂度选择 `direct``collaboration`
- `_select_request_mode()` 会结合角色数量、多步骤信号、显式协作表达进行判断
- 简单请求保持 Direct Mode不被协作逻辑污染
### 2.2 任务拆解
已具备:
- `_build_collaboration_tasks()` 将复杂请求拆成 2~4 个结构化子任务
- 每个 task 带有:
- `task_id`
- `role`
- `owner_agent_id`
- `goal`
- `expected_evidence`
- 父子任务关系
### 2.3 Owner 分配与执行
已具备:
- 每个 task 在构建时已有明确 owner
- `_assign_agent_for_task_role()` 将 task role 映射到具体执行角色
- `_run_collaboration_flow()` 逐个调度子任务执行
### 2.4 结果回收
已具备:
- `_collect_task_result()` 汇总单 task 的 summary / evidence / status
- `_apply_task_result_to_state()` 将结果写回 runtime state
- `task_results` / `active_tasks` / `task_hierarchy` 形成结构化回收链路
### 2.5 verifier 收尾
已具备:
- `_verify_collaboration_results()` 对协作结果做完整性验证
- 若任务缺失 / evidence 缺失 / 有失败任务,则 verifier 会给出 failed verdict
- 最终结果统一写入:
- `verification_status`
- `verification_summary`
- `verification_evidence`
---
## 3. 本次补强Phase / Checkpoint 显式化
本次实现不是从零做 Phase 2而是把已经存在的协作流程**显式建模**。
### 3.1 新增 runtime phase 字段
`backend/app/agents/state.py` 中补充:
- `current_phase`
- `phase_history`
- `current_checkpoint`
- `checkpoint_history`
初始化默认值:
- `current_phase = "phase_0_bootstrap"`
- `current_checkpoint = "bootstrap.initialized"`
### 3.2 新增 Phase 2 关键 checkpoint
`_run_collaboration_flow()` 中,当前已记录:
- `collaboration.tasks_planning`
- `collaboration.tasks_ready`
- `collaboration.task_dispatch`
- `collaboration.task_result_collected`
这使得 Phase 2 不再只是“逻辑存在”,而是已经具备**阶段感知和关键节点回溯能力**。
---
## 4. 当前实现对应关系
| 设计目标 | 当前状态 | 落点 |
|------|------|------|
| Mode 切换 | 已实现 | `master_node`, `_select_request_mode` |
| 任务拆解 | 已实现 | `_build_collaboration_tasks` |
| Owner 分配 | 已实现 | task schema + role assignment |
| 结果回收 | 已实现 | `task_results`, `_collect_task_result` |
| verifier 收尾 | 已实现 | `_verify_collaboration_results` |
| Phase/Checkpoint 显式化 | 已实现 | `state.py`, `graph.py` |
| 独立 Coordinator 类 | 未单独抽出 | 当前仍内嵌在 `graph.py` |
| 独立 MessageBus 实例 | 未单独抽出 | 当前以 `message_trace` 代替 |
---
## 5. 与原方案相比的调整
原文档把 Coordinator、MessageBus、Assigner 设计成独立模块;当前代码并未完全按该物理结构落地,而是采用了**先在 graph 内完成闭环,再逐步抽象模块**的路线。
因此文档需要调整为:
### 已有基线
- 结构化协作逻辑已可运行
- message trace / task trace / verifier trace 已存在
- 不需要为了“Phase 2 完成”而强行拆出新文件
### 后续可选增强
-`_run_collaboration_flow()` 中的分解 / 分配 / 汇总逻辑抽成 `coordinator.py`
- 将 message trace 提升为显式 `message_bus.py`
- 将 task decomposition 策略独立为 `collaboration/decomposer.py`
---
## 6. 验收结论
当前 Phase 2 可以按“已实现基线”验收:
- [x] 复杂请求会进入协作模式
- [x] 子任务具备结构化 task schema
- [x] 每个子任务有明确 owner
- [x] 子任务结果可被统一回收
- [x] verifier 强制参与结果收尾
- [x] Direct Mode 保持独立
- [x] phase / checkpoint 已可记录
- [ ] 仍未拆成独立 coordinator/message bus 模块
---
## 7. 真实边界
当前 Phase 2 **已经不是草案**,但也还不是最终工程形态。
它目前代表的是:
> Jarvis 已经具备受控协作运行能力,并且这条协作链路已经进入“可追踪、可验证、可持续增强”的状态。
仍未完成的,只是工程层面的进一步解耦,而不是能力从 0 到 1。

View File

@@ -0,0 +1,195 @@
# Phase 3动态协作阶段Dynamic Collaboration
日期2026-04-03
状态:已实现受限基线,仍有扩展空间
---
## 1. 阶段目标
在 Phase 2 已具备任务拆解、owner 分配、结果回收、verifier 收尾的基础上,让 Jarvis 获得**受约束的动态协作能力**。
这一阶段的重点不是做成无限 swarm而是做到
- 协作运行时能追踪 parent / root / depth
- 动态创建行为受到权限和预算限制
- 内部消息和事件能留下轨迹
- 长流程可以中断和恢复
- 所有动态行为都可观察、可审计
---
## 2. 当前代码中已落地的能力
当前 Jarvis 已经在 `backend/app/agents/graph.py` 中落地了 Phase 3 的核心基线。
### 2.1 Agent tree tracking
当前 state 已具备:
- `agent_id`
- `parent_agent_id`
- `root_agent_id`
- `collaboration_depth`
- `spawned_agent_ids`
这意味着当前已经可以追踪:
- 谁是根 agent
- 谁创建了谁
- 当前协作深度是多少
- 本轮生成了哪些 child agents
### 2.2 受限动态创建
当前已具备:
- `_create_child_agent()` 负责生成 child agent id
- `_spawn_permission_for_role()` 控制哪些角色可创建子 agent
- 基于 budget / role policy / depth 的限制已存在
- 若不满足条件,会记录 `agent.spawn.blocked`
换句话说,当前已经不是“自由创建”,而是**受治理的动态创建**。
### 2.3 生命周期事件
当前已具备事件类型:
- `agent.created`
- `agent.spawn.blocked`
- `agent.message.sent`
- `agent.message.received`
- `agent.interrupt.requested`
- `agent.interrupt.completed`
- `agent.recovery.started`
- `agent.recovery.completed`
- `agent.task.interrupted`
- `agent.task.recovered`
- `agent.task.reassigned`
- `agent.collaboration.budget.updated`
本次又新增:
- `agent.phase.changed`
- `agent.checkpoint.recorded`
说明当前运行时已经具备较完整的**协作生命周期可见性**。
### 2.4 Interrupt / Recovery
当前已具备:
- `_record_interrupt()`
- `_record_recovery()`
- `interrupted_tasks`
- `recovery_trace`
- `recovery_points`
这使得长流程协作已经具备最小中断 / 恢复链路,而不只是一次性执行。
---
## 3. 本次补强Phase 3 显式阶段与检查点
本次改动把原本分散在运行时内部的动态协作过程,进一步显式化。
### 3.1 Phase 切换
在协作 flow 中,现在会显式进入:
- `phase_2_controlled_collaboration`
- `phase_3_dynamic_collaboration`
- `phase_4_visibility_and_verification`
其中 `phase_3_dynamic_collaboration` 表示:
- 子任务已进入 dispatch / spawn / child execution 阶段
- 运行时已经处于真实的动态协作执行态
### 3.2 Dynamic Collaboration checkpoints
当前已记录的关键 checkpoint 包括:
- `collaboration.task_dispatch`
- `collaboration.task_result_collected`
它们分别表示:
- 某个 task 已被派发给 child agent 执行
- 某个 task 的结果已被回收并进入统一状态
这使得 Phase 3 已经从“逻辑隐含”变成“运行时显式可追踪”。
---
## 4. 当前实现对应关系
| 设计目标 | 当前状态 | 落点 |
|------|------|------|
| Parent/root/depth 追踪 | 已实现 | `state.py` + `graph.py` |
| 受限动态创建 | 已实现 | `_create_child_agent`, `_spawn_permission_for_role` |
| 预算快照 | 已实现 | `budget_state`, `collaboration_budget_history` |
| 生命周期事件 | 已实现 | `event_trace`, `schemas/event.py` |
| 中断 / 恢复记录 | 已实现 | `_record_interrupt`, `_record_recovery` |
| phase / checkpoint 显式化 | 已实现 | `state.py`, `graph.py` |
| 独立 EventBus | 未独立落地 | 当前以 `event_trace` 代替 |
| Worker 主动发起新一轮协作 | 未完全开放 | 当前仍以受控主路径为主 |
---
## 5. 与原方案相比的调整
原方案强调:
- 独立 `event_bus.py`
- 独立 `dynamic/`
- 独立 `recovery/`
而当前代码采取的是:
- 先把动态协作基线做到 `graph.py` 内可运行
- 再通过 event_trace / recovery_trace / budget_state 稳定对外暴露
- 最后再决定是否做物理模块拆分
因此文档应调整为:
### 已完成的能力层
- 动态协作并不是未来规划,而是已存在的运行时能力
- 当前已经具备最小治理、最小动态创建、最小恢复机制
### 尚未完成的工程层
- 还没有真正的发布/订阅式 `EventBus`
- 还没有对外开放 worker 自主再委派能力
- 还没有把 dynamic / recovery 拆成单独模块目录
---
## 6. 验收结论
当前 Phase 3 可以按“已实现受限基线”验收:
- [x] parent / root / depth 可追踪
- [x] child agent 创建行为受预算和权限约束
- [x] spawn blocked 会留下事件证据
- [x] 协作消息与事件链路可重建
- [x] interrupt / recovery 有最小闭环
- [x] phase / checkpoint 已可记录
- [ ] 尚未形成真正的发布订阅 EventBus
- [ ] 尚未开放更自由的 worker→worker 动态协作
---
## 7. 真实边界
当前 Phase 3 的真实状态是:
> Jarvis 已经具备“带约束的动态协作运行时”,但仍不是一个无限扩张、自由重组的 swarm 平台。
这正是当前阶段应该保持的边界:
- 先保证治理
- 再考虑放宽动态能力
- 先保证 trace / verifier / budget 成熟
- 再考虑更强自治

View File

@@ -0,0 +1,285 @@
# Phase 4可视化与隔离执行阶段Visibility + Isolation
日期2026-04-03
状态Day 4-5 最小闭环已完成(后端可见性 API + runtime summary + Agents 页面首屏接入 + 隔离设计)
---
## 1. 阶段目标
把 Jarvis 的多 agent 系统从“内部能跑”升级为“可看、可查、可调试、可规划隔离执行”的系统。
本阶段分两部分:
- **可见性**:把 runtime state 中已经存在的协作数据稳定对外暴露
- **隔离执行设计**:先明确最小可落地方案,不在 Day 4 内强行做完整实现
---
## 2. Day 4 已落地范围
### 2.1 可见性数据源
Day 4 已明确:可见性 API 的单一数据源是 **conversation continuity snapshot 中保存的 runtime state**,而不是数据库推断或日志二次解析。
当前已作为读取基础的数据包括:
- `event_trace`:关键生命周期事件
- `message_trace`thread / message 流向
- `active_tasks`:当前任务清单
- `task_results`:任务执行结果
- `task_hierarchy`:父子任务关系
- `verification_status`
- `verification_summary`
- `verification_evidence`
- `thread_id`
- `agent_id`
- `root_agent_id`
- `spawned_agent_ids`
- `tool_outcomes`
### 2.2 已实现的只读 API
当前已经在 `backend/app/routers/agent.py` 下提供以下只读接口:
- `GET /api/agents/visibility/events`
- 支持 `conversation_id``agent_id``thread_id``event_type`、时间范围、分页过滤
- `GET /api/agents/visibility/topology`
- 返回当前协作拓扑、节点、边、任务摘要、task hierarchy
- `GET /api/agents/visibility/tasks/{task_id}/evidence`
- 返回 task、task result、关联 tool outcomes、verifier 结果
- `GET /api/agents/visibility/threads/{thread_id}/messages`
- 返回指定 thread 的消息历史
- `GET /api/agents/visibility/verifier`
- 返回当前会话 verifier 状态、摘要、证据
- `GET /api/agents/visibility/runtime-summary`
- 返回 execution mode、phase/checkpoint、verifier、isolation、cost、recent events 聚合摘要
### 2.3 已补测试
Day 4 已新增后端 API 测试文件:
- `backend/tests/backend/app/agents/test_visibility_api.py`
覆盖场景包括:
- event filter + pagination
- topology 构建
- task evidence 查询
- thread message 重建
- verifier 查询
- 非法 datetime 参数校验
---
## 3. 当前实现边界
Day 4 **已经完成的是后端可见性最小闭环**,但有意不做以下内容:
- 不做前端调试面板 UI
- 不做 SSE / WebSocket 实时推送
- 不做独立新的 visibility 存储层
- 不做完整 worktree / sandbox 执行实现
- 不做自由蜂群式调度
因此 Day 4 的定位应是:
> **已具备可见性查询 API 与隔离执行设计,不等于已经具备完整实时调试平台或完整隔离执行运行时。**
---
## 4. 隔离执行最小方案
## 4.1 设计目标
对于更复杂、可能污染主工作目录或需要更强安全边界的任务Day 4 先定义一个清晰的隔离分层策略,供后续 Phase 4+ 实现使用。
### 4.2 隔离级别
| 级别 | 名称 | 适用场景 | Day 4 状态 |
|------|------|----------|------------|
| L0 | 无隔离 | 普通问答、轻量检索、只读分析 | 已存在 |
| L1 | Session State 隔离 | 需要隔离上下文/记忆但不改文件 | 设计完成 |
| L2 | Worktree 隔离 | 代码修改、文件写入、需要独立目录 | **推荐主方案** |
| L3 | Sandbox Container | 高风险命令、需要更强 OS 级边界 | 仅保留扩展位 |
### 4.3 技术选型结论
Day 4 的最小技术选型如下:
#### 首选:**Git worktree 隔离**
适用:
- 代码生成
- 批量重构
- 多 agent 并行改文件
- 需要避免污染主工作目录的执行型任务
原因:
- 与当前 git 仓库工作流天然兼容
- 可以复用已有分支 / review / merge 流程
- 隔离成本低于容器
- 更适合作为 Jarvis 当前阶段的最小可落地方案
#### 次选:**Session state 隔离**
适用:
- 多轮复杂分析
- 需要隔离上下文污染
- 只读或低风险任务
原因:
- 实现成本低
- 不依赖文件系统隔离
- 可先于完整 worktree runtime 落地
#### 暂不在 Day 4 实现:**Sandbox container**
适用:
- 高风险 shell 命令
- 潜在破坏性任务
- 需要系统级资源控制
结论:
- Phase 4 不做完整容器运行时
- 仅保留接口和策略位置,后续再做
---
## 5. Session State 隔离策略
Session state 隔离的最小原则:
1. 每个隔离 worker 拥有独立的 memory / turn context
2. 默认不继承完整 message history只注入必要共享上下文
3. 输出只回传:
- task result
- evidence
- verifier-ready summary
4. 不把中间临时推理状态直接合并回主 state
建议最小 state 结构:
- `isolation_mode`: `none | session | worktree | sandbox`
- `isolation_id`
- `isolation_parent_conversation_id`
- `isolation_workspace_path`(如适用)
- `isolation_metadata`
---
## 6. Worktree 隔离策略
Worktree 隔离的最小原则:
1. 每个执行型 worker 对应独立 worktree
2. worktree 使用独立 branch 命名
3. 主工作目录不直接写入
4. 结果通过 diff / changed files / task result 回收
5. 清理策略必须可控,避免遗留脏目录
建议目录模式:
- `.worktrees/jarvis/<conversation-or-run-id>/<worker-id>/`
建议 branch 模式:
- `jarvis/<conversation-or-run-id>/<worker-id>`
最小回收物:
- `modified_files`
- `git_diff_summary`
- `task_result`
- `verification_evidence`
---
## 7. 最小 Isolation Execution API 定义
Day 4 先定义接口边界,不要求马上完整实现。
### 7.1 任务执行请求侧
建议未来 runtime 接口至少支持:
- `isolation_mode`
- `workspace_strategy`
- `allow_merge_back`
- `cleanup_policy`
示意:
```json
{
"task_id": "task-123",
"goal": "refactor agent router",
"isolation_mode": "worktree",
"workspace_strategy": "ephemeral",
"allow_merge_back": false,
"cleanup_policy": "on_success"
}
```
### 7.2 执行结果侧
建议统一返回:
```json
{
"task_id": "task-123",
"status": "completed",
"isolation": {
"mode": "worktree",
"workspace_path": ".worktrees/jarvis/run-1/worker-2",
"branch": "jarvis/run-1/worker-2",
"cleanup_status": "pending"
},
"evidence": [],
"summary": "Refactor completed"
}
```
---
## 8. 验收结论
Day 4 最小闭环完成时,当前应以以下标准为准:
- [x] 可按条件查询 event trace
- [x] 可查询协作拓扑与任务摘要
- [x] 可查询 task 执行证据
- [x] 可重建 thread message 历史
- [x] 可查询 verifier 结果
- [x] 有 Day 4 后端 API 测试覆盖
- [x] 有隔离执行最小设计方案
- [ ] 不包含实时 SSE/UI
- [ ] 不包含完整 worktree/sandbox runtime 实现
---
## 9. 本阶段完成后的真实状态
完成 Day 4 后Jarvis 当前具备:
- 受限多 agent runtime 的可见性查询能力
- 面向调试与验收的后端只读 API
- 基于 continuity snapshot 的稳定可见性数据源
- 面向后续实现的 isolation strategy 设计
当前 **尚未具备**
- 实时事件推送平台
- 前端可视化调试面板
- 完整 worktree 执行编排
- 容器级 sandbox 执行器
这意味着:
> Day 4 已完成最小闭环,但后续仍可继续扩展为完整可视化 UI 和隔离执行 runtime。

View File

@@ -0,0 +1,624 @@
# Phase 5高级特性Advanced Features
日期2026-04-03
状态:规划中
---
## 1. 阶段目标
Phase 5 包含一系列**高级特性**,在完成 Phase 1-4 后根据实际需求选择性实施。
这些特性不直接影响核心功能,但可以显著提升系统的**可用性、安全性和可扩展性**。
---
## 2. 特性清单
### 2.1 Full Sandbox 隔离
**目标**实现完整的Docker级隔离
**Phase 4已做**Worktree隔离
**Phase 5补充**
- 完整的容器生命周期管理
- 资源限制(CPU/内存/网络)
- 文件系统配额
- 安全策略配置
```python
class FullSandbox:
"""完整沙箱隔离"""
async def execute(
self,
task: Task,
config: SandboxConfig
) -> TaskResult:
"""
在完整沙箱中执行
特点:
- 完全隔离的网络
- 资源限制
- 持久化存储(可选)
- 安全策略
"""
# 1. 创建容器
container = await self.client.containers.run(
image=config.image,
detach=True,
mem_limit=config.memory_limit,
cpu_period=config.cpu_period,
network_mode="isolated", # 完全隔离网络
volumes=config.volumes,
)
try:
# 2. 执行任务
result = await self._execute_in_container(container, task)
return result
finally:
# 3. 清理
await container.remove(force=True)
```
### 2.2 Persistence 层
**目标**将Event/Message持久化到数据库
**Phase 1-3已做**内存trace
**Phase 5补充**
- Event持久化存储
- Message持久化存储
- 支持历史查询
- 数据导出/归档
```python
class EventPersistence:
"""事件持久化"""
async def save_event(self, event: Event):
"""保存事件到数据库"""
...
async def query_events(
self,
conversation_id: str,
event_types: list[str] | None = None,
start_time: datetime | None = None,
end_time: datetime | None = None,
limit: int = 100
) -> list[Event]:
"""查询历史事件"""
...
class MessagePersistence:
"""消息持久化"""
async def save_message(self, message: AgentMessage):
"""保存消息到数据库"""
...
async def get_thread_history(
self,
thread_id: str,
limit: int = 100
) -> list[AgentMessage]:
"""获取线程历史"""
...
```
### 2.3 Multi-turn Memory
**目标**:支持跨会话的长期记忆
**当前**每个会话独立memory
**Phase 5补充**
- 重要信息提取
- 跨会话上下文复用
- 知识更新机制
- 遗忘策略
```python
class MultiTurnMemory:
"""跨会话记忆"""
def extract_important_info(
self,
conversation_summary: str,
user_profile: UserProfile
) -> list[MemoryEntry]:
"""从对话中提取重要信息"""
...
async def get_relevant_context(
self,
current_request: str,
user_id: str
) -> list[MemoryEntry]:
"""获取与当前请求相关的记忆"""
...
def update_memory(
self,
user_id: str,
new_info: MemoryEntry
):
"""更新记忆"""
...
def decay_old_memories(self, user_id: str):
"""遗忘旧记忆"""
...
```
### 2.4 Cost Monitoring
**目标**实时监控Token成本
**Phase 3已有**Budget模型
**Phase 5补充**
- 实时Token计数
- 成本估算
- 告警机制
- 使用报告
```python
class CostMonitor:
"""成本监控"""
async def track_usage(
self,
conversation_id: str,
model: str,
input_tokens: int,
output_tokens: int
):
"""跟踪使用量"""
...
async def estimate_cost(
self,
conversation_id: str
) -> CostEstimate:
"""估算当前会话成本"""
...
async def check_budget(
self,
user_id: str,
expected_tokens: int
) -> bool:
"""检查预算是否足够"""
...
async def send_alert(
self,
user_id: str,
threshold: float
):
"""发送告警"""
...
# 使用示例
@dataclass
class CostEstimate:
total_tokens: int
estimated_cost: float
breakdown: dict[str, int] # per-model
threshold_percent: float # 相对于用户限额
```
### 2.5 Advanced UI
**目标**:完整的前端协作面板
**Phase 4已有**API
**Phase 5补充**
- 实时协作拓扑图
- Agent对话界面
- 任务看板
- 成本仪表盘
```
┌────────────────────────────────────────────────────────────────────┐
│ Jarvis 协作面板 │
├────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────────────────────────────────────┐ │
│ │ 拓扑图 │ │ 当前会话 │ │
│ │ │ │ │ │
│ │ [Master] │ │ User: 帮我分析这个项目... │ │
│ │ │ │ │ │ │
│ │ [Coord] │ │ [Coordinator]: 拆分为3个子任务 │ │
│ │ ┌─┴─┐ │ │ - Task 1: 检索相关知识 │ │
│ │ │ │ │ │ - Task 2: 执行分析 │ │
│ │ [W1] [W2] │ │ - Task 3: 汇总报告 │ │
│ │ │ │ │ │
│ │ 点击查看详情 │ │ [Worker-1]: 正在检索... │ │
│ └─────────────┘ └─────────────────────────────────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────────────────────────────────────┐ │
│ │ 任务列表 │ │ 成本监控 │ │
│ │ │ │ │ │
│ │ ☑ Task 1 │ │ Token: 12,345 / 50,000 │ │
│ │ ◐ Task 2 │ │ 成本: $0.23 / $5.00 │ │
│ │ ○ Task 3 │ │ [████████████████░░░░░] 24% │ │
│ └─────────────┘ └─────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────┘
```
### 2.6 Plugin System
**目标**:支持第三方插件扩展
**设计参考**Claude Code CLI的插件系统
```python
class PluginSystem:
"""插件系统"""
async def load_plugin(self, plugin_path: str) -> Plugin:
"""加载插件"""
...
async def execute_plugin(
self,
plugin_id: str,
context: dict
) -> Any:
"""执行插件"""
...
@dataclass
class Plugin:
"""插件定义"""
plugin_id: str
name: str
version: str
capabilities: list[str] # 提供的工具/能力
hooks: list[str] # 生命周期钩子
async def execute(self, context: dict) -> Any:
...
@dataclass
class PluginManifest:
"""插件清单"""
tools: list[ToolManifest]
commands: list[CommandManifest]
hooks: list[str]
```
### 2.7 TagMemo — 仿生记忆系统
**目标**:实现基于遗忘曲线的智能记忆系统
**设计参考**VCPToolBox的TagMemo V6/V7 RAG系统
**核心概念**
1. **LIF神经元模型** — 脉冲传播机制
- 记忆不是静态存储,而是动态激活
- 重要记忆获得更高的激活频率
2. **Core Tags vs Normal Tags** — 核心记忆
- Core Tags获得1.2-1.4x权重加成
- 核心记忆有虚拟召回能力
3. **遗忘曲线** — 不是无限存储
- 模拟生物遗忘,不是简单删除
- 基于重要性动态计算衰减率
```python
class MemoryImportance(str, Enum):
"""记忆重要性等级"""
CORE = "core" # 核心记忆1.2-1.4x权重
HIGH = "high" # 高重要性
MEDIUM = "medium" # 中等重要性
LOW = "low" # 低重要性,会自然遗忘
@dataclass
class TagMemoEntry:
"""TagMemo记忆条目"""
entry_id: str
content: str
# VCPToolBox借鉴
importance: MemoryImportance = MemoryImportance.MEDIUM
decay_rate: float = 0.1 # 遗忘率
last_activated: datetime = field(default_factory=datetime.now)
activation_count: int = 0 # 激活次数
# EPA模块可选
logic_depth: int = 0 # 逻辑深度
resonance_score: float = 0.0 # 共振分数
class TagMemoMemory:
"""
仿生记忆系统
特点:
- 遗忘曲线模拟
- 重要性权重
- 动态激活
"""
async def add_memory(
self,
content: str,
importance: MemoryImportance = MemoryImportance.MEDIUM,
tags: list[str] | None = None
) -> TagMemoEntry:
"""添加记忆"""
entry = TagMemoEntry(
entry_id=generate_id(),
content=content,
importance=importance,
decay_rate=self._calculate_decay_rate(importance),
tags=tags or []
)
await self._storage.save(entry)
return entry
def should_retain(self, entry: TagMemoEntry, days_elapsed: int) -> bool:
"""
判断记忆是否应该保留
基于动态Beta公式
β = σ(L·log(1+R) - S·noise_penalty)
"""
if entry.importance == MemoryImportance.CORE:
return True # 核心记忆永远保留
# 遗忘概率 = 基础衰减 × 时间 × 噪声惩罚
retention_prob = math.exp(
-entry.decay_rate * days_elapsed * self._noise_factor
)
return random.random() < retention_prob
async def get_relevant_memories(
self,
query: str,
limit: int = 5
) -> list[TagMemoEntry]:
"""获取相关记忆(带权重)"""
candidates = await self._vector_search(query, limit=limit * 2)
# 按重要性权重排序
weighted = []
for entry in candidates:
weight = self._calculate_weight(entry)
weighted.append((entry, weight))
weighted.sort(key=lambda x: x[1], reverse=True)
return [e for e, _ in weighted[:limit]]
def _calculate_weight(self, entry: TagMemoEntry) -> float:
"""计算记忆权重"""
base = 1.0
# 重要性权重
if entry.importance == MemoryImportance.CORE:
base *= 1.3
elif entry.importance == MemoryImportance.HIGH:
base *= 1.1
# 激活频率奖励
base *= (1 + math.log(1 + entry.activation_count))
# 时间衰减
days = (datetime.now() - entry.last_activated).days
base *= math.exp(-0.01 * days)
return base
```
### 2.8 AgentDream — 仿生梦境系统
**目标**AI在"睡眠"时自动整理和巩固记忆
**设计参考**VCPToolBox的AgentDream bijective morphic system
**三层时间记忆涟漪**
| 时间层 | 范围 | 特点 |
|--------|------|------|
| 短期记忆 | 0-7天 | 高频共振,快速激活 |
| 中期记忆 | 7-90天 | 弱共振,需要触发 |
| 长期记忆 | >90天 | 遗忘边界,需要特殊唤醒 |
```python
class DreamLayer(str, Enum):
"""梦境记忆层"""
SHORT_TERM = "short_term" # 0-7天
MID_TERM = "mid_term" # 7-90天
LONG_TERM = "long_term" # >90天
@dataclass
class DreamMemory:
"""梦境记忆结构"""
layer: DreamLayer
resonance_bridges: list[str] = field(default_factory=list) # 共振桥接
consolidation_level: float = 0.0 # 巩固程度 0-1
class AgentDreamEngine:
"""
仿生梦境引擎
功能:
- 定时触发记忆整理
- 跨层共振发现
- 遗忘边界管理
"""
async def dream(self, user_id: str) -> DreamReport:
"""
执行梦境整理
流程:
1. 获取近期记忆0-7天
2. 与中期记忆建立共振桥
3. 评估哪些记忆应该升级/遗忘
4. 生成梦境叙事报告
"""
short_term = await self._get_memories(
user_id,
layer=DreamLayer.SHORT_TERM
)
mid_term = await self._get_memories(
user_id,
layer=DreamLayer.MID_TERM
)
# 发现共振
bridges = await self._discover_resonance_bridges(
short_term, mid_term
)
# 评估记忆
to_promote = [] # 升级到中期
to_forget = [] # 标记遗忘
to_consolidate = [] # 巩固
for memory in short_term:
if memory.activation_count > 10:
to_promote.append(memory)
elif memory.activation_count < 2:
to_forget.append(memory)
else:
to_consolidate.append(memory)
# 执行整理
await self._promote_memories(to_promote)
await self._apply_forgetting(to_forget)
await self._consolidate(to_consolidate, bridges)
# 生成梦境报告
return await self._generate_dream_narrative(
promoted=to_promote,
forgotten=to_forget,
bridges=bridges
)
async def _discover_resonance_bridges(
self,
short_term: list[TagMemoEntry],
mid_term: list[TagMemoEntry]
) -> list[tuple[TagMemoEntry, TagMemoEntry, float]]:
"""发现跨层共振桥接"""
bridges = []
for s in short_term:
for m in mid_term:
similarity = await self._calculate_resonance(s, m)
if similarity > 0.7:
bridges.append((s, m, similarity))
return bridges
async def _generate_dream_narrative(
self,
promoted: list,
forgotten: list,
bridges: list
) -> DreamReport:
"""生成第一人称梦境叙事"""
narrative = f"""
【梦境记录】
今夜整理了{len(promoted)}段重要记忆,它们已经被巩固:
{', '.join(m.content for m in promoted[:3])}
发现了{len(bridges)}条记忆共振:
{self._describe_bridges(bridges)}
{len(forgotten)}段记忆正在消散...
"""
return DreamReport(
narrative=narrative,
promoted_count=len(promoted),
forgotten_count=len(forgotten),
bridges_count=len(bridges)
)
```
**触发机制**
```python
# 定时任务每天凌晨3点执行梦境整理
@scheduler.scheduled(cron="0 3 * * *")
async def scheduled_agent_dream():
"""AI睡眠时整理记忆"""
for user_id in await get_active_users():
try:
report = await agent_dream_engine.dream(user_id)
logger.info(f"Dream complete for {user_id}: {report.summary}")
except Exception as e:
logger.error(f"Dream failed for {user_id}: {e}")
```
---
## 3. 实施优先级
| 特性 | 优先级 | 依赖 | 建议实施时机 |
|------|--------|------|--------------|
| Cost Monitoring | 🔴 高 | Phase 3 | 正式上线前 |
| TagMemo | 🟡 中 | Phase 2 | 用户反馈需要更好记忆时 |
| AgentDream | 🟢 低 | Phase 5+TagMemo | 凌晨调度资源时 |
| Persistence | 🟡 中 | Phase 1-3 | 有审计需求时 |
| Multi-turn Memory | 🟡 中 | Phase 1-2 | 用户反馈需要时 |
| Advanced UI | 🟡 中 | Phase 4 | 有前端资源时 |
| Full Sandbox | 🟢 低 | Phase 4 | 有安全需求时 |
| Plugin System | 🟢 低 | Phase 1 | 有社区需求时 |
---
## 4. 风险点
| 风险 | 缓解措施 |
|------|----------|
| 功能蔓延 | 严格控制每个特性的scope |
| 性能影响 | Persistence要考虑异步和索引优化 |
| 成本增加 | Full Sandbox资源限制要明确 |
---
## 5. 验收标准
| 特性 | 验收标准 |
|------|----------|
| Cost Monitoring | 能实时显示Token使用量和估算成本 |
| Persistence | 事件和消息可持久化存储和查询 |
| Multi-turn Memory | 跨会话可复用关键信息 |
| Advanced UI | 有可用的前端协作面板 |
| Full Sandbox | 容器隔离完整,资源限制生效 |
| Plugin System | 插件可加载和执行 |
---
## 6. 本阶段完成后预期结果
完成后Jarvis 将具备:
- ✅ 完整的成本监控能力
- ✅ 仿生记忆系统TagMemo
- ✅ AI梦境整理AgentDream
- ✅ 历史数据持久化
- ✅ 跨会话的智能记忆
- ✅ 完整的协作可视化UI
- ✅ 高级隔离执行环境
- ✅ 可扩展的插件系统
**Phase 5 为可选特性,根据实际需求选择性实施。**

View File

@@ -0,0 +1,339 @@
# Jarvis Agent Phase 6-10 Checklist
> 开发时对照勾选,完成后打 [x]
---
## Phase 6工具系统重构
**工作量**6-10 周
### 6.1 基础设施
- [x] 创建 `backend/app/agents/tools/registry.py` — ToolRegistry 类
- [x] 创建 `backend/app/agents/tools/manifest.py` — ToolManifest 数据类
- [x] 创建 `backend/app/agents/tools/base.py` — BaseTool 基础类
- [x] 迁移 task.py 到注册表
- [x] 迁移 schedule.py 到注册表
- [x] 迁移 search.py 到注册表
- [x] 迁移 forum.py 到注册表
- [x] 迁移 time_reasoning.py 到注册表
- [x] 实现向后兼容层
### 6.2 Hook 系统
- [x] 创建 `backend/app/agents/tools/hooks/types.py` — HookType, HookAction, HookContext, HookResult
- [x] 创建 `backend/app/agents/tools/hooks/manager.py` — HookManager 类
- [x] 创建 `backend/app/agents/tools/hook_executor.py` — HookExecutor 类
### 6.3 流式执行
- [x] 创建 `backend/app/agents/tools/streaming_executor.py` — StreamingToolExecutor
- [x] 为流式工具添加 `is_streaming` 标志
- [x] 更新 AgentService 集成流式执行
### 6.4 新工具集
- [x] 创建 `backend/app/agents/tools/builtins/file_tools.py` — GlobTool
- [x] 创建 `backend/app/agents/tools/builtins/file_tools.py` — GrepTool
- [x] 创建 `backend/app/agents/tools/builtins/file_tools.py` — EditTool
- [x] 创建 `backend/app/agents/tools/builtins/file_tools.py` — LintTool
- [x] 创建 `backend/app/agents/tools/builtins/system_tools.py` — BashTool
- [x] 创建 `backend/app/agents/tools/builtins/system_tools.py` — PowerShellTool
- [x] 创建 `backend/app/agents/tools/builtins/system_tools.py` — ScheduleCronTool
- [x] 创建 `backend/app/agents/tools/builtins/dev_tools.py` — LSPTool
- [x] 创建 `backend/app/agents/tools/builtins/dev_tools.py` — GitTool
- [x] 创建 `backend/app/agents/tools/builtins/collaboration_tools.py` — TeamAgentTool
- [x] 创建 `backend/app/agents/tools/builtins/collaboration_tools.py` — TaskBroadcastTool
### 6.5 测试
- [x] 单元测试: registry
- [x] 单元测试: hook
- [x] 单元测试: streaming executor
- [x] 集成测试: 工具注册到执行完整流程
- [x] 回归测试: Sub-Commander 不受影响
- [ ] 性能测试: 工具调用延迟 < 100ms
### Phase 6 验收
- [x] 所有现有工具已在 Registry 中注册
- [x] PreTool/PostTool Hook 能正确拦截
- [x] 流式工具可流式返回
- [x] 向后兼容: 现有 Sub-Commander 工具调用不受影响
---
## Phase 7Hook 拦截层
**前置依赖**Phase 6.2
**工作量**3-4 周
### 7.1 Hook 类型定义
- [x] 验证 `hooks/types.py` — HookType, HookAction, HookContext, HookResult
### 7.2 内置 Hook
- [x] 创建 `backend/app/agents/tools/hooks/builtins/audit_log.py` — AuditLogHook
- [x] 创建 `backend/app/agents/tools/hooks/builtins/dangerous_confirmation.py` — DangerousConfirmationHook
- [x] 创建 `backend/app/agents/tools/hooks/builtins/security_scan.py` — SecurityScanHook
### 7.3 Hook 配置
- [x] HookManager.load_config()
- [x] HookManager.execute_pre_hooks()
- [x] HookManager.execute_post_hooks()
- [x] HookManager.execute_error_hook()
- [x] 配置持久化
### 7.4 自定义 Hook
- [ ] 创建 `hooks/custom/loader.py` — 自定义 Hook 加载器
### 7.5 API
- [ ] POST `/api/hooks/config` — 更新 Hook 配置
- [ ] GET `/api/hooks/config` — 获取 Hook 配置
- [ ] GET `/api/hooks/available` — 列出可用 Hook
### 7.6 测试
- [ ] 测试: 危险操作被 Pre Hook 拦截
- [ ] 测试: 敏感信息被 Post Hook 脱敏
- [ ] 测试: 异常被 Error Hook 记录
- [ ] 回归测试: 不配置 Hook 时系统正常运行
### Phase 7 验收
- [x] 危险操作被 Pre Hook 正确拦截
- [x] 结果中的敏感信息被 Post Hook 正确脱敏
- [x] Hook 配置可保存和加载
- [ ] Hook 执行开销 < 10ms
---
## Phase 8插件生态
**前置依赖**Phase 6, Phase 7
**工作量**4-5 周
### 8.1 插件结构
- [x] 创建 `backend/app/agents/plugins/manifest.py` — PluginManifest
- [ ] 定义 `plugins/manifest.json` schema
- [ ] 验证插件清单格式
### 8.2 PluginManager
- [x] 创建 `backend/app/agents/plugins/manager.py` — PluginManager
- [x] 实现 PluginManager.install()
- [x] 实现 PluginManager.uninstall()
- [x] 实现 PluginManager.enable()
- [x] 实现 PluginManager.disable()
- [x] 实现 PluginManager.reload()
- [x] 实现 PluginManager.list_plugins()
### 8.3 插件隔离
- [x] 创建 `backend/app/agents/plugins/sandbox.py` — PluginSandbox
- [x] 实现模块加载隔离
- [x] 实现文件系统权限控制
- [x] 实现网络权限控制
### 8.4 插件市场
- [ ] 创建 `backend/app/services/plugin_marketplace.py` — PluginMarketplace
- [ ] 实现 search()
- [ ] 实现 get_plugin()
- [ ] 实现 download_plugin()
### 8.5 内置插件
- [ ] 创建 `plugins/builtins/code_helper/` — lint, format, explain_code
- [ ] 创建 `plugins/builtins/git_helper/` — git_status, git_log, git_diff
- [ ] 创建 `plugins/builtins/web_helper/` — fetch_url, parse_html
- [ ] 创建 `plugins/builtins/file_organizer/` — organize_files, cleanup_duplicates
### 8.6 API
- [ ] GET `/api/plugins` — 列出插件
- [ ] POST `/api/plugins/install` — 安装插件
- [ ] POST `/api/plugins/{id}/enable` — 启用插件
- [ ] POST `/api/plugins/{id}/disable` — 禁用插件
- [ ] DELETE `/api/plugins/{id}` — 卸载插件
- [ ] GET `/api/marketplace/plugins` — 搜索市场
### 8.7 测试
- [ ] 测试: 插件安装/启用/禁用/卸载
- [ ] 测试: 插件隔离有效
- [ ] 测试: 市场搜索和下载
### Phase 8 验收
- [x] 可以从目录安装插件
- [x] 插件的工具和 Hook 正确注册
- [x] 插件的工具和 Hook 正确注销
- [x] 插件无法访问未授权资源
- [ ] 插件加载时间 < 1s
---
## Phase 9Skills 注册表
**前置依赖**Phase 6
**工作量**3-4 周
### 9.1 SkillRegistry 增强
- [x] 增强 `backend/app/agents/skills/registry.py` — SkillRegistry
- [x] 创建 `backend/app/agents/skills/metadata.py` — SkillMetadata
- [x] 实现 load_all()
- [x] 实现 get_skill()
- [x] 实现 search()
- [x] 实现 get_skill_context()
### 9.2 Skills 加载器
- [x] 创建 `backend/app/agents/skills/loaders/local_loader.py`
- [x] 创建 `backend/app/agents/skills/loaders/plugin_loader.py`
- [ ] 创建 `backend/app/agents/skills/loaders/mcp_loader.py`
### 9.3 MCP Skill Builder
- [x] 创建 `backend/app/agents/skills/mcp_builder.py` — MCPSkillBuilder
- [x] 实现 discover_skills_from_mcp()
- [x] 实现 _tool_to_skill()
- [x] 实现 _group_to_skill()
### 9.4 内置 Skills
- [ ] 创建 `backend/app/agents/skills/bundled.py` — BUNDLED_SKILLS
- [ ] 实现 code-analysis skill
- [ ] 实现 git-helper skill
- [ ] 实现 web-research skill
- [ ] 实现 file-management skill
- [ ] 实现 task-planning skill
### 9.5 Agent 集成
- [ ] AgentService.build_skill_context()
- [ ] Skill 上下文注入 Agent prompt
- [ ] Skill 触发检测
### 9.6 API
- [ ] GET `/api/skills` — 列出 Skills
- [ ] GET `/api/skills/search` — 搜索 Skills
- [ ] GET `/api/skills/{name}` — 获取 Skill 详情
### 9.7 测试
- [ ] 测试: 本地 Skills 加载
- [ ] 测试: MCP Skills 发现
- [ ] 测试: Skill 上下文注入
### Phase 9 验收
- [x] 能加载 local_skills_dir 下的所有 SKILL.md
- [x] 能从 MCP 服务器发现和加载 Skills
- [ ] 内置 Skills 默认加载
- [ ] Skill 内容正确注入 Agent prompt
---
## Phase 10高级编排
**前置依赖**Phase 6-9
**工作量**5-6 周
### 10.1 Team 多 Agent 协作
- [x] 创建 `backend/app/agents/team/leader.py` — TeamLeader
- [ ] 创建 `backend/app/agents/team/member.py` — TeamMember
- [ ] 创建 `backend/app/agents/team/task.py` — TeamTask
- [x] 实现 create_team()
- [x] 实现 assign_task()
- [x] 实现 broadcast_task()
- [x] 实现 collect_results()
- [x] 实现 get_team_status()
### 10.2 远程传输层
- [ ] 创建 `backend/app/agents/transport/structured_io.py` — StructuredIO
- [x] 创建 `backend/app/agents/transport/remote.py` — RemoteTransport
- [x] 实现 send_response()
- [x] 实现 send_event()
- [x] 实现 send_tool_call()
- [ ] 实现 WebSocket 连接管理
### 10.3 高级会话管理
- [ ] 创建 `backend/app/agents/session/manager.py` — AgentSession
- [ ] 创建 `backend/app/agents/session/context.py` — SessionContext
- [ ] 创建 `backend/app/agents/session/persistence.py` — SessionPersistence
- [ ] 实现 initialize()
- [ ] 实现 process_message()
- [ ] 实现 spawn_child_session()
- [ ] 实现 get_session_summary()
- [ ] 实现 persist()
### 10.4 后台任务系统
- [x] 创建 `backend/app/agents/background/manager.py` — BackgroundTaskManager
- [ ] 创建 `backend/app/agents/background/scheduler.py`
- [ ] 创建 `backend/app/agents/background/executor.py`
- [x] 实现 submit_task()
- [x] 实现 cancel_task()
- [x] 实现 get_task_status()
- [x] 实现 list_tasks()
### 10.5 协调整合
- [ ] 创建/修改 `backend/app/agents/coordinator.py`
- [ ] Team 协作与现有 graph 集成
- [ ] 远程传输与现有 service 集成
### 10.6 测试
- [ ] 测试: Team 创建/分配/收集/状态
- [ ] 测试: 会话层级/持久化/子会话
- [ ] 测试: 后台任务提交/调度/取消
### Phase 10 验收
- [x] 可以创建和管理 Agent 团队
- [x] 任务能正确分配给合适的成员
- [x] 能收集和聚合多成员的结果
- [ ] 支持结构化的输入输出格式
- [x] 支持远程 Agent 通信
- [ ] 支持复杂的会话层级和状态管理
- [x] 支持定时和异步后台任务
- [ ] 支持从父会话创建子会话
---
## 总验收
### 向后兼容
- [x] 现有 Sub-Commander 不受影响
- [x] 现有 API 不受影响
- [ ] 现有数据库 schema 不需修改
### 性能
- [ ] 工具调用延迟 < 100ms
- [ ] Hook 执行开销 < 10ms
- [ ] 插件加载时间 < 1s
### 安全
- [x] 插件隔离有效
- [x] Hook 可以拒绝危险操作
- [x] 敏感信息正确脱敏
---
*最后更新2026-04-04*

View File

@@ -0,0 +1,322 @@
# Phase 6工具系统重构Tool System Refactoring
日期2026-04-04
状态:待开始
Demo参考claw-code-main — tools/, StreamingToolExecutor, toolOrchestration
---
## 1. 阶段目标
建立**分层工具执行架构**从现有的扁平工具调用进化为具有注册表、编排层、Hook 拦截能力的工具系统。
**与现有系统的区别**
| 现有 | 目标 |
|------|------|
| Sub-Commander 直接调用 Tools | ToolRegistry → ToolOrchestration → HookExecutor → ToolExecutor → Tools |
| 工具集固定 | 工具注册表支持动态注册 |
| 无流式工具执行 | StreamingToolExecutor 支持流式 |
| 无工具拦截 | PreTool/PostTool Hook 机制 |
---
## 2. 架构设计
### 2.1 新的工具执行架构
```
┌─────────────────────────────────────────────────────────────────┐
│ Agent / Sub-Commander │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ ToolOrchestration Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ ToolRegistry│ │HookExecutor │ │ StreamingToolExecutor │ │
│ │ (注册表) │ │ (拦截层) │ │ (流式执行器) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ ToolExecutor │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Built-in │ │ External │ │ MCP │ │ Plugin │ │
│ │ Tools │ │ Tools │ │ Tools │ │ Tools │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 2.2 核心组件
#### ToolRegistry工具注册表
```python
# backend/app/agents/tools/registry.py
@dataclass
class ToolManifest:
"""工具元数据"""
name: str
description: str
category: ToolCategory
parameters: dict # JSON Schema
return_schema: dict
permission_class: PermissionClass # READ/WRITE/EXTERNAL
side_effect_scope: SideEffectScope # NONE/LOCAL_STATE/DB_WRITE/NETWORK
requires_confirmation: bool
is_streaming: bool = False
tags: list[str] = field(default_factory=list)
class ToolRegistry:
"""工具注册表"""
def __init__(self):
self._tools: dict[str, ToolManifest] = {}
self._executors: dict[str, Callable] = {}
self._hooks: dict[str, list[HookConfig]] = defaultdict(list)
def register(
self,
manifest: ToolManifest,
executor: Callable,
hooks: list[HookConfig] | None = None
):
"""注册工具"""
self._tools[manifest.name] = manifest
self._executors[manifest.name] = executor
if hooks:
for hook in hooks:
self._hooks[manifest.name].append(hook)
def get(self, name: str) -> ToolManifest | None:
"""获取工具元数据"""
return self._tools.get(name)
def get_executor(self, name: str) -> Callable | None:
"""获取工具执行器"""
return self._executors.get(name)
def list_by_category(self, category: ToolCategory) -> list[ToolManifest]:
"""按类别列出工具"""
return [t for t in self._tools.values() if t.category == category]
def list_all(self) -> list[ToolManifest]:
"""列出所有工具"""
return list(self._tools.values())
```
#### HookExecutor拦截执行器
```python
# backend/app/agents/tools/hook_executor.py
@dataclass
class HookConfig:
"""Hook 配置"""
name: str
hook_type: HookType # PRE_TOOL_USE / POST_TOOL_USE / TOOL_ERROR / TOOL_SKIP
handler: Callable
filter_names: list[str] | None = None # 只对特定工具生效None 表示全部
class HookExecutor:
"""工具 Hook 执行器"""
async def pre_execute(self, tool_name: str, arguments: dict) -> HookResult:
"""PreToolUse - 工具执行前拦截"""
hooks = self._get_hooks(tool_name, HookType.PRE_TOOL_USE)
ctx = HookContext(
tool_name=tool_name,
arguments=arguments,
phase=HookPhase.PRE
)
for hook in hooks:
result = await hook.handler(ctx)
if result.action == HookAction.DENY:
return HookResult(
action=HookAction.DENY,
message=result.message,
modified_args=None
)
elif result.action == HookAction.MODIFY:
ctx.arguments = result.modified_args
return HookResult(action=HookAction.ALLOW, modified_args=ctx.arguments)
async def post_execute(
self,
tool_name: str,
arguments: dict,
result: Any
) -> HookResult:
"""PostToolUse - 工具执行后拦截"""
hooks = self._get_hooks(tool_name, HookType.POST_TOOL_USE)
ctx = HookContext(
tool_name=tool_name,
arguments=arguments,
result=result,
phase=HookPhase.POST
)
for hook in hooks:
result = await hook.handler(ctx)
if result.action == HookAction.MODIFY_RESULT:
ctx.result = result.modified_result
return HookResult(action=HookAction.CONTINUE, modified_result=ctx.result)
```
#### StreamingToolExecutor流式工具执行器
```python
# backend/app/agents/tools/streaming_executor.py
class StreamingToolExecutor:
"""流式工具执行器"""
async def execute_streaming(
self,
tool_name: str,
arguments: dict,
callback: Callable[[dict], Awaitable[None]]
) -> Any:
"""执行流式工具"""
manifest = self.registry.get(tool_name)
if not manifest.is_streaming:
raise ValueError(f"Tool {tool_name} does not support streaming")
executor = self.registry.get_executor(tool_name)
# 使用 aiterator 进行流式执行
async for chunk in executor(**arguments):
event = {"type": "chunk", "data": chunk}
await callback(event)
yield chunk
final_event = {"type": "done", "tool": tool_name}
await callback(final_event)
```
---
## 3. 新增工具集
### 3.1 文件操作工具
| 工具 | 描述 | 权限级别 |
|------|------|---------|
| GlobTool | 按模式匹配文件 | READ |
| GrepTool | 在文件中搜索内容 | READ |
| LintTool | 代码检查 | READ |
| EditTool | 编辑文件 | WRITE |
### 3.2 开发工具
| 工具 | 描述 | 权限级别 |
|------|------|---------|
| LSPTool | Language Server Protocol | READ |
| BashTool | 执行 Bash 命令 | EXTERNAL |
| PowerShellTool | 执行 PowerShell | EXTERNAL |
### 3.3 系统工具
| 工具 | 描述 | 权限级别 |
|------|------|---------|
| ScheduleCronTool | Cron 定时任务 | WRITE |
| ProcessTool | 进程管理 | EXTERNAL |
### 3.4 协作工具
| 工具 | 描述 | 权限级别 |
|------|------|---------|
| TeamAgentTool | 团队 Agent 协作 | EXTERNAL |
| TaskBroadcastTool | 任务广播 | WRITE |
### 3.5 高级工具
| 工具 | 描述 | 权限级别 |
|------|------|---------|
| RemoteTriggerTool | 远程触发 | EXTERNAL |
| MCPClientTool | MCP 客户端 | EXTERNAL |
| AskUserQuestionTool | 向用户提问 | READ |
---
## 4. 文件结构变化
```
backend/app/agents/tools/
├── __init__.py # 现有 - 需重构
├── registry.py # 新增 - 工具注册表
├── manifest.py # 新增 - 工具元数据
├── hook_executor.py # 新增 - Hook 执行器
├── hook_config.py # 新增 - Hook 配置
├── streaming_executor.py # 新增 - 流式执行器
├── base.py # 新增 - 基础工具类
├── builtins/ # 新增 - 内置工具
│ ├── __init__.py
│ ├── file_tools.py # Glob, Grep, Edit, Lint
│ ├── system_tools.py # Bash, PowerShell, Cron
│ ├── dev_tools.py # LSP, Git
│ └── collaboration_tools.py # Team, Broadcast
├── task.py # 现有 - 保留
├── schedule.py # 现有 - 保留
├── search.py # 现有 - 保留
├── forum.py # 现有 - 保留
└── time_reasoning.py # 现有 - 保留
```
---
## 5. 迁移计划
### 阶段 6.1基础设施1-2周
- 创建 ToolRegistry 类
- 创建 ToolManifest 数据类
- 迁移现有工具到注册表
### 阶段 6.2Hook 系统2-3周
- 创建 HookExecutor
- 实现 PreTool/PostTool 机制
- 添加内置 Hook确认危险操作、日志记录
### 阶段 6.3流式执行1-2周
- 实现 StreamingToolExecutor
- 为支持流式的工具添加 streaming 标志
- 更新 AgentService 集成
### 阶段 6.4新工具集2-3周
- 实现新增的工具类
- 添加完整的工具文档
- 单元测试覆盖
---
## 6. 验收标准
| 检查点 | 标准 |
|--------|------|
| 工具注册 | 所有现有工具已在 Registry 中注册 |
| Hook 执行 | PreTool/PostTool Hook 能正确拦截 |
| 流式执行 | 标记为 streaming 的工具可流式返回 |
| 新工具 | 新增工具集中每个工具可正常执行 |
| 向后兼容 | 现有 Sub-Commander 工具调用不受影响 |
---
## 7. Demo 借鉴
| claw-code 实现 | Jarvis 对应 |
|----------------|------------|
| `src/tools/` | `backend/app/agents/tools/` |
| `StreamingToolExecutor.ts` | `streaming_executor.py` |
| `toolExecution.ts` | `hook_executor.py` |
| `toolHooks.ts` | `hook_config.py` |
| `ToolManifest` | `manifest.py` |

View File

@@ -0,0 +1,536 @@
# Phase 7Hook 拦截层Hook Interception Layer
日期2026-04-04
状态:待开始
前置依赖Phase 6工具系统重构
Demo参考claw-code-main — toolHooks.ts, hook pipeline
---
## 1. 阶段目标
实现完整的 **PreTool/PostTool Hook 拦截机制**,允许在工具执行前、执行后、异常时进行拦截、修改、拒绝和审计。
**Hook 是工具系统的基础设施**,不是独立功能,必须依赖 Phase 6 的注册表。
---
## 2. Hook 类型定义
### 2.1 Hook 类型
```python
# backend/app/agents/tools/hooks/types.py
class HookType(Enum):
"""Hook 类型"""
PRE_TOOL_USE = "pre_tool_use" # 工具执行前
POST_TOOL_USE = "post_tool_use" # 工具执行后
TOOL_ERROR = "tool_error" # 工具执行异常
TOOL_SKIP = "tool_skip" # 工具被跳过
class HookAction(Enum):
"""Hook 执行动作"""
ALLOW = "allow" # 允许执行
DENY = "deny" # 拒绝执行
MODIFY = "modify" # 修改参数
MODIFY_RESULT = "modify_result" # 修改结果
CONTINUE = "continue" # 继续后置Hook用
@dataclass
class HookContext:
"""Hook 执行上下文"""
tool_name: str
arguments: dict
phase: HookType
result: Any = None
error: Exception | None = None
session_id: str | None = None
user_id: str | None = None
metadata: dict = field(default_factory=dict)
@dataclass
class HookResult:
"""Hook 执行结果"""
action: HookAction
message: str | None = None
modified_args: dict | None = None
modified_result: Any = None
```
---
## 3. 内置 Hook 实现
### 3.1 危险操作确认 Hook
```python
# backend/app/agents/tools/hooks/dangerous_confirmation.py
class DangerousConfirmationHook:
"""
危险操作确认 Hook
对危险操作WRITE/EXTERNAL 权限)执行前确认
"""
DANGEROUS_PATTERNS = [
"delete",
"remove",
"drop",
"truncate",
"rm ",
"rmdir",
"shutdown",
"reboot",
"format",
"fdisk",
]
def __init__(self, confirmation_service: ConfirmationService):
self.confirmation_service = confirmation_service
async def pre_execute(self, ctx: HookContext) -> HookResult:
"""检查是否危险操作"""
manifest = tool_registry.get(ctx.tool_name)
# 只对 WRITE/EXTERNAL 权限的工具生效
if manifest.permission_class not in [PermissionClass.WRITE, PermissionClass.EXTERNAL]:
return HookResult(action=HookAction.ALLOW)
# 检查是否包含危险模式
args_str = str(ctx.arguments).lower()
for pattern in self.DANGEROUS_PATTERNS:
if pattern in args_str:
# 请求用户确认
confirmed = await self.confirmation_service.request_confirmation(
user_id=ctx.user_id,
message=f"危险操作:{ctx.tool_name}\n参数:{ctx.arguments}",
timeout_seconds=300
)
if not confirmed:
return HookResult(
action=HookAction.DENY,
message="用户拒绝执行危险操作"
)
return HookResult(action=HookAction.ALLOW)
```
### 3.2 安全扫描 Hook
```python
# backend/app/agents/tools/hooks/security_scan.py
class SecurityScanHook:
"""
安全扫描 Hook
执行后扫描结果中的敏感信息
"""
SENSITIVE_PATTERNS = [
r"password\s*=\s*['\"][^'\"]+['\"]",
r"api[_-]?key\s*=\s*['\"][^'\"]+['\"]",
r"secret\s*=\s*['\"][^'\"]+['\"]",
r"-----BEGIN (RSA |EC |DSA )?PRIVATE KEY-----",
r"sk-[a-zA-Z0-9]{20,}",
]
async def post_execute(self, ctx: HookContext) -> HookResult:
"""扫描结果中的敏感信息"""
if ctx.result is None:
return HookResult(action=HookAction.CONTINUE)
result_str = str(ctx.result)
for pattern in self.SENSITIVE_PATTERNS:
if re.search(pattern, result_str, re.IGNORECASE):
# 脱敏处理
sanitized = re.sub(pattern, "[REDACTED]", result_str, flags=re.IGNORECASE)
return HookResult(
action=HookAction.MODIFY_RESULT,
message="检测到敏感信息,已脱敏",
modified_result=sanitized
)
return HookResult(action=HookAction.CONTINUE)
```
### 3.3 日志记录 Hook
```python
# backend/app/agents/tools/hooks/audit_log.py
class AuditLogHook:
"""
审计日志 Hook
记录所有工具调用
"""
async def pre_execute(self, ctx: HookContext) -> HookResult:
"""记录调用前状态"""
logger.info(
"tool_call_start",
extra={
"tool": ctx.tool_name,
"args": ctx.arguments,
"session_id": ctx.session_id,
"user_id": ctx.user_id,
"timestamp": datetime.now().isoformat(),
}
)
return HookResult(action=HookAction.ALLOW)
async def post_execute(self, ctx: HookContext) -> HookResult:
"""记录调用后状态"""
logger.info(
"tool_call_end",
extra={
"tool": ctx.tool_name,
"success": ctx.error is None,
"duration_ms": ctx.metadata.get("duration_ms", 0),
"session_id": ctx.session_id,
"user_id": ctx.user_id,
"timestamp": datetime.now().isoformat(),
}
)
return HookResult(action=HookAction.CONTINUE)
async def on_error(self, ctx: HookContext) -> HookResult:
"""记录错误"""
logger.error(
"tool_call_error",
extra={
"tool": ctx.tool_name,
"error": str(ctx.error),
"error_type": type(ctx.error).__name__,
"session_id": ctx.session_id,
"user_id": ctx.user_id,
"timestamp": datetime.now().isoformat(),
}
)
return HookResult(action=HookAction.CONTINUE)
```
---
## 4. Hook 配置系统
### 4.1 Hook 配置格式
```json
{
"hooks": {
"pre_tool_use": {
"enabled": true,
"handlers": [
{
"type": "audit_log",
"filter": null
},
{
"type": "dangerous_confirmation",
"filter": {
"permissions": ["WRITE", "EXTERNAL"]
}
}
]
},
"post_tool_use": {
"enabled": true,
"handlers": [
{
"type": "security_scan",
"filter": null
},
{
"type": "audit_log",
"filter": null
}
]
},
"tool_error": {
"enabled": true,
"handlers": [
{
"type": "audit_log",
"filter": null
}
]
}
}
}
```
### 4.2 HookManager
```python
# backend/app/agents/tools/hooks/manager.py
class HookManager:
"""Hook 管理器"""
def __init__(self):
self._builtin_hooks: dict[str, type] = {
"audit_log": AuditLogHook,
"dangerous_confirmation": DangerousConfirmationHook,
"security_scan": SecurityScanHook,
}
self._config: HookConfig = {}
self._enabled = False
def load_config(self, config: dict):
"""加载 Hook 配置"""
self._config = config
self._enabled = config.get("enabled", True)
async def execute_pre_hooks(
self,
tool_name: str,
arguments: dict,
context: dict
) -> HookResult:
"""执行前置 Hook"""
if not self._enabled:
return HookResult(action=HookAction.ALLOW)
pre_config = self._config.get("pre_tool_use", {})
if not pre_config.get("enabled", True):
return HookResult(action=HookAction.ALLOW)
ctx = HookContext(
tool_name=tool_name,
arguments=arguments,
phase=HookType.PRE_TOOL_USE,
**context
)
for handler_config in pre_config.get("handlers", []):
handler = self._create_handler(handler_config)
if not self._should_apply(handler_config, tool_name):
continue
result = await handler.pre_execute(ctx)
if result.action == HookAction.DENY:
return result
elif result.action == HookAction.MODIFY:
ctx.arguments = result.modified_args
return HookResult(action=HookAction.ALLOW, modified_args=ctx.arguments)
async def execute_post_hooks(
self,
tool_name: str,
arguments: dict,
result: Any,
context: dict
) -> HookResult:
"""执行后置 Hook"""
if not self._enabled:
return HookResult(action=HookAction.CONTINUE, modified_result=result)
post_config = self._config.get("post_tool_use", {})
if not post_config.get("enabled", True):
return HookResult(action=HookAction.CONTINUE, modified_result=result)
ctx = HookContext(
tool_name=tool_name,
arguments=arguments,
phase=HookType.POST_TOOL_USE,
result=result,
**context
)
for handler_config in post_config.get("handlers", []):
handler = self._create_handler(handler_config)
if not self._should_apply(handler_config, tool_name):
continue
hook_result = await handler.post_execute(ctx)
if hook_result.action == HookAction.MODIFY_RESULT:
ctx.result = hook_result.modified_result
return HookResult(action=HookAction.CONTINUE, modified_result=ctx.result)
```
---
## 5. 用户自定义 Hook
### 5.1 Hook API
```python
# backend/app/routers/hooks.py
@router.post("/api/hooks/config")
async def update_hook_config(
config: HookConfigUpdate,
current_user: User = Depends(get_current_user)
):
"""更新 Hook 配置"""
# 验证配置格式
await hook_manager.validate_config(config)
# 保存配置
await hook_config_service.save_config(
user_id=current_user.id,
config=config
)
# 重新加载
hook_manager.load_config(config)
return {"status": "ok"}
@router.get("/api/hooks/config")
async def get_hook_config(
current_user: User = Depends(get_current_user)
):
"""获取 Hook 配置"""
config = await hook_config_service.get_config(current_user.id)
return config
@router.get("/api/hooks/available")
async def list_available_hooks():
"""列出所有可用的 Hook"""
return {
"builtin": [
{"name": "audit_log", "description": "审计日志"},
{"name": "dangerous_confirmation", "description": "危险操作确认"},
{"name": "security_scan", "description": "安全扫描"},
],
"custom": hook_manager.list_custom_hooks()
}
```
---
## 6. 与工具系统集成
### 6.1 修改 ToolExecutor
```python
# backend/app/agents/tools/executor.py
class ToolExecutor:
"""工具执行器(集成 Hook"""
def __init__(
self,
registry: ToolRegistry,
hook_manager: HookManager
):
self.registry = registry
self.hook_manager = hook_manager
async def execute(
self,
tool_name: str,
arguments: dict,
context: dict
) -> ToolResult:
"""执行工具(带 Hook"""
# 1. Pre Hook
pre_result = await self.hook_manager.execute_pre_hooks(
tool_name, arguments, context
)
if pre_result.action == HookAction.DENY:
return ToolResult(
success=False,
error=pre_result.message,
tool_name=tool_name
)
if pre_result.modified_args:
arguments = pre_result.modified_args
# 2. 执行工具
try:
manifest = self.registry.get(tool_name)
executor = self.registry.get_executor(tool_name)
start_time = datetime.now()
result = await executor(**arguments)
duration_ms = (datetime.now() - start_time).total_seconds() * 1000
# 3. Post Hook
post_result = await self.hook_manager.execute_post_hooks(
tool_name, arguments, result, context
)
if post_result.action == HookAction.MODIFY_RESULT:
result = post_result.modified_result
return ToolResult(
success=True,
result=result,
tool_name=tool_name,
duration_ms=duration_ms
)
except Exception as e:
# 4. Error Hook
error_result = await self.hook_manager.execute_error_hook(
tool_name, arguments, e, context
)
return ToolResult(
success=False,
error=str(e),
tool_name=tool_name
)
```
---
## 7. 文件结构
```
backend/app/agents/tools/hooks/
├── __init__.py
├── types.py # Hook 类型定义
├── manager.py # Hook 管理器
├── builtins/ # 内置 Hook
│ ├── __init__.py
│ ├── audit_log.py
│ ├── dangerous_confirmation.py
│ └── security_scan.py
└── custom/ # 用户自定义 Hook
├── __init__.py
└── loader.py
```
---
## 8. 验收标准
| 检查点 | 标准 |
|--------|------|
| Pre Hook 执行 | 危险操作被正确拦截 |
| Post Hook 执行 | 结果中的敏感信息被正确脱敏 |
| Error Hook 执行 | 异常被正确记录 |
| 配置持久化 | Hook 配置可保存和加载 |
| API 可用 | 用户可通过 API 配置 Hook |
| 向后兼容 | 不配置 Hook 时系统正常运行 |
---
## 9. Demo 借鉴
| claw-code | Jarvis 对应 |
|-----------|------------|
| `toolHooks.ts` | `hooks/types.py`, `hooks/manager.py` |
| `PreToolUse` | `HookType.PRE_TOOL_USE` |
| `PostToolUse` | `HookType.POST_TOOL_USE` |
| Hook 配置解析 | `hooks/config.py` |

View File

@@ -0,0 +1,628 @@
# Phase 8插件生态Plugin Ecosystem
日期2026-04-04
状态:待开始
前置依赖Phase 6工具系统重构、Phase 7Hook 拦截层)
Demo参考claw-code-main — plugins/, PluginLifecycle
---
## 1. 阶段目标
建立完整的**插件生命周期管理系统**,允许第三方开发者通过插件扩展 Jarvis 的功能。
**核心能力**
- 插件安装/卸载/启用/禁用
- 插件沙箱隔离执行
- 插件提供的工具/Hook/命令注册
- 内置插件集
---
## 2. 插件架构
### 2.1 插件结构
```
my-plugin/
├── manifest.json # 插件清单
├── SKILL.md # 插件技能文档(可选)
├── README.md # 插件说明
├── tools/ # 插件提供的工具
│ ├── __init__.py
│ └── my_tool.py
├── hooks/ # 插件提供的 Hook
│ ├── __init__.py
│ └── my_hook.py
├── commands/ # 插件提供的命令
│ ├── __init__.py
│ └── my_command.py
└── static/ # 静态资源
└── icon.png
```
### 2.2 插件清单 (manifest.json)
```json
{
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"description": "A sample plugin for Jarvis",
"author": "Developer Name",
"license": "MIT",
"homepage": "https://github.com/developer/my-plugin",
"compatibility": {
"jarvis_version": ">=2.0.0",
"api_version": "1.0"
},
"capabilities": {
"tools": [
{
"name": "my_tool",
"description": "Does something useful",
"parameters": {
"type": "object",
"properties": {
"input": {"type": "string"}
},
"required": ["input"]
}
}
],
"hooks": [
{
"name": "my_pre_hook",
"type": "pre_tool_use",
"description": "Custom pre-hook"
}
],
"commands": [
{
"name": "/my-command",
"description": "Custom slash command"
}
]
},
"permissions": {
"network": false,
"filesystem": "read",
"database": false
},
"dependencies": {}
}
```
---
## 3. 核心组件
### 3.1 PluginManifest
```python
# backend/app/agents/plugins/manifest.py
@dataclass
class ToolSpec:
"""工具规格"""
name: str
description: str
parameters: dict
return_schema: dict | None = None
@dataclass
class HookSpec:
"""Hook 规格"""
name: str
type: HookType
description: str
filter_names: list[str] | None = None
@dataclass
class CommandSpec:
"""命令规格"""
name: str
description: str
parameters: list | None = None
@dataclass
class PluginManifest:
"""插件清单"""
id: str
name: str
version: str
description: str
author: str
license: str
homepage: str | None = None
compatibility: dict = field(default_factory=dict)
capabilities: dict = field(default_factory=dict)
permissions: dict = field(default_factory=dict)
dependencies: dict = field(default_factory=dict)
@classmethod
def from_file(cls, path: Path) -> "PluginManifest":
"""从文件加载"""
with open(path / "manifest.json") as f:
data = json.load(f)
return cls(**data)
def validate(self) -> list[str]:
"""验证清单有效性"""
errors = []
if not self.id:
errors.append("Plugin ID is required")
if not self.version:
errors.append("Plugin version is required")
if "tools" in self.capabilities:
for tool in self.capabilities["tools"]:
if "name" not in tool:
errors.append(f"Tool missing name in plugin {self.id}")
return errors
```
### 3.2 PluginManager
```python
# backend/app/agents/plugins/manager.py
class PluginState(Enum):
"""插件状态"""
INSTALLED = "installed"
ENABLED = "enabled"
DISABLED = "disabled"
ERROR = "error"
@dataclass
class Plugin:
"""插件实例"""
manifest: PluginManifest
path: Path
state: PluginState
module: Any = None
error_message: str | None = None
tools: list[ToolManifest] = field(default_factory=list)
hooks: list[HookConfig] = field(default_factory=list)
commands: list[CommandSpec] = field(default_factory=list)
class PluginManager:
"""插件管理器"""
def __init__(
self,
plugin_dir: Path,
tool_registry: ToolRegistry,
hook_manager: HookManager
):
self.plugin_dir = plugin_dir
self.tool_registry = tool_registry
self.hook_manager = hook_manager
self._plugins: dict[str, Plugin] = {}
self._hooks = []
async def install(self, plugin_path: Path | str) -> Plugin:
"""
安装插件
流程:
1. 验证插件清单
2. 检查兼容性
3. 复制到插件目录
4. 加载插件模块
"""
plugin_path = Path(plugin_path)
# 1. 加载清单
manifest = PluginManifest.from_file(plugin_path)
# 2. 验证
errors = manifest.validate()
if errors:
raise PluginValidationError(errors)
# 3. 检查版本兼容性
await self._check_compatibility(manifest)
# 4. 创建插件目录
target_dir = self.plugin_dir / manifest.id
if target_dir.exists():
raise PluginAlreadyExistsError(manifest.id)
shutil.copytree(plugin_path, target_dir)
# 5. 创建插件实例
plugin = Plugin(
manifest=manifest,
path=target_dir,
state=PluginState.INSTALLED
)
# 6. 加载模块
await self._load_plugin(plugin)
self._plugins[manifest.id] = plugin
return plugin
async def uninstall(self, plugin_id: str) -> bool:
"""卸载插件"""
plugin = self._plugins.get(plugin_id)
if not plugin:
raise PluginNotFoundError(plugin_id)
# 禁用插件
if plugin.state == PluginState.ENABLED:
await self.disable(plugin_id)
# 移除工具和 Hook
for tool in plugin.tools:
self.tool_registry.unregister(tool.name)
for hook in plugin.hooks:
self.hook_manager.remove_hook(hook)
# 删除插件目录
shutil.rmtree(plugin.path)
del self._plugins[plugin_id]
return True
async def enable(self, plugin_id: str) -> Plugin:
"""启用插件"""
plugin = self._plugins.get(plugin_id)
if not plugin:
raise PluginNotFoundError(plugin_id)
# 注册工具
for tool in plugin.tools:
self.tool_registry.register(
tool,
self._create_tool_executor(plugin, tool.name)
)
# 注册 Hook
for hook in plugin.hooks:
self.hook_manager.add_hook(hook)
plugin.state = PluginState.ENABLED
return plugin
async def disable(self, plugin_id: str) -> Plugin:
"""禁用插件"""
plugin = self._plugins.get(plugin_id)
if not plugin:
raise PluginNotFoundError(plugin_id)
# 注销工具
for tool in plugin.tools:
self.tool_registry.unregister(tool.name)
# 注销 Hook
for hook in plugin.hooks:
self.hook_manager.remove_hook(hook)
plugin.state = PluginState.DISABLED
return plugin
async def reload(self, plugin_id: str) -> Plugin:
"""重新加载插件"""
plugin = self._plugins.get(plugin_id)
if not plugin:
raise PluginNotFoundError(plugin_id)
was_enabled = plugin.state == PluginState.ENABLED
if was_enabled:
await self.disable(plugin_id)
await self._load_plugin(plugin)
if was_enabled:
await self.enable(plugin_id)
return plugin
async def list_plugins(self) -> list[Plugin]:
"""列出所有插件"""
return list(self._plugins.values())
```
---
## 4. 插件隔离
### 4.1 PluginSandbox
```python
# backend/app/agents/plugins/sandbox.py
class PluginSandbox:
"""插件沙箱"""
def __init__(self, plugin: Plugin, permissions: dict):
self.plugin = plugin
self.permissions = permissions
self._env = {}
async def load_module(self) -> Any:
"""在沙箱中加载插件模块"""
# 设置受限的环境
self._env = {
"__name__": f"plugin.{self.plugin.manifest.id}",
"__file__": str(self.plugin.path),
}
if not self.permissions.get("filesystem", False):
# 只读文件系统
self._env["__builtins__"] = {
k: v for k, v in __builtins__.items()
if k in ["print", "len", "range", "str", "int", "float", "list", "dict", "tuple", "set", "bool", "type", "isinstance", "hasattr", "getattr", "setattr", "open"]
}
# 加载模块
spec = importlib.util.spec_from_file_location(
f"plugin_{self.plugin.manifest.id}",
self.plugin.path / "__init__.py"
)
module = importlib.util.module_from_spec(spec)
sys.modules[f"plugin_{self.plugin.manifest.id}"] = module
try:
spec.loader.exec_module(module)
except Exception as e:
raise PluginLoadError(str(e))
return module
def check_permission(self, permission: str) -> bool:
"""检查权限"""
return self.permissions.get(permission, False)
```
---
## 5. 插件市场
### 5.1 PluginMarketplace
```python
# backend/app/services/plugin_marketplace.py
@dataclass
class MarketplacePlugin:
"""市场中的插件"""
manifest: PluginManifest
download_count: int
rating: float
reviews_count: int
last_updated: datetime
class PluginMarketplace:
"""插件市场"""
def __init__(self, http_client: httpx.AsyncClient):
self.http_client = http_client
self.cache: dict[str, MarketplacePlugin] = {}
self.cache_ttl = timedelta(hours=1)
async def search(
self,
query: str,
category: str | None = None,
limit: int = 20
) -> list[MarketplacePlugin]:
"""搜索插件"""
# TODO: 连接到实际的市场 API
return []
async def get_plugin(
self,
plugin_id: str
) -> MarketplacePlugin | None:
"""获取插件详情"""
# 检查缓存
if plugin_id in self.cache:
return self.cache[plugin_id]
# TODO: 从市场 API 获取
return None
async def download_plugin(
self,
plugin_id: str,
target_dir: Path
) -> Path:
"""下载插件"""
# TODO: 实现下载逻辑
pass
```
---
## 6. 内置插件
### 6.1 内置插件列表
| 插件 | 描述 | 提供的工具 |
|------|------|-----------|
| `code-helper` | 代码助手 | `lint`, `format`, `explain_code` |
| `git-helper` | Git 助手 | `git_status`, `git_log`, `git_diff` |
| `web-helper` | Web 助手 | `fetch_url`, `parse_html` |
| `file-organizer` | 文件整理 | `organize_files`, `cleanup_duplicates` |
### 6.2 BuiltinPlugins
```python
# backend/app/agents/plugins/builtins.py
BUILTIN_PLUGINS = [
{
"id": "code-helper",
"name": "Code Helper",
"version": "1.0.0",
"description": "代码辅助工具集",
"capabilities": {
"tools": [
{
"name": "lint",
"description": "检查代码风格",
"parameters": {
"type": "object",
"properties": {
"code": {"type": "string"},
"language": {"type": "string"}
}
}
},
{
"name": "format",
"description": "格式化代码",
"parameters": {
"type": "object",
"properties": {
"code": {"type": "string"},
"language": {"type": "string"}
}
}
}
]
}
}
]
```
---
## 7. API 接口
```python
# backend/app/routers/plugins.py
@router.get("/api/plugins")
async def list_plugins(
current_user: User = Depends(get_current_user)
):
"""列出用户已安装的插件"""
plugins = await plugin_manager.list_plugins()
return {
"plugins": [
{
"id": p.manifest.id,
"name": p.manifest.name,
"version": p.manifest.version,
"state": p.state.value,
"description": p.manifest.description
}
for p in plugins
]
}
@router.post("/api/plugins/install")
async def install_plugin(
request: InstallPluginRequest,
current_user: User = Depends(get_current_user)
):
"""安装插件"""
plugin = await plugin_manager.install(Path(request.path))
return {"plugin_id": plugin.manifest.id}
@router.post("/api/plugins/{plugin_id}/enable")
async def enable_plugin(
plugin_id: str,
current_user: User = Depends(get_current_user)
):
"""启用插件"""
plugin = await plugin_manager.enable(plugin_id)
return {"state": plugin.state.value}
@router.post("/api/plugins/{plugin_id}/disable")
async def disable_plugin(
plugin_id: str,
current_user: User = Depends(get_current_user)
):
"""禁用插件"""
plugin = await plugin_manager.disable(plugin_id)
return {"state": plugin.state.value}
@router.delete("/api/plugins/{plugin_id}")
async def uninstall_plugin(
plugin_id: str,
current_user: User = Depends(get_current_user)
):
"""卸载插件"""
await plugin_manager.uninstall(plugin_id)
return {"status": "ok"}
@router.get("/api/marketplace/plugins")
async def search_marketplace(
q: str | None = None,
category: str | None = None,
limit: int = 20
):
"""搜索插件市场"""
plugins = await marketplace.search(q, category, limit)
return {"plugins": plugins}
```
---
## 8. 文件结构
```
backend/app/agents/plugins/
├── __init__.py
├── manifest.py # 插件清单
├── manager.py # 插件管理器
├── sandbox.py # 插件沙箱
├── builtins/ # 内置插件
│ ├── __init__.py
│ ├── code_helper/
│ ├── git_helper/
│ └── web_helper/
├── marketplace.py # 插件市场
└── router.py # API 路由(可选)
backend/app/routers/
└── plugins.py # 插件 API 路由
```
---
## 9. 验收标准
| 检查点 | 标准 |
|--------|------|
| 插件安装 | 可以从目录安装插件 |
| 插件启用 | 插件的工具和 Hook 正确注册 |
| 插件禁用 | 插件的工具和 Hook 正确注销 |
| 插件卸载 | 插件目录正确删除 |
| 插件隔离 | 插件无法访问未授权资源 |
| 市场搜索 | 可以搜索插件市场 |
| 内置插件 | 内置插件默认安装 |
---
## 10. Demo 借鉴
| claw-code | Jarvis 对应 |
|-----------|------------|
| `src/plugins/builtinPlugins.ts` | `plugins/builtins.py` |
| `src/plugins/bundled/index.ts` | `plugins/builtins/` |
| `PluginLifecycle` | `PluginManager` |
| `PluginInstallationManager` | `PluginManager.install` |
| `/plugin`, `/reload-plugins` | `/api/plugins/*` |

View File

@@ -0,0 +1,508 @@
# Phase 9Skills 注册表Skills Registry
日期2026-04-04
状态:待开始
前置依赖Phase 6工具系统重构
Demo参考claw-code-main — skills/loadSkillsDir.ts, bundledSkills.ts, mcpSkillBuilders.ts
---
## 1. 阶段目标
建立完整的 **Skills 动态加载和注册系统**,支持:
- 本地文件 Skills现有
- 插件提供的 Skills
- MCP 动态发现的 Skills
- Skills 注册表和搜索
- Bundled Skills 集
**现有状态**Jarvis 已有基础的 `skill_registry.py`,需要增强为完整系统。
---
## 2. Skills 架构
### 2.1 Skill 结构
```markdown
# SKILL.md
## Skill Metadata
name: my-skill
description: Does something useful
version: 1.0.0
author: Developer
## Triggers
- keywords: ["skill", "my"]
- patterns: ["/my-skill"]
## Capabilities
- tools: ["my_tool"]
- hooks: ["my_hook"]
## Configuration
```json
{
"option1": "value1"
}
```
```
### 2.2 SkillRegistry 增强
```python
# backend/app/agents/skills/registry.py
@dataclass
class SkillMetadata:
"""Skill 元数据"""
name: str
description: str
version: str
author: str | None = None
triggers: list[str] = field(default_factory=list)
patterns: list[str] = field(default_factory=list)
tools: list[str] = field(default_factory=list)
hooks: list[str] = field(default_factory=list)
commands: list[str] = field(default_factory=list)
config_schema: dict | None = None
source: SkillSource = SkillSource.LOCAL
file_path: Path | None = None
class SkillSource(Enum):
"""Skill 来源"""
LOCAL = "local" # 本地文件
PLUGIN = "plugin" # 插件提供
MCP = "mcp" # MCP 动态发现
BUNDLED = "bundled" # 内置
class SkillRegistry:
"""Skills 注册表"""
def __init__(
self,
local_skills_dir: Path,
tool_registry: ToolRegistry,
hook_manager: HookManager
):
self.local_skills_dir = local_skills_dir
self.tool_registry = tool_registry
self.hook_manager = hook_manager
self._skills: dict[str, SkillMetadata] = {}
self._index: dict[str, list[str]] = defaultdict(list) # keyword -> skill names
async def load_all(self):
"""加载所有 Skills"""
await self._load_local_skills()
await self._load_bundled_skills()
async def _load_local_skills(self):
"""加载本地 Skills"""
if not self.local_skills_dir.exists():
return
for skill_file in self.local_skills_dir.rglob("SKILL.md"):
try:
metadata = await self._parse_skill_file(skill_file)
metadata.source = SkillSource.LOCAL
self._register_skill(metadata)
except Exception as e:
logger.warning(f"Failed to load skill from {skill_file}: {e}")
async def _load_bundled_skills(self):
"""加载内置 Skills"""
for skill_def in BUNDLED_SKILLS:
metadata = SkillMetadata(
name=skill_def["name"],
description=skill_def["description"],
version=skill_def["version"],
author="Jarvis Team",
triggers=skill_def.get("triggers", []),
tools=skill_def.get("tools", []),
source=SkillSource.BUNDLED
)
self._register_skill(metadata)
async def _parse_skill_file(self, path: Path) -> SkillMetadata:
"""解析 SKILL.md 文件"""
content = path.read_text(encoding="utf-8")
# 简单的 frontmatter 解析
metadata = SkillMetadata(
name=path.parent.name,
description="",
version="1.0.0"
)
# 解析 triggers
for line in content.split("\n"):
if line.startswith("- keywords:"):
keywords = line.split("[")[1].split("]")[0]
metadata.triggers = [k.strip() for k in keywords.split(",")]
return metadata
def _register_skill(self, metadata: SkillMetadata):
"""注册 Skill"""
self._skills[metadata.name] = metadata
# 更新索引
for keyword in metadata.triggers:
self._index[keyword].append(metadata.name)
# 注册关联的工具
for tool_name in metadata.tools:
self.tool_registry.register_tool_for_skill(metadata.name, tool_name)
async def get_skill(self, name: str) -> SkillMetadata | None:
"""获取 Skill"""
return self._skills.get(name)
async def search(
self,
query: str,
limit: int = 10
) -> list[SkillMetadata]:
"""搜索 Skills"""
results = []
query_lower = query.lower()
# 精确匹配 name
if query_lower in self._skills:
results.append(self._skills[query_lower])
# 关键词匹配
for skill_name in self._index.get(query_lower, []):
if skill_name not in results:
results.append(self._skills[skill_name])
# 描述匹配
for skill in self._skills.values():
if skill in results:
continue
if query_lower in skill.description.lower():
results.append(skill)
return results[:limit]
async def get_skill_context(
self,
skill_name: str,
context: dict
) -> str:
"""获取 Skill 上下文"""
skill = self._skills.get(skill_name)
if not skill:
return ""
# 读取 SKILL.md 内容
if skill.file_path and skill.file_path.exists():
content = skill.file_path.read_text(encoding="utf-8")
# 移除 metadata 部分,只保留 instructions
return content
return ""
```
---
## 3. MCP Skill Builder
### 3.1 MCPSkillBuilder
```python
# backend/app/agents/skills/mcp_builder.py
class MCPSkillBuilder:
"""
MCP Skill Builder
从 MCP 服务器动态构建 Skills
"""
def __init__(
self,
mcp_client: MCPClient,
skill_registry: SkillRegistry
):
self.mcp_client = mcp_client
self.skill_registry = skill_registry
async def discover_skills_from_mcp(
self,
mcp_server_id: str
) -> list[SkillMetadata]:
"""从 MCP 服务器发现 Skills"""
# 获取 MCP 服务器提供的工具
tools = await self.mcp_client.list_tools(mcp_server_id)
skills = []
# 按工具前缀分组
tool_groups: dict[str, list] = defaultdict(list)
for tool in tools:
parts = tool.name.split("_")
if len(parts) > 1:
prefix = parts[0]
tool_groups[prefix].append(tool)
else:
# 单工具,作为独立 skill
skill = self._tool_to_skill(tool, mcp_server_id)
skills.append(skill)
# 创建分组 skill
for prefix, group_tools in tool_groups.items():
skill = self._group_to_skill(prefix, group_tools, mcp_server_id)
skills.append(skill)
return skills
def _tool_to_skill(
self,
tool: MCPProtocolTool,
server_id: str
) -> SkillMetadata:
"""将单个 MCP 工具转换为 Skill"""
return SkillMetadata(
name=f"{server_id}_{tool.name}",
description=tool.description or f"MCP tool: {tool.name}",
version="1.0.0",
tools=[tool.name],
source=SkillSource.MCP,
config_schema={
"server_id": server_id,
"tool_name": tool.name
}
)
def _group_to_skill(
self,
prefix: str,
tools: list[MCPProtocolTool],
server_id: str
) -> SkillMetadata:
"""将一组 MCP 工具转换为 Skill"""
return SkillMetadata(
name=f"{server_id}_{prefix}",
description=f"MCP {prefix} tools: {', '.join(t.name for t in tools)}",
version="1.0.0",
tools=[t.name for t in tools],
source=SkillSource.MCP,
config_schema={
"server_id": server_id,
"prefix": prefix
}
)
```
---
## 4. 内置 Skills
```python
# backend/app/agents/skills/bundled.py
BUNDLED_SKILLS = [
{
"name": "code-analysis",
"description": "代码分析技能 - 分析代码结构、复杂度、依赖关系",
"version": "1.0.0",
"triggers": ["分析代码", "代码分析", "code analysis"],
"tools": ["grep", "glob", "lint"]
},
{
"name": "git-helper",
"description": "Git 操作助手 - 管理 Git 仓库和操作",
"version": "1.0.0",
"triggers": ["git", "版本控制", "commit"],
"tools": ["git_status", "git_log", "git_diff"]
},
{
"name": "web-research",
"description": "网络研究技能 - 搜索和抓取网页内容",
"version": "1.0.0",
"triggers": ["搜索", "研究", "research", "web search"],
"tools": ["web_search", "web_fetch"]
},
{
"name": "file-management",
"description": "文件管理技能 - 组织和管理文件",
"version": "1.0.0",
"triggers": ["文件", "整理", "file management"],
"tools": ["glob", "file_read", "file_write", "organize"]
},
{
"name": "task-planning",
"description": "任务规划技能 - 拆解和规划复杂任务",
"version": "1.0.0",
"triggers": ["规划", "任务拆解", "task planning"],
"tools": ["create_task", "get_tasks", "update_task"]
}
]
```
---
## 5. Skill 与 Agent 集成
### 5.1 修改 AgentService
```python
# backend/app/services/agent_service.py (修改部分)
async def build_skill_context(
self,
skill_names: list[str],
context: dict
) -> str:
"""构建 Skill 上下文"""
parts = []
for skill_name in skill_names:
skill_context = await self.skill_registry.get_skill_context(
skill_name, context
)
if skill_context:
parts.append(f"\n\n=== Skill: {skill_name} ===\n{skill_context}")
return "\n".join(parts)
async def chat(
self,
message: str,
context: dict
) -> AgentResponse:
"""处理聊天消息(集成 Skills"""
# 1. 检测触发的 Skills
triggered_skills = await self.skill_registry.search(message)
# 2. 构建 Skill 上下文
skill_context = await self.build_skill_context(
[s.name for s in triggered_skills],
context
)
# 3. 注入到 system prompt
if skill_context:
context["skill_context"] = skill_context
# 4. 继续正常处理
return await self._process_message(message, context)
```
---
## 6. API 接口
```python
# backend/app/routers/skills.py
@router.get("/api/skills")
async def list_skills(
source: SkillSource | None = None,
current_user: User = Depends(get_current_user)
):
"""列出所有 Skills"""
skills = await skill_registry.list_all()
if source:
skills = [s for s in skills if s.source == source]
return {
"skills": [
{
"name": s.name,
"description": s.description,
"version": s.version,
"source": s.source.value,
"triggers": s.triggers
}
for s in skills
]
}
@router.get("/api/skills/search")
async def search_skills(
q: str,
limit: int = 10
):
"""搜索 Skills"""
results = await skill_registry.search(q, limit)
return {"skills": results}
@router.get("/api/skills/{skill_name}")
async def get_skill(
skill_name: str,
current_user: User = Depends(get_current_user)
):
"""获取 Skill 详情"""
skill = await skill_registry.get_skill(skill_name)
if not skill:
raise HTTPException(404, "Skill not found")
return {
"name": skill.name,
"description": skill.description,
"version": skill.version,
"source": skill.source.value,
"triggers": skill.triggers,
"tools": skill.tools,
"hooks": skill.hooks
}
```
---
## 7. 文件结构
```
backend/app/agents/skills/
├── __init__.py
├── registry.py # Skills 注册表(增强)
├── metadata.py # Skill 元数据
├── mcp_builder.py # MCP Skill Builder
├── bundled.py # 内置 Skills
├── loaders/ # 加载器
│ ├── __init__.py
│ ├── local_loader.py # 本地文件加载
│ ├── plugin_loader.py # 插件 Skill 加载
│ └── mcp_loader.py # MCP Skill 加载
└── context.py # Skill 上下文构建
backend/app/routers/
└── skills.py # Skills API 路由
```
---
## 8. 验收标准
| 检查点 | 标准 |
|--------|------|
| 本地 Skills | 能加载 local_skills_dir 下的所有 SKILL.md |
| MCP Skills | 能从 MCP 服务器发现和加载 Skills |
| Bundled Skills | 内置 Skills 默认加载 |
| Skill 搜索 | 能按关键词搜索 Skills |
| Skill 上下文 | Skill 内容正确注入 Agent prompt |
| API 可用 | Skills API 可用 |
---
## 9. Demo 借鉴
| claw-code | Jarvis 对应 |
|-----------|------------|
| `src/skills/loadSkillsDir.ts` | `skills/loaders/local_loader.py` |
| `src/skills/bundledSkills.ts` | `skills/bundled.py` |
| `src/skills/mcpSkillBuilders.ts` | `skills/mcp_builder.py` |
| `/skills` command | `/api/skills` |
| Skill registry pipeline | `SkillRegistry` |