From 5ab7783b095b8c2c7016b9e59b7966b6e0c753db Mon Sep 17 00:00:00 2001 From: zizi Date: Mon, 13 Jul 2026 16:28:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A1=86=E6=9E=B6:=20parse=E8=AF=95=E6=8B=86?= =?UTF-8?q?=E4=B8=89=E8=BD=AE=E7=97=85=E7=90=86=E4=BF=AE=E5=A4=8D=E2=80=94?= =?UTF-8?q?=E2=80=94ingest=E5=AD=97=E6=AE=B5=E6=8C=87=E7=BA=B9=E6=94=B9?= =?UTF-8?q?=E5=9E=8B+0=E5=8D=A1=E4=B8=8D=E8=BD=AF=E5=88=A0(=E9=98=B2?= =?UTF-8?q?=E9=87=8D=E8=B7=91=E8=87=AA=E5=99=AC=E6=8D=9F=E8=80=97)+parse?= =?UTF-8?q?=5Fllm=E5=88=A4=E9=87=8D=E5=90=8D=E5=BD=95=E6=8E=92=E9=99=A4?= =?UTF-8?q?=E6=9C=AC=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../skills/parse-book/scripts/parse_ingest.py | 30 ++++++++++++++----- .../skills/parse-book/scripts/parse_llm.py | 7 +++-- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/.claude/skills/parse-book/scripts/parse_ingest.py b/.claude/skills/parse-book/scripts/parse_ingest.py index cf348ba..3cdb01a 100644 --- a/.claude/skills/parse-book/scripts/parse_ingest.py +++ b/.claude/skills/parse-book/scripts/parse_ingest.py @@ -171,12 +171,15 @@ def patterns(work_id, chapter_order, file_): ch_id, src = chapter_of(conn, work_id, chapter_order) contracts = {t: field_contract(conn, t) for t in PATTERN_TYPES} book = conn.execute("SELECT title FROM muse_content_work WHERE id=%s", (work_id,)).fetchone()[0] - # 幂等:重跑本章 = 软删本章旧 draft - conn.execute( - """UPDATE muse_knowledge_draft SET deleted=TRUE, updater=%s - WHERE tenant_id=%s AND source_type='parse_book' AND source_id=%s - AND draft_payload->>'章序'=%s AND deleted=FALSE""", - (ACTOR, TENANT, work_id, str(chapter_order))) + # 幂等:重跑本章 = 软删本章旧 draft。 + # 但新轮 0 卡时保留旧卡——LLM 判卡有随机性,重跑「0 卡」不应清掉上轮已验证的产出 + #(实测组合病:判重名录+随机性+软删 → 每次重跑都损耗卡) + if cards: + conn.execute( + """UPDATE muse_knowledge_draft SET deleted=TRUE, updater=%s + WHERE tenant_id=%s AND source_type='parse_book' AND source_id=%s + AND draft_payload->>'章序'=%s AND deleted=FALSE""", + (ACTOR, TENANT, work_id, str(chapter_order))) ok, rejected = 0, [] for i, c in enumerate(cards): t = c.get("型") @@ -189,10 +192,19 @@ def patterns(work_id, chapter_order, file_): if not (src_ref.get("书名") and src_ref.get("回目")): reasons.append("出处不完整(需书名+回目)") fields = c.get("字段") or {} - if t in contracts: + retyped = None + if t in contracts and fields: illegal = set(fields) - contracts[t] if illegal: - reasons.append(f"字段越合同:{sorted(illegal)}") + # 字段指纹改型:LLM 的 type 标注不可靠(实测 M3 系统性全标 craft), + # 五型字段 key 互不重叠——字段集合全命中唯一他型合同时,以字段为准改型 + fits = [t2 for t2 in PATTERN_TYPES + if t2 != t and fields and set(fields) <= contracts[t2]] + if len(fits) == 1: + retyped = f"{t}→{fits[0]}(字段指纹改型)" + c["型"] = t = fits[0] + else: + reasons.append(f"字段越合同:{sorted(illegal)}") texts = [c.get("名称"), c.get("一句话摘要"), *fields.values()] leak = leak_check([x for x in texts if x], src) if leak: @@ -203,6 +215,8 @@ def patterns(work_id, chapter_order, file_): payload = {"型": t, "名称": c["名称"], "一句话摘要": c.get("一句话摘要", ""), "字段": fields, "出处": src_ref, "目标库": "公共范式库", "章序": chapter_order, "来源": f"拆书@{book}", "状态": "草稿"} + if retyped: + payload["改型"] = retyped # 审计:机械改型可追溯 cid = f"parse-{work_id}-{chapter_order}-{i}-" + hashlib.sha256( json.dumps(payload, ensure_ascii=False, sort_keys=True).encode()).hexdigest()[:8] conn.execute( diff --git a/.claude/skills/parse-book/scripts/parse_llm.py b/.claude/skills/parse-book/scripts/parse_llm.py index 8b712d9..8f4f9b4 100644 --- a/.claude/skills/parse-book/scripts/parse_llm.py +++ b/.claude/skills/parse-book/scripts/parse_llm.py @@ -167,10 +167,13 @@ def main(work_id, from_, to, model): JOIN muse_content_chapter c ON c.id=s.chapter_id WHERE s.tenant_id=%s AND s.work_id=%s AND c.order_no<%s AND s.deleted=FALSE""", (TENANT, work_id, ch)).fetchall() for e in ents] + # 判重名录排除本章自己的卡:否则重跑时 M3 对着自己首轮的卡判重不出卡, + # ingest 幂等软删旧卡后插 0 张——卡被自己删了(重跑自噬,实测病) cards = conn.execute( """SELECT draft_payload->>'型', draft_payload->>'名称' FROM muse_knowledge_draft - WHERE tenant_id=%s AND source_type='parse_book' AND deleted=FALSE""", - (TENANT,)).fetchall() + WHERE tenant_id=%s AND source_type='parse_book' AND deleted=FALSE + AND NOT (source_id=%s AND (draft_payload->>'章序')::int=%s)""", + (TENANT, work_id, ch)).fetchall() outline = None if s_st == "done": r = conn.execute(