框架: 语义判重小样校准——阈值0.78→0.60实测定+终判近邻带字段证据+名称互含判据; 批9c收尾小样记录入锚点

This commit is contained in:
zizi 2026-07-18 03:35:14 +08:00
parent 2173c8f7ba
commit 9f90f399ac
4 changed files with 37 additions and 14 deletions

View File

@ -63,7 +63,11 @@ MILESTONE_FIELDS = {"演变历程"}
# 生命周期枚举(设计稿 §4.2):每条里程碑「周期」的取值域。 # 生命周期枚举(设计稿 §4.2):每条里程碑「周期」的取值域。
LIFECYCLE = ("登场", "成长", "高光", "退场", "结局") LIFECYCLE = ("登场", "成长", "高光", "退场", "结局")
# 语义判重(P1,设计稿 §8.2):召回同书近邻相似度 ≥ 此阈值才交 M3 终判。 # 语义判重(P1,设计稿 §8.2):召回同书近邻相似度 ≥ 此阈值才交 M3 终判。
DEDUP_SIM_THRESHOLD = 0.78 # 0.78→0.60(2026-07-18 补3洞零落库小样实测校准):改名场景(洞① 病例)查询侧是初观察薄快照、
# 存储侧是长成的厚卡,文本不对称压低余弦——同实体改名对实测 0.63-0.66("联邦生物机甲技术"vs
# 卡「生物机甲」0.6565),0.78 永不触发语义层;无关对实测 ≤0.51,0.60 落在分离带内留边距。
# 放宽只增终判候选量,并卡仍须 M3 终判"同型同一实体"才发生(跨型/无关由终判把关)。
DEDUP_SIM_THRESHOLD = 0.60
CHAP_BIG = 10 ** 9 # 章号缺失/待人工的里程碑,排序时排到最后 CHAP_BIG = 10 ** 9 # 章号缺失/待人工的里程碑,排序时排到最后
STEP_MAX = 80 # 里程碑「台阶」机械字数上限(洞③):提示词目标 ≤40 字,机械上限 80,双层防跑飞长文 STEP_MAX = 80 # 里程碑「台阶」机械字数上限(洞③):提示词目标 ≤40 字,机械上限 80,双层防跑飞长文
@ -785,35 +789,40 @@ def _entity_embed_text(ent):
def recall_neighbors(conn, vec, work_id, top=6): def recall_neighbors(conn, vec, work_id, top=6):
"""用**现成向量**在 example_knowledge_embedding 召回同书 ≥阈值 近邻卡。 """用**现成向量**在 example_knowledge_embedding 召回同书 ≥阈值 近邻卡。
返回 [(did, 型, 名称, 摘要, 相似度)…] 按相似度降序、过滤 <阈值。 返回 [(did, 型, 名称, 摘要, 字段摘选, 相似度)…] 按相似度降序、过滤 <阈值。
字段摘选(2026-07-18 小样校准):终判证据不能只有一句话摘要——实测 M3 在"改名候选 vs 厚卡"上
因近邻证据太薄保守判无关(境界阶梯/力量来源这类字段才是同一实体的强证据),故召回时带出
字段 JSON 截断 300 字随近邻进终判 prompt(与候选侧字段 600 字对称)。
三段式连接(洞①核心红线):嵌入已在预判段批量算好并挪出——本函数只跑向量召回 SQL、不再发嵌入 HTTP, 三段式连接(洞①核心红线):嵌入已在预判段批量算好并挪出——本函数只跑向量召回 SQL、不再发嵌入 HTTP,
调用方用短连接查完即关(不再沿用窗内长连接跨 LLM/嵌入调用存活,这正是本次要治的连接红线)。 调用方用短连接查完即关(不再沿用窗内长连接跨 LLM/嵌入调用存活,这正是本次要治的连接红线)。
嵌入表无同书向量(试跑期未 embed)时返回 []——优雅降级,判重回退纯机械,绝不阻断主流程。""" 嵌入表无同书向量(试跑期未 embed)时返回 []——优雅降级,判重回退纯机械,绝不阻断主流程。"""
qvec = json.dumps(vec) qvec = json.dumps(vec)
rows = conn.execute( rows = conn.execute(
"""SELECT d.id, d.draft_payload->>'type', d.draft_payload->>'名称', """SELECT d.id, d.draft_payload->>'type', d.draft_payload->>'名称',
d.draft_payload->>'一句话摘要', 1 - (e.embedding <=> %s::vector) AS score d.draft_payload->>'一句话摘要', d.draft_payload->>'字段',
1 - (e.embedding <=> %s::vector) AS score
FROM example_knowledge_embedding e FROM example_knowledge_embedding e
JOIN muse_knowledge_draft d ON d.id = e.draft_id JOIN muse_knowledge_draft d ON d.id = e.draft_id
WHERE e.tenant_id=%s AND e.deleted=FALSE AND d.deleted=FALSE WHERE e.tenant_id=%s AND e.deleted=FALSE AND d.deleted=FALSE
AND d.source_type=%s AND d.work_id=%s AND d.source_type=%s AND d.work_id=%s
ORDER BY score DESC LIMIT %s""", ORDER BY score DESC LIMIT %s""",
(qvec, TENANT, SOURCE_TYPE, work_id, top)).fetchall() (qvec, TENANT, SOURCE_TYPE, work_id, top)).fetchall()
return [(r[0], r[1], r[2], r[3], float(r[4])) for r in rows return [(r[0], r[1], r[2], r[3], (r[4] or "")[:300], float(r[5])) for r in rows
if float(r[4]) >= DEDUP_SIM_THRESHOLD] if float(r[5]) >= DEDUP_SIM_THRESHOLD]
def dedup_judge_prompt(ent, neighbors): def dedup_judge_prompt(ent, neighbors):
"""M3 终判 prompt:候选新实体 vs 每个同书近邻,判 同一实体 / 前身 / 后继 / 无关。""" """M3 终判 prompt:候选新实体 vs 每个同书近邻,判 同一实体 / 前身 / 后继 / 无关。
nb = "\n".join(f"{i + 1}. 卡号{did}|{t}|{nm}|{brief or ''}" 近邻带字段摘选(小样校准):只给一句话摘要时模型证据不足会保守判无关,字段是强证据。"""
for i, (did, t, nm, brief, _s) in enumerate(neighbors)) nb = "\n".join(f"{i + 1}. 卡号{did}|{t}|{nm}|{brief or ''}|字段摘选:{fs or '(无)'}"
for i, (did, t, nm, brief, fs, _s) in enumerate(neighbors))
return f"""【功能指令(parse-book 作品面升格·语义判重终判)】 return f"""【功能指令(parse-book 作品面升格·语义判重终判)】
下面是一个"候选新实体"和若干"同书既有卡"(向量召回的近邻)。逐一判断候选与每张近邻卡的关系,四选一: 下面是一个"候选新实体"和若干"同书既有卡"(向量召回的近邻)。逐一判断候选与每张近邻卡的关系,四选一:
- 同一实体:同一对象的改名/化名/不同侧面(如"影杀者"与"IV代纯机械机甲·影杀者")——**仅同型可判**。 - 同一实体:同一对象的改名/化名/不同侧面(如"影杀者"与"IV代纯机械机甲·影杀者")——**仅同型可判**。
- 前身:候选是该近邻卡的上一代/来源(同一条进化链的相邻代际,如"铁头(一代)"之于"铁卫(二代)")。 - 前身:候选是该近邻卡的上一代/来源(同一条进化链的相邻代际,如"铁头(一代)"之于"铁卫(二代)")。
- 后继:候选是该近邻卡的下一代/继承者。 - 后继:候选是该近邻卡的下一代/继承者。
- 无关:只是题材相近,各自独立。 - 无关:只是题材相近,各自独立。
判据:看名称/摘要/字段是否指向同一对象或同一条演变链;**跨型(如具体机甲 item vs 整套体系 power_system)绝不判同一实体,最多判前身/后继**——一整套体系不等于其中一台机体。 判据:看名称/摘要/字段是否指向同一对象或同一条演变链;**同型且名称互含(如"生物机甲"与"联邦生物机甲技术")通常是同一实体的简称/全称,除非字段证据明确指向不同对象**;名称不互含但摘要/字段描述同一套力量来源、同一条境界阶梯、同一批代表单位的,也应判同一实体(改名不改实质);**跨型(如具体机甲 item vs 整套体系 power_system)绝不判同一实体,最多判前身/后继**——一整套体系不等于其中一台机体。
【候选新实体】 【候选新实体】
型={ent.get("型", "")}|名称={ent.get("名称", "")}|摘要={ent.get("一句话摘要", "")} 型={ent.get("型", "")}|名称={ent.get("名称", "")}|摘要={ent.get("一句话摘要", "")}
@ -841,8 +850,8 @@ def semantic_dedup(ent, neighbors, call):
data, _ = call(dedup_judge_prompt(ent, neighbors), ("判定",)) data, _ = call(dedup_judge_prompt(ent, neighbors), ("判定",))
except (SensitiveHardStop, RuntimeError): except (SensitiveHardStop, RuntimeError):
return "new", None return "new", None
nb_type = {did: t for did, t, _, _, _ in neighbors} nb_type = {did: t for did, t, _, _, _, _ in neighbors}
nb_name = {did: nm for did, _, nm, _, _ in neighbors} nb_name = {did: nm for did, _, nm, _, _, _ in neighbors}
ent_type = ent.get("型", "") ent_type = ent.get("型", "")
chain = [] chain = []
for j in [x for x in (data.get("判定") or []) if isinstance(x, dict)]: for j in [x for x in (data.get("判定") or []) if isinstance(x, dict)]:

