fischer-agentkit/src/agentkit/org/context.py

174 lines
6.1 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.

"""OrganizationContext - 组织上下文,管理 AgentProfile 与能力矩阵"""
from __future__ import annotations
import logging
from dataclasses import dataclass, field
logger = logging.getLogger(__name__)
@dataclass
class AgentProfile:
"""Agent 档案 - 描述组织中一个 Agent 的能力与状态"""
name: str
agent_type: str # "react", "rewoo", "plan_exec", "reflexion", "direct"
capabilities: list[str] # capability tag strings
skills: list[str] # skill names
current_load: int = 0 # number of active tasks
max_concurrency: int = 1
availability: bool = True
specializations: list[str] = field(default_factory=list)
model: str = "default"
execution_mode: str = "react"
class OrganizationContext:
"""组织上下文 - 管理 Agent 档案与能力矩阵,支持基于能力的 Agent 发现"""
def __init__(self) -> None:
self._agents: dict[str, AgentProfile] = {}
self._capability_matrix: dict[str, list[str]] = {} # capability -> [agent_names]
def register_agent(self, profile: AgentProfile) -> None:
"""注册 Agent 档案"""
self._agents[profile.name] = profile
# 更新能力矩阵
for cap in profile.capabilities:
cap_lower = cap.lower()
if cap_lower not in self._capability_matrix:
self._capability_matrix[cap_lower] = []
if profile.name not in self._capability_matrix[cap_lower]:
self._capability_matrix[cap_lower].append(profile.name)
logger.info(f"Agent profile '{profile.name}' registered")
def unregister_agent(self, name: str) -> None:
"""注销 Agent 档案"""
profile = self._agents.pop(name, None)
if profile is None:
return
# 清理能力矩阵
for cap in profile.capabilities:
cap_lower = cap.lower()
if cap_lower in self._capability_matrix:
self._capability_matrix[cap_lower] = [
n for n in self._capability_matrix[cap_lower] if n != name
]
if not self._capability_matrix[cap_lower]:
del self._capability_matrix[cap_lower]
logger.info(f"Agent profile '{name}' unregistered")
def get_agent_profile(self, name: str) -> AgentProfile | None:
"""获取 Agent 档案"""
return self._agents.get(name)
def list_agents(self) -> list[AgentProfile]:
"""列出所有 Agent 档案"""
return list(self._agents.values())
def find_best_agent(
self,
required_capabilities: list[str],
exclude: list[str] | None = None,
) -> AgentProfile | None:
"""根据能力需求找到最佳 Agent
逻辑:
1. 找到拥有所有所需能力的 Agent
2. 在匹配的 Agent 中,优先选择 current_load 较低的
3. 排除 exclude 列表中的 Agent
4. 排除不可用的 Agent
5. 没有匹配则返回 None
"""
exclude_set = set(exclude or [])
# 对每个所需能力,查找拥有该能力的 Agent 名称集合
candidate_names: set[str] | None = None
for cap in required_capabilities:
cap_lower = cap.lower()
agents_with_cap = set(self._capability_matrix.get(cap_lower, []))
if candidate_names is None:
candidate_names = agents_with_cap
else:
candidate_names &= agents_with_cap
if not candidate_names:
return None
# 过滤排除和不可用的 Agent按 load 排序
candidates = [
self._agents[name]
for name in candidate_names
if name not in exclude_set
and name in self._agents
and self._agents[name].availability
]
if not candidates:
return None
candidates.sort(key=lambda p: p.current_load)
return candidates[0]
def update_load(self, name: str, delta: int) -> None:
"""更新 Agent 负载"""
profile = self._agents.get(name)
if profile is not None:
profile.current_load = max(0, profile.current_load + delta)
def set_availability(self, name: str, available: bool) -> None:
"""设置 Agent 可用性"""
profile = self._agents.get(name)
if profile is not None:
profile.availability = available
@classmethod
def from_agent_pool(cls, agent_pool, skill_registry) -> OrganizationContext:
"""从 AgentPool 和 SkillRegistry 构建 OrganizationContext
Args:
agent_pool: AgentPool 实例,提供运行时 Agent 列表
skill_registry: SkillRegistry 实例,提供 Skill 配置查询
"""
ctx = cls()
if agent_pool is None or skill_registry is None:
return ctx
for agent_info in agent_pool.list_agents():
agent_name = agent_info["name"]
agent_type = agent_info.get("agent_type", "react")
# 尝试从 skill_registry 获取 SkillConfig
capabilities: list[str] = []
skills: list[str] = []
execution_mode = "react"
model = "default"
max_concurrency = 1
try:
skill = skill_registry.get(agent_name)
config = skill.config
capabilities = [cap.tag for cap in config.capabilities]
execution_mode = config.execution_mode
model = config.llm.get("model", "default") if config.llm else "default"
max_concurrency = config.max_concurrency
skills = [agent_name]
except Exception:
# Agent 不在 skill_registry 中,使用默认值
skills = [agent_name]
profile = AgentProfile(
name=agent_name,
agent_type=agent_type,
capabilities=capabilities,
skills=skills,
execution_mode=execution_mode,
model=model,
max_concurrency=max_concurrency,
)
ctx.register_agent(profile)
return ctx