agent-Specialization/modules/multi_agent/sub_agent_context.py
JOJO 4fcd0ccc4d feat(multi-agent): 子智能体系统升级
- prompt改造:子智能体注入AGENTS.md/执行环境/工作区信息/skills/项目记忆
- 工具增加:read_skill/recall_project_memory/todo_create/todo_update_task/save_webpage
- 上下文压缩:深度压缩机制,记录current_context_tokens,默认150k阈值可配置
- 模型升级:sub_agent_models.json支持thinkmode_status/extra_parameter,与主智能体对齐
- 角色管理三层结构:源码树预设multi_agent_roles/ + 运行态host/web预设 + web按用户隔离
- 启动同步:initialize_system调用sync_preset_roles同步预设到host和web运行态
- 模式判断:API用session.host_mode,工具用data_dir路径判断,不依赖IS_HOST_MODE
- 前端:个人空间新增子智能体管理页(角色CRUD/压缩阈值/模型选择),复用个人空间样式
- 入口改造:登录页移除多智能体按钮,QuickMenu加模式切换项,运行中对话禁止切换
- 工具调整:多智能体模式create_sub_agent去掉timeout/deliverables_dir参数
- skill禁止:sub-agent-guide在多智能体模式下禁止阅读
- .agents/统一为.astrion/路径修复
2026-07-14 02:28:45 +08:00

261 lines
9.8 KiB
Python
Raw Permalink 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.

