- 引入AgentTeamOrchestrator支持多agent协同任务执行 - 增加第三方swarms库依赖并配置git协议替换以改善包管理 - 扩展DelegationManager支持团队任务调度和进度跟踪 - 实现中文bigram分词算法提升中文任务检索准确性 - 调整A2AClient和DelegationManager超时时间从30秒增至600秒 - 优化AgentRunResult状态判断逻辑增加有意义摘要检测 - 修改Dockerfile配置npm仓库镜像地址和git协议映射 - 更新CLI命令行接口支持网关端口配置传递 - 调整提供者超时配置机制增强请求稳定性 - 移除过时的support_group字段简化agent描述符结构 - 增强错误处理和进度事件报告机制改进用户体验
115 lines
3.9 KiB
Python
115 lines
3.9 KiB
Python
"""Thin adapters between nanobot agents and the vendored swarms runtime."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import sys
|
|
from collections.abc import Awaitable, Callable
|
|
from dataclasses import dataclass, field
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from nanobot.agent.agent_registry import AgentDescriptor
|
|
from nanobot.agent.run_result import AgentRunResult
|
|
|
|
MemberRunner = Callable[[AgentDescriptor, str, str, list[str]], Awaitable[AgentRunResult]]
|
|
|
|
|
|
def _candidate_swarms_roots() -> list[Path]:
|
|
"""Return likely vendored swarms paths across source and packaged layouts."""
|
|
module_path = Path(__file__).resolve()
|
|
candidates = [
|
|
module_path.parents[2] / "third_party" / "swarms",
|
|
Path("/opt/app/backend/third_party/swarms"),
|
|
Path("/app/third_party/swarms"),
|
|
Path.cwd() / "third_party" / "swarms",
|
|
Path.cwd() / "backend" / "third_party" / "swarms",
|
|
]
|
|
unique: list[Path] = []
|
|
seen: set[str] = set()
|
|
for candidate in candidates:
|
|
key = str(candidate)
|
|
if key in seen:
|
|
continue
|
|
seen.add(key)
|
|
unique.append(candidate)
|
|
return unique
|
|
|
|
|
|
def ensure_swarms_importable() -> None:
|
|
"""Put the vendored swarms checkout on `sys.path` if needed."""
|
|
for swarms_root in _candidate_swarms_roots():
|
|
if swarms_root.exists() and str(swarms_root) not in sys.path:
|
|
sys.path.insert(0, str(swarms_root))
|
|
return
|
|
|
|
|
|
def load_swarms_runtime() -> dict[str, Any]:
|
|
"""Lazy-load swarms classes without making package import fragile."""
|
|
ensure_swarms_importable()
|
|
from swarms import AutoSwarmBuilder # type: ignore
|
|
from swarms.structs.groupchat import GroupChat # type: ignore
|
|
from swarms.structs.swarm_router import SwarmRouter # type: ignore
|
|
|
|
return {
|
|
"AutoSwarmBuilder": AutoSwarmBuilder,
|
|
"GroupChat": GroupChat,
|
|
"SwarmRouter": SwarmRouter,
|
|
}
|
|
|
|
|
|
def __getattr__(name: str) -> Any:
|
|
if name in {"AutoSwarmBuilder", "GroupChat", "SwarmRouter"}:
|
|
return load_swarms_runtime()[name]
|
|
raise AttributeError(name)
|
|
|
|
|
|
def safe_swarms_name(agent_id: str) -> str:
|
|
"""Return a GroupChat-friendly ASCII-ish name for @mentions."""
|
|
normalized = "".join(ch if ch.isalnum() else "_" for ch in str(agent_id or "agent"))
|
|
normalized = normalized.strip("_") or "agent"
|
|
return f"agent_{normalized}"
|
|
|
|
|
|
@dataclass(eq=False)
|
|
class NanobotAgentAdapter:
|
|
"""Callable wrapper that lets swarms invoke a nanobot agent descriptor."""
|
|
|
|
descriptor: AgentDescriptor
|
|
run_id: str
|
|
loop: asyncio.AbstractEventLoop
|
|
member_runner: MemberRunner
|
|
skills: list[str]
|
|
results: list[AgentRunResult] = field(default_factory=list, init=False)
|
|
|
|
def __post_init__(self) -> None:
|
|
self.agent_name = safe_swarms_name(self.descriptor.id)
|
|
self.name = self.agent_name
|
|
self.system_prompt = self.descriptor.system_prompt or self.descriptor.description
|
|
self.__name__ = self.agent_name
|
|
|
|
def __call__(self, conversation_context: str) -> str:
|
|
return self.run(conversation_context)
|
|
|
|
def run(self, task: str, *args: Any, **kwargs: Any) -> str:
|
|
delegated_task = self._task_with_skills(task)
|
|
future = asyncio.run_coroutine_threadsafe(
|
|
self.member_runner(self.descriptor, delegated_task, self.run_id, list(self.skills)),
|
|
self.loop,
|
|
)
|
|
result = future.result(timeout=300)
|
|
self.results.append(result)
|
|
if result.status != "ok":
|
|
return f"Error from {self.agent_name}: {result.summary}"
|
|
return result.summary
|
|
|
|
def _task_with_skills(self, conversation_context: str) -> str:
|
|
if not self.skills:
|
|
return conversation_context
|
|
return (
|
|
"Required skills for this delegated team member:\n"
|
|
f"{', '.join(self.skills)}\n\n"
|
|
"Swarms conversation context:\n"
|
|
f"{conversation_context}"
|
|
).strip()
|