- 重写子智能体执行核心,不再启动 easyagent Node.js 子进程 - 新增 modules/sub_agent/ 包集中管理子智能体逻辑 - 工具调用复用主进程 WebTerminal.handle_tool_call,自然经过沙箱/容器链路 - 子智能体模型独立读取 ~/.agents/<mode>/config/sub_agent_models.json - 支持 8 个工具:read_file/write_file/edit_file(replacements+replace_all)/run_command/web_search/extract_webpage/search_workspace/read_mediafile - 修复子智能体进度弹窗:标题颜色、write_file 显示、过滤非 progress 条目、统一滚动条样式 - 更新 AGENTS.md / CLAUDE.md 子智能体描述 - 新增 test/test_sub_agent_regression.py 回归测试
70 lines
2.4 KiB
Python
70 lines
2.4 KiB
Python
"""子智能体统计摘要与消息构建工具。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
from typing import Any, Dict, Optional
|
|
|
|
|
|
class SubAgentStatsMixin:
|
|
"""提供子智能体任务耗时、统计摘要与结果消息组装能力。"""
|
|
|
|
@staticmethod
|
|
def _compute_elapsed_seconds(task: Dict) -> Optional[int]:
|
|
try:
|
|
created_at = float(task.get("created_at") or 0)
|
|
updated_at = float(task.get("updated_at") or time.time())
|
|
except (TypeError, ValueError):
|
|
return None
|
|
if created_at <= 0:
|
|
return None
|
|
return int(round(max(0.0, updated_at - created_at)))
|
|
|
|
@staticmethod
|
|
def _coerce_stat_int(value: Any) -> int:
|
|
try:
|
|
return max(0, int(value))
|
|
except (TypeError, ValueError):
|
|
return 0
|
|
|
|
def _build_stats_summary(self, stats: Optional[Dict[str, Any]]) -> str:
|
|
if not isinstance(stats, dict):
|
|
stats = {}
|
|
api_calls = self._coerce_stat_int(stats.get("api_calls") or stats.get("turn_count"))
|
|
files_read = self._coerce_stat_int(stats.get("files_read"))
|
|
write_files = self._coerce_stat_int(stats.get("write_files"))
|
|
edit_files = self._coerce_stat_int(stats.get("edit_files"))
|
|
searches = self._coerce_stat_int(stats.get("searches"))
|
|
web_pages = self._coerce_stat_int(stats.get("web_pages"))
|
|
commands = self._coerce_stat_int(stats.get("commands"))
|
|
lines = [
|
|
f"调用了{api_calls}次",
|
|
f"阅读了{files_read}次文件",
|
|
f"写入了{write_files}次文件",
|
|
f"编辑了{edit_files}次文件",
|
|
f"搜索了{searches}次内容",
|
|
f"查看了{web_pages}个网页",
|
|
f"运行了{commands}个指令",
|
|
]
|
|
return "\n".join(lines)
|
|
|
|
def _compose_sub_agent_message(
|
|
self,
|
|
*,
|
|
prefix: str,
|
|
stats_summary: str,
|
|
summary: str,
|
|
deliverables_dir: Optional[str] = None,
|
|
duration_seconds: Optional[int] = None,
|
|
) -> str:
|
|
parts = [prefix]
|
|
if stats_summary:
|
|
parts.append(stats_summary)
|
|
if duration_seconds is not None:
|
|
parts.append(f"运行了{duration_seconds}秒")
|
|
if summary:
|
|
parts.append(summary)
|
|
if deliverables_dir:
|
|
parts.append(f"交付目录:{deliverables_dir}")
|
|
return "\n\n".join(parts)
|