View File

@ -179,6 +179,14 @@ def test_prompts_disciplines():
[(10, {"名称": "张三"}), (11, {"名称": "李四"})], []) [(10, {"名称": "张三"}), (11, {"名称": "李四"})], [])
check("relation-构造器可渲染", "人物关系增量" in rel) check("relation-构造器可渲染", "人物关系增量" in rel)
# 判重终判 prompt(小样校准后):近邻六元组带字段摘选进证据 + 名称互含判据
nbs = [(6223, "power_system", "生物机甲", "机甲体系", '{"境界阶梯":"IV代→V代"}', 0.66)]
jp = pu.dedup_judge_prompt({"型": "power_system", "名称": "联邦生物机甲技术",
"一句话摘要": "s", "字段": {}}, nbs)
check("judge-近邻字段摘选入证据", "字段摘选:" in jp and "境界阶梯" in jp)
check("judge-名称互含判据", "同型且名称互含" in jp)
check("judge-跨型禁判同一实体仍在", "绝不判同一实体" in jp)
if __name__ == "__main__": if __name__ == "__main__":
for fn in (test_classify_new_name, test_debut_milestone, test_clean_milestone_guard, for fn in (test_classify_new_name, test_debut_milestone, test_clean_milestone_guard,

View File

@ -65,7 +65,7 @@ flowchart TB
end end
subgraph P1["第二步 P1:串链 + 语义判重(治病根 3、4)"] subgraph P1["第二步 P1:串链 + 语义判重(治病根 3、4)"]
B1["加『前身/后继』链接字段<br/>把碎卡串成一条进化链"] B1["加『前身/后继』链接字段<br/>把碎卡串成一条进化链"]
B2["判重打开语义近邻(≥0.78)<br/>+ M3 终判 → 治改名/跨型漏并"] B2["判重打开语义近邻(≥0.60·小样实测校准)<br/>+ M3 终判 → 治改名/跨型漏并"]
end end
subgraph FIX["两点硬性细化(贯穿全程)"] subgraph FIX["两点硬性细化(贯穿全程)"]
C1["① 里程碑用真实章号索引<br/>绝不用运行时窗号 [窗N]"] C1["① 里程碑用真实章号索引<br/>绝不用运行时窗号 [窗N]"]
@ -231,7 +231,7 @@ flowchart TB
在判重步,除现有的名字精确匹配/子串外,新增(就是打开 v6 已设计、暂时关着的那级): 在判重步,除现有的名字精确匹配/子串外,新增(就是打开 v6 已设计、暂时关着的那级):
1. 对候选新实体算向量(走 `embed` skill),用 `search` 召回**同书同型**相似度 ≥ 0.78 的近邻卡。 1. 对候选新实体算向量(走 `embed` skill),用 `search` 召回**同书同型**相似度 ≥ 0.60 的近邻卡(原定 0.78,2026-07-18 零落库小样实测校准:同实体改名对余弦 0.63-0.66、不同体系对 ≤0.58,0.78 永不触发语义层;依据详见 parse_upgrade.py 阈值注释)。
2. 召回的近邻交给 M3 终判(走 `parse_llm.py` 的 `m3_json` 入口,构造"这两张卡是不是同一实体 / 是不是前身后继关系"的判断),判定三选一:**同一实体**(并卡)/ **前身后继**(不并卡但串链)/ **无关**(各自立卡)。 2. 召回的近邻交给 M3 终判(走 `parse_llm.py` 的 `m3_json` 入口,构造"这两张卡是不是同一实体 / 是不是前身后继关系"的判断),判定三选一:**同一实体**(并卡)/ **前身后继**(不并卡但串链)/ **无关**(各自立卡)。
这样"影杀者"和"IV 代纯机械机甲·影杀者"这种改名、以及跨型指代同一条线的,都能被语义召回并正确处理。 这样"影杀者"和"IV 代纯机械机甲·影杀者"这种改名、以及跨型指代同一条线的,都能被语义召回并正确处理。
@ -246,7 +246,7 @@ flowchart TB
1. **条目从字符串变对象,合并逻辑改动面不小**。`parse_upgrade.py` 现有一大套针对字符串条目的守卫(剥前缀、剥尾残、拦垃圾、拆粘连、窗序归位),全要跟着改造成处理里程碑对象;存量卡还是字符串,迁移要兼容。→ 缓解:分两步落,先补字段和提示词(新卡受益),再做存量迁移;对象结构压到三字段降低崩坏面。 1. **条目从字符串变对象,合并逻辑改动面不小**。`parse_upgrade.py` 现有一大套针对字符串条目的守卫(剥前缀、剥尾残、拦垃圾、拆粘连、窗序归位),全要跟着改造成处理里程碑对象;存量卡还是字符串,迁移要兼容。→ 缓解:分两步落,先补字段和提示词(新卡受益),再做存量迁移;对象结构压到三字段降低崩坏面。
2. **模型抽结构化对象比抽字符串更容易格式崩**。→ 缓解:三字段极简;保留降级——抽不出完整对象就退化成"章号 + 一句话"最小对象,别整条丢。 2. **模型抽结构化对象比抽字符串更容易格式崩**。→ 缓解:三字段极简;保留降级——抽不出完整对象就退化成"章号 + 一句话"最小对象,别整条丢。
3. **语义判重增加调用量和跨型误并风险**。→ 缓解:0.78 阈值 + 同型才自动并 + 跨型仅提示;判重近邻可像 v6 说的那样批量做、不逐条烧。 3. **语义判重增加调用量和跨型误并风险**。→ 缓解:0.60 阈值(实测校准) + 同型才自动并 + 跨型仅提示;判重近邻可像 v6 说的那样批量做、不逐条烧。
4. **存量迁移精度损失**:无内嵌章号的条目只能靠窗映射到 12 章宽区间。→ 接受为存量固有局限,新卡不受影响。 4. **存量迁移精度损失**:无内嵌章号的条目只能靠窗映射到 12 章宽区间。→ 接受为存量固有局限,新卡不受影响。
5. **改 schema 后必须重跑种子**,否则库里字段合同和 YAML 不一致,抽取会按旧合同把新字段裁掉。→ 实施清单里钉死"改 YAML 后重跑 `seed_schemas.py`"这一步。 5. **改 schema 后必须重跑种子**,否则库里字段合同和 YAML 不一致,抽取会按旧合同把新字段裁掉。→ 实施清单里钉死"改 YAML 后重跑 `seed_schemas.py`"这一步。

View File

@ -92,3 +92,9 @@
## 批9d 插入记录(2026-07-17/18) ## 批9d 插入记录(2026-07-17/18)
批9c 全量重抽启动前,创始人插入方向级任务:第一性原理分析各类卡是否符合要求,抽最重要的点完善 SoT。已完成并落 design-docs(提交 2124a793):新增专题-07(知识消费契约与质量闭环,"以用定卡")+ 七册配套拍板(含 aiContext 值域升级、参照作品面、双层拍板、purpose 枚举统一)。分析证据链见 `2026-07-17-卡的第一性原理与知识效用闭环.md`。**批9c 收尾已由创始人下"继续"口令启动(2026-07-18),compact 恢复后从这里接**:下一步 = **派 fable5 实现子代理补 3 洞**(尚未派出;任务清单 #9-12 已建)。子代理提示词要点:改 parse_upgrade.py——②观察/更新提示词令实体首现必立"登场"里程碑且归并不丢;③体系卡演变历程只收体系级变化、单位/个体事件归各自实体卡、台阶一句话限长;①语义判重连接改读-算-写三段式(opus 审计点名:现持窗级连接跨嵌入与 M3 调用)+ 新卡/更新卡写段增量嵌入落 example_knowledge_embedding(重抽期间判重才有向量可召回)+ 接入重抽流程。红线:三段式连接、软删、LLM 走 chat_governed(M3)、绝对章号、中文注释、最小改动面、不跑库写。之后顺序:主代理逐段核验 → 零落库小样验 3 洞(深空样窗:生物机甲/铁头判同归线、登场里程碑在、体系卡无单位事件)→ 呈报样张等创始人放行全量。**创始人 2026-07-18 拍板通过重抽后路线:批10 = 消费最小闭环 + 回放评测首跑**——顺序:①升格卡向量落库(补齐可命中)→②统一读取器 read-context 从设计桩变实(8587 张卡第一次被创作流消费)→③单书规划线回放首跑(冻结前 N 章知识推演下一台阶、对照原书评分,验收对齐专题-07 §4/§7);不再继续扩书拆卡。 批9c 全量重抽启动前,创始人插入方向级任务:第一性原理分析各类卡是否符合要求,抽最重要的点完善 SoT。已完成并落 design-docs(提交 2124a793):新增专题-07(知识消费契约与质量闭环,"以用定卡")+ 七册配套拍板(含 aiContext 值域升级、参照作品面、双层拍板、purpose 枚举统一)。分析证据链见 `2026-07-17-卡的第一性原理与知识效用闭环.md`。**批9c 收尾已由创始人下"继续"口令启动(2026-07-18),compact 恢复后从这里接**:下一步 = **派 fable5 实现子代理补 3 洞**(尚未派出;任务清单 #9-12 已建)。子代理提示词要点:改 parse_upgrade.py——②观察/更新提示词令实体首现必立"登场"里程碑且归并不丢;③体系卡演变历程只收体系级变化、单位/个体事件归各自实体卡、台阶一句话限长;①语义判重连接改读-算-写三段式(opus 审计点名:现持窗级连接跨嵌入与 M3 调用)+ 新卡/更新卡写段增量嵌入落 example_knowledge_embedding(重抽期间判重才有向量可召回)+ 接入重抽流程。红线:三段式连接、软删、LLM 走 chat_governed(M3)、绝对章号、中文注释、最小改动面、不跑库写。之后顺序:主代理逐段核验 → 零落库小样验 3 洞(深空样窗:生物机甲/铁头判同归线、登场里程碑在、体系卡无单位事件)→ 呈报样张等创始人放行全量。**创始人 2026-07-18 拍板通过重抽后路线:批10 = 消费最小闭环 + 回放评测首跑**——顺序:①升格卡向量落库(补齐可命中)→②统一读取器 read-context 从设计桩变实(8587 张卡第一次被创作流消费)→③单书规划线回放首跑(冻结前 N 章知识推演下一台阶、对照原书评分,验收对齐专题-07 §4/§7);不再继续扩书拆卡。
## 批9c 收尾执行记录(2026-07-18,补3洞→小样全过,待创始人放行全量)
- **补洞实现(fable5 子代理)+ 主代理逐段核验 ✅**:`parse_upgrade.py` 每窗重构为 读1(短连)→观察(无连)→预判段(嵌入/召回/终判无长连)→写(窗事务)→嵌段(commit后三段式增量嵌入落 example_knowledge_embedding,软删旧活行+upsert);机械判重判据抽 `_classify_new_name` 纯函数预判/写段共用;四条归并路径换 `_merge_material`(演变历程完整不截);`new_card` 接 `_debut_milestone` 登场机械兜底;观察/更新提示词加登场硬约束+体系级纪律+台阶≤40字;`_clean_milestone` 加 80 字硬闸(主代理修复:截断必须放在内嵌章号抽取与周期推断**之后**,防截丢兜底线索);`build_embed_text` 补 type 键型修正(升格卡型认出)。离线自测 43 项全绿(`test_parse_upgrade_offline.py`)。提交 `2173c8f` + 校准提交。
- **零落库小样 ✅(深空窗35-37,观察3+终判若干+嵌入2批,≈$0.15,零卡表写入)**:报告 `/tmp/muse_upgrade_smoke3_report.txt` + probe4。洞② 登场:172 张适用型新卡 100% 有登场(模型自给 114+机械兜底 58,双保险成立)。洞③:299 条台阶 0 条超 80 字硬闸(>40 软目标 29 条);体系卡「联邦生物机甲技术」2 条里程碑均体系级、当前态字段干净。洞①:**阈值 0.78 实测证伪**——同实体改名对余弦仅 0.63-0.66("联邦生物机甲技术"vs 卡6223 = 0.6565)永不触发语义层,不同体系对 ≤0.58 → **阈值校准 0.78→0.60**;终判证据不对称修复(recall_neighbors 近邻带字段摘选 300 字入终判 prompt + 判据补"同型名称互含≈简称全称""改名不改实质")。校准后五探针全对:互含改名×2 → merge 6223;**非互含改名**("卡依斯反应驱动战械体系",语义层真正要接的残余缺口,机械子串层接不住)→ 0.6128 过阈 → merge 6223;反向安全(帝国4代纯机械体系)→ 0.576 不过阈 → new(误并方向守住);铁头 → 0.9456 → merge 1154;跨型全部未并。边距诚实说明:非互含 0.613 vs 反向 0.576 分离带窄,第二道守门=终判 M3(带字段证据)+ 同型才并 + 别名审计行(verdict_by='semantic')可回溯。
- **待办=呈报创始人放行全量重抽(任务#12)**。全量执行时注意本文档上方"全量重抽的管线坑"三条(清串行状态表 / cid 冲突吞卡 / 勿用 --redo-window 破串行),跑法:`run --work-id N --semantic-dedup` 逐书串行。