"""Runtime role and task-model resolution.""" from __future__ import annotations from dataclasses import dataclass from scripts.lib.model_config import resolve_model_profile ROLE_DEFAULTS = { "dr_plan": { "skills": ["document-ingest", "search-gateway", "search-strategy", "source-quality", "length-budget", "mckinsey-method"], "temperature": 0.4, "max_tokens": 12000, "max_concurrency": 1, }, "dr_pm": { "skills": ["length-budget", "evidence-table", "mckinsey-method"], "temperature": 0.2, "max_tokens": 8000, "max_concurrency": 1, }, "dr_searcher": { "skills": ["search-gateway", "search-strategy", "source-quality"], "temperature": 0.1, "max_tokens": 6000, "max_concurrency": 6, }, "dr_analyst": { "skills": ["search-gateway", "search-strategy", "source-quality", "evidence-table", "mckinsey-method"], "temperature": 0.3, "max_tokens": 14000, "max_concurrency": 6, }, "dr_verifier": { "skills": ["search-gateway", "search-strategy", "source-quality", "evidence-table"], "temperature": 0.2, "max_tokens": 10000, "max_concurrency": 4, }, "dr_chief_editor": { "skills": ["mckinsey-method", "evidence-table", "output-hygiene"], "temperature": 0.2, "max_tokens": 16000, "max_concurrency": 1, }, "dr_editor_in_chief": { "skills": ["mckinsey-method", "citation-manager", "humanizer-cn", "output-hygiene"], "temperature": 0.4, "max_tokens": 20000, "max_concurrency": 1, }, "dr_reporter": { "skills": ["pdf-reportlab", "citation-manager", "output-hygiene"], "temperature": 0.1, "max_tokens": 6000, "max_concurrency": 1, }, } ROLE_IDENTITIES = { "dr_plan": ( "你是 Deep Research 的 Phase1 研究架构师。你的工作不是列目录,而是先消化材料、访谈和初步搜索," "形成可被证伪的主判断、章节命题和求证路线。你要大胆假设,但必须给 Phase2 留下清晰的验证和推翻条件。" ), "dr_pm": ( "你是 Deep Research 的研究项目经理。你的职责是把研究意图转化为可并发执行、可回收校验的任务," "控制碎片化、重复检索和上下文污染。" ), "dr_searcher": ( "你是 Deep Research 的信源发现员。你的职责是用短英文关键词和轴向词找到高质量入口," "优先官方、法规、学术和一手材料;你不写结论,只交付可追溯来源。" ), "dr_analyst": ( "你是 Deep Research 的章节证据分析师。你的职责不是写一篇像样的空泛文章,而是围绕 Phase1 命题" "小心求证:提取材料原文、检索权威证据、寻找反方边界,并把证据整理成可审计的结构化 packet。" ), "dr_verifier": ( "你是 Deep Research 的独立反方审校员。你的默认姿态是质疑:找证据缺口、适用边界、反例和过度推断," "并指出哪些结论必须降级或回炉。" ), "dr_chief_editor": ( "你是 Deep Research 的 Phase3 总编审校。你的职责是通读 Phase1 假设与 Phase2 证据,判断二者是否自洽," "优先指出结构性失败、证据不足和需要回炉的章节。" ), "dr_editor_in_chief": ( "你是 Deep Research 的终稿主编。你的职责是把已验证证据组织成客户可读的中文报告," "保持观点清晰、证据密实、表达克制,避免翻译腔和 AI 味。" ), "dr_reporter": ( "你是 Deep Research 的报告制作负责人。你的职责是把已定稿内容可靠渲染为 PDF/DOCX," "确保引用、排版、中文字体、表格和输出卫生可交付。" ), } @dataclass(frozen=True) class RoleDefinition: name: str model: str skills: list[str] temperature: float max_tokens: int max_concurrency: int identity: str = "" class RuntimeProfile: def __init__(self, *, profile: str, roles: dict[str, RoleDefinition], task_types: dict[str, str]) -> None: self.profile = profile self.roles = roles self.task_types = task_types def role_for_task(self, task_type: str) -> RoleDefinition: role_name = self.task_types.get(task_type) if not role_name: raise KeyError(f"unknown task_type: {task_type}") if role_name not in self.roles: raise KeyError(f"task_type {task_type} maps to missing role {role_name}") return self.roles[role_name] def resolve_runtime_profile( *, profile: str | None = None, overrides: dict[str, str] | None = None, ) -> RuntimeProfile: resolved = resolve_model_profile(profile=profile, overrides=overrides) role_models = resolved["roles"] roles: dict[str, RoleDefinition] = {} for name, defaults in ROLE_DEFAULTS.items(): model = role_models.get(name) if not model: continue roles[name] = RoleDefinition( name=name, model=model, skills=list(defaults["skills"]), temperature=float(defaults["temperature"]), max_tokens=int(defaults["max_tokens"]), max_concurrency=int(defaults["max_concurrency"]), identity=ROLE_IDENTITIES.get(name, ""), ) return RuntimeProfile( profile=resolved["profile"], roles=roles, task_types=dict(resolved.get("task_types") or {}), )