auto-sync: 2026-05-17 06:03:05

This commit is contained in:
cfdaily
2026-05-17 06:03:05 +08:00
parent 0707846102
commit bfe3cb7ae6
+212
View File
@@ -0,0 +1,212 @@
"""Bootstrap 拼装 — L0-L3 四层上下文构建
L0 铁律层(~500 tokens → Hook 注入,不占 bootstrap
L1 角色层(~2000 tokens → SOUL.md / IDENTITY.mdAgent 自带)
L2 引擎注入层(~1500 tokens → prompt_templates 按 role 拼装
L3 被动参考层(按需加载) → Skill description 四要素
Bootstrap 按 role 精确注入:执行者注入 Guardrail + 审查协议,
审查者注入审查协议,庞统注入审查协议。
"""
from __future__ import annotations
import json
import logging
import re
from pathlib import Path
from typing import Any, Dict, List, Optional
logger = logging.getLogger("moziplus-v2.bootstrap")
# 粗略 token 估算:英文 ~4 chars/token,中文 ~2 chars/token
CHARS_PER_TOKEN = 3.0
def estimate_tokens(text: str) -> int:
"""估算文本 token 数"""
return max(1, int(len(text) / CHARS_PER_TOKEN))
class BootstrapBuilder:
"""Bootstrap 四层上下文构建器"""
def __init__(
self,
template_dir: Optional[Path] = None,
max_tokens: int = 4096,
):
"""
Args:
template_dir: prompt_templates 目录路径
max_tokens: bootstrap 最大 token 数
"""
self.template_dir = template_dir
self.max_tokens = max_tokens
def build(
self,
role: str,
task_context: Optional[Dict[str, Any]] = None,
project_context: Optional[Dict[str, Any]] = None,
guardrail_rules: Optional[str] = None,
review_protocols: Optional[str] = None,
experiences: Optional[List[Dict[str, Any]]] = None,
skill_descriptions: Optional[List[Dict[str, Any]]] = None,
) -> str:
"""构建 bootstrap 文本
Args:
role: 角色(executor / reviewer / planner / pangtong
task_context: 任务上下文(黑板数据)
project_context: 项目背景
guardrail_rules: Guardrail 规则(仅执行者)
review_protocols: 审查协议(执行者+审查者+庞统)
experiences: 相关经验列表
skill_descriptions: Skill 描述列表
Returns:
拼装好的 bootstrap 文本
"""
layers: List[str] = []
# L2a: 操作规范
role_template = self._load_template(f"{role}.md")
if role_template:
layers.append(role_template)
# L2b: 项目背景
if project_context:
layers.append(self._format_project_context(project_context))
# L2c: 任务上下文
if task_context:
layers.append(self._format_task_context(task_context))
# L2d: 前序信息(depends_on 产出摘要)
if task_context and task_context.get("depends_on_outputs"):
layers.append(self._format_depends_on(task_context["depends_on_outputs"]))
# L2e: Guardrail 规则(仅执行者)
if role == "executor" and guardrail_rules:
layers.append(guardrail_rules)
# L2f: 审查协议(执行者+审查者+庞统)
if role in ("executor", "reviewer", "pangtong") and review_protocols:
layers.append(review_protocols)
# L2g: 经验注入
if experiences:
layers.append(self._format_experiences(experiences))
# L3: Skill descriptions
if skill_descriptions:
layers.append(self._format_skills(skill_descriptions))
bootstrap = "\n\n---\n\n".join(layers)
# 检查 token 限制
tokens = estimate_tokens(bootstrap)
if tokens > self.max_tokens:
logger.warning("Bootstrap exceeds token limit: %d > %d, truncating",
tokens, self.max_tokens)
bootstrap = self._truncate_to_tokens(bootstrap, self.max_tokens)
return bootstrap
def _load_template(self, filename: str) -> Optional[str]:
"""加载 prompt template"""
if not self.template_dir:
return None
path = self.template_dir / filename
if path.exists():
return path.read_text(encoding="utf-8")
return None
def _format_project_context(self, ctx: Dict[str, Any]) -> str:
"""格式化项目背景"""
parts = ["## 项目背景"]
if ctx.get("name"):
parts.append(f"项目: {ctx['name']}")
if ctx.get("description"):
parts.append(f"描述: {ctx['description']}")
if ctx.get("agents"):
parts.append(f"团队: {', '.join(ctx['agents'])}")
return "\n".join(parts)
def _format_task_context(self, ctx: Dict[str, Any]) -> str:
"""格式化任务上下文"""
parts = ["## 任务上下文"]
if ctx.get("task_id"):
parts.append(f"任务ID: {ctx['task_id']}")
if ctx.get("title"):
parts.append(f"标题: {ctx['title']}")
if ctx.get("description"):
parts.append(f"描述: {ctx['description']}")
if ctx.get("task_type"):
parts.append(f"类型: {ctx['task_type']}")
if ctx.get("must_haves"):
parts.append(f"必须完成: {ctx['must_haves']}")
if ctx.get("risk_level"):
parts.append(f"风险级别: {ctx['risk_level']}")
return "\n".join(parts)
def _format_depends_on(self, outputs: List[Dict[str, Any]]) -> str:
"""格式化前序产出摘要"""
parts = ["## 前序产出"]
for out in outputs:
parts.append(f"- [{out.get('task_id', '?')}] {out.get('summary', '无摘要')}")
return "\n".join(parts)
def _format_experiences(self, experiences: List[Dict[str, Any]]) -> str:
"""格式化经验注入"""
parts = ["## 相关经验"]
for exp in experiences[:5]: # 最多 5 条
parts.append(f"- [{exp.get('category', '?')}] {exp.get('summary', '')}")
return "\n".join(parts)
def _format_skills(self, skills: List[Dict[str, Any]]) -> str:
"""格式化 Skill 描述"""
parts = ["## 可用技能"]
for skill in skills[:10]:
parts.append(f"- **{skill.get('name', '?')}**: {skill.get('description', '')}")
return "\n".join(parts)
def _truncate_to_tokens(self, text: str, max_tokens: int) -> str:
"""截断到指定 token 数"""
max_chars = int(max_tokens * CHARS_PER_TOKEN)
if len(text) <= max_chars:
return text
return text[:max_chars] + "\n\n[... bootstrap truncated]"
def build_for_task(
self,
task: Any,
role: str,
project_config: Optional[Dict] = None,
experiences: Optional[List[Dict]] = None,
) -> str:
"""从 Task 对象构建 bootstrap(便捷方法)"""
task_context = {
"task_id": task.id,
"title": task.title,
"description": task.description,
"task_type": task.task_type,
"must_haves": task.must_haves,
"risk_level": task.risk_level,
}
project_context = None
if project_config:
project_context = {
"name": project_config.get("name"),
"description": project_config.get("description"),
"agents": project_config.get("agents", []),
}
return self.build(
role=role,
task_context=task_context,
project_context=project_context,
experiences=experiences,
)