Files
multiAgentTry/backend/app/adapters/native_llm_agent.py
Claude Code dc398d7c7b 完整实现 Swarm 多智能体协作系统
- 新增 CLIPluginAdapter 统一接口 (backend/app/core/agent_adapter.py)
- 新增 LLM 服务层,支持 Anthropic/OpenAI/DeepSeek/Ollama (backend/app/services/llm_service.py)
- 新增 Agent 执行引擎,支持文件锁自动管理 (backend/app/services/agent_executor.py)
- 新增 NativeLLMAgent 原生 LLM 适配器 (backend/app/adapters/native_llm_agent.py)
- 新增进程管理器 (backend/app/services/process_manager.py)
- 新增 Agent 控制 API (backend/app/routers/agents_control.py)
- 新增 WebSocket 实时通信 (backend/app/routers/websocket.py)
- 更新前端 AgentsPage,支持启动/停止 Agent
- 测试通过:Agent 启动、批量操作、栅栏同步

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:32:11 +08:00

498 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.
"""
原生 LLM Agent 适配器
直接调用 LLM API 实现 Agent不需要外部进程。
这是主要使用的适配器类型。
"""
import asyncio
import logging
from typing import Dict, Optional, Any
from datetime import datetime
from ..core.agent_adapter import (
CLIPluginAdapter,
Task,
Result,
AgentCapabilities
)
from ..services.llm_service import ModelRouter, get_llm_service
from ..services.agent_executor import AgentExecutor, get_agent_executor
from ..services.meeting_scheduler import get_meeting_scheduler
from ..services.agent_registry import get_agent_registry
from ..services.heartbeat import get_heartbeat_service
logger = logging.getLogger(__name__)
class NativeLLMAgent(CLIPluginAdapter):
"""
原生 LLM Agent
直接通过 LLM API 实现的 Agent特点
1. 无需外部进程,完全异步
2. 支持所有主流 LLM 提供商
3. 自动管理资源(文件锁、心跳)
4. 支持会议协作
"""
def __init__(
self,
agent_id: str,
name: str,
role: str,
model: str,
config: Dict[str, Any] = None,
llm_service: ModelRouter = None,
executor: AgentExecutor = None
):
self._id = agent_id
self._name = name
self._role = role
self._model = model
self.config = config or {}
self._version = "1.0.0"
# 获取服务
self.llm_service = llm_service or get_llm_service()
self.executor = executor or get_agent_executor(self.llm_service)
self.scheduler = get_meeting_scheduler()
self.registry = get_agent_registry()
self.heartbeat_service = get_heartbeat_service()
# 状态
self._is_running = False
self._current_task: Optional[Task] = None
logger.info(f"NativeLLMAgent 初始化: {self._id} ({self._name})")
# ========== 属性 ==========
@property
def id(self) -> str:
return self._id
@property
def name(self) -> str:
return self._name
@property
def version(self) -> str:
return self._version
@property
def role(self) -> str:
return self._role
@property
def model(self) -> str:
return self._model
@property
def capabilities(self) -> AgentCapabilities:
"""返回 Agent 能力声明"""
return AgentCapabilities(
can_execute_code=True,
can_read_files=True,
can_write_files=True,
can_analyze_code=True,
can_generate_tests=self._role in ["developer", "qa"],
can_review_code=self._role == "reviewer",
supported_languages=["Python", "JavaScript", "TypeScript", "Java", "Go"],
max_context_length=200000
)
@property
def is_running(self) -> bool:
return self._is_running
# ========== 核心能力 ==========
async def execute(self, task: Task) -> Result:
"""
执行任务
通过 AgentExecutor 协调 LLM 调用和资源管理
"""
self._current_task = task
try:
# 获取或注册 Agent 信息
agent_info = await self._ensure_registered()
# 使用执行引擎执行任务
result = await self.executor.execute_task(
agent_info,
task,
context=self.config.get("context", {})
)
logger.info(f"任务执行完成: {task.task_id} -> {result.success}")
return result
except Exception as e:
logger.error(f"任务执行失败: {task.task_id}: {e}", exc_info=True)
return Result(
success=False,
output="",
error=str(e)
)
finally:
self._current_task = None
async def join_meeting(self, meeting_id: str, timeout: int = 300) -> str:
"""
加入会议等待队列(栅栏同步)
当最后一个参与者到达时,会议自动开始
"""
logger.info(f"Agent {self._id} 等待会议: {meeting_id}")
# 更新心跳为等待状态
await self.update_heartbeat("waiting", f"等待会议: {meeting_id}", 0)
# 调用会议调度器
result = await self.scheduler.wait_for_meeting(
self._id,
meeting_id,
timeout=timeout
)
logger.info(f"会议 {meeting_id} 结果: {result}")
return result
async def write_state(self, state: Dict) -> None:
"""
写入状态到注册表
状态包含:当前任务、进度、临时数据等
"""
task = state.get("task", "")
progress = state.get("progress", 0)
await self.registry.update_state(self._id, task, progress)
logger.debug(f"状态已更新: {self._id} -> {progress}%")
async def read_others(self, agent_id: str) -> Dict:
"""
读取其他 Agent 的状态
用于 Agent 之间互相了解工作状态
"""
agent = await self.registry.get_agent(agent_id)
if not agent:
return {"error": f"Agent {agent_id} 不存在"}
state = await self.registry.get_state(agent_id)
heartbeat = await self.heartbeat_service.get_heartbeat(agent_id)
return {
"agent": {
"agent_id": agent.agent_id,
"name": agent.name,
"role": agent.role,
"model": agent.model,
"status": agent.status
},
"state": {
"current_task": state.current_task if state else None,
"progress": state.progress if state else 0
} if state else None,
"heartbeat": {
"status": heartbeat.status if heartbeat else None,
"last_seen": heartbeat.last_seen.isoformat() if heartbeat else None,
"is_alive": heartbeat.is_alive() if heartbeat else False
} if heartbeat else None
}
async def update_heartbeat(self, status: str, task: str = "", progress: int = 0) -> None:
"""
更新心跳
参数:
status: working, waiting, idle, error
task: 当前任务描述
progress: 进度 0-100
"""
await self.heartbeat_service.update_heartbeat(
self._id,
status,
task,
progress
)
# ========== 生命周期 ==========
async def initialize(self) -> None:
"""Agent 初始化"""
logger.info(f"Agent 初始化: {self._id}")
# 确保已注册
await self._ensure_registered()
# 发送初始心跳
await self.update_heartbeat("idle", "", 0)
self._is_running = True
async def shutdown(self) -> None:
"""Agent 关闭"""
logger.info(f"Agent 关闭: {self._id}")
# 更新状态为离线
await self.update_heartbeat("offline", "", 0)
self._is_running = False
async def health_check(self) -> bool:
"""健康检查"""
try:
heartbeat = await self.heartbeat_service.get_heartbeat(self._id)
return heartbeat is not None and heartbeat.is_alive()
except Exception:
return False
# ========== 会议相关 ==========
async def propose(self, meeting_id: str, content: str, step: str = "") -> None:
"""在会议中提出提案"""
from ..services.meeting_recorder import get_meeting_recorder
recorder = get_meeting_recorder()
await recorder.add_discussion(
meeting_id,
self._id,
self._role.upper(),
content,
step
)
logger.debug(f"提案已添加: {self._id} -> {meeting_id}")
async def discuss(self, meeting_id: str, content: str, step: str = "") -> None:
"""在会议中参与讨论"""
await self.propose(meeting_id, content, step)
async def vote(self, meeting_id: str, proposal_id: str, agree: bool) -> None:
"""对提案进行投票"""
# TODO: 实现投票机制
logger.debug(f"投票: {self._id} -> {proposal_id}: {agree}")
# ========== 私有方法 ==========
async def _ensure_registered(self):
"""确保 Agent 已注册"""
agent = await self.registry.get_agent(self._id)
if agent is None:
# 注册 Agent
agent = await self.registry.register_agent(
self._id,
self._name,
self._role,
self._model,
self.config.get("description", f"{self._name} - {self._role}")
)
logger.info(f"Agent 已注册: {self._id}")
return agent
# ========== 高级功能 ==========
async def collaborate_with(
self,
other_agent_ids: list,
task: str,
meeting_id: str = None
) -> Dict:
"""
与其他 Agent 协作完成任务
流程:
1. 创建或加入会议
2. 等待所有 Agent 到达
3. 讨论和分工
4. 执行分配的任务
5. 汇总结果
"""
if not meeting_id:
# 生成会议 ID
import uuid
meeting_id = f"meeting_{uuid.uuid4().hex[:12]}"
# 创建会议
await self.scheduler.create_meeting(
meeting_id,
f"协作任务: {task[:50]}",
[self._id] + other_agent_ids
)
# 更新心跳
await self.update_heartbeat("waiting", f"等待协作会议: {meeting_id}", 0)
# 提出初始提案
await self.propose(meeting_id, f"任务: {task}")
return {
"meeting_id": meeting_id,
"status": "waiting",
"participants": [self._id] + other_agent_ids
}
async def start_collaboration_loop(
self,
meeting_id: str,
max_iterations: int = 5
) -> Dict:
"""
启动协作循环
持续参与会议讨论,直到达成共识或达到最大迭代次数
"""
from ..services.meeting_recorder import get_meeting_recorder
recorder = get_meeting_recorder()
for iteration in range(max_iterations):
# 等待会议开始
result = await self.join_meeting(meeting_id)
if result == "started":
# 获取会议信息
meeting = await recorder.get_meeting(meeting_id)
if meeting and meeting.status == "completed":
return {
"status": "consensus_reached",
"consensus": meeting.consensus,
"iterations": iteration + 1
}
# 分析当前讨论,提出自己的观点
await self._analyze_and_respond(meeting_id)
elif result == "timeout":
return {
"status": "timeout",
"iterations": iteration + 1
}
return {
"status": "max_iterations_reached",
"iterations": max_iterations
}
async def _analyze_and_respond(self, meeting_id: str) -> None:
"""分析会议讨论并响应"""
from ..services.meeting_recorder import get_meeting_recorder
recorder = get_meeting_recorder()
meeting = await recorder.get_meeting(meeting_id)
if not meeting:
return
# 获取最近的讨论
recent_discussions = meeting.discussions[-5:] if meeting.discussions else []
# 构建分析提示
discussion_summary = "\n".join([
f"{d.agent} ({d.timestamp}): {d.content}"
for d in recent_discussions
])
response_prompt = f"""
你是 {self._name} ({self._role})。
以下是会议讨论的摘要:
{discussion_summary}
请基于你的角色 ({self._role}),给出你的回应:
- 如果你不同意之前的提案,说明理由
- 如果你有更好的建议,提出新方案
- 如果你同意,可以表示支持或补充细节
保持简洁,直接回应。
"""
# 调用 LLM 生成响应
if self.llm_service:
try:
llm_response = await self.llm_service.route_task(
task=response_prompt,
messages=[
{"role": "system", "content": f"你是 {self._name},一个 {self._role}"},
{"role": "user", "content": response_prompt}
]
)
# 发送响应到会议
await self.discuss(meeting_id, llm_response.content)
except Exception as e:
logger.error(f"生成会议响应失败: {e}")
class NativeLLMAgentFactory:
"""原生 LLM Agent 工厂类"""
@staticmethod
async def create(
agent_id: str,
name: str = None,
role: str = "developer",
model: str = "claude-sonnet-4.6",
config: Dict = None
) -> NativeLLMAgent:
"""
创建并初始化一个 Agent
参数:
agent_id: Agent 唯一标识
name: 显示名称(默认从 agent_id 生成)
role: 角色 (architect, pm, developer, qa, reviewer)
model: 使用的模型
config: 额外配置
"""
if name is None:
name = agent_id.replace("-", " ").title()
agent = NativeLLMAgent(
agent_id=agent_id,
name=name,
role=role,
model=model,
config=config
)
# 初始化 Agent
await agent.initialize()
return agent
@staticmethod
async def create_team(team_config: Dict) -> Dict[str, NativeLLMAgent]:
"""
创建一个 Agent 团队
配置格式:
{
"team_id": "dev-team-1",
"agents": [
{"id": "arch-001", "role": "architect", "model": "claude-opus-4.6"},
{"id": "dev-001", "role": "developer", "model": "claude-sonnet-4.6"},
{"id": "qa-001", "role": "qa", "model": "claude-haiku-4.6"}
]
}
"""
agents = {}
for agent_config in team_config.get("agents", []):
agent = await NativeLLMAgentFactory.create(
agent_id=agent_config["id"],
role=agent_config.get("role", "developer"),
model=agent_config.get("model", "claude-sonnet-4.6"),
config=agent_config.get("config", {})
)
agents[agent.id] = agent
return agents