[modify] manage multi agent logic

This commit is contained in:
Mark
2025-12-27 13:57:03 +08:00
parent 3bcde42ebb
commit 9edfd6c16f
5 changed files with 1284 additions and 1291 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,8 @@ import datetime
from typing import Optional, List, Dict, Any, Union
from pydantic import BaseModel, Field, ConfigDict, field_serializer
from app.schemas import ModelParameters
# ==================== 子 Agent 配置 ====================
@@ -30,7 +32,7 @@ class ExecutionConfig(BaseModel):
parallel_limit: int = Field(default=3, ge=1, le=10, description="并行限制")
retry_on_failure: bool = Field(default=True, description="失败时是否重试")
max_retries: int = Field(default=3, ge=0, le=10, description="最大重试次数")
# 新增:路由模式配置
routing_mode: str = Field(
default="master_agent",
@@ -41,14 +43,14 @@ class ExecutionConfig(BaseModel):
default=True,
description="是否启用规则快速路径(性能优化,高置信度关键词直接返回)"
)
# 新增:结果整合模式配置
result_merge_mode: str = Field(
default="smart",
pattern="^(smart|master)$",
description="结果整合模式smart规则去重快速| masterMaster Agent 智能整合,连贯)"
)
# 新增:子 Agent 执行模式配置
sub_agent_execution_mode: str = Field(
default="parallel",
@@ -82,6 +84,11 @@ class MultiAgentConfigUpdate(BaseModel):
"""更新多 Agent 配置"""
master_agent_id: Optional[uuid.UUID] = None
master_agent_name: Optional[str] = Field(None, max_length=100, description="主 Agent 名称")
default_model_config_id : uuid.UUID = Field(description="默认模型配置ID")
model_parameters: ModelParameters | None = Field(
default_factory=ModelParameters,
description="模型参数配置temperature、max_tokens 等)"
)
orchestration_mode: Optional[str] = Field(
None,
pattern="^(sequential|parallel|conditional|loop)$"
@@ -99,11 +106,16 @@ class MultiAgentConfigUpdate(BaseModel):
class MultiAgentConfigSchema(BaseModel):
"""多 Agent 配置输出"""
model_config = ConfigDict(from_attributes=True)
id: uuid.UUID
app_id: uuid.UUID
master_agent_id: uuid.UUID
master_agent_name: Optional[str]
default_model_config_id : uuid.UUID | None = Field(description="默认模型配置ID")
model_parameters: ModelParameters | None = Field(
default_factory=ModelParameters,
description="模型参数配置temperature、max_tokens 等)"
)
orchestration_mode: str
sub_agents: List[Dict[str, Any]]
routing_rules: Optional[List[Dict[str, Any]]]
@@ -112,11 +124,11 @@ class MultiAgentConfigSchema(BaseModel):
is_active: bool
created_at: datetime.datetime
updated_at: datetime.datetime
@field_serializer("created_at", when_used="json")
def _serialize_created_at(self, dt: datetime.datetime):
return int(dt.timestamp() * 1000) if dt else None
@field_serializer("updated_at", when_used="json")
def _serialize_updated_at(self, dt: datetime.datetime):
return int(dt.timestamp() * 1000) if dt else None

View File

@@ -5,63 +5,63 @@ import uuid
from typing import Dict, Any, List, Optional, Tuple
from sqlalchemy.orm import Session
from app.schemas import ModelParameters
from app.services.conversation_state_manager import ConversationStateManager
from app.models import ModelConfig, AgentConfig
from app.core.logging_config import get_business_logger
from app.services.model_service import ModelApiKeyService
logger = get_business_logger()
class MasterAgentRouter:
"""Master Agent 路由器
让 Master Agent 作为"大脑",负责:
1. 分析用户意图
2. 选择最合适的 Sub Agent
3. 决定是否需要多 Agent 协作
4. 管理会话上下文
优势:
- 更智能的决策(基于完整上下文)
- 减少 LLM 调用次数
- 架构更清晰Master Agent 真正起作用)
"""
def __init__(
self,
db: Session,
master_agent_config: AgentConfig,
master_model_config: ModelConfig,
model_parameters: ModelParameters,
sub_agents: Dict[str, Any],
state_manager: ConversationStateManager,
enable_rule_fast_path: bool = True
):
"""初始化 Master Agent 路由器
Args:
db: 数据库会话
master_agent_config: Master Agent 配置
master_model_config: Master Agent 使用的模型配置
sub_agents: 子 Agent 配置字典
state_manager: 会话状态管理器
enable_rule_fast_path: 是否启用规则快速路径(性能优化)
"""
self.db = db
self.master_agent_config = master_agent_config
self.master_model_config = master_model_config
self.model_parameters = model_parameters
self.sub_agents = sub_agents
self.state_manager = state_manager
self.enable_rule_fast_path = enable_rule_fast_path
logger.info(
"Master Agent 路由器初始化",
extra={
"master_agent": master_agent_config.name,
"sub_agent_count": len(sub_agents),
"enable_rule_fast_path": enable_rule_fast_path
}
)
async def route(
self,
message: str,
@@ -69,12 +69,12 @@ class MasterAgentRouter:
variables: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""智能路由决策
Args:
message: 用户消息
conversation_id: 会话 ID
variables: 变量参数
Returns:
路由决策结果
"""
@@ -85,12 +85,12 @@ class MasterAgentRouter:
"conversation_id": conversation_id
}
)
# 1. 获取会话状态
state = None
if conversation_id:
state = self.state_manager.get_state(conversation_id)
# 2. 尝试规则快速路径(可选的性能优化)
if self.enable_rule_fast_path:
rule_result = self._try_rule_fast_path(message, state)
@@ -102,7 +102,7 @@ class MasterAgentRouter:
"confidence": rule_result["confidence"]
}
)
# 更新会话状态
if conversation_id:
self.state_manager.update_state(
@@ -112,12 +112,12 @@ class MasterAgentRouter:
rule_result.get("topic"),
rule_result["confidence"]
)
return rule_result
# 3. 调用 Master Agent 做决策
decision = await self._master_agent_decide(message, state, variables)
# 4. 更新会话状态
if conversation_id:
self.state_manager.update_state(
@@ -127,7 +127,7 @@ class MasterAgentRouter:
decision.get("topic"),
decision["confidence"]
)
logger.info(
"Master Agent 路由完成",
extra={
@@ -136,22 +136,22 @@ class MasterAgentRouter:
"confidence": decision["confidence"]
}
)
return decision
def _try_rule_fast_path(
self,
message: str,
state: Optional[Dict[str, Any]]
) -> Optional[Dict[str, Any]]:
"""尝试规则快速路径(性能优化)
对于明确的关键词匹配,直接返回结果,不调用 Master Agent
Args:
message: 用户消息
state: 会话状态
Returns:
如果命中规则返回决策结果,否则返回 None
"""
@@ -178,15 +178,15 @@ class MasterAgentRouter:
"confidence_threshold": 0.9
}
]
message_lower = message.lower()
for rule in high_confidence_rules:
matched_keywords = [kw for kw in rule["keywords"] if kw in message_lower]
if matched_keywords:
confidence = len(matched_keywords) / len(rule["keywords"])
if confidence >= rule["confidence_threshold"]:
# 查找对应的 agent
for agent_id, agent_data in self.sub_agents.items():
@@ -201,9 +201,9 @@ class MasterAgentRouter:
"need_collaboration": False,
"routing_method": "rule"
}
return None
async def _master_agent_decide(
self,
message: str,
@@ -211,35 +211,35 @@ class MasterAgentRouter:
variables: Optional[Dict[str, Any]]
) -> Dict[str, Any]:
"""让 Master Agent 做路由决策
Args:
message: 用户消息
state: 会话状态
variables: 变量参数
Returns:
决策结果
"""
# 1. 构建决策 prompt
prompt = self._build_decision_prompt(message, state, variables)
# 2. 调用 Master Agent 的 LLM
try:
response = await self._call_master_agent_llm(prompt)
# 3. 解析决策
decision = self._parse_decision(response)
# 4. 验证决策
decision = self._validate_decision(decision)
return decision
except Exception as e:
logger.error(f"Master Agent 决策失败: {str(e)}")
# 降级到默认 agent
return self._get_fallback_decision(message)
def _build_decision_prompt(
self,
message: str,
@@ -247,12 +247,12 @@ class MasterAgentRouter:
variables: Optional[Dict[str, Any]]
) -> str:
"""构建 Master Agent 的决策 prompt
Args:
message: 用户消息
state: 会话状态
variables: 变量参数
Returns:
prompt 字符串
"""
@@ -260,29 +260,29 @@ class MasterAgentRouter:
agent_descriptions = []
for agent_id, agent_data in self.sub_agents.items():
agent_info = agent_data.get("info", {})
name = agent_info.get("name", "未命名")
role = agent_info.get("role", "")
capabilities = agent_info.get("capabilities", [])
# 简化格式:一行描述
desc = f"- {agent_id}: {name}"
if role:
desc += f" ({role})"
if capabilities:
desc += f" - {', '.join(capabilities[:3])}" # 只取前3个能力
agent_descriptions.append(desc)
agents_text = "\n".join(agent_descriptions)
# 2. 构建会话上下文
context_text = ""
if state:
current_agent = state.get("current_agent_id")
last_topic = state.get("last_topic")
same_turns = state.get("same_agent_turns", 0)
if current_agent:
context_text = f"""
当前会话上下文:
@@ -290,10 +290,10 @@ class MasterAgentRouter:
- 上一个主题: {last_topic}
- 连续使用轮数: {same_turns}
"""
# 获取第一个可用的 agent_id 作为示例
example_agent_id = next(iter(self.sub_agents.keys())) if self.sub_agents else "agent_id"
# 3. 构建完整 prompt简化版提升性能
prompt = f"""路由任务:分析问题并选择合适的 Agent。
@@ -331,15 +331,15 @@ class MasterAgentRouter:
6. 只做路由决策,不要回答问题内容
请返回 JSON"""
return prompt
async def _call_master_agent_llm(self, prompt: str) -> str:
"""调用 Master Agent 的 LLM
Args:
prompt: 提示词
Returns:
LLM 响应
"""
@@ -347,16 +347,13 @@ class MasterAgentRouter:
from app.core.models import RedBearLLM
from app.core.models.base import RedBearModelConfig
from app.models import ModelApiKey, ModelType
# 获取 API Key 配置
api_key_config = self.db.query(ModelApiKey).filter(
ModelApiKey.model_config_id == self.master_model_config.id,
ModelApiKey.is_active == True
).first()
api_key_config = ModelApiKeyService.get_a_api_key(self.db, self.master_model_config.id)
if not api_key_config:
raise Exception("Master Agent 模型没有可用的 API Key")
logger.info(
"调用 Master Agent LLM",
extra={
@@ -364,39 +361,44 @@ class MasterAgentRouter:
"model_name": api_key_config.model_name
}
)
temperature = 0.3 # 决策任务使用较低温度
max_tokens = 1000
extra_params = None
if self.model_parameters:
extra_params = {"temperature": temperature,
"max_tokens":max_tokens
}
# 创建 RedBearModelConfig
model_config = RedBearModelConfig(
model_name=api_key_config.model_name,
provider=api_key_config.provider,
api_key=api_key_config.api_key,
base_url=api_key_config.api_base,
temperature=0.3, # 决策任务使用较低温度
max_tokens=1000
extra_params = extra_params
)
# 创建 LLM 实例
llm = RedBearLLM(model_config, type=ModelType.CHAT)
# 调用模型
response = await llm.ainvoke(prompt)
# 提取响应内容
if hasattr(response, 'content'):
return response.content
else:
return str(response)
except Exception as e:
logger.error(f"Master Agent LLM 调用失败: {str(e)}")
raise
def _parse_decision(self, response: str) -> Dict[str, Any]:
"""解析 Master Agent 的决策
Args:
response: LLM 响应
Returns:
决策字典
"""
@@ -405,29 +407,29 @@ class MasterAgentRouter:
json_match = re.search(r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}', response, re.DOTALL)
if json_match:
decision = json.loads(json_match.group())
# 添加默认值
decision.setdefault("confidence", 0.8)
decision.setdefault("strategy", "master_agent")
decision.setdefault("routing_method", "master_agent")
decision.setdefault("need_collaboration", False)
decision.setdefault("collaboration_agents", [])
return decision
else:
raise ValueError("无法从响应中提取 JSON")
except Exception as e:
logger.error(f"解析 Master Agent 决策失败: {str(e)}")
logger.debug(f"原始响应: {response}")
raise
def _validate_decision(self, decision: Dict[str, Any]) -> Dict[str, Any]:
"""验证决策的有效性
Args:
decision: 决策字典
Returns:
验证后的决策
"""
@@ -439,26 +441,26 @@ class MasterAgentRouter:
decision["selected_agent_id"] = self._get_default_agent_id()
decision["confidence"] = 0.5
decision["reasoning"] = "原始选择无效,使用默认 Agent"
# 验证 confidence
confidence = decision.get("confidence", 0.8)
if not isinstance(confidence, (int, float)) or confidence < 0 or confidence > 1:
decision["confidence"] = 0.8
# 验证协作 agents
if decision.get("need_collaboration"):
# 检查是否是问题拆分模式
if decision.get("need_decomposition") or decision.get("sub_questions"):
# 问题拆分模式
sub_questions = decision.get("sub_questions", [])
# 验证每个子问题
valid_sub_questions = []
for sub_q in sub_questions:
if isinstance(sub_q, dict):
agent_id = sub_q.get("agent_id")
question = sub_q.get("question")
if agent_id in self.sub_agents and question:
# 确保有必要的字段
sub_q.setdefault("order", len(valid_sub_questions) + 1)
@@ -475,9 +477,9 @@ class MasterAgentRouter:
"available_agents": list(self.sub_agents.keys())
}
)
decision["sub_questions"] = valid_sub_questions
# 如果所有子问题都验证失败,降级处理
if not valid_sub_questions and sub_questions:
logger.warning(
@@ -496,10 +498,10 @@ class MasterAgentRouter:
first_agent_id = next(iter(self.sub_agents.keys()))
decision["selected_agent_id"] = first_agent_id
logger.info(f"降级使用默认 Agent: {first_agent_id}")
# 设置协作策略为 decomposition
decision["collaboration_strategy"] = "decomposition"
logger.info(
"问题拆分决策验证完成",
extra={
@@ -510,7 +512,7 @@ class MasterAgentRouter:
else:
# 普通协作模式
collaboration_agents = decision.get("collaboration_agents", [])
# 如果是简单列表格式,转换为详细格式
if collaboration_agents and isinstance(collaboration_agents[0], str):
collaboration_agents = [
@@ -522,7 +524,7 @@ class MasterAgentRouter:
}
for i, agent_id in enumerate(collaboration_agents)
]
# 验证每个协作 agent
valid_agents = []
for agent_info in collaboration_agents:
@@ -541,13 +543,13 @@ class MasterAgentRouter:
"task": "协作处理",
"order": len(valid_agents) + 1
})
decision["collaboration_agents"] = valid_agents
# 设置默认协作策略
if not decision.get("collaboration_strategy"):
decision["collaboration_strategy"] = "sequential"
logger.info(
"协作决策验证完成",
extra={
@@ -555,20 +557,20 @@ class MasterAgentRouter:
"strategy": decision.get("collaboration_strategy")
}
)
return decision
def _get_fallback_decision(self, message: str) -> Dict[str, Any]:
"""获取降级决策(当 Master Agent 失败时)
Args:
message: 用户消息
Returns:
降级决策
"""
default_agent_id = self._get_default_agent_id()
return {
"selected_agent_id": default_agent_id,
"confidence": 0.5,
@@ -579,15 +581,15 @@ class MasterAgentRouter:
"collaboration_agents": [],
"routing_method": "fallback"
}
def _get_default_agent_id(self) -> str:
"""获取默认 Agent ID
Returns:
默认 Agent ID
"""
if self.sub_agents:
# 返回第一个 agent
return next(iter(self.sub_agents.keys()))
return "default-agent"

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,19 @@
"""多 Agent 配置管理服务"""
import uuid
from typing import Optional, List, Tuple, Any
from typing import Optional, List, Tuple, Any, Annotated
from fastapi import Depends
from sqlalchemy.orm import Session
from sqlalchemy import select, desc
from app.db import get_db
from app.models import MultiAgentConfig, App, AgentConfig
from app.schemas.multi_agent_schema import (
MultiAgentConfigCreate,
MultiAgentConfigUpdate,
MultiAgentRunRequest
)
from app.services.model_service import ModelApiKeyService
from app.services.multi_agent_orchestrator import MultiAgentOrchestrator
from app.core.exceptions import ResourceNotFoundException, BusinessException
from app.core.error_codes import BizCode
@@ -21,10 +25,10 @@ logger = get_business_logger()
def convert_uuids_to_str(obj: Any) -> Any:
"""递归转换对象中的所有 UUID 为字符串
Args:
obj: 要转换的对象dict, list, UUID 等)
Returns:
转换后的对象
"""
@@ -40,10 +44,10 @@ def convert_uuids_to_str(obj: Any) -> Any:
class MultiAgentService:
"""多 Agent 配置管理服务"""
def __init__(self, db: Session):
self.db = db
def create_config(
self,
app_id: uuid.UUID,
@@ -51,12 +55,12 @@ class MultiAgentService:
created_by: uuid.UUID
) -> MultiAgentConfig:
"""创建多 Agent 配置
Args:
app_id: 应用 ID
data: 配置数据
created_by: 创建者 ID
Returns:
多 Agent 配置
"""
@@ -64,7 +68,7 @@ class MultiAgentService:
app = self.db.get(App, app_id)
if not app:
raise ResourceNotFoundException("应用", str(app_id))
# 2. 检查是否已有有效配置
existing = self.db.scalars(
select(MultiAgentConfig)
@@ -76,22 +80,22 @@ class MultiAgentService:
).first()
if existing:
raise BusinessException("应用已有多 Agent 配置", BizCode.DUPLICATE_RESOURCE)
# 3. 验证主 Agent 存在
master_agent = self.db.get(AgentConfig, data.master_agent_id)
if not master_agent:
raise ResourceNotFoundException("主 Agent", str(data.master_agent_id))
# 4. 验证子 Agent 存在
for sub_agent in data.sub_agents:
agent = self.db.get(AgentConfig, sub_agent.agent_id)
if not agent:
raise ResourceNotFoundException("子 Agent", str(sub_agent.agent_id))
# 5. 创建配置(转换 UUID 为字符串以支持 JSON 序列化)
sub_agents_data = [convert_uuids_to_str(sub_agent.model_dump()) for sub_agent in data.sub_agents]
routing_rules_data = [convert_uuids_to_str(rule.model_dump()) for rule in data.routing_rules] if data.routing_rules else None
# 处理 execution_config可能是 None、字典或 Pydantic 模型)
if data.execution_config is None:
execution_config_data = {}
@@ -99,7 +103,7 @@ class MultiAgentService:
execution_config_data = convert_uuids_to_str(data.execution_config)
else:
execution_config_data = convert_uuids_to_str(data.execution_config.model_dump())
config = MultiAgentConfig(
app_id=app_id,
master_agent_id=data.master_agent_id,
@@ -110,11 +114,11 @@ class MultiAgentService:
execution_config=execution_config_data,
aggregation_strategy=data.aggregation_strategy
)
self.db.add(config)
self.db.commit()
self.db.refresh(config)
logger.info(
"创建多 Agent 配置成功",
extra={
@@ -124,15 +128,15 @@ class MultiAgentService:
"sub_agent_count": len(data.sub_agents)
}
)
return config
def get_config(self, app_id: uuid.UUID) -> Optional[MultiAgentConfig]:
"""获取多 Agent 配置
Args:
app_id: 应用 ID
Returns:
多 Agent 配置,如果不存在返回 None
"""
@@ -144,24 +148,25 @@ class MultiAgentService:
)
.order_by(MultiAgentConfig.updated_at.desc())
).first()
def get_multi_agent_configs(self, app_id: uuid.UUID) -> Optional[dict]:
"""通过 app_id 获取最新有效的多智能体配置,并将 agent_id 转换为 app_id
Args:
app_id: 应用 ID
Returns:
转换后的配置字典,如果不存在返回 None
"""
config = self.get_config(app_id)
if not config:
return None
# 转换 master_agent_id (release_id) 为 app_id
master_release = self.db.get(AppRelease, config.master_agent_id)
master_app_id = master_release.app_id if master_release else config.master_agent_id
#兼容代码
if not config.default_model_config_id:
master_release = self.db.get(AppRelease, config.master_agent_id)
config.default_model_config_id = master_release.default_model_config_id if master_release else None
# 转换 sub_agents 中的 agent_id (release_id) 为 app_id
converted_sub_agents = []
for sub_agent in config.sub_agents:
@@ -176,13 +181,13 @@ class MultiAgentService:
except Exception as e:
logger.warning(f"转换 sub_agent agent_id 失败: {release_id}, 错误: {str(e)}")
converted_sub_agents.append(sub_agent_copy)
# 构建返回的配置字典
return {
"id": config.id,
"app_id": config.app_id,
"master_agent_id": master_app_id,
"master_agent_name": config.master_agent_name,
"default_model_config_id": config.default_model_config_id,
"model_parameters": config.model_parameters,
"orchestration_mode": config.orchestration_mode,
"sub_agents": converted_sub_agents,
"routing_rules": config.routing_rules,
@@ -192,133 +197,134 @@ class MultiAgentService:
"created_at": config.created_at,
"updated_at": config.updated_at
}
def get_published_config_by_agent_id(self, agent_id: uuid.UUID) -> Optional[dict]:
"""通过 agent_id 获取当前发布版本的完整配置
Args:
agent_id: Agent 配置 ID
Returns:
当前发布版本的配置字典,如果没有发布版本则返回 None
"""
from app.models import AppRelease
# 查询 Agent 配置
agent_config = self.db.get(AgentConfig, agent_id)
if not agent_config:
logger.warning(f"Agent 配置不存在: {agent_id}")
return None
# 获取关联的应用
app = self.db.get(App, agent_config.app_id)
if not app or not app.current_release_id:
logger.warning(f"应用未发布或不存在: app_id={agent_config.app_id}")
return None
# 获取当前发布版本
release = self.db.get(AppRelease, app.current_release_id)
if not release:
logger.warning(f"发布版本不存在: release_id={app.current_release_id}")
return None
# 从发布版本的 config 中获取完整配置
# config 是一个 JSON 对象,包含了发布时的配置快照
config_data = release.config
if config_data and isinstance(config_data, dict):
return config_data
return None
def get_published_by_agent_id(self, agent_id: uuid.UUID) -> Optional[AppRelease]:
"""通过 agent_id 获取当前发布版本的完整配置
Args:
agent_id: Agent 配置 ID
Returns:
当前发布版本的配置字典,如果没有发布版本则返回 None
"""
# 获取关联的应用
app = self.db.get(App, agent_id)
if not app or not app.current_release_id:
logger.warning(f"应用未发布或不存在: app_id={agent_id}")
return None
# 获取当前发布版本
release = self.db.get(AppRelease, app.current_release_id)
if not release:
logger.warning(f"发布版本不存在: release_id={app.current_release_id}")
return None
return release
def update_config(
self,
app_id: uuid.UUID,
data: MultiAgentConfigUpdate
) -> MultiAgentConfig:
"""更新多 Agent 配置
Args:
app_id: 应用 ID
data: 更新数据
Returns:
更新后的配置
"""
config = self.get_config(app_id)
if not config:
# 1. 验证应用存在
app = self.db.get(App, app_id)
if not app:
raise ResourceNotFoundException("应用", str(app_id))
# 2. 验证主 Agent 存在并获取发布版本 ID
master_app_release = self.get_published_by_agent_id(data.master_agent_id)
if not master_app_release:
raise ResourceNotFoundException("主 Agent 未发布或不存在", str(data.master_agent_id))
# 使用发布版本 ID
data.master_agent_id = master_app_release.id
# 3. 验证子 Agent 存在并获取发布版本 ID
for sub_agent in data.sub_agents:
agent_app_release = self.get_published_by_agent_id(sub_agent.agent_id)
if not agent_app_release:
raise ResourceNotFoundException("子 Agent 未发布或不存在", str(sub_agent.agent_id))
def check_config_data(self,app_id: uuid.UUID, data: MultiAgentConfigUpdate) -> MultiAgentConfig:
# 1. 验证应用存在
app = self.db.get(App, app_id)
if not app:
raise ResourceNotFoundException("应用", str(app_id))
# 2. 验证模型配置
model_api_key = ModelApiKeyService.get_a_api_key(self.db,data.default_model_config_id)
if not model_api_key:
raise ResourceNotFoundException("模型配置", str(data.default_model_config_id))
# 3. 验证子 Agent 存在并获取发布版本 ID
for sub_agent in data.sub_agents:
agent_app_release = self.get_published_by_agent_id(sub_agent.agent_id)
if not agent_app_release:
raise ResourceNotFoundException("子 Agent 未发布或不存在", str(sub_agent.agent_id))
# 使用发布版本 ID
sub_agent.agent_id = agent_app_release.id
# 5. 创建配置(转换 UUID 为字符串以支持 JSON 序列化)
sub_agents_data = [convert_uuids_to_str(sub_agent.model_dump()) for sub_agent in data.sub_agents]
# routing_rules_data = [convert_uuids_to_str(rule.model_dump()) for rule in data.routing_rules] if data.routing_rules else None
# 处理 execution_config(可能是 None、字典或 Pydantic 模型)
if data.execution_config is None:
execution_config_data = {}
elif isinstance(data.execution_config, dict):
sub_agent.agent_id = agent_app_release.id
# 5. 创建配置(转换 UUID 为字符串以支持 JSON 序列化)
sub_agents_data = [convert_uuids_to_str(sub_agent.model_dump()) for sub_agent in data.sub_agents]
# routing_rules_data = [convert_uuids_to_str(rule.model_dump()) for rule in data.routing_rules] if data.routing_rules else None
# 处理 execution_config可能是 None、字典或 Pydantic 模型)
if data.execution_config is None:
execution_config_data = {}
elif isinstance(data.execution_config, dict):
execution_config_data = convert_uuids_to_str(data.execution_config)
else:
execution_config_data = convert_uuids_to_str(data.execution_config.model_dump())
config = MultiAgentConfig(
else:
execution_config_data = convert_uuids_to_str(data.execution_config.model_dump())
config = MultiAgentConfig(
app_id=app_id,
master_agent_id=data.master_agent_id,
master_agent_name=data.master_agent_name,
default_model_config_id=data.default_model_config_id,
model_parameters=data.model_parameters,
orchestration_mode=data.orchestration_mode,
sub_agents=sub_agents_data,
# routing_rules=routing_rules_data,
execution_config=execution_config_data,
aggregation_strategy=data.aggregation_strategy
)
return config
def update_config(
self,
app_id: uuid.UUID,
data: MultiAgentConfigUpdate
) -> MultiAgentConfig:
"""更新多 Agent 配置
Args:
app_id: 应用 ID
data: 更新数据
Returns:
更新后的配置
"""
config = self.get_config(app_id)
newConfig = self.check_config_data(app_id, data)
if not config:
config = newConfig
self.db.add(config)
self.db.commit()
self.db.refresh(config)
logger.info(
"创建多 Agent 配置成功",
extra={
@@ -329,56 +335,17 @@ class MultiAgentService:
}
)
return config
# raise ResourceNotFoundException("多 Agent 配置", str(app_id))
# 更新字段
if data.master_agent_id is not None:
# 验证主 Agent 存在
# 3. 验证主 Agent 存在并获取发布配置
master_app_release = self.get_published_by_agent_id(data.master_agent_id)
if not master_app_release:
raise ResourceNotFoundException("主 Agent 未发布或", str(data.master_agent_id))
config.master_agent_id = master_app_release.id
if data.master_agent_name is not None:
config.master_agent_name = data.master_agent_name
if data.orchestration_mode is not None:
config.orchestration_mode = data.orchestration_mode
if data.sub_agents is not None:
# 验证子 Agent 存在,并获取其发布的 config_id
updated_sub_agents = []
for sub_agent in data.sub_agents:
agent_app_release = self.get_published_by_agent_id(sub_agent.agent_id)
if not agent_app_release:
raise ResourceNotFoundException("子 Agent 未发布或", str(sub_agent.agent_id))
sub_agent.agent_id = agent_app_release.id
sub_agent_dict = convert_uuids_to_str(sub_agent.model_dump())
updated_sub_agents.append(sub_agent_dict)
config.sub_agents = updated_sub_agents
# if data.routing_rules is not None:
# config.routing_rules = [convert_uuids_to_str(rule.model_dump()) for rule in data.routing_rules] if data.routing_rules else None
if data.execution_config is not None:
if isinstance(data.execution_config, dict):
execution_config_data = convert_uuids_to_str(data.execution_config)
else:
execution_config_data = convert_uuids_to_str(data.execution_config.model_dump())
config.execution_config = execution_config_data
if data.aggregation_strategy is not None:
config.aggregation_strategy = data.aggregation_strategy
if data.is_active is not None:
config.is_active = data.is_active
config.default_model_config_id = newConfig.default_model_config_id
config.model_parameters = newConfig.model_parameters
config.orchestration_mode = newConfig.orchestration_mode
config.sub_agents = newConfig.sub_agents
config.routing_rules = newConfig.routing_rules
config.execution_config = newConfig.execution_config
config.aggregation_strategy = newConfig.aggregation_strategy
self.db.commit()
self.db.refresh(config)
logger.info(
"更新多 Agent 配置成功",
extra={
@@ -386,23 +353,23 @@ class MultiAgentService:
"app_id": str(app_id)
}
)
return config
def delete_config(self, app_id: uuid.UUID) -> None:
"""删除多 Agent 配置
Args:
app_id: 应用 ID
"""
config = self.get_config(app_id)
if not config:
raise ResourceNotFoundException("多 Agent 配置", str(app_id))
# 逻辑删除多 Agent 配置
config.is_active = False
self.db.commit()
logger.info(
"删除多 Agent 配置成功",
extra={
@@ -410,18 +377,18 @@ class MultiAgentService:
"app_id": str(app_id)
}
)
async def run(
self,
app_id: uuid.UUID,
request: MultiAgentRunRequest
) -> dict:
"""运行多 Agent 任务
Args:
app_id: 应用 ID
request: 运行请求
Returns:
执行结果
"""
@@ -429,13 +396,13 @@ class MultiAgentService:
config = self.get_config(app_id)
if not config:
raise ResourceNotFoundException("多 Agent 配置", str(app_id))
if not config.is_active:
raise BusinessException("多 Agent 配置已禁用", BizCode.RESOURCE_DISABLED)
# 2. 创建编排器
orchestrator = MultiAgentOrchestrator(self.db, config)
# 3. 执行任务
result = await orchestrator.execute(
message=request.message,
@@ -446,9 +413,9 @@ class MultiAgentService:
web_search=getattr(request, 'web_search', False), # 网络搜索参数
memory=getattr(request, 'memory', True) # 记忆功能参数
)
return result
async def run_stream(
self,
app_id: uuid.UUID,
@@ -457,11 +424,11 @@ class MultiAgentService:
user_rag_memory_id :str
):
"""运行多 Agent 任务(流式返回)
Args:
app_id: 应用 ID
request: 运行请求
Yields:
SSE 格式的事件流
"""
@@ -469,13 +436,13 @@ class MultiAgentService:
config = self.get_config(app_id)
if not config:
raise ResourceNotFoundException("多 Agent 配置", str(app_id))
if not config.is_active:
raise BusinessException("多 Agent 配置已禁用", BizCode.RESOURCE_DISABLED)
# 2. 创建编排器
orchestrator = MultiAgentOrchestrator(self.db, config)
# 3. 流式执行任务
async for event in orchestrator.execute_stream(
message=request.message,
@@ -489,113 +456,113 @@ class MultiAgentService:
user_rag_memory_id=user_rag_memory_id
):
yield event
def add_sub_agent(
self,
app_id: uuid.UUID,
agent_id: uuid.UUID,
name: str,
role: Optional[str] = None,
priority: int = 1,
capabilities: Optional[List[str]] = None
) -> MultiAgentConfig:
"""添加子 Agent
Args:
app_id: 应用 ID
agent_id: Agent ID
name: Agent 名称
role: 角色描述
priority: 优先级
capabilities: 能力列表
Returns:
更新后的配置
"""
config = self.get_config(app_id)
if not config:
raise ResourceNotFoundException("多 Agent 配置", str(app_id))
# 验证 Agent 存在
agent = self.db.get(AgentConfig, agent_id)
if not agent:
raise ResourceNotFoundException("Agent", str(agent_id))
# 检查是否已存在
for sub_agent in config.sub_agents:
if sub_agent["agent_id"] == str(agent_id):
raise BusinessException("Agent 已存在于配置中", BizCode.DUPLICATE_RESOURCE)
# 添加子 Agent
new_sub_agent = {
"agent_id": str(agent_id),
"name": name,
"role": role,
"priority": priority,
"capabilities": capabilities or []
}
config.sub_agents.append(new_sub_agent)
# 标记为已修改
self.db.add(config)
self.db.commit()
self.db.refresh(config)
logger.info(
"添加子 Agent 成功",
extra={
"config_id": str(config.id),
"agent_id": str(agent_id),
"agent_name": name
}
)
return config
def remove_sub_agent(
self,
app_id: uuid.UUID,
agent_id: uuid.UUID
) -> MultiAgentConfig:
"""移除子 Agent
Args:
app_id: 应用 ID
agent_id: Agent ID
Returns:
更新后的配置
"""
config = self.get_config(app_id)
if not config:
raise ResourceNotFoundException("多 Agent 配置", str(app_id))
# 查找并移除
original_count = len(config.sub_agents)
config.sub_agents = [
sub_agent for sub_agent in config.sub_agents
if sub_agent["agent_id"] != str(agent_id)
]
if len(config.sub_agents) == original_count:
raise ResourceNotFoundException("子 Agent", str(agent_id))
# 标记为已修改
self.db.add(config)
self.db.commit()
self.db.refresh(config)
logger.info(
"移除子 Agent 成功",
extra={
"config_id": str(config.id),
"agent_id": str(agent_id)
}
)
return config
# def add_sub_agent(
# self,
# app_id: uuid.UUID,
# agent_id: uuid.UUID,
# name: str,
# role: Optional[str] = None,
# priority: int = 1,
# capabilities: Optional[List[str]] = None
# ) -> MultiAgentConfig:
# """添加子 Agent
# Args:
# app_id: 应用 ID
# agent_id: Agent ID
# name: Agent 名称
# role: 角色描述
# priority: 优先级
# capabilities: 能力列表
# Returns:
# 更新后的配置
# """
# config = self.get_config(app_id)
# if not config:
# raise ResourceNotFoundException("多 Agent 配置", str(app_id))
# # 验证 Agent 存在
# agent = self.db.get(AgentConfig, agent_id)
# if not agent:
# raise ResourceNotFoundException("Agent", str(agent_id))
# # 检查是否已存在
# for sub_agent in config.sub_agents:
# if sub_agent["agent_id"] == str(agent_id):
# raise BusinessException("Agent 已存在于配置中", BizCode.DUPLICATE_RESOURCE)
# # 添加子 Agent
# new_sub_agent = {
# "agent_id": str(agent_id),
# "name": name,
# "role": role,
# "priority": priority,
# "capabilities": capabilities or []
# }
# config.sub_agents.append(new_sub_agent)
# # 标记为已修改
# self.db.add(config)
# self.db.commit()
# self.db.refresh(config)
# logger.info(
# "添加子 Agent 成功",
# extra={
# "config_id": str(config.id),
# "agent_id": str(agent_id),
# "agent_name": name
# }
# )
# return config
# def remove_sub_agent(
# self,
# app_id: uuid.UUID,
# agent_id: uuid.UUID
# ) -> MultiAgentConfig:
# """移除子 Agent
# Args:
# app_id: 应用 ID
# agent_id: Agent ID
# Returns:
# 更新后的配置
# """
# config = self.get_config(app_id)
# if not config:
# raise ResourceNotFoundException("多 Agent 配置", str(app_id))
# # 查找并移除
# original_count = len(config.sub_agents)
# config.sub_agents = [
# sub_agent for sub_agent in config.sub_agents
# if sub_agent["agent_id"] != str(agent_id)
# ]
# if len(config.sub_agents) == original_count:
# raise ResourceNotFoundException("子 Agent", str(agent_id))
# # 标记为已修改
# self.db.add(config)
# self.db.commit()
# self.db.refresh(config)
# logger.info(
# "移除子 Agent 成功",
# extra={
# "config_id": str(config.id),
# "agent_id": str(agent_id)
# }
# )
# return config
def list_configs(
self,
workspace_id: uuid.UUID,
@@ -603,12 +570,12 @@ class MultiAgentService:
pagesize: int = 20
) -> Tuple[List[MultiAgentConfig], int]:
"""列出多 Agent 配置
Args:
workspace_id: 工作空间 ID
page: 页码
pagesize: 每页数量
Returns:
配置列表和总数
"""
@@ -619,13 +586,21 @@ class MultiAgentService:
.where(App.workspace_id == workspace_id)
.order_by(desc(MultiAgentConfig.created_at))
)
# 总数
count_stmt = stmt.with_only_columns(MultiAgentConfig.id)
total = len(self.db.execute(count_stmt).all())
# 分页
stmt = stmt.offset((page - 1) * pagesize).limit(pagesize)
configs = list(self.db.scalars(stmt).all())
return configs, total
# ==================== 依赖注入函数 ====================
def get_multi_agent_service(
db: Annotated[Session, Depends(get_db)]
) -> MultiAgentService:
"""获取工作流服务(依赖注入)"""
return MultiAgentService(db)