Files
JARVIS/backend/app/agents/coordinator.py

509 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Agent 协调整器 - Phase 10.5
统一协调所有 Agent 组件TeamLeader, RemoteTransport, BackgroundTaskManager, SessionManager
"""
from typing import Any
from app.agents.background.manager import BackgroundTaskManager, get_background_task_manager
from app.agents.session.manager import AgentSession, create_agent_session, get_agent_session
from app.agents.team.leader import TeamLeader
from app.agents.transport.remote import RemoteTransport
class AgentCoordinator:
"""Agent 协调整器
统一协调所有 Agent 组件,提供单一入口处理各类 Agent 操作。
"""
def __init__(
self,
background_manager: BackgroundTaskManager | None = None,
):
"""
Args:
background_manager: 后台任务管理器None 则使用全局单例
"""
self._team_leaders: dict[str, TeamLeader] = {}
self._remote_transport = RemoteTransport()
self._background_manager = background_manager or get_background_task_manager()
self._sessions: dict[str, AgentSession] = {}
# === Team 协作方法 ===
def create_team(self, team_id: str, members: list[str]) -> dict[str, Any]:
"""创建团队
Args:
team_id: 团队 ID
members: 成员 ID 列表
Returns:
团队创建结果
"""
if team_id in self._team_leaders:
return {"status": "error", "message": f"Team '{team_id}' already exists"}
leader = TeamLeader(team_id=team_id, members=members)
self._team_leaders[team_id] = leader
return {
"status": "created",
"team_id": team_id,
"members": members,
}
def get_team(self, team_id: str) -> TeamLeader | None:
"""获取团队
Args:
team_id: 团队 ID
Returns:
TeamLeader 或 None
"""
return self._team_leaders.get(team_id)
def assign_task(self, team_id: str, description: str, member: str) -> dict[str, Any]:
"""创建并分配任务
Args:
team_id: 团队 ID
description: 任务描述
member: 成员 ID
Returns:
分配结果
"""
leader = self._team_leaders.get(team_id)
if not leader:
return {"status": "error", "message": f"Team '{team_id}' not found"}
task_id = leader.create_task(description)
success = leader.assign_task(task_id, member)
return {
"status": "assigned" if success else "error",
"task_id": task_id,
"assignee": member,
}
def broadcast_task(self, team_id: str, description: str) -> dict[str, Any]:
"""广播任务给所有成员
Args:
team_id: 团队 ID
description: 任务描述
Returns:
广播结果
"""
leader = self._team_leaders.get(team_id)
if not leader:
return {"status": "error", "message": f"Team '{team_id}' not found"}
task_ids = leader.broadcast_task(description)
return {
"status": "broadcast",
"team_id": team_id,
"task_ids": task_ids,
"member_count": len(leader.members),
}
def collect_team_results(self, team_id: str) -> dict[str, Any]:
"""收集团队任务结果
Args:
team_id: 团队 ID
Returns:
收集结果
"""
leader = self._team_leaders.get(team_id)
if not leader:
return {"status": "error", "message": f"Team '{team_id}' not found"}
results = leader.collect_results()
status = leader.get_team_status()
return {
"status": "collected",
"team_id": team_id,
"results": results,
"completed": status["completed"],
"failed": status["failed"],
}
def get_team_status(self, team_id: str) -> dict[str, Any]:
"""获取团队状态
Args:
team_id: 团队 ID
Returns:
团队状态
"""
leader = self._team_leaders.get(team_id)
if not leader:
return {"status": "error", "message": f"Team '{team_id}' not found"}
return leader.get_team_status()
# === 后台任务方法 ===
def submit_background_task(
self,
name: str,
coro: Any,
*args,
**kwargs,
) -> dict[str, Any]:
"""提交后台任务
Args:
name: 任务名称
coro: 协程函数
*args: 位置参数
**kwargs: 关键字参数
Returns:
提交结果
"""
task_id = self._background_manager.submit_task(name, coro, *args, **kwargs)
return {
"status": "submitted",
"task_id": task_id,
"name": name,
}
def cancel_background_task(self, task_id: str) -> dict[str, Any]:
"""取消后台任务
Args:
task_id: 任务 ID
Returns:
取消结果
"""
success = self._background_manager.cancel_task(task_id)
return {
"status": "cancelled" if success else "error",
"task_id": task_id,
}
def get_background_task_status(self, task_id: str) -> dict[str, Any]:
"""获取后台任务状态
Args:
task_id: 任务 ID
Returns:
任务状态
"""
task = self._background_manager.get_task_status(task_id)
if not task:
return {"status": "error", "message": f"Task '{task_id}' not found"}
return {
"status": "found",
"task_id": task.id,
"name": task.name,
"task_status": task.status.value,
"result": task.result,
"error": task.error,
}
def list_background_tasks(self) -> dict[str, Any]:
"""列出所有后台任务
Returns:
任务列表
"""
tasks = self._background_manager.list_tasks()
return {
"status": "list",
"count": len(tasks),
"tasks": [
{
"id": t.id,
"name": t.name,
"status": t.status.value,
}
for t in tasks
],
}
# === 会话方法 ===
def create_session(
self,
user_id: str | None = None,
parent_session_id: str | None = None,
) -> dict[str, Any]:
"""创建会话
Args:
user_id: 用户 ID
parent_session_id: 父会话 ID
Returns:
创建结果
"""
session = create_agent_session(
user_id=user_id,
parent_session_id=parent_session_id,
)
self._sessions[session.session_id] = session
return {
"status": "created",
"session_id": session.session_id,
"user_id": user_id,
"parent_session_id": parent_session_id,
}
def get_session(self, session_id: str) -> AgentSession | None:
"""获取会话
Args:
session_id: 会话 ID
Returns:
AgentSession 或 None
"""
return self._sessions.get(session_id) or get_agent_session(session_id)
async def process_session_message(
self,
session_id: str,
message: str,
response: str,
) -> dict[str, Any]:
"""处理会话消息
Args:
session_id: 会话 ID
message: 用户消息
response: 助手响应
Returns:
处理结果
"""
session = self.get_session(session_id)
if not session:
return {"status": "error", "message": f"Session '{session_id}' not found"}
await session.process_message(message, response)
return {
"status": "processed",
"session_id": session_id,
"message_count": session.context.message_count,
}
async def spawn_child_session(
self,
session_id: str,
user_id: str | None = None,
) -> dict[str, Any]:
"""创建子会话
Args:
session_id: 父会话 ID
user_id: 用户 ID
Returns:
创建结果
"""
session = self.get_session(session_id)
if not session:
return {"status": "error", "message": f"Session '{session_id}' not found"}
child = await session.spawn_child_session(user_id=user_id)
self._sessions[child.session_id] = child
return {
"status": "spawned",
"parent_session_id": session_id,
"child_session_id": child.session_id,
"depth": child.context.depth,
}
def get_session_summary(self, session_id: str) -> dict[str, Any]:
"""获取会话摘要
Args:
session_id: 会话 ID
Returns:
会话摘要
"""
import asyncio
session = self.get_session(session_id)
if not session:
return {"status": "error", "message": f"Session '{session_id}' not found"}
# get_session_summary is async, so we need to run it
try:
loop = asyncio.get_event_loop()
if loop.is_running():
# Create a future
future = asyncio.ensure_future(session.get_session_summary())
return {"status": "found", "summary": future}
else:
return {
"status": "found",
"summary": loop.run_until_complete(session.get_session_summary()),
}
except RuntimeError:
# No event loop, create one
return {"status": "found", "summary": asyncio.run(session.get_session_summary())}
# === 远程传输方法 ===
def register_remote_handler(self, event_type: str, handler: Any) -> None:
"""注册远程消息处理器
Args:
event_type: 事件类型
handler: 处理函数
"""
self._remote_transport.register_handler(event_type, handler)
async def send_remote_response(
self,
session_id: str,
response: dict[str, Any],
) -> bool:
"""发送远程响应
Args:
session_id: 会话 ID
response: 响应数据
Returns:
是否发送成功
"""
return await self._remote_transport.send_response(session_id, response)
async def send_remote_event(
self,
session_id: str,
event: dict[str, Any],
) -> bool:
"""发送远程事件
Args:
session_id: 会话 ID
event: 事件数据
Returns:
是否发送成功
"""
return await self._remote_transport.send_event(session_id, event)
async def send_remote_tool_call(
self,
session_id: str,
tool_call: dict[str, Any],
) -> bool:
"""发送远程工具调用
Args:
session_id: 会话 ID
tool_call: 工具调用数据
Returns:
是否发送成功
"""
return await self._remote_transport.send_tool_call(session_id, tool_call)
# === 统一协调入口 ===
async def coordinate(self, request: dict[str, Any]) -> dict[str, Any]:
"""统一协调入口
根据请求类型协调各类 Agent 操作。
Args:
request: 请求数据,包含:
- action: 操作类型 (team_create, team_assign, task_submit, session_create, etc.)
- 其他参数根据 action 不同而不同
Returns:
协调结果
"""
action = request.get("action")
if action == "team_create":
return self.create_team(
team_id=request["team_id"],
members=request["members"],
)
elif action == "team_assign":
return self.assign_task(
team_id=request["team_id"],
description=request["description"],
member=request["member"],
)
elif action == "team_broadcast":
return self.broadcast_task(
team_id=request["team_id"],
description=request["description"],
)
elif action == "team_collect":
return self.collect_team_results(team_id=request["team_id"])
elif action == "team_status":
return self.get_team_status(team_id=request["team_id"])
elif action == "task_submit":
return self.submit_background_task(
name=request["name"],
coro=request["coro"],
*request.get("args", []),
**request.get("kwargs", {}),
)
elif action == "task_cancel":
return self.cancel_background_task(task_id=request["task_id"])
elif action == "task_status":
return self.get_background_task_status(task_id=request["task_id"])
elif action == "session_create":
return self.create_session(
user_id=request.get("user_id"),
parent_session_id=request.get("parent_session_id"),
)
elif action == "session_message":
return await self.process_session_message(
session_id=request["session_id"],
message=request["message"],
response=request["response"],
)
elif action == "session_spawn":
return await self.spawn_child_session(
session_id=request["session_id"],
user_id=request.get("user_id"),
)
elif action == "session_summary":
return self.get_session_summary(session_id=request["session_id"])
else:
return {"status": "error", "message": f"Unknown action: {action}"}
# 全局单例
_coordinator: AgentCoordinator | None = None
def get_agent_coordinator() -> AgentCoordinator:
"""获取全局 Agent 协调整器"""
global _coordinator
if _coordinator is None:
_coordinator = AgentCoordinator()
return _coordinator