- 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/路径修复
134 lines
4.3 KiB
Python
134 lines
4.3 KiB
Python
"""多智能体模式的系统提示词。
|
||
|
||
所有 prompt 正文均从 prompts/multi_agent/ 下的文本文件加载,避免在代码中硬编码。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
from typing import List, Optional
|
||
|
||
try:
|
||
from config import PROMPTS_DIR
|
||
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
|
||
|
||
|
||
_MULTI_AGENT_PROMPTS_DIR = Path(PROMPTS_DIR) / "multi_agent"
|
||
_TEMPLATE_CACHE: dict[str, str] = {}
|
||
|
||
|
||
def _load_template(name: str) -> str:
|
||
"""从 prompts/multi_agent/<name>.txt 加载模板,带缓存。"""
|
||
cached = _TEMPLATE_CACHE.get(name)
|
||
if cached is not None:
|
||
return cached
|
||
|
||
template_path = _MULTI_AGENT_PROMPTS_DIR / f"{name}.txt"
|
||
if not template_path.exists():
|
||
raise FileNotFoundError(f"多智能体 prompt 模板缺失: {template_path}")
|
||
|
||
content = template_path.read_text(encoding="utf-8")
|
||
_TEMPLATE_CACHE[name] = content
|
||
return content
|
||
|
||
|
||
def _format_template(name: str, **kwargs) -> str:
|
||
"""加载模板并用 str.format 填充占位符。"""
|
||
template = _load_template(name)
|
||
return template.format(**kwargs)
|
||
|
||
|
||
# 为兼容外部仍可通过常量访问正文,但值来自文件;首次访问时加载。
|
||
def __getattr__(name: str) -> str:
|
||
if name == "MULTI_AGENT_MASTER_PROMPT_BODY":
|
||
return _load_template("master")
|
||
if name == "MULTI_AGENT_SUB_AGENT_PROMPT_BODY":
|
||
return _load_template("sub_agent")
|
||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||
|
||
|
||
def build_multi_agent_master_prompt(workspace_path: str, base: str = "") -> str:
|
||
"""构造主智能体(Team Leader)的系统提示词。
|
||
|
||
`base` 一般为现有 MainTerminal 的 base 提示词(环境/工具概览等),
|
||
我们在末尾追加多智能体模式专属正文。
|
||
"""
|
||
body = _load_template("master")
|
||
if base and base.strip():
|
||
return f"{base.rstrip()}\n\n{body}\n"
|
||
return f"{body}\n"
|
||
|
||
|
||
def build_multi_agent_sub_agent_prompt(
|
||
role_body: str,
|
||
display_name: str,
|
||
workspace_path: str,
|
||
*,
|
||
data_dir: str = "",
|
||
sandbox_mode: str = "",
|
||
) -> str:
|
||
"""构造子智能体的系统提示词。
|
||
|
||
`role_body` 为该角色 Markdown 文件 frontmatter 之后的自定义 prompt。
|
||
`display_name` 为该实例的显示名(如 `UI Operator_1`)。
|
||
`workspace_path` 为工作区路径,用于注入动态上下文。
|
||
`data_dir` 为数据目录路径,用于推断 private skills 目录。
|
||
`sandbox_mode` 为沙箱模式(sandbox/direct/host/docker)。
|
||
|
||
所有动态信息在子智能体创建那一刻快照写入,后续冻结不更新。
|
||
"""
|
||
# 角色专属 prompt(模板正文)
|
||
role_prompt = _format_template("sub_agent", display_name=display_name, role_body=role_body.strip())
|
||
|
||
# 动态上下文(AGENTS.md / 执行环境 / 工作区信息 / skills / 项目记忆等)
|
||
try:
|
||
from modules.multi_agent.sub_agent_context import build_sub_agent_dynamic_context
|
||
dynamic_context = build_sub_agent_dynamic_context(
|
||
workspace_path,
|
||
data_dir=data_dir,
|
||
sandbox_mode=sandbox_mode,
|
||
)
|
||
except Exception:
|
||
dynamic_context = ""
|
||
|
||
if dynamic_context:
|
||
return f"{role_prompt.rstrip()}\n\n---\n\n{dynamic_context}\n"
|
||
return f"{role_prompt}\n"
|
||
|
||
|
||
def build_available_agents_prompt() -> str:
|
||
"""构造「可用的子智能体角色」动态 prompt。
|
||
|
||
列出当前可创建的所有角色名称与说明,供 Team Leader 参考。
|
||
若没有任何可用角色,返回空字符串。
|
||
"""
|
||
try:
|
||
from modules.multi_agent.role_store import list_roles
|
||
|
||
roles = list_roles()
|
||
except Exception:
|
||
return ""
|
||
|
||
if not roles:
|
||
return ""
|
||
|
||
lines: List[str] = []
|
||
for role in roles:
|
||
name = getattr(role, "name", "") or ""
|
||
description = getattr(role, "description", "") or ""
|
||
role_id = getattr(role, "role_id", "") or ""
|
||
if description:
|
||
lines.append(f"- {name}(role_id: {role_id}):{description}")
|
||
else:
|
||
lines.append(f"- {name}(role_id: {role_id})")
|
||
|
||
if not lines:
|
||
return ""
|
||
|
||
return _format_template("available_agents", agents_list="\n".join(lines))
|