框架: 升格全量重抽准备脚本——卡软删+串行状态表清理(逐表核实约束定边界)
This commit is contained in:
parent
9f90f399ac
commit
26f5dad589
105
.claude/skills/parse-book/scripts/reset_upgrade_work.py
Normal file
105
.claude/skills/parse-book/scripts/reset_upgrade_work.py
Normal file
@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
"""parse-book skill:升格全量重抽准备——清一本书的升格产出与串行状态(2026-07-18 批9c 收尾)。
|
||||
|
||||
为什么需要它(锚点「全量重抽的管线坑」第 1 条):升格是**串行有状态**管线,直接重跑会被
|
||||
旧状态卡死——窗全是 done 不再处理、预扫/判重底册装着旧代卡把新观察并进废卡、旧审计行会被
|
||||
撤销机制错还原到已软删的旧卡上、别名表全行唯一索引让新别名被"冲突即跳过"静默吞掉。
|
||||
|
||||
清理边界(逐表核实过约束后定的,2026-07-18):
|
||||
· muse_knowledge_draft 升格卡 → **软删**(deleted=TRUE,红线:知识数据绝不物删,可回滚)
|
||||
· example_upgrade_window → 保留窗行(窗参数未变、from_chapter 锚幂等),仅重置 status=pending
|
||||
· example_upgrade_alias → **硬删**该书行——uk(tenant_id,work_id,alias) 是全行唯一索引(非部分索引),
|
||||
软删旧行会挡住重抽新 INSERT(ON CONFLICT DO NOTHING 静默吞)→ 别名判重空转
|
||||
· example_upgrade_presence → 硬删该书行(运行状态留档,重抽全量重生成;undo 机制既有做法即硬删)
|
||||
· example_upgrade_card_state → 硬删该书行(卡水位挂旧卡 draft_id,新卡新水位)
|
||||
· example_upgrade_audit → 硬删该书行——undo_window 的还原 JOIN **不过滤 d.deleted**,
|
||||
留着旧审计行,重抽期间任何同窗撤销都会把旧代软删卡错还原(污染)
|
||||
· example_knowledge_embedding → **不动**:旧卡向量行靠判重召回 SQL 的 JOIN d.deleted=FALSE 天然排除,
|
||||
软删状态保留可回溯;重抽边抽边嵌写新卡向量
|
||||
|
||||
用法:
|
||||
reset_upgrade_work.py --work-id N 预览(只读统计,不写库)
|
||||
reset_upgrade_work.py --work-id N --execute 真清(单事务,全清或全不清)
|
||||
"""
|
||||
import sys
|
||||
import pathlib
|
||||
|
||||
import click
|
||||
import psycopg
|
||||
|
||||
# 复用管线的连接与租户常量(与 parse_upgrade 同源)
|
||||
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
|
||||
from parse_llm import DSN, TENANT # noqa: E402
|
||||
|
||||
SOURCE_TYPE = "upgrade_book"
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("--work-id", type=int, required=True)
|
||||
@click.option("--execute", is_flag=True, help="真执行(默认只预览统计)")
|
||||
def main(work_id, execute):
|
||||
with psycopg.connect(DSN) as conn:
|
||||
title = conn.execute("SELECT title FROM muse_content_work WHERE id=%s",
|
||||
(work_id,)).fetchone()[0]
|
||||
# 预览统计:各表将被处理的行数
|
||||
cards = conn.execute(
|
||||
"""SELECT count(*) FROM muse_knowledge_draft
|
||||
WHERE tenant_id=%s AND work_id=%s AND source_type=%s AND deleted=FALSE""",
|
||||
(TENANT, work_id, SOURCE_TYPE)).fetchone()[0]
|
||||
wins = conn.execute(
|
||||
"""SELECT count(*), count(*) FILTER (WHERE status='done') FROM example_upgrade_window
|
||||
WHERE tenant_id=%s AND work_id=%s AND deleted=FALSE""",
|
||||
(TENANT, work_id)).fetchone()
|
||||
ali = conn.execute("SELECT count(*) FROM example_upgrade_alias WHERE tenant_id=%s AND work_id=%s",
|
||||
(TENANT, work_id)).fetchone()[0]
|
||||
pres = conn.execute("SELECT count(*) FROM example_upgrade_presence WHERE tenant_id=%s AND work_id=%s",
|
||||
(TENANT, work_id)).fetchone()[0]
|
||||
# card_state 无 work 冗余错?——有 work_id 列(建表即有);audit 无 work_id,靠 JOIN 卡定位
|
||||
cs = conn.execute("SELECT count(*) FROM example_upgrade_card_state WHERE tenant_id=%s AND work_id=%s",
|
||||
(TENANT, work_id)).fetchone()[0]
|
||||
aud = conn.execute(
|
||||
"""SELECT count(*) FROM example_upgrade_audit a
|
||||
WHERE a.tenant_id=%s AND a.draft_id IN
|
||||
(SELECT id FROM muse_knowledge_draft WHERE work_id=%s AND source_type=%s)""",
|
||||
(TENANT, work_id, SOURCE_TYPE)).fetchone()[0]
|
||||
click.echo(f"《{title}》work={work_id} 重抽准备{'(执行)' if execute else '(预览)'}:")
|
||||
click.echo(f" 软删活升格卡 {cards} | 重置窗 {wins[0]}(其中done {wins[1]}) | "
|
||||
f"硬删 别名{ali} 留档{pres} 卡水位{cs} 审计{aud}")
|
||||
if not execute:
|
||||
click.echo(" (预览模式未写库;加 --execute 真清)")
|
||||
return
|
||||
# 单事务执行:全清或全不清(中途失败自动回滚,不留半清状态)
|
||||
conn.execute(
|
||||
"""UPDATE muse_knowledge_draft SET deleted=TRUE, updater='upgrade-reset'
|
||||
WHERE tenant_id=%s AND work_id=%s AND source_type=%s AND deleted=FALSE""",
|
||||
(TENANT, work_id, SOURCE_TYPE))
|
||||
conn.execute(
|
||||
"""UPDATE example_upgrade_window SET status='pending', error_message=NULL,
|
||||
updater='upgrade-reset' WHERE tenant_id=%s AND work_id=%s AND deleted=FALSE""",
|
||||
(TENANT, work_id))
|
||||
# 审计先删(靠卡 JOIN 定位,卡还查得到——虽然刚软删,JOIN 不看 deleted)
|
||||
conn.execute(
|
||||
"""DELETE FROM example_upgrade_audit
|
||||
WHERE tenant_id=%s AND draft_id IN
|
||||
(SELECT id FROM muse_knowledge_draft WHERE work_id=%s AND source_type=%s)""",
|
||||
(TENANT, work_id, SOURCE_TYPE))
|
||||
conn.execute("DELETE FROM example_upgrade_alias WHERE tenant_id=%s AND work_id=%s",
|
||||
(TENANT, work_id))
|
||||
conn.execute("DELETE FROM example_upgrade_presence WHERE tenant_id=%s AND work_id=%s",
|
||||
(TENANT, work_id))
|
||||
conn.execute("DELETE FROM example_upgrade_card_state WHERE tenant_id=%s AND work_id=%s",
|
||||
(TENANT, work_id))
|
||||
conn.commit()
|
||||
# 清后复核:底册应为空、窗应全 pending
|
||||
left = conn.execute(
|
||||
"""SELECT count(*) FROM muse_knowledge_draft
|
||||
WHERE tenant_id=%s AND work_id=%s AND source_type=%s AND deleted=FALSE""",
|
||||
(TENANT, work_id, SOURCE_TYPE)).fetchone()[0]
|
||||
pend = conn.execute(
|
||||
"""SELECT count(*) FILTER (WHERE status='pending'), count(*) FROM example_upgrade_window
|
||||
WHERE tenant_id=%s AND work_id=%s AND deleted=FALSE""", (TENANT, work_id)).fetchone()
|
||||
click.echo(f" ✅ 清后复核:活升格卡 {left}(应0)| 窗 pending {pend[0]}/{pend[1]}(应全 pending)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
x
Reference in New Issue
Block a user