diff --git a/.claude/skills/parse-book/scripts/parse_upgrade.py b/.claude/skills/parse-book/scripts/parse_upgrade.py index 1329bcb..85c8c7f 100644 --- a/.claude/skills/parse-book/scripts/parse_upgrade.py +++ b/.claude/skills/parse-book/scripts/parse_upgrade.py @@ -245,11 +245,12 @@ WIN_PREFIX_RE = None # 延迟编译(模块顶部 import re 已有) def _strip_prefix(s): - """剥条目行首的 [窗N]/[本窗] 前缀(可能多重堆叠),返回纯内容。""" + """剥条目行首的窗号类前缀(可能多重堆叠;含模型自造的 [窗本窗] 等变体),返回纯内容。""" import re as _re global WIN_PREFIX_RE if WIN_PREFIX_RE is None: - WIN_PREFIX_RE = _re.compile(r"^(?:\[窗\d+\]\s*|\[本窗\]\s*)+") + # [窗…]/[本窗…] 任意变体全剥(窗29实测模型自造 "[窗本窗]",仅数字版剥不掉) + WIN_PREFIX_RE = _re.compile(r"^(?:\[[窗本][^\]]{0,6}\]\s*)+") return WIN_PREFIX_RE.sub("", str(s)).strip() @@ -263,9 +264,11 @@ def _clean_alias(al): return al -def merge_card(conn, draft_id, win_no, changes, alias_new): +def merge_card(conn, draft_id, win_no, changes, alias_new, valid_keys=None): """按 5.1 三类规则合并变更字段:数组/白名单=追加(剥模型自带前缀+同文去重后带窗号), - 标量=覆写留审计(增量式假全量拦截转追加——抽检#4 信息回退病)。""" + 标量=覆写留审计(增量式假全量拦截转追加——抽检#4 信息回退病)。 + valid_keys:该型合同的合法字段 key 集——越合同 key 裁剪留审计(窗29实测模型把 + 整段条目文本误当字段 key 写入,无校验会把卡体字段区打烂)。""" payload = conn.execute("SELECT draft_payload FROM muse_knowledge_draft WHERE id=%s", (draft_id,)).fetchone()[0] fields = payload.setdefault("字段", {}) @@ -274,6 +277,15 @@ def merge_card(conn, draft_id, win_no, changes, alias_new): alias_new = list(alias_new or []) + \ [a for a in (changes.pop("别名新增", None) or []) if isinstance(a, str)] for k, v in changes.items(): + if valid_keys is not None and k not in valid_keys: + # 越合同字段:裁剪留审计(方案守卫条款),畸形长 key(条目误当 key)一并挡下 + 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], + json.dumps(v, ensure_ascii=False)[:2000], TENANT)) + 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 \ @@ -542,10 +554,14 @@ def run(work_id, max_windows, max_calls, model, redo_window): cards.append((did, p, obs_pts)) upd, _ = call(update_prompt(contracts, title, a, b, text, cards), ("更新",)) valid = {d for d, _, _ in cards} + # 每卡按其型的合同 key 集校验(+一句话摘要),越合同 key 裁剪留审计 + did2keys = {d: {f["key"] for f in contracts.get(p.get("type"), {}).get("字段", [])} + | {"一句话摘要"} for d, p, _ in cards} for u in [x for x in (upd.get("更新") or []) if isinstance(x, dict)]: if u.get("draft_id") in valid and (u.get("变更字段") or u.get("别名新增")): merge_card(conn, u["draft_id"], win_no, - u.get("变更字段"), u.get("别名新增")) + u.get("变更字段"), u.get("别名新增"), + valid_keys=did2keys.get(u["draft_id"])) # ⑤ 关系增量(核心角色=本窗有更新的 character + 在场 character,≤8) char_cards = [] seen = set()