release: v0.20 Codex-ready skill-driven core

This commit is contained in:
kai
2026-05-07 08:21:28 +08:00
parent 0644a68ecc
commit 68e45bcf41
45 changed files with 3005 additions and 157 deletions
+455 -26
View File
@@ -192,10 +192,10 @@ def write_material_brief(
def _axis_prompt_brief(axis: str, method: ResearchMethod) -> str:
prompts = {
"input_material_findings": "从用户材料中提取现场事实、审计发现、复盘记录和内部答复,并标注原始材料位置。",
"nmpa_fda_ema_ich_who_baseline": "把 NMPA、FDA、EMA、ICH、WHO、药典或 Annex 1 等要求转化为可核验的法规基线。",
"quality_system_gap": "把现场发现映射到质量体系流程缺口,覆盖偏差、变更、CAPA、文件、培训和数据完整性。",
"manufacturing_process_risk": "围绕生产工艺、设施、公用系统、CPP/CQA、验证和无菌保障识别系统性风险。",
"operations_management_gap": "诊断运营管理、跨部门协同、会议机制、指标体系和交付节奏的结构性问题。",
"nmpa_fda_ema_ich_who_baseline": "把 NMPA、FDA、EMA、ICH、WHO、药典或 Annex 1 等要求转化为可核验的法规基线,并纳入 FDA warning letters 与会议材料作为执法尺度参照",
"quality_system_gap": "把现场发现映射到质量体系流程缺口,覆盖偏差、变更、CAPA、文件、培训和数据完整性;优先检索 FDA warning letters 中同类缺陷的执法表述",
"manufacturing_process_risk": "围绕生产工艺、设施、公用系统、CPP/CQA、验证和无菌保障识别系统性风险,并用 FDA warning letters / inspection enforcement examples 校准严重度",
"operations_management_gap": "诊断运营管理、跨部门协同、会议机制、指标体系和交付节奏的结构性问题,并参考 FDA 会议纪要/meeting materials 中对质量治理的关注点",
"team_capability": "识别人员能力、岗位职责、质量文化和管理梯队方面的缺口与建设路径。",
"capa_roadmap": "把差距转化为短中长期 CAPA 组合,要求绑定 owner、期限、优先级、关闭证据和复核机制。",
"verification_evidence": "定义整改完成后可被审计接受的验证证据,包括记录、报告、趋势和管理评审输入。",
@@ -213,13 +213,366 @@ def _material_paths(manifest: dict[str, Any]) -> list[dict[str, str]]:
return materials
def _keywords_from_title(title: str) -> list[str]:
english = re.findall(r"[A-Za-z][A-Za-z0-9/+-]{1,}", title)
chinese_parts = re.split(r"[,、;;:\s]+|和|与|及|的|在|为|从|来自|集中|决定|需要|形成|成为|不是|而是", title)
domain_terms = [
"审计",
"商业化",
"阶段门",
"风险",
"法规",
"欧盟",
"NMPA",
"GMP",
"ICH",
"无菌",
"RABS",
"First Air",
"APS",
"灯检",
"隧道",
"原液",
"WFI",
"SCADA",
"EMS",
"CPP",
"CQA",
"PPQ",
"清洁验证",
"偏差",
"变更",
"CAPA",
"数据完整性",
"人员",
"培训",
"质量文化",
"运营",
"跨部门",
"指标",
"团队",
"CDMO",
"整改",
"owner",
]
title_terms = [term for term in domain_terms if term in title]
keywords = [item.strip() for item in [*english, *title_terms, *chinese_parts] if len(item.strip()) >= 2]
seen: set[str] = set()
unique: list[str] = []
for keyword in keywords:
if keyword not in seen:
seen.add(keyword)
unique.append(keyword)
return unique[:12]
def _material_lines_for_chapter(project_root: Path, manifest: dict[str, Any], title: str, *, limit: int = 4) -> list[str]:
keywords = _keywords_from_title(title)
candidates: list[tuple[int, int, str]] = []
order = 0
for item in manifest.get("material_inventory") or []:
rel = item.get("ocr_extracted_to") or item.get("extracted_to")
if not rel:
continue
path = project_root / rel
if not path.exists():
continue
for raw in path.read_text(encoding="utf-8").splitlines():
line = raw.strip()
if len(line) < 8 or len(line) > 220:
continue
if line.startswith("#") or line.startswith("- source_path:") or line.startswith("- extracted_at:"):
continue
if "OCR Material:" in line:
continue
if re.match(r"^(审计对象|审计执行方|审计执行人|审计时间)[::]", line):
continue
score = sum(1 for keyword in keywords if keyword and keyword in line)
if score:
order += 1
candidates.append((score, order, f"{rel}{line}"))
candidates.sort(key=lambda item: (-item[0], item[1]))
return [line for _, _, line in candidates[:limit]]
def _minimum_evidence_for_method(method: ResearchMethod) -> dict[str, Any]:
if method.key == "gmp_quality_operations_diagnosis":
return {
"local_material_quotes": 2,
"official_regulatory_or_guideline_sources": 2,
"enforcement_or_best_practice_precedents": 1,
"counter_evidence_or_boundary_conditions": 1,
"actionable_remediation_items": 3,
}
return {
"high_quality_sources": 4,
"tier_1_2_sources": 2,
"counter_evidence_or_boundary_conditions": 1,
"decision_relevant_implications": 2,
}
def _central_thesis(manifest: dict[str, Any], method: ResearchMethod) -> str:
topic = manifest.get("topic") or manifest.get("report_title") or "本研究主题"
if method.key == "gmp_quality_operations_diagnosis":
return (
f"初始主判断:{topic} 不应只按审计风险项数量来评价,而应从商业化 readiness、"
"质量体系运行成熟度、生产工艺证据链和运营协同能力四条线同时诊断。Phase 2 必须用"
"现场材料原文、官方法规/指南、执法案例或标杆实践来证明、修正或推翻这一判断。"
)
return (
f"初始主判断:{topic} 需要先形成可被证据推翻的观点型框架,再由 Phase 2 按方法论证据线"
"逐项求证;不能把并发检索结果直接堆砌成报告。"
)
def _strategy_for_chapter(title: str, method: ResearchMethod) -> dict[str, Any]:
"""Return non-tautological Phase 1 strategy text for a chapter title."""
if method.key != "gmp_quality_operations_diagnosis":
return {
"core_question": f"本章需要判断:在什么证据条件下“{title}”成立,它会怎样改变最终决策?",
"bold_hypothesis": f"初始假设不是复述标题,而是预判“{title}”背后存在一个可被验证的因果机制;Phase 2 需要找证据支持、修正或推翻这个机制。",
"writing_claim": f"本章要把“{title}”写成一个可被证据检验的判断,而不是资料综述。",
"counter_evidence": [
"是否存在更简单的替代解释,能削弱本章主判断?",
"关键证据是否只来自单一来源或利益相关来源?",
"是否有反例显示本章判断只适用于部分场景?",
],
}
strategies = [
(
("审计", "阶段门"),
{
"core_question": "审计报告的低/中风险项计数,是否低估了白帆从临床/受托生产走向商业化标准时需要跨过的阶段门?",
"bold_hypothesis": "初始假设:白帆的硬件和文件基础总体可用,但审计材料暴露的是商业化 readiness 缺口,而不是简单的若干孤立缺陷;Phase 2 应验证这些缺口是否集中在无菌保障、工艺验证、质量闭环和运营节奏。",
"writing_claim": "本章要先把“风险项清单”翻译成管理层可决策的阶段门地图,说明哪些问题影响商业化放行、客户审计和技术转移节奏。",
"counter_evidence": [
"是否已有整改证据证明这些问题只是审计时点的临时缺口?",
"低/中风险评级是否足以说明商业化阶段门影响有限?",
"审计范围有限是否导致本章不能外推到整体体系成熟度?",
],
},
),
(
("法规", "欧盟", "NMPA", "ICH"),
{
"core_question": "如果按 EU Annex 1、NMPA GMP、ICH Q9/Q10 以及 FDA 执法尺度校准,哪些现场发现的严重度和整改优先级会发生变化?",
"bold_hypothesis": "初始假设:白帆按国内 GMP 逻辑已具备基础合规框架,但若以欧盟无菌标准和质量风险管理要求衡量,部分“低风险/建议项”会转化为体系成熟度缺口。",
"writing_claim": "本章要建立后文共用的法规基线,避免整改优先级只跟随原审计评级,而忽略国际化和商业化标准。",
"counter_evidence": [
"相关国际标准是否并不适用于当前产品阶段或委托生产边界?",
"NMPA 与欧盟/美国要求之间是否存在可接受差异?",
"是否有企业内部标准已经覆盖但审计材料未呈现?",
],
},
),
(
("无菌", "RABS", "First Air", "APS", "灯检"),
{
"core_question": "制剂线的主要无菌风险,是硬件布局不足,还是人员干预、首次气流保护、APS 覆盖和灯检标准执行证据不足?",
"bold_hypothesis": "初始假设:白帆制剂车间硬件基础并非主要短板,真正风险在于关键无菌行为和模拟验证是否能持续证明受控;Phase 2 应重点查 First Air、RABS 干预、APS 场景设计和灯检阳性样品管理。",
"writing_claim": "本章要把无菌保障从“设施看起来合规”推进到“关键操作和验证证据可被审计接受”。",
"counter_evidence": [
"现场是否已有完整视频复核、APS 覆盖和再培训有效性证据?",
"观察到的无菌动作问题是否只是个别人员或单次拍摄偏差?",
"灯检和 RABS 风险是否已有 SOP、趋势和复核记录闭环?",
],
},
),
(
("原液", "WFI", "SCADA", "EMS"),
{
"core_question": "原液和公用系统的风险是否被一次性封闭工艺掩盖,真正缺口在 WFI、SCADA/EMS、离线记录和异常升级证据链?",
"bold_hypothesis": "初始假设:一次性反应器和封闭转移降低了暴露风险,但不能自动证明系统受控;Phase 2 应验证 WFI 冷却回流、环境/压差报警、SCADA 数据和离线检测记录是否形成完整证据链。",
"writing_claim": "本章要说明原液与公用系统不是“硬件先进即可”,而是要证明关键状态、报警、数据和异常处理持续受控。",
"counter_evidence": [
"WFI、SCADA/EMS 和离线记录是否已有验证报告与趋势复核?",
"一次性系统是否已经充分降低共线和交叉污染风险?",
"被指出的公用系统风险是否只是设计建议而非实际偏差?",
],
},
),
(
("工艺", "CPP", "CQA", "PPQ", "清洁验证"),
{
"core_question": "现有 IND 阶段工艺规程和批记录,距离商业化 PPQ、控制策略和清洁验证所需证据还差在哪里?",
"bold_hypothesis": "初始假设:白帆目前的工艺文件足以支撑临床阶段执行,但不足以支撑商业化批记录、CPP/CQA 控制、PPQ 和清洁验证闭环;Phase 2 应查明哪些字段、参数和验证证据必须前置补齐。",
"writing_claim": "本章要把技术转移风险具体化为文件、参数、验证和批记录的硬门槛。",
"counter_evidence": [
"是否已有商业化模板、控制策略或 PPQ 草案未体现在审计材料中?",
"当前项目阶段是否尚不需要完整商业化批记录要求?",
"清洁验证和工艺验证是否已有主计划覆盖?",
],
},
),
(
("偏差", "变更", "CAPA", "数据完整性"),
{
"core_question": "白帆的问题是没有质量流程,还是流程之间的事件分类、升级、CAPA 有效性和数据完整性尚未形成运行闭环?",
"bold_hypothesis": "初始假设:白帆已有偏差、变更和 CAPA 的流程框架,但事件何时启动偏差、何时作为变更、如何证明 CAPA 有效,以及电子/纸质数据如何贯通,仍存在运行机制缺口。",
"writing_claim": "本章要把质量体系从“有 SOP”推进到“事件能被正确分类、调查、纠正、验证并趋势复核”。",
"counter_evidence": [
"是否有趋势分析、管理评审和 CAPA effectiveness check 证明体系已经闭环?",
"个别事件分类问题是否不足以代表体系性缺口?",
"电子系统和纸质记录之间是否已有数据完整性控制?",
],
},
),
(
("人员", "培训", "质量文化"),
{
"core_question": "培训记录齐全是否真的转化为一线无菌行为、偏差判断和质量风险意识?哪些证据能证明培训有效?",
"bold_hypothesis": "初始假设:白帆不缺培训台账,缺的是把培训结果转化为现场行为的一致性证据;如果 First Air、干预动作、事件判断和灯检执行仍需反复提醒,问题就不是“再培训一次”,而是培训有效性确认和质量文化运行机制不足。",
"writing_claim": "本章要把人员问题从“有没有培训”改写为“培训是否改变行为、降低风险、形成可复核证据”。",
"counter_evidence": [
"现场抽问、资格确认和再培训记录是否已证明人员理解到位?",
"被观察到的行为问题是否只发生在少数岗位或单次演示?",
"是否有岗位胜任力矩阵、年度复评和行为观察数据支撑人员能力?",
],
},
),
(
("运营", "跨部门", "指标", "review"),
{
"core_question": "白帆当前整改和生产准备依赖个人推动,还是已经形成跨部门例会、问题升级、指标看板和管理层复核的运营系统?",
"bold_hypothesis": "初始假设:运营短板不在于团队不努力,而在于缺少固定节奏和可视化管理系统;如果 owner、关闭证据、升级阈值和管理层 review 不稳定,整改会停留在临时协调,难以支撑商业化节奏。",
"writing_claim": "本章要说明运营管理是 GMP 风险的放大器:没有节奏、看板和升级机制,技术和质量问题会反复跨部门漂移。",
"counter_evidence": [
"是否已经存在稳定 PMO/例会/看板,只是未进入审计材料?",
"短期临时协调是否足以覆盖当前项目阶段,不需要完整运营系统?",
"owner、期限和关闭证据是否已经在复盘文件中基本清楚?",
],
},
),
(
("团队", "CDMO", "能力矩阵"),
{
"core_question": "对标成熟 CDMO,白帆最需要补齐的是人数、岗位能力,还是 QA/MSAT/工程/项目管理之间的角色分工?",
"bold_hypothesis": "初始假设:白帆的能力缺口不是简单扩编,而是商业化 CDMO 所需的角色矩阵尚未完全成型;Phase 2 应验证 QA 独立性、MSAT 工艺支持、工程保障、生产班组和 PMO 协同能力。",
"writing_claim": "本章要给出面向商业化的团队能力地图,说明哪些能力必须自建,哪些可外部支持,哪些要通过机制补齐。",
"counter_evidence": [
"现有人员是否已具备商业化经验,只是材料未体现?",
"对标 CDMO 是否会高估当前阶段所需组织复杂度?",
"是否可通过顾问、外包或客户支持临时补足能力?",
],
},
),
(
("整改", "owner", "路线图"),
{
"core_question": "哪些整改必须立即完成,哪些属于体系补强,哪些是能力建设?每项如何绑定 owner、关闭证据和复核窗口?",
"bold_hypothesis": "初始假设:如果整改只按问题清单逐条关闭,会漏掉体系性根因;更有效的路线应分为立即纠偏、90 天体系补强和中长期能力建设三层,并为每层定义关闭证据。",
"writing_claim": "本章要把诊断转化为可执行 CAPA 组合,而不是泛泛的改进建议。",
"counter_evidence": [
"是否已有整改计划足以覆盖 owner、期限、关闭证据和 QA verification",
"部分整改是否应前移或后移,避免资源过载?",
"哪些建议若缺少法规证据,不应被列为强制整改?",
],
},
),
(
("管理层", "CAPA", "总表"),
{
"core_question": "管理层应通过什么样的 CAPA 总表、法规映射表和复核节奏,持续判断整改是否真正降低风险?",
"bold_hypothesis": "初始假设:白帆需要的不只是一次性报告,而是一套管理层可追踪的整改仪表盘;否则 CAPA 关闭会变成文件动作,无法证明风险趋势下降和商业化 readiness 提升。",
"writing_claim": "本章要把报告成果固化成管理层治理工具:CAPA 总表、法规映射、证据包和复核节奏。",
"counter_evidence": [
"现有管理评审或质量例会是否已经能承担这个功能?",
"过度表格化是否会增加一线负担而不改善风险?",
"哪些指标真正能反映风险降低,而不是制造形式化 KPI?",
],
},
),
]
for needles, strategy in strategies:
if any(needle in title for needle in needles):
return strategy
return {
"core_question": f"本章需要判断“{title}”背后的真实风险、适用边界和整改优先级。",
"bold_hypothesis": f"初始假设:{title} 不是孤立问题,而是质量体系、工艺证据或运营机制中的一个可验证缺口;Phase 2 必须用材料原文和外部证据判断其严重度。",
"writing_claim": f"本章要把“{title}”转化为可执行的诊断结论和整改要求。",
"counter_evidence": [
"该问题是否已有充分整改或验证证据?",
"是否只是阶段性限制,而非系统性缺口?",
"外部标准是否适用于当前业务边界?",
],
}
def build_chapter_planning(
project_root: Path,
manifest: dict[str, Any],
method: ResearchMethod,
titles: list[str],
*,
quota: int,
) -> list[dict[str, Any]]:
"""Build hypothesis-driven chapter plans that become Phase 2 prompt context."""
lanes = list(method.integrated_lanes or method.task_axes)
minimum_evidence = _minimum_evidence_for_method(method)
plans: list[dict[str, Any]] = []
for idx, title in enumerate(titles, start=1):
chapter_id = f"ch{idx:02d}"
material_lines = _material_lines_for_chapter(project_root, manifest, title)
if not material_lines:
material_lines = ["未在材料中自动匹配到足够线索;Phase 2 必须先回读全部输入材料并补充原文摘录。"]
strategy = _strategy_for_chapter(title, method)
core_question = strategy["core_question"]
bold_hypothesis = strategy["bold_hypothesis"]
verification_plan = [
"先从允许的本地材料提取 2-4 条原文证据,保留出处和上下文。",
f"再按方法论 evidence lanes 求证:{''.join(lanes)}",
"每个核心判断至少匹配 2 个独立高质量来源;不足时降级为待验证判断。",
"主动搜索反方证据、低严重度解释、适用范围限制或替代原因。",
"输出时把证据、判断、整改/建议和待补证据分开,避免直接写成散文化正文。",
]
counter_evidence = strategy["counter_evidence"]
writing_claim = strategy["writing_claim"]
phase2_prompt_context = "\n".join(
[
f"章节:{chapter_id} {title}",
core_question,
bold_hypothesis,
"材料起点:",
*[f"- {line}" for line in material_lines],
"求证路线:",
*[f"- {item}" for item in verification_plan],
"必须寻找的反方/边界:",
*[f"- {item}" for item in counter_evidence],
f"写作主张:{writing_claim}",
f"最低证据要求:{json.dumps(minimum_evidence, ensure_ascii=False)}",
]
)
plans.append(
{
"chapter_id": chapter_id,
"title": title,
"suggested_words": quota,
"core_question": core_question,
"bold_hypothesis": bold_hypothesis,
"why_this_matters": "本章用于把 Phase1 的判断转化为 Phase2 可验证命题,并为最终报告保留清晰主线。",
"material_starting_points": material_lines,
"evidence_lanes": lanes,
"verification_plan": verification_plan,
"counter_evidence_to_seek": counter_evidence,
"writing_claim": writing_claim,
"minimum_evidence": minimum_evidence,
"phase2_prompt_context": phase2_prompt_context,
}
)
return plans
def build_research_brief_payload(
project_root: Path,
manifest: dict[str, Any],
method: ResearchMethod,
chapter_planning: list[dict[str, Any]] | None = None,
) -> dict[str, Any]:
"""Create the file-backed Phase 1 research brief used by task-card generation."""
axes = list(method.task_axes)
chapter_planning = chapter_planning or []
return {
"version": "0.21-alpha",
"topic": manifest.get("topic", project_root.name),
@@ -228,6 +581,13 @@ def build_research_brief_payload(
"work_language": "zh",
"tone": "事实型、整改导向、面向管理层和质量/生产负责人;避免空泛咨询腔。",
"central_question": f"如何基于已提供材料和权威法规/最佳实践,系统诊断“{manifest.get('topic', project_root.name)}”并形成可执行整改路线图?",
"central_thesis": _central_thesis(manifest, method),
"phase_logic": {
"phase1": "大胆假设:结合输入材料、访谈信息和初步搜索,定下主基调、章节命题和求证路线。",
"phase2": "小心求证:worker 只围绕 Phase1 命题收集、验证、证伪和补证,不自行重写研究方向。",
"phase3": "一致性审校:检查 Phase1 假设与 Phase2 证据是否自洽,指出需要回炉的章节或证据缺口。",
},
"phase2_mode": "chapter_integrated",
"success_criteria": [
"每个核心判断都能回到用户材料、权威法规、最佳实践或反方证据。",
"短中长期整改建议必须绑定优先级、责任、关闭证据和复核机制。",
@@ -239,8 +599,10 @@ def build_research_brief_payload(
"research_brief_path": "phase1/research_brief.json",
},
"materials": _material_paths(manifest),
"chapter_planning": chapter_planning,
"task_planning": {
"chapter_source": "phase1/framework.md",
"phase2_mode": "chapter_integrated",
"axes": axes,
"required_skills": [
"deep-research",
@@ -275,29 +637,62 @@ def write_research_brief(
project_root: Path,
manifest: dict[str, Any] | None = None,
method: ResearchMethod | None = None,
chapter_planning: list[dict[str, Any]] | None = None,
) -> tuple[Path, Path]:
manifest = manifest or load_manifest(project_root)
method = method or ResearchMethodRegistry().get(manifest.get("research_method"))
payload = build_research_brief_payload(project_root, manifest, method)
payload = build_research_brief_payload(project_root, manifest, method, chapter_planning=chapter_planning)
json_path = project_root / "phase1" / "research_brief.json"
md_path = project_root / "phase1" / "research_brief.md"
json_path.parent.mkdir(parents=True, exist_ok=True)
hypothesis_path = project_root / "phase1" / "hypothesis_map.json"
json_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
hypothesis_path.write_text(json.dumps(payload.get("chapter_planning") or [], ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
lines = [
f"# Phase 1 Research Brief{payload['topic']}",
"",
f"- research_method: {payload['research_method']}",
f"- work_language: {payload['work_language']}",
f"- tone: {payload['tone']}",
f"- phase2_mode: {payload['phase2_mode']}",
"",
"## 中心问题",
"",
payload["central_question"],
"",
"## 成功标准",
"## 主基调 / 大胆假设",
"",
payload["central_thesis"],
"",
"## Phase 逻辑",
"",
]
for phase_name, phase_text in payload["phase_logic"].items():
lines.append(f"- `{phase_name}`{phase_text}")
lines.extend([
"",
"## 成功标准",
"",
])
lines.extend(f"- {item}" for item in payload["success_criteria"])
if payload.get("chapter_planning"):
lines.extend(["", "## 章节命题与求证计划", ""])
for item in payload["chapter_planning"]:
lines.extend(
[
f"### {item['chapter_id']} {item['title']}",
"",
f"- 核心问题:{item['core_question']}",
f"- 大胆假设:{item['bold_hypothesis']}",
f"- 写作主张:{item['writing_claim']}",
f"- 证据线:{''.join(item['evidence_lanes'])}",
"- 材料起点:",
]
)
lines.extend(f" - {line}" for line in item["material_starting_points"])
lines.extend(["- 求证计划:"])
lines.extend(f" - {line}" for line in item["verification_plan"])
lines.extend([""])
lines.extend(["", "## 任务切分原则", ""])
planning = payload["task_planning"]
lines.append(planning["fragmentation_guard"])
@@ -377,30 +772,49 @@ CHAPTER_TEMPLATES: dict[str, list[str]] = {
"落地机制决定咨询建议能否转化为成果",
],
"gmp_quality_operations_diagnosis": [
"现场审计发现需要先转化为可验证的系统性问题图谱",
"法规基线决定质量体系差距的严重度与整改边界",
"生产工艺体系风险来自流程、设施、公用系统和验证证据的耦合缺口",
"偏差、变更、CAPA 和数据完整性决定质量系统能否闭环",
"人员能力与质量文化决定制度是否真正落地",
"运营管理问题需要区分组织、流程、会议机制和指标体系缺口",
"跨部门协同断点会放大 GMP 风险和交付风险",
"标杆实践应转化为短中长期整改组合而非口号",
"整改路线图必须绑定责任、优先级、证据和复核机制",
"管理层治理机制决定白帆能否从一次整改转向持续改进",
"从审计清单到商业化阶段门",
"法规基线重新校准整改优先级",
"制剂无菌保障:从硬件合规到行为受控",
"原液与公用系统:封闭工艺背后的证据缺口",
"工艺文件与验证:商业化转移的硬门槛",
"质量系统闭环:偏差、变更、CAPA 与数据完整性",
"人员能力:培训有效性比培训记录更关键",
"运营节奏:从临时协调转向管理系统",
"团队建设:按 CDMO 能力矩阵补齐角色",
"整改路线图:立即纠偏、体系补强、能力建设",
"管理层看板:用 CAPA 总表驱动复核",
],
}
def render_framework(project_root: Path, *, method_key: str | None = None, chapter_count: int = 10) -> Path:
def _existing_chapter_titles(project_root: Path) -> list[str]:
framework_path = project_root / "phase1" / "framework.md"
if not framework_path.exists():
return []
from scripts.runtime.tasks import parse_framework_chapters
chapters = parse_framework_chapters(framework_path.read_text(encoding="utf-8"))
return [chapter.title for chapter in chapters if chapter.title]
def render_framework(
project_root: Path,
*,
method_key: str | None = None,
chapter_count: int = 10,
preserve_existing_outline: bool = False,
) -> Path:
manifest = load_manifest(project_root)
registry = ResearchMethodRegistry()
method = registry.get(method_key or manifest.get("research_method"))
if method_key:
manifest["research_method"] = method.key
titles = CHAPTER_TEMPLATES.get(method.key) or CHAPTER_TEMPLATES["mckinsey_market"]
existing_titles = _existing_chapter_titles(project_root) if preserve_existing_outline else []
titles = existing_titles or CHAPTER_TEMPLATES.get(method.key) or CHAPTER_TEMPLATES["mckinsey_market"]
chapter_count = max(8, min(15, chapter_count))
selected = titles[:chapter_count]
selected = titles[:chapter_count] if not existing_titles else titles
quota = max(800, int(manifest.get("target_words", 30000)) // len(selected))
chapter_planning = build_chapter_planning(project_root, manifest, method, selected, quota=quota)
sections = "\n".join(f"- {item}" for item in method.framework_sections)
axes = "".join(method.task_axes)
material_text = render_material_inventory(manifest.get("material_inventory") or [])
@@ -430,17 +844,32 @@ def render_framework(project_root: Path, *, method_key: str | None = None, chapt
"",
"## 中心假设",
"",
f"围绕“{manifest['topic']}”形成可被证据支持或证伪的中文主线;所有核心判断必须绑定来源 ID。",
_central_thesis(manifest, method),
"",
"Phase1 的职责是大胆假设:基于材料、访谈和初步搜索定下主基调、章节命题和求证路线。Phase2 的职责是小心求证:验证、证伪、补证,而不是重新发明报告方向。Phase3 则检查 Phase1 假设与 Phase2 证据是否自洽。",
"",
]
for idx, title in enumerate(selected, start=1):
for item in chapter_planning:
lines.extend(
[
f"## 第{idx}{title}",
f"## 第{int(item['chapter_id'][2:])}{item['title']}",
"",
f"建议字数:约 {quota} 字。",
f"研究思路:围绕 `{method.key}` 的方法框架,从 {axes} 等任务轴并发收集 evidence packet,再由 chapter assembly 收束为完整中文章节。",
"证据要求:至少 2 个独立 Tier 1-2 信源;不足时在正文标注待验证;必须包含反方证据。",
f"建议字数:约 {item['suggested_words']} 字。",
f"本章要解决的问题:{item['core_question']}",
f"大胆假设:{item['bold_hypothesis']}",
f"写作主张:{item['writing_claim']}",
f"证据线:{''.join(item['evidence_lanes'])}",
"",
"材料起点:",
*[f"- {line}" for line in item["material_starting_points"]],
"",
"求证计划:",
*[f"- {line}" for line in item["verification_plan"]],
"",
"必须寻找的反方/边界:",
*[f"- {line}" for line in item["counter_evidence_to_seek"]],
"",
f"最低证据要求:`{json.dumps(item['minimum_evidence'], ensure_ascii=False)}`",
"",
]
)
@@ -457,7 +886,7 @@ def render_framework(project_root: Path, *, method_key: str | None = None, chapt
out = project_root / "phase1" / "framework.md"
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text("\n".join(lines), encoding="utf-8")
research_brief_md, research_brief_json = write_research_brief(project_root, manifest, method)
research_brief_md, research_brief_json = write_research_brief(project_root, manifest, method, chapter_planning=chapter_planning)
manifest["phase1"] = {
"status": "completed",
"approved": False,