框架: 升格管线 7 处守卫落地(fable5 抽检 5 高危根治)
- 结构垃圾拦截:变更包字符串化文本拒收留审计(H2 窗75事故,追加+覆写双路) - 摘要特判写卡顶层+纳入更新流程(M4 影子摘要/600章旧摘要) - 追加条目按窗号稳定排序归位(M2 补跑窗时间线倒流) - 立卡初始别名同步入 alias 表(L3 覆盖率26%致判重空转) - 同型名称子串防漏并:转观察材料留复核标记(H5 果子/开心果子) - 别名拒单字(M5) - prompt 纪律:禁相对时间指代/只写本实体信息/摘要跟进(H1 M3)
This commit is contained in:
parent
0ac8f7ce04
commit
0892bcad7c
@ -22,6 +22,7 @@
|
||||
"""
|
||||
import json
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
import click
|
||||
@ -194,8 +195,9 @@ def update_prompt(contracts, title, a, b, text, cards_with_obs):
|
||||
下列每张卡给出「当前卡全文」与「本窗观察点」。对照本窗正文,**只输出需要变更的字段**:
|
||||
- 覆写类字段(性格底色/说话方式/当前状态等标量):**必须输出该字段完整的新全量值**——旧值里仍然成立的信息要保留进新值,禁止只写"新增…"式增量(那会把旧信息抹掉);
|
||||
- 追加类字段(成长弧线/演变轨迹等数组):只输出本窗新增条目(不要重抄旧条目,不要自己加 [窗N] 前缀,系统会加);
|
||||
- 没有变化的字段不要输出;整卡无实质变化则不输出该卡。
|
||||
纪律:以正文为证据,不脑补;数值必须正文原样出现,禁止推算;字段 key 必须来自该型合同;每卡别名有新发现可在「别名新增」里给——只收**该实体自己**的新别名(他人对它的称呼算,它对别的实体的称呼不算),且必须是可在正文原样出现的纯名字(禁带括号注释与说明文字)。
|
||||
- 没有变化的字段不要输出;整卡无实质变化则不输出该卡;
|
||||
- 该实体的身份/处境已发生重大变化时,把「一句话摘要」也作为变更字段输出(写当前态全量)。
|
||||
纪律:以正文为证据,不脑补;数值必须正文原样出现,禁止推算;字段 key 必须来自该型合同;**禁止「本窗/本段/近期」等相对时间指代**——写绝对状态或带章号(卡会脱离本窗被单独阅读);只写**该实体自己**的信息(他人的任命/心理/变化不得写入本卡);每卡别名有新发现可在「别名新增」里给——只收**该实体自己**的新别名(他人对它的称呼算,它对别的实体的称呼不算),且必须是可在正文原样出现的纯名字(禁带括号注释与说明文字,禁单字)。
|
||||
|
||||
【相关型字段合同】
|
||||
{render_entity_contracts(contracts, sorted({p.get("type") for _, p, _ in cards_with_obs} & set(ENTITY_TYPES)))}
|
||||
@ -255,15 +257,30 @@ def _strip_prefix(s):
|
||||
|
||||
|
||||
def _clean_alias(al):
|
||||
"""别名机械准入(抽检#6):拒收含括号注释/超长的备忘录式别名——预扫精确匹配永不命中=死数据。"""
|
||||
"""别名机械准入(抽检#6):拒收含括号注释/超长的备忘录式别名——预扫精确匹配永不命中=死数据。
|
||||
单字别名一并拒收(复检 M5:'新'字入表后预扫全文命中率爆炸,纯噪声)。"""
|
||||
al = (al or "").strip()
|
||||
if not al or len(al) > 12:
|
||||
if not al or len(al) < 2 or len(al) > 12:
|
||||
return None
|
||||
if any(c in al for c in "()()。,,:"):
|
||||
return None
|
||||
return al
|
||||
|
||||
|
||||
def _is_garbage(text):
|
||||
"""结构垃圾检测(抽检 H2 根治):窗75 实测模型把原始变更包字符串化塞进字段值,
|
||||
7 张卡被 {'draft_id':...} 类程序结构污染。含结构特征的文本一律拒收留审计。"""
|
||||
t = str(text)
|
||||
return ("draft_id" in t or "变更字段" in t or "别名新增" in t
|
||||
or t.lstrip().startswith(("{'", '{"', "[{")))
|
||||
|
||||
|
||||
def _entry_win(x):
|
||||
"""追加条目的窗号(无 [窗N] 前缀的初卡条目记 0,排最前)。"""
|
||||
m = re.match(r"^\[窗(\d+)\]", str(x))
|
||||
return int(m.group(1)) if m else 0
|
||||
|
||||
|
||||
def merge_card(conn, draft_id, win_no, changes, alias_new, valid_keys=None):
|
||||
"""按 5.1 三类规则合并变更字段:数组/白名单=追加(剥模型自带前缀+同文去重后带窗号),
|
||||
标量=覆写留审计(增量式假全量拦截转追加——抽检#4 信息回退病)。
|
||||
@ -292,6 +309,27 @@ def merge_card(conn, draft_id, win_no, changes, alias_new, valid_keys=None):
|
||||
(draft_id, win_no, ("越合同:" + str(k))[:100],
|
||||
json.dumps(v, ensure_ascii=False)[:2000], TENANT))
|
||||
continue
|
||||
if k == "一句话摘要":
|
||||
# 摘要住卡顶层而非"字段"子对象——此前写进子对象成"影子摘要"且顶层摘要
|
||||
# 从不更新(抽检 M4:600 章前的旧摘要一直挂着)。特判写顶层,同守水位闸。
|
||||
if isinstance(v, str) and v.strip() and not _is_garbage(v):
|
||||
if win_no < watermark:
|
||||
conn.execute(
|
||||
"""INSERT INTO example_upgrade_audit
|
||||
(draft_id, window_no, field_name, old_value, new_value, tenant_id)
|
||||
VALUES (%s,%s,%s,NULL,%s,%s)""",
|
||||
(draft_id, win_no, "迟到覆写弃用:一句话摘要",
|
||||
json.dumps(v, ensure_ascii=False)[:2000], TENANT))
|
||||
else:
|
||||
conn.execute(
|
||||
"""INSERT INTO example_upgrade_audit
|
||||
(draft_id, window_no, field_name, old_value, new_value, tenant_id)
|
||||
VALUES (%s,%s,%s,%s,%s,%s)""",
|
||||
(draft_id, win_no, "一句话摘要",
|
||||
json.dumps(payload.get("一句话摘要"), ensure_ascii=False),
|
||||
json.dumps(v.strip(), ensure_ascii=False), TENANT))
|
||||
payload["一句话摘要"] = v.strip()
|
||||
continue
|
||||
is_append = k in APPEND_FIELDS or isinstance(v, list) or isinstance(fields.get(k), list)
|
||||
# 覆写值以增量口吻开头=模型把增量当全量(抽检#4:新值会抹掉旧基线),转追加不覆写
|
||||
if not is_append and isinstance(v, str) and \
|
||||
@ -303,10 +341,27 @@ def merge_card(conn, draft_id, win_no, changes, alias_new, valid_keys=None):
|
||||
seen = {_strip_prefix(x) for x in old} # 同文去重(抽检#1 重复病)
|
||||
for it in items:
|
||||
core = _strip_prefix(it) # 剥模型自带前缀(抽检#1 堆叠病)
|
||||
if core and _is_garbage(core):
|
||||
# 结构垃圾条目拒收留审计(窗75 事故根治:变更包字符串化混进字段值)
|
||||
conn.execute(
|
||||
"""INSERT INTO example_upgrade_audit
|
||||
(draft_id, window_no, field_name, old_value, new_value, tenant_id)
|
||||
VALUES (%s,%s,%s,NULL,%s,%s)""",
|
||||
(draft_id, win_no, ("垃圾拦截:" + str(k))[:100],
|
||||
str(core)[:2000], TENANT))
|
||||
continue
|
||||
if core and core not in seen:
|
||||
old.append(f"[窗{win_no}] {core}")
|
||||
seen.add(core)
|
||||
fields[k] = old
|
||||
# 窗序归位(抽检 M2:补跑/重试窗条目尾插致时间线倒流)——稳定排序,同窗保持原序
|
||||
fields[k] = sorted(old, key=_entry_win)
|
||||
elif isinstance(v, str) and _is_garbage(v):
|
||||
# 结构垃圾覆写值拒收留审计(同窗75 事故根治)
|
||||
conn.execute(
|
||||
"""INSERT INTO example_upgrade_audit
|
||||
(draft_id, window_no, field_name, old_value, new_value, tenant_id)
|
||||
VALUES (%s,%s,%s,NULL,%s,%s)""",
|
||||
(draft_id, win_no, ("垃圾拦截:" + str(k))[:100], v[:2000], TENANT))
|
||||
elif win_no < watermark:
|
||||
# 迟到覆写弃用:本窗时序早于卡已生长到的窗位,覆写会让卡态倒流——
|
||||
# 只留审计(标记可查),卡体保持高窗态不动
|
||||
@ -386,6 +441,15 @@ def new_card(conn, work_id, win_no, ent):
|
||||
(draft_id, window_no, field_name, old_value, new_value, tenant_id)
|
||||
VALUES (%s,%s,%s,NULL,%s,%s)""",
|
||||
(did, win_no, k, json.dumps(v, ensure_ascii=False), TENANT))
|
||||
# 初始别名同步入 alias 表(抽检 L3 根源:立卡别名只写卡内不入表,
|
||||
# 别名表覆盖率仅 26%,"别名精确判重"层大面积空转导致同人两卡漏并)
|
||||
for al in payload["别名"]:
|
||||
conn.execute(
|
||||
"""INSERT INTO example_upgrade_alias
|
||||
(work_id, canonical_name, alias, evidence_window, verdict_by, tenant_id)
|
||||
VALUES (%s,%s,%s,%s,'init',%s)
|
||||
ON CONFLICT (tenant_id, work_id, alias) DO NOTHING""",
|
||||
(work_id, raw, al, win_no, TENANT))
|
||||
conn.execute(
|
||||
"""INSERT INTO example_upgrade_card_state (draft_id, work_id, watermark_window, tenant_id)
|
||||
VALUES (%s,%s,%s,%s) ON CONFLICT (draft_id) DO NOTHING""",
|
||||
@ -542,6 +606,24 @@ def run(work_id, max_windows, max_calls, model, redo_window):
|
||||
ON CONFLICT (tenant_id, work_id, alias) DO NOTHING""",
|
||||
(work_id, hint, nm, win_no, TENANT))
|
||||
continue
|
||||
# 同型名称互为子串(抽检 H5:「果子」vs「开心果子」同人两卡):
|
||||
# 不直接立卡,转观察材料并入既有卡由更新步 AI 甄别(软防护,留复核标记)
|
||||
sub_hit = next(
|
||||
(ex for ex, (d0, t0, _) in name_map.items()
|
||||
if t0 == ent.get("型", "") and len(nm) >= 2 and len(ex) >= 2
|
||||
and nm != ex and (nm in ex or ex in nm)), None)
|
||||
if sub_hit:
|
||||
did = name_map[sub_hit][0]
|
||||
to_update.setdefault(did, []).append(
|
||||
f"(名称疑似同一实体「{nm}」≈「{sub_hit}」,请甄别后再并入)"
|
||||
f"初卡材料:{json.dumps(ent, ensure_ascii=False)[:400]}")
|
||||
conn.execute(
|
||||
"""INSERT INTO example_upgrade_alias
|
||||
(work_id, canonical_name, alias, evidence_window, verdict_by, tenant_id)
|
||||
VALUES (%s,%s,%s,%s,'substr',%s)
|
||||
ON CONFLICT (tenant_id, work_id, alias) DO NOTHING""",
|
||||
(work_id, sub_hit, nm, win_no, TENANT))
|
||||
continue
|
||||
chaps = set(ent.get("出场章", []))
|
||||
hist = presence.get((ent.get("型", ""), nm), set())
|
||||
if len(chaps | hist) >= 2: # 跨章(含跨窗合计)→ 立卡
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user