v0.21 alpha add research brief and compressed findings
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
@@ -11,6 +12,7 @@ from typing import Any
|
||||
from scripts.runtime.artifacts import PROJECTS_DIR, ensure_phase_dirs, load_manifest, write_manifest
|
||||
from scripts.runtime.materials import ingest_input_materials, render_material_inventory
|
||||
from scripts.runtime.methods import ResearchMethod, ResearchMethodRegistry
|
||||
from scripts.runtime.tasks import AXIS_ROUTES
|
||||
|
||||
|
||||
def utc_now_iso() -> str:
|
||||
@@ -187,6 +189,132 @@ def write_material_brief(
|
||||
return out
|
||||
|
||||
|
||||
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": "诊断运营管理、跨部门协同、会议机制、指标体系和交付节奏的结构性问题。",
|
||||
"team_capability": "识别人员能力、岗位职责、质量文化和管理梯队方面的缺口与建设路径。",
|
||||
"capa_roadmap": "把差距转化为短中长期 CAPA 组合,要求绑定 owner、期限、优先级、关闭证据和复核机制。",
|
||||
"verification_evidence": "定义整改完成后可被审计接受的验证证据,包括记录、报告、趋势和管理评审输入。",
|
||||
"counter": "主动寻找反方证据、限制条件和可能降低严重度或改变优先级的解释,避免单向论证。",
|
||||
}
|
||||
return prompts.get(axis, f"按照 `{method.key}` 方法,对 {axis} 轴进行证据收集、证伪和结构化归纳。")
|
||||
|
||||
|
||||
def _material_paths(manifest: dict[str, Any]) -> list[dict[str, str]]:
|
||||
materials: list[dict[str, str]] = []
|
||||
for item in manifest.get("material_inventory") or []:
|
||||
rel = item.get("ocr_extracted_to") or item.get("extracted_to") or item.get("copied_to")
|
||||
if rel:
|
||||
materials.append({"path": rel, "role": "input_material"})
|
||||
return materials
|
||||
|
||||
|
||||
def build_research_brief_payload(
|
||||
project_root: Path,
|
||||
manifest: dict[str, Any],
|
||||
method: ResearchMethod,
|
||||
) -> dict[str, Any]:
|
||||
"""Create the file-backed Phase 1 research brief used by task-card generation."""
|
||||
axes = list(method.task_axes)
|
||||
return {
|
||||
"version": "0.21-alpha",
|
||||
"topic": manifest.get("topic", project_root.name),
|
||||
"research_method": method.key,
|
||||
"method_name": method.name,
|
||||
"work_language": "zh",
|
||||
"tone": "事实型、整改导向、面向管理层和质量/生产负责人;避免空泛咨询腔。",
|
||||
"central_question": f"如何基于已提供材料和权威法规/最佳实践,系统诊断“{manifest.get('topic', project_root.name)}”并形成可执行整改路线图?",
|
||||
"success_criteria": [
|
||||
"每个核心判断都能回到用户材料、权威法规、最佳实践或反方证据。",
|
||||
"短中长期整改建议必须绑定优先级、责任、关闭证据和复核机制。",
|
||||
"章节写作必须先收束主线,再使用 evidence packet;不得按 packet 机械拼贴。",
|
||||
],
|
||||
"phase2_inputs": {
|
||||
"material_brief_path": "phase1/material_brief.md",
|
||||
"framework_path": "phase1/framework.md",
|
||||
"research_brief_path": "phase1/research_brief.json",
|
||||
},
|
||||
"materials": _material_paths(manifest),
|
||||
"task_planning": {
|
||||
"chapter_source": "phase1/framework.md",
|
||||
"axes": axes,
|
||||
"required_skills": [
|
||||
"deep-research",
|
||||
"search-gateway",
|
||||
"search-strategy",
|
||||
"source-quality",
|
||||
"evidence-table",
|
||||
"citation-manager",
|
||||
],
|
||||
"search_routes_by_axis": {axis: AXIS_ROUTES.get(axis, ["general"]) for axis in axes},
|
||||
"axis_prompt_briefs": {axis: _axis_prompt_brief(axis, method) for axis in axes},
|
||||
"stop_conditions": [
|
||||
"每张任务卡至少形成 3 条可追溯 evidence_items,且不得编造 candidate_sources 以外来源。",
|
||||
"关键 claim 不足 2 个独立 Tier 1-2 信源时,必须写入 open_questions 和证据缺口。",
|
||||
"必须包含 counter_evidence;找不到反方证据时记录检索路径和限制。",
|
||||
],
|
||||
"fragmentation_guard": "并发 worker 只生产 evidence packet;章节主线由 compressed_findings 收束,禁止直接把 packet 堆成正文。",
|
||||
},
|
||||
"clarification_notes": {
|
||||
"requires_user_review": True,
|
||||
"questions_source": "phase1/material_brief.md",
|
||||
"decision_items": [
|
||||
"确认报告用途、受众和脱敏边界。",
|
||||
"确认研究方法是否适配当前场景;MECE 只是可选方法之一。",
|
||||
"确认任务切分和检索策略是否足以让低成本模型独立执行。",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def write_research_brief(
|
||||
project_root: Path,
|
||||
manifest: dict[str, Any] | None = None,
|
||||
method: ResearchMethod | 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)
|
||||
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)
|
||||
json_path.write_text(json.dumps(payload, 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']}",
|
||||
"",
|
||||
"## 中心问题",
|
||||
"",
|
||||
payload["central_question"],
|
||||
"",
|
||||
"## 成功标准",
|
||||
"",
|
||||
]
|
||||
lines.extend(f"- {item}" for item in payload["success_criteria"])
|
||||
lines.extend(["", "## 任务切分原则", ""])
|
||||
planning = payload["task_planning"]
|
||||
lines.append(planning["fragmentation_guard"])
|
||||
lines.append("")
|
||||
for axis in planning["axes"]:
|
||||
routes = "、".join(planning["search_routes_by_axis"].get(axis, []))
|
||||
prompt = planning["axis_prompt_briefs"].get(axis, "")
|
||||
lines.append(f"- `{axis}`:{prompt} 检索路径:{routes}")
|
||||
lines.extend(["", "## 必读 Skills", ""])
|
||||
lines.extend(f"- {name}" for name in planning["required_skills"])
|
||||
lines.extend(["", "## 停止条件", ""])
|
||||
lines.extend(f"- {item}" for item in planning["stop_conditions"])
|
||||
lines.append("")
|
||||
md_path.write_text("\n".join(lines), encoding="utf-8")
|
||||
return md_path, json_path
|
||||
|
||||
|
||||
CHAPTER_TEMPLATES: dict[str, list[str]] = {
|
||||
"mckinsey_market": [
|
||||
"核心结论先行界定市场机会与约束",
|
||||
@@ -329,10 +457,14 @@ 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)
|
||||
manifest["phase1"] = {
|
||||
"status": "completed",
|
||||
"approved": False,
|
||||
"framework_path": "phase1/framework.md",
|
||||
"research_brief_path": str(research_brief_md.relative_to(project_root)),
|
||||
"research_brief_json_path": str(research_brief_json.relative_to(project_root)),
|
||||
"requires_user_interview": True,
|
||||
"research_method": method.key,
|
||||
"updated_at": utc_now_iso(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user