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
638 lines
17 KiB
Markdown
638 lines
17 KiB
Markdown
# 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` |
|