feat: 添加swarms团队编排功能并优化agent委派系统
- 引入AgentTeamOrchestrator支持多agent协同任务执行 - 增加第三方swarms库依赖并配置git协议替换以改善包管理 - 扩展DelegationManager支持团队任务调度和进度跟踪 - 实现中文bigram分词算法提升中文任务检索准确性 - 调整A2AClient和DelegationManager超时时间从30秒增至600秒 - 优化AgentRunResult状态判断逻辑增加有意义摘要检测 - 修改Dockerfile配置npm仓库镜像地址和git协议映射 - 更新CLI命令行接口支持网关端口配置传递 - 调整提供者超时配置机制增强请求稳定性 - 移除过时的support_group字段简化agent描述符结构 - 增强错误处理和进度事件报告机制改进用户体验
This commit is contained in:
@ -28,6 +28,8 @@ from nanobot.agent.process_events import (
|
||||
process_run_context,
|
||||
)
|
||||
from nanobot.agent.run_result import AgentRunResult
|
||||
from nanobot.agent_team.orchestrator import AgentTeamOrchestrator
|
||||
from nanobot.agent_team.types import BridgeResult
|
||||
from nanobot.bus.events import InboundMessage, OutboundMessage
|
||||
from nanobot.bus.queue import MessageBus
|
||||
from nanobot.providers.base import LLMProvider
|
||||
@ -61,12 +63,13 @@ class DelegationManager:
|
||||
def __init__(
|
||||
self,
|
||||
provider: LLMProvider,
|
||||
model: str | None,
|
||||
workspace: Path,
|
||||
bus: MessageBus,
|
||||
registry: AgentRegistry,
|
||||
skills_loader: "SkillsLoader | None",
|
||||
local_executor: Any,
|
||||
timeout_seconds: int = 30,
|
||||
timeout_seconds: int = 600,
|
||||
poll_interval_seconds: int = 2,
|
||||
card_cache_ttl_seconds: int = 300,
|
||||
max_parallel_agents: int = 4,
|
||||
@ -76,6 +79,7 @@ class DelegationManager:
|
||||
allow_local_delegation: bool = True,
|
||||
allow_plugin_delegation: bool = True,
|
||||
allow_local_fallback: bool = True,
|
||||
gateway_port: int = 18790,
|
||||
):
|
||||
self.provider = provider
|
||||
self.workspace = workspace
|
||||
@ -97,6 +101,18 @@ class DelegationManager:
|
||||
authz_config=authz_config,
|
||||
backend_identity=backend_identity,
|
||||
)
|
||||
# 新 orchestrator 只负责 agent team 路径;单 agent 委派仍走原有逻辑。
|
||||
self.agent_team_orchestrator = AgentTeamOrchestrator(
|
||||
workspace=workspace,
|
||||
provider=provider,
|
||||
model=model,
|
||||
registry=registry,
|
||||
bus=bus,
|
||||
local_executor=local_executor,
|
||||
member_runner=self._run_team_member_for_swarms,
|
||||
max_parallel_agents=self.max_parallel_agents,
|
||||
gateway_port=gateway_port,
|
||||
)
|
||||
self._running_tasks: dict[str, DelegationRun] = {}
|
||||
self._direct_announcement_callback: DirectAnnouncementCallback | None = None
|
||||
|
||||
@ -273,6 +289,20 @@ class DelegationManager:
|
||||
"""返回当前正在执行的委派数量。"""
|
||||
return len(self._running_tasks)
|
||||
|
||||
@staticmethod
|
||||
def _clean_metadata(metadata: dict[str, Any]) -> dict[str, Any]:
|
||||
"""删除空值,避免过程事件 metadata 出现大量噪声字段。"""
|
||||
cleaned: dict[str, Any] = {}
|
||||
for key, value in metadata.items():
|
||||
if value is None:
|
||||
continue
|
||||
if isinstance(value, str) and not value.strip():
|
||||
continue
|
||||
if isinstance(value, (list, tuple, set, dict)) and not value:
|
||||
continue
|
||||
cleaned[key] = value
|
||||
return cleaned
|
||||
|
||||
@staticmethod
|
||||
def _ui_status(status: str | None) -> str:
|
||||
"""把底层状态归一化成前端更稳定的显示状态。"""
|
||||
@ -287,6 +317,29 @@ class DelegationManager:
|
||||
return "error"
|
||||
return probe or "running"
|
||||
|
||||
async def _emit_team_progress(
|
||||
self,
|
||||
run_id: str,
|
||||
text: str,
|
||||
*,
|
||||
stage_label: str,
|
||||
metadata: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
"""为 agent team 根 run 发一条过程可观察事件。"""
|
||||
await emit_process_event(
|
||||
"process_run_progress",
|
||||
run_id=run_id,
|
||||
actor_type="system",
|
||||
actor_id="agent-group",
|
||||
actor_name="Agent Team",
|
||||
text=text,
|
||||
metadata=self._clean_metadata({
|
||||
"source": "agent_team_dispatch",
|
||||
"stage_label": stage_label,
|
||||
**(metadata or {}),
|
||||
}),
|
||||
)
|
||||
|
||||
async def _emit_agent_started(
|
||||
self,
|
||||
run_id: str,
|
||||
@ -310,7 +363,6 @@ class DelegationManager:
|
||||
metadata={
|
||||
"kind": descriptor.kind,
|
||||
"protocol": descriptor.protocol,
|
||||
"support_group": descriptor.support_group,
|
||||
"support_streaming": descriptor.support_streaming,
|
||||
"delegated_task": task,
|
||||
},
|
||||
@ -371,31 +423,56 @@ class DelegationManager:
|
||||
actor_type="system",
|
||||
actor_id="agent-group",
|
||||
actor_name="Agent Team",
|
||||
source="agent_team",
|
||||
title=label,
|
||||
status="running",
|
||||
metadata={"targets": targets},
|
||||
metadata=self._clean_metadata({
|
||||
"source": "agent_team_dispatch",
|
||||
"phase": "dispatch",
|
||||
"stage_label": "团队任务已创建",
|
||||
"planned_targets": targets,
|
||||
"selected_targets": targets,
|
||||
"selected_count": len(targets),
|
||||
}),
|
||||
)
|
||||
|
||||
async def _emit_group_finished(self, run_id: str, label: str, results: list[AgentRunResult]) -> None:
|
||||
"""发送 agent team 结束事件。"""
|
||||
async def _emit_group_finished(
|
||||
self,
|
||||
run_id: str,
|
||||
label: str,
|
||||
results: list[AgentRunResult],
|
||||
*,
|
||||
status: str = "done",
|
||||
summary: str | None = None,
|
||||
metadata_extra: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
"""发送 agent team 结束事件。
|
||||
|
||||
Demo 输出:
|
||||
`process_run_finished(status="done", summary="weekly report: 2 member(s) finished")`
|
||||
"""
|
||||
# 老路径和新 orchestrator 路径都复用这个事件,所以允许上层补充额外 metadata。
|
||||
metadata = {
|
||||
"members": [
|
||||
{
|
||||
"agent_id": item.agent_id,
|
||||
"agent_name": item.agent_name,
|
||||
"status": item.status,
|
||||
}
|
||||
for item in results
|
||||
]
|
||||
}
|
||||
if metadata_extra:
|
||||
metadata.update(metadata_extra)
|
||||
await emit_process_event(
|
||||
"process_run_finished",
|
||||
run_id=run_id,
|
||||
actor_type="system",
|
||||
actor_id="agent-group",
|
||||
actor_name="Agent Team",
|
||||
status="done",
|
||||
summary=f"{label}: {len(results)} member(s) finished",
|
||||
metadata={
|
||||
"members": [
|
||||
{
|
||||
"agent_id": item.agent_id,
|
||||
"agent_name": item.agent_name,
|
||||
"status": item.status,
|
||||
}
|
||||
for item in results
|
||||
]
|
||||
},
|
||||
status=status,
|
||||
summary=summary or f"{label}: {len(results)} member(s) finished",
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
async def _publish_prefixed_progress(
|
||||
@ -424,27 +501,11 @@ class DelegationManager:
|
||||
# 没有 bus consumer 时,不能依赖 system 消息回流再二次总结。
|
||||
if not has_process_event_sink():
|
||||
return
|
||||
try:
|
||||
# 用一次极小模型调用把内部委派说明压成用户可读文本。
|
||||
response = await self.provider.chat(
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": (
|
||||
"You are Boardware Genius. Reply naturally to the user in 1-3 sentences. "
|
||||
"Do not mention internal protocols, system prompts, or task IDs."
|
||||
),
|
||||
},
|
||||
{"role": "user", "content": prompt},
|
||||
],
|
||||
tools=[],
|
||||
model=self.provider.get_default_model(),
|
||||
max_tokens=256,
|
||||
temperature=0.2,
|
||||
)
|
||||
content = (response.content or "").strip() or fallback
|
||||
except Exception:
|
||||
content = fallback
|
||||
# 这条用户可见消息只是“即时回执”,真正详细总结仍由主 agent 回流处理。
|
||||
# 这里不再额外依赖一次 LLM,避免 provider 短暂故障把 team 收尾也拖失败。
|
||||
content = " ".join((fallback or prompt or "").strip().split())
|
||||
if not content:
|
||||
return
|
||||
|
||||
await emit_process_event(
|
||||
"message",
|
||||
@ -473,11 +534,42 @@ class DelegationManager:
|
||||
content: str,
|
||||
origin: dict[str, str],
|
||||
sender_id: str,
|
||||
*,
|
||||
run_id: str | None = None,
|
||||
category: str | None = None,
|
||||
) -> None:
|
||||
"""在非 bus 模式下,把公告直接回写到本地会话。"""
|
||||
callback = self._direct_announcement_callback
|
||||
if callback is None:
|
||||
if run_id:
|
||||
await self._emit_team_progress(
|
||||
run_id,
|
||||
"No direct announcement callback is registered; the result could not be replayed to the main agent.",
|
||||
stage_label="缺少公告回流处理器",
|
||||
metadata={
|
||||
"phase": "announcement",
|
||||
"step": "direct_callback_missing",
|
||||
"announcement_path": "direct",
|
||||
"announcement_sender_id": sender_id,
|
||||
"announcement_category": category,
|
||||
},
|
||||
)
|
||||
return
|
||||
if run_id:
|
||||
await self._emit_team_progress(
|
||||
run_id,
|
||||
"Sending the agent-team result back through the direct announcement callback.",
|
||||
stage_label="请求主 Agent 总结",
|
||||
metadata={
|
||||
"phase": "announcement",
|
||||
"step": "direct_callback_start",
|
||||
"announcement_path": "direct",
|
||||
"announcement_sender_id": sender_id,
|
||||
"announcement_category": category,
|
||||
"origin_channel": origin.get("channel"),
|
||||
"origin_chat_id": origin.get("chat_id"),
|
||||
},
|
||||
)
|
||||
try:
|
||||
await callback(
|
||||
content,
|
||||
@ -485,7 +577,34 @@ class DelegationManager:
|
||||
sender_id,
|
||||
not has_process_event_sink(),
|
||||
)
|
||||
if run_id:
|
||||
await self._emit_team_progress(
|
||||
run_id,
|
||||
"The direct announcement callback completed successfully.",
|
||||
stage_label="主 Agent 总结完成",
|
||||
metadata={
|
||||
"phase": "announcement",
|
||||
"step": "direct_callback_complete",
|
||||
"announcement_path": "direct",
|
||||
"announcement_sender_id": sender_id,
|
||||
"announcement_category": category,
|
||||
},
|
||||
)
|
||||
except Exception as exc:
|
||||
if run_id:
|
||||
await self._emit_team_progress(
|
||||
run_id,
|
||||
f"Direct announcement callback failed: {exc}",
|
||||
stage_label="主 Agent 总结失败",
|
||||
metadata={
|
||||
"phase": "announcement",
|
||||
"step": "direct_callback_failed",
|
||||
"announcement_path": "direct",
|
||||
"announcement_sender_id": sender_id,
|
||||
"announcement_category": category,
|
||||
"error": str(exc),
|
||||
},
|
||||
)
|
||||
logger.warning("Failed to handle direct delegation announcement: {}", exc)
|
||||
|
||||
async def _run_dispatch(
|
||||
@ -510,23 +629,80 @@ class DelegationManager:
|
||||
if is_group:
|
||||
planned_targets = list(targets)
|
||||
await self._emit_group_started(run_id, label, planned_targets)
|
||||
results = await self._run_group(
|
||||
task,
|
||||
label,
|
||||
None,
|
||||
targets,
|
||||
strategy,
|
||||
skills,
|
||||
origin=origin,
|
||||
run_id=run_id,
|
||||
announce_via_bus=announce_via_bus,
|
||||
await self._emit_team_progress(
|
||||
run_id,
|
||||
"Agent team dispatch accepted and moved into swarms orchestration.",
|
||||
stage_label="开始团队编排",
|
||||
metadata={
|
||||
"phase": "dispatch",
|
||||
"strategy": strategy,
|
||||
"execution_path": "swarms",
|
||||
"announce_via_bus": announce_via_bus,
|
||||
"requested_targets": planned_targets,
|
||||
},
|
||||
)
|
||||
await self._emit_group_finished(run_id, label, results)
|
||||
await self._announce_group_result(
|
||||
logger.info(
|
||||
"Agent team [{}] dispatch started: mode=swarms announce_via_bus={} requested_targets={}",
|
||||
run_id,
|
||||
announce_via_bus,
|
||||
planned_targets,
|
||||
)
|
||||
await self._emit_team_progress(
|
||||
run_id,
|
||||
"DelegationManager handed the task to AgentTeamOrchestrator.",
|
||||
stage_label="编排器接管任务",
|
||||
metadata={
|
||||
"phase": "orchestrator",
|
||||
"step": "handoff_to_orchestrator",
|
||||
"requested_targets": planned_targets,
|
||||
},
|
||||
)
|
||||
orchestrated = await self.agent_team_orchestrator.run_task(
|
||||
task=task,
|
||||
label=label,
|
||||
skills=skills,
|
||||
origin=origin,
|
||||
announce_via_bus=announce_via_bus,
|
||||
run_id=run_id,
|
||||
)
|
||||
await self._emit_team_progress(
|
||||
run_id,
|
||||
"AgentTeamOrchestrator returned a final bridge result.",
|
||||
stage_label="编排器已返回结果",
|
||||
metadata={
|
||||
"phase": "orchestrator",
|
||||
"step": "orchestrator_result_ready",
|
||||
"execution_mode": orchestrated.mode.value,
|
||||
"candidate_procedure_id": (
|
||||
orchestrated.candidate_procedure.id
|
||||
if orchestrated.candidate_procedure is not None
|
||||
else None
|
||||
),
|
||||
"attempt_count": len(orchestrated.attempts),
|
||||
"success": orchestrated.success,
|
||||
},
|
||||
)
|
||||
await self._emit_group_finished(
|
||||
run_id,
|
||||
label,
|
||||
orchestrated.last_member_results(),
|
||||
status="done" if orchestrated.success else "error",
|
||||
summary=orchestrated.summary,
|
||||
metadata_extra={
|
||||
"execution_mode": orchestrated.mode.value,
|
||||
"candidate_procedure_id": (
|
||||
orchestrated.candidate_procedure.id
|
||||
if orchestrated.candidate_procedure is not None
|
||||
else None
|
||||
),
|
||||
"attempts": [attempt.to_dict() for attempt in orchestrated.attempts],
|
||||
},
|
||||
)
|
||||
await self._announce_orchestrator_result(
|
||||
run_id,
|
||||
label,
|
||||
task,
|
||||
results,
|
||||
orchestrated,
|
||||
origin,
|
||||
announce_via_bus=announce_via_bus,
|
||||
)
|
||||
@ -591,6 +767,16 @@ class DelegationManager:
|
||||
summary=f"Error: {exc}",
|
||||
)
|
||||
if is_group:
|
||||
await self._emit_team_progress(
|
||||
run_id,
|
||||
f"Agent team execution failed before announcement: {exc}",
|
||||
stage_label="团队执行失败",
|
||||
metadata={
|
||||
"phase": "error",
|
||||
"step": "dispatch_failed",
|
||||
"error": str(exc),
|
||||
},
|
||||
)
|
||||
await emit_process_event(
|
||||
"process_run_finished",
|
||||
run_id=run_id,
|
||||
@ -777,94 +963,57 @@ class DelegationManager:
|
||||
and ("subagent" in lowered or "sub-agent" in lowered)
|
||||
)
|
||||
|
||||
async def _run_group(
|
||||
async def _run_team_member_for_swarms(
|
||||
self,
|
||||
descriptor: AgentDescriptor,
|
||||
task: str,
|
||||
label: str,
|
||||
target: str | None,
|
||||
targets: list[str],
|
||||
strategy: str,
|
||||
parent_run_id: str,
|
||||
skills: list[str],
|
||||
origin: dict[str, str],
|
||||
run_id: str,
|
||||
announce_via_bus: bool,
|
||||
) -> list[AgentRunResult]:
|
||||
"""并行执行一组 agent,并汇总结果。"""
|
||||
resolved_targets = list(targets)
|
||||
if target:
|
||||
resolved_targets.append(target)
|
||||
if not resolved_targets:
|
||||
# 未显式给出目标时,根据任务文本自动挑若干个候选 agent。
|
||||
suggestions = [
|
||||
agent for agent in self.registry.suggest_agents(task, limit=self.max_parallel_agents * 2)
|
||||
if self._descriptor_allowed(agent)
|
||||
]
|
||||
resolved_targets = [agent.id for agent in suggestions]
|
||||
if not resolved_targets:
|
||||
descriptor = self.registry.get_agent("local-subagent")
|
||||
if descriptor and self._descriptor_allowed(descriptor):
|
||||
resolved_targets = [descriptor.id]
|
||||
if not resolved_targets:
|
||||
raise ValueError("No agents available for group delegation")
|
||||
resolved_targets = list(dict.fromkeys(resolved_targets))
|
||||
|
||||
descriptors: list[AgentDescriptor] = []
|
||||
missing: list[str] = []
|
||||
for item in resolved_targets:
|
||||
descriptor = self.registry.get_agent(item)
|
||||
if descriptor is None:
|
||||
missing.append(item)
|
||||
else:
|
||||
self._ensure_descriptor_allowed(descriptor)
|
||||
descriptors.append(descriptor)
|
||||
if missing:
|
||||
raise ValueError(f"Agent(s) not found: {', '.join(missing)}")
|
||||
|
||||
semaphore = asyncio.Semaphore(self.max_parallel_agents)
|
||||
|
||||
async def _run_one(descriptor: AgentDescriptor) -> AgentRunResult:
|
||||
# group 内每个成员都分配独立 child run_id,便于前端区分子树。
|
||||
child_run_id = new_run_id("agent")
|
||||
async with semaphore:
|
||||
try:
|
||||
await self._emit_agent_started(
|
||||
child_run_id,
|
||||
descriptor,
|
||||
label,
|
||||
parent_run_id=run_id,
|
||||
task=task,
|
||||
)
|
||||
result = await self._execute_descriptor(
|
||||
descriptor,
|
||||
task,
|
||||
label,
|
||||
skill_names=skills,
|
||||
event_callback=self._build_progress_callback(
|
||||
origin,
|
||||
descriptor,
|
||||
event_run_id=child_run_id,
|
||||
tracking_run_id=run_id,
|
||||
publish_via_bus=announce_via_bus,
|
||||
),
|
||||
task_callback=self._build_task_callback(run_id, descriptor),
|
||||
process_run_id=child_run_id,
|
||||
)
|
||||
await self._emit_agent_finished(child_run_id, descriptor, result)
|
||||
return result
|
||||
except asyncio.CancelledError:
|
||||
await self._emit_agent_cancelled(child_run_id, descriptor, label)
|
||||
raise
|
||||
except Exception as exc:
|
||||
result = AgentRunResult(
|
||||
agent_id=descriptor.id,
|
||||
agent_name=descriptor.name,
|
||||
status="error",
|
||||
summary=f"Error: {exc}",
|
||||
)
|
||||
await self._emit_agent_finished(child_run_id, descriptor, result)
|
||||
return result
|
||||
results = await asyncio.gather(*[_run_one(agent) for agent in descriptors])
|
||||
return results
|
||||
) -> AgentRunResult:
|
||||
"""Execute one swarms-selected nanobot agent as a process child run."""
|
||||
state = self._running_tasks.get(parent_run_id)
|
||||
label = "Agent Team" if state is None else state.label
|
||||
origin = {"channel": "system", "chat_id": "direct"} if state is None else state.origin
|
||||
announce_via_bus = True if state is None else state.announce_via_bus
|
||||
child_run_id = new_run_id("agent")
|
||||
try:
|
||||
self._ensure_descriptor_allowed(descriptor)
|
||||
await self._emit_agent_started(
|
||||
child_run_id,
|
||||
descriptor,
|
||||
label,
|
||||
parent_run_id=parent_run_id,
|
||||
task=task,
|
||||
)
|
||||
result = await self._execute_descriptor(
|
||||
descriptor,
|
||||
task,
|
||||
label,
|
||||
skill_names=skills,
|
||||
event_callback=self._build_progress_callback(
|
||||
origin,
|
||||
descriptor,
|
||||
event_run_id=child_run_id,
|
||||
tracking_run_id=parent_run_id,
|
||||
publish_via_bus=announce_via_bus,
|
||||
),
|
||||
task_callback=self._build_task_callback(parent_run_id, descriptor),
|
||||
process_run_id=child_run_id,
|
||||
)
|
||||
await self._emit_agent_finished(child_run_id, descriptor, result)
|
||||
return result
|
||||
except asyncio.CancelledError:
|
||||
await self._emit_agent_cancelled(child_run_id, descriptor, label)
|
||||
raise
|
||||
except Exception as exc:
|
||||
result = AgentRunResult(
|
||||
agent_id=descriptor.id,
|
||||
agent_name=descriptor.name,
|
||||
status="error",
|
||||
summary=f"Error: {exc}",
|
||||
)
|
||||
await self._emit_agent_finished(child_run_id, descriptor, result)
|
||||
return result
|
||||
|
||||
async def _execute_descriptor(
|
||||
self,
|
||||
@ -1164,52 +1313,102 @@ class DelegationManager:
|
||||
)
|
||||
logger.debug("Delegation [{}] announced result", run_id)
|
||||
|
||||
async def _announce_group_result(
|
||||
async def _announce_orchestrator_result(
|
||||
self,
|
||||
run_id: str,
|
||||
label: str,
|
||||
task: str,
|
||||
results: list[AgentRunResult],
|
||||
result: BridgeResult,
|
||||
origin: dict[str, str],
|
||||
*,
|
||||
announce_via_bus: bool,
|
||||
) -> None:
|
||||
"""公告 agent team 汇总结果。"""
|
||||
lines = [f"[Agent team '{label}' completed]", "", f"Task: {task}", "", "Members:"]
|
||||
for result in results:
|
||||
lines.append(f"- {result.agent_name} ({result.agent_id}): {result.status}")
|
||||
lines.extend(["", "Results:"])
|
||||
for result in results:
|
||||
lines.append(f"### {result.agent_name} ({result.status})")
|
||||
lines.append(result.summary)
|
||||
lines.append("")
|
||||
lines.append(
|
||||
"Summarize this naturally for the user. Mention disagreements or failures if any."
|
||||
"""公告 orchestrator 驱动的 agent team 结果。
|
||||
|
||||
Demo 输出:
|
||||
`[Agent team 'weekly report' completed]\nExecution mode: swarms\nMatched procedure: procedure-a1b2c3d4`
|
||||
"""
|
||||
# 这里显式保留 mode / procedure 信息,方便主 agent 做更准确的用户总结。
|
||||
await self._emit_team_progress(
|
||||
run_id,
|
||||
"Preparing orchestrated agent-team summary for the main agent.",
|
||||
stage_label="整理团队结果",
|
||||
metadata={
|
||||
"phase": "announcement",
|
||||
"step": "build_orchestrator_summary",
|
||||
"execution_mode": result.mode.value,
|
||||
"attempt_count": len(result.attempts),
|
||||
},
|
||||
)
|
||||
status_text = "completed" if result.success else "failed"
|
||||
lines = [
|
||||
f"[Agent team '{label}' {status_text}]",
|
||||
"",
|
||||
f"Task: {task}",
|
||||
f"Execution mode: {result.mode.value}",
|
||||
]
|
||||
if result.matched_procedure is not None:
|
||||
lines.append(
|
||||
"Matched procedure: "
|
||||
f"{result.matched_procedure.id} "
|
||||
f"(confidence={result.matched_procedure.confidence:.2f})"
|
||||
)
|
||||
if result.attempts:
|
||||
lines.extend(["", "Attempts:"])
|
||||
for attempt in result.attempts:
|
||||
attempt_status = "ok" if attempt.success else "error"
|
||||
lines.append(f"- {attempt.mode.value}: {attempt_status}")
|
||||
if attempt.error:
|
||||
lines.append(f" error: {attempt.error}")
|
||||
|
||||
member_results = result.last_member_results()
|
||||
if member_results:
|
||||
lines.extend(["", "Members:"])
|
||||
for item in member_results:
|
||||
lines.append(f"- {item.agent_name} ({item.agent_id}): {item.status}")
|
||||
lines.extend(["", "Results:"])
|
||||
for item in member_results:
|
||||
lines.append(f"### {item.agent_name} ({item.status})")
|
||||
lines.append(item.summary)
|
||||
lines.append("")
|
||||
|
||||
lines.extend([
|
||||
"Final summary:",
|
||||
result.summary,
|
||||
"",
|
||||
"Summarize this naturally for the user. Mention disagreements or failures if any.",
|
||||
])
|
||||
summary = "\n".join(lines).strip()
|
||||
if announce_via_bus:
|
||||
await self._publish_announcement(
|
||||
summary,
|
||||
origin,
|
||||
sender_id="delegation-group",
|
||||
sender_id="delegation-team",
|
||||
run_id=run_id,
|
||||
category="agent_team_orchestrated",
|
||||
)
|
||||
else:
|
||||
await self._notify_direct_announcement(
|
||||
summary,
|
||||
origin,
|
||||
"delegation-group",
|
||||
"delegation-team",
|
||||
run_id=run_id,
|
||||
category="agent_team_orchestrated",
|
||||
)
|
||||
await self._emit_direct_user_message(
|
||||
summary,
|
||||
"Agent team 已完成,请查看各 agent 的结果与最终结论。",
|
||||
"Agent team 已完成,请查看最终结论与各次尝试摘要。",
|
||||
)
|
||||
logger.debug("Agent team [{}] announced result", run_id)
|
||||
logger.debug("Agent team [{}] announced orchestrated result", run_id)
|
||||
|
||||
async def _publish_announcement(
|
||||
self,
|
||||
content: str,
|
||||
origin: dict[str, str],
|
||||
sender_id: str,
|
||||
*,
|
||||
run_id: str | None = None,
|
||||
category: str | None = None,
|
||||
) -> None:
|
||||
"""通过 system inbound 消息把公告重新送回主 agent 链路。"""
|
||||
msg = InboundMessage(
|
||||
@ -1219,3 +1418,18 @@ class DelegationManager:
|
||||
content=content,
|
||||
)
|
||||
await self.bus.publish_inbound(msg)
|
||||
if run_id:
|
||||
await self._emit_team_progress(
|
||||
run_id,
|
||||
"Team summary has been published back to the main agent via the system bus.",
|
||||
stage_label="团队结果已回流",
|
||||
metadata={
|
||||
"phase": "announcement",
|
||||
"step": "bus_publish_complete",
|
||||
"announcement_path": "bus",
|
||||
"announcement_sender_id": sender_id,
|
||||
"announcement_category": category,
|
||||
"origin_channel": origin.get("channel"),
|
||||
"origin_chat_id": origin.get("chat_id"),
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user