diff --git a/.claude/skills/review-cards/scripts/review_cards.py b/.claude/skills/review-cards/scripts/review_cards.py index 25ac23a..7ae9b98 100644 --- a/.claude/skills/review-cards/scripts/review_cards.py +++ b/.claude/skills/review-cards/scripts/review_cards.py @@ -139,6 +139,56 @@ def review(work_id, batch, model, dry_run): + ("(dry-run 未写库)" if dry_run else "")) +@cli.command() +@click.option("--work-id", type=int, help="限定书") +@click.option("--out", required=True, type=click.Path(), help="输出 markdown 路径(仓库 docs/ 下,给创始人质检)") +def export(work_id, out): + """全部活卡导出为人读样张(创始人确认门的入口物料——他看文件,不读数据库)。 + 每卡:审核判定/三角色判词/字段全文/实例章号/审计标记;按书分节、判定排序。""" + order = {"pass": 0, "revise": 1, "reject": 2, None: 3} + with psycopg.connect(DSN) as conn: + rows = conn.execute( + """SELECT w.title, d.id, d.draft_payload FROM muse_knowledge_draft d + JOIN muse_content_work w ON w.id=d.source_id + WHERE d.tenant_id=%s AND d.source_type='parse_book' AND d.deleted=FALSE""" + + (" AND d.source_id=%s" if work_id else "") + " ORDER BY w.title, d.id", + [TENANT] + ([work_id] if work_id else [])).fetchall() + books = {} + for title, did, p in rows: + books.setdefault(title, []).append((did, p)) + stats = {"pass": 0, "revise": 0, "reject": 0, None: 0} + for cards in books.values(): + for _, p in cards: + stats[(p.get("审核") or {}).get("判定")] += 1 + lines = [f"# 公共范式卡样张({len(rows)} 张)\n", + f"> 审核判定:**pass {stats['pass']} / revise {stats['revise']} / reject {stats['reject']}**" + + (f"(未审 {stats[None]})" if stats[None] else "") + "。", + "> pass=建议入公共库;revise=有料但需改;reject=不够格(多为单场景记录冒充范式)。", + "> 「实例」里的人名/专名是溯源信息(设计允许),卡身字段应无专名。\n"] + for title, cards in books.items(): + cards.sort(key=lambda x: (order[(x[1].get("审核") or {}).get("判定")], -(x[1].get("审核") or {}).get("均分", 0))) + lines.append(f"\n## 《{title}》({len(cards)} 张)\n") + for did, p in cards: + audit = p.get("审核") or {} + lines.append(f"### [{audit.get('判定', '未审')} {audit.get('均分', '')}] " + f"{p.get('名称')}({p.get('型')}·库号{did})\n") + lines.append(f"**摘要**:{p.get('一句话摘要')}\n") + for k, v in (p.get("字段") or {}).items(): + lines.append(f"- **{k}**:{v}") + if p.get("实例"): + lines.append("- **实例**:" + ";".join( + f"第{i.get('章')}章「{i.get('定位')}」" for i in p["实例"])) + marks = [m for m in ("跨窗待证", "判重", "裁剪字段", "改型") if p.get(m)] + if marks: + lines.append("- **审计标记**:" + ";".join( + f"{m}={json.dumps(p[m], ensure_ascii=False)}" for m in marks)) + for role, r in (audit.get("角色") or {}).items(): + lines.append(f"- *{role}*({'/'.join(map(str, r.get('分', [])))}):{r.get('判词')}") + lines.append("") + pathlib.Path(out).write_text("\n".join(lines)) + click.echo(f"样张已导出:{out}({len(rows)} 卡)") + + @cli.command() @click.option("--work-id", type=int, help="限定书") @click.option("--batch", required=True, help="要对照的审核批次号(先跑 review)")