"""多智能体子智能体动态上下文构建。
在子智能体创建时,为其系统提示词注入工作区相关信息:
- 执行环境(宿主机/docker + 沙箱/直接)
- AGENTS.md 项目规范
- 工作区信息(项目名称、运行环境、当前时间,不含 file_tree / recent_uploads
- .astrion/ 目录结构说明skills / memory / review / user_upload
- 可用 skill 列表
- 项目记忆索引
所有信息在子智能体创建那一刻快照写入 system_prompt.txt后续冻结不更新。
"""
from __future__ import annotations
import platform
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional
try:
from config import (
PROMPTS_DIR,
WORKSPACE_SKILLS_DIRNAME,
WORKSPACE_MEMORY_DIRNAME,
WORKSPACE_REVIEW_DIRNAME,
TERMINAL_SANDBOX_MODE,
)
from config.paths import IS_HOST_MODE
except ImportError:
import sys
project_root = Path(__file__).resolve().parents[2]
if str(project_root) not in sys.path:
sys.path.insert(0, str(project_root))
from config import (
PROMPTS_DIR,
WORKSPACE_SKILLS_DIRNAME,
WORKSPACE_MEMORY_DIRNAME,
WORKSPACE_REVIEW_DIRNAME,
TERMINAL_SANDBOX_MODE,
)
from config.paths import IS_HOST_MODE
def _load_agents_md(workspace_path: str) -> Optional[str]:
"""加载工作区根目录的 AGENTS.md 文件内容。"""
try:
project_path = Path(workspace_path)
if not project_path.exists():
return None
agents_md_files = list(project_path.rglob("AGENTS.md"))
if not agents_md_files:
return None
latest_file = max(agents_md_files, key=lambda p: p.stat().st_mtime)
content = latest_file.read_text(encoding="utf-8")
return content.strip() if content else None
except Exception:
return None
def _build_execution_env_section(workspace_path: str, *, sandbox_mode: str = "") -> str:
"""构建执行环境信息段(精简版,只含宿主机/docker + 沙箱/直接)。"""
mode = (sandbox_mode or TERMINAL_SANDBOX_MODE or "").lower()
if IS_HOST_MODE:
runtime_label = "宿主机模式"
# host 模式下区分 sandbox / direct
if mode == "direct":
exec_label = "直接执行(完全访问权限)"
rules = (
"- 当前为宿主机直接执行模式\n"
"- 仅在必须时执行高权限操作,保持最小化命令范围\n"
"- 涉及删除/覆盖/系统级变更前,先说明风险再执行"
)
else:
exec_label = "沙箱执行"
rules = (
"- 所有命令默认在系统 OS 沙箱中执行\n"
"- 若操作受系统权限限制:先提供安全替代方案;若仍无法满足,说明需要更高权限\n"
"- 不要通过复杂绕过手段规避沙箱限制"
)
else:
runtime_label = "Docker 容器模式"
exec_label = "容器内执行"
rules = (
"- 所有命令在 Docker 容器内执行\n"
"- 工作区挂载在容器内 /workspace 路径下\n"
"- 网络可能受限,仅允许 localhost"
)
return (
f"## 执行环境\n\n"
f"- 运行环境:{runtime_label}\n"
f"- 执行方式:{exec_label}\n\n"
f"### 当前规则\n{rules}\n"
)
def _build_workspace_section(workspace_path: str) -> str:
"""构建工作区信息段(精简版,不含 file_tree 和 recent_uploads"""
project_path = Path(workspace_path)
project_name = project_path.name or "未命名项目"
system_info = f"{platform.system()} {platform.release()}"
current_time = datetime.now().strftime("%Y年%m月%d%H点24小时制")
if IS_HOST_MODE:
resource_limit = "宿主机模式无限制"
else:
resource_limit = "容器模式,受 Docker 资源限制"
return (
f"## 工作区信息\n\n"
f"- 项目名称:{project_name}\n"
f"- 运行环境:{system_info}\n"
f"- 资源限制:{resource_limit}\n"
f"- 当前时间:{current_time}\n"
)
def _build_astrion_dir_section(workspace_path: str) -> str:
"""构建 .astrion/ 目录结构说明段。"""
return (
f"## 工作区数据目录(.astrion/\n\n"
f"工作区内的 `.astrion/` 目录存储以下内容:\n"
f"- `{WORKSPACE_SKILLS_DIRNAME}/`可用技能skill目录每个 skill 含 SKILL.md 说明\n"
f"- `{WORKSPACE_MEMORY_DIRNAME}/`:项目记忆,以 Markdown 文件形式存放项目约定/决策/调试坑\n"
f"- `{WORKSPACE_REVIEW_DIRNAME}/`:对话回顾文件\n"
f"- `user_upload/`:用户上传的文件\n\n"
f"用户上传的文件位于 `.astrion/user_upload/` 目录下。"
f"项目记忆以文件形式存放在 `{WORKSPACE_MEMORY_DIRNAME}/` 下。"
f"技能skill存放在 `{WORKSPACE_SKILLS_DIRNAME}/` 下。\n"
)
def _build_agents_md_section(workspace_path: str) -> str:
"""构建 AGENTS.md 项目规范段。"""
content = _load_agents_md(workspace_path)
if not content:
return ""
return (
f"## AGENTS.md 项目规范\n\n"
f"> 以下是工作区根目录的 AGENTS.md 文件内容,请在回答时参考这些项目规范:\n\n"
f"{content}\n\n"
f"---\n"
f"注意:以上规范来自工作区根目录的 AGENTS.md 文件,若与未来代码冲突,以实际代码为准。\n"
)
def _build_skills_section(workspace_path: str, data_dir: str = "") -> str:
"""构建可用 skill 列表段。"""
try:
from modules.skills_manager import (
get_skills_catalog,
build_skills_list,
infer_private_skills_dir,
)
private_dir = infer_private_skills_dir(data_dir) if data_dir else None
catalog = get_skills_catalog(private_dir=private_dir)
# 子智能体不区分 enabled/disabled列出全部可用 skill
skills_list = build_skills_list(catalog, enabled=None)
if not skills_list:
return ""
return (
f"## 可用 AgentSkill\n\n"
f"agent skills 系统已启用,以下是可用的 skills含简要说明\n\n"
f"{skills_list}\n\n"
f"使用技能时,优先用 read_skill 通过技能名读取 SKILL.md"
f"需要阅读 skill 目录中的其他文件时再使用 read_file\n"
)
except Exception:
return ""
def _build_project_memory_section(workspace_path: str) -> str:
"""构建项目记忆索引段(只列索引,不展开内容)。"""
try:
memory_dir = Path(workspace_path) / WORKSPACE_MEMORY_DIRNAME
if not memory_dir.exists() or not memory_dir.is_dir():
return ""
entries: List[str] = []
for md_file in sorted(memory_dir.glob("*.md")):
try:
text = md_file.read_text(encoding="utf-8")
name = None
description = None
if text.startswith("---"):
end = text.find("---", 3)
if end > 0:
frontmatter = text[3:end]
for line in frontmatter.strip().split("\n"):
ls = line.strip()
if ls.startswith("name:"):
name = ls.split(":", 1)[1].strip()
elif ls.startswith("description:"):
description = ls.split(":", 1)[1].strip()
file_name = md_file.name
display_name = name or md_file.stem
desc = description or ""
if desc:
entries.append(f".astrion/memory/{file_name}{desc}")
else:
entries.append(f".astrion/memory/{file_name}:当需要{display_name}时,应该索引本记忆")
except Exception:
pass
if not entries:
return ""
return (
f"## 项目记忆\n\n"
f"项目记忆以文件形式存放在 `.astrion/memory/` 下。\n"
f"开始涉及项目约定、历史决策或已知问题的任务前,先检查项目记忆索引,"
f"如有相关记忆则用 recall_project_memory 读取。\n\n"
f"### 项目记忆索引\n\n"
+ "\n".join(entries)
+ "\n"
)
except Exception:
return ""
def build_sub_agent_dynamic_context(
workspace_path: str,
*,
data_dir: str = "",
sandbox_mode: str = "",
) -> str:
"""构建子智能体的动态上下文信息(拼接到 system prompt 末尾)。
所有信息在子智能体创建那一刻快照,后续冻结不更新。
Args:
workspace_path: 工作区路径
data_dir: 数据目录路径(用于推断 private skills 目录)
sandbox_mode: 沙箱模式sandbox/direct/host/docker
"""
sections: List[str] = []
# 1. 执行环境
sections.append(_build_execution_env_section(workspace_path, sandbox_mode=sandbox_mode))
# 2. 工作区信息(精简版)
sections.append(_build_workspace_section(workspace_path))
# 3. .astrion/ 目录说明
sections.append(_build_astrion_dir_section(workspace_path))
# 4. AGENTS.md
agents_md = _build_agents_md_section(workspace_path)
if agents_md:
sections.append(agents_md)
# 5. 可用 skill
skills = _build_skills_section(workspace_path, data_dir=data_dir)
if skills:
sections.append(skills)
# 6. 项目记忆索引
memory = _build_project_memory_section(workspace_path)
if memory:
sections.append(memory)
return "\n".join(sections)