agent-Specialization/modules/multi_agent/prompts.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

134 lines
4.3 KiB
Python
Raw 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.

"""多智能体模式的系统提示词。
所有 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))