修复: 用原文证据绑定升格里程碑章号
This commit is contained in:
parent
96ebbc1d4c
commit
c969fa637d
@ -58,7 +58,7 @@ disable-model-invocation: true
|
||||
|
||||
**窗行陷阱(放量首日实测)**:`--window` 参数变化后重切,旧窗行会按 from_order 占位,新的大窗被「已有大纲跳过」→ 中间章域永远漏出卡(验收期 1–3 章小窗占住 from_order=1,放量 1–34 章大窗被跳过)。**换窗参数重切前必须先删该书全部窗行**(窗行是可再生中间产物;卡挂「窗起」,cards 重出时按窗软删重出)。
|
||||
|
||||
**作品面升格执行器 `scripts/parse_upgrade.py`(与上面范式拆书管线并行的另一条线,命令 `windows`/`run`/`status`)**:把参考书正文按窗抽成「会随剧情长大的实体卡」(升格卡,`source_type=upgrade_book`),设计见 `docs/2026-07-16-升格卡改造设计.md`。`run` 默认**不发嵌入**;加 `--semantic-dedup` 开语义判重(治改名/跨型漏并)时,每窗按**读→算→写三段式短连接**跑——观察/近邻召回/M3 终判都在**无长连接**段发 LLM 与嵌入 HTTP(不再持窗级连接跨调用存活),写段只查预判结果落库;并**边抽边嵌**:本窗新建/更新卡在窗事务提交后增量嵌入落库(软删旧向量+upsert 新行),**后窗即可语义召回前窗刚长成的卡**,不再依赖"同书须预先全量 embed"。嵌段失败只告警、不牵连已 done 的窗。
|
||||
**作品面升格执行器 `scripts/parse_upgrade.py`(与上面范式拆书管线并行的另一条线,命令 `windows`/`run`/`status`)**:把参考书正文按窗抽成「会随剧情长大的实体卡」(升格卡,`source_type=upgrade_book`),设计见 `docs/2026-07-16-升格卡改造设计.md`。里程碑除真实章号外,模型输出必须临时携带所标章节正文短引;系统机械核验后删除证据,错章、缺证据或改写证据均拒收入库并留审计。`run` 默认**不发嵌入**;加 `--semantic-dedup` 开语义判重(治改名/跨型漏并)时,每窗按**读→算→写三段式短连接**跑——观察/近邻召回/M3 终判都在**无长连接**段发 LLM 与嵌入 HTTP(不再持窗级连接跨调用存活),写段只查预判结果落库;并**边抽边嵌**:本窗新建/更新卡在窗事务提交后增量嵌入落库(软删旧向量+upsert 新行),**后窗即可语义召回前窗刚长成的卡**,不再依赖"同书须预先全量 embed"。嵌段失败只告警、不牵连已 done 的窗。
|
||||
|
||||
## 步骤(自底向上,与创作期规划的自顶向下互为镜像)
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@ import json
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
import unicodedata
|
||||
|
||||
import click
|
||||
import psycopg
|
||||
@ -132,16 +133,26 @@ def cut_windows(conn, work_id):
|
||||
|
||||
# ── 每窗材料与已知名加载 ──
|
||||
|
||||
def load_window_text(conn, work_id, a, b):
|
||||
"""窗内正文拼接:第N章《标题》+正文。"""
|
||||
def load_window_material(conn, work_id, a, b):
|
||||
"""返回窗正文拼接文本与按绝对章号索引的原始正文。"""
|
||||
|
||||
rows = conn.execute(
|
||||
"""SELECT c.order_no, c.title, b2.content_text
|
||||
FROM muse_content_chapter c
|
||||
JOIN muse_content_block b2 ON b2.chapter_id=c.id AND b2.deleted=FALSE
|
||||
WHERE c.tenant_id=%s AND c.work_id=%s AND c.order_no BETWEEN %s AND %s
|
||||
AND c.deleted=FALSE ORDER BY c.order_no""",
|
||||
(TENANT, work_id, a, b)).fetchall()
|
||||
return "\n\n".join(f"## 第{o}章 {t}\n{x}" for o, t, x in rows)
|
||||
(TENANT, work_id, a, b)).fetchall()
|
||||
return (
|
||||
"\n\n".join(f"## 第{o}章 {t}\n{x}" for o, t, x in rows),
|
||||
{int(order_no): str(content or "") for order_no, _, content in rows},
|
||||
)
|
||||
|
||||
|
||||
def load_window_text(conn, work_id, a, b):
|
||||
"""兼容旧调用:只返回带章标题的窗内正文拼接文本。"""
|
||||
|
||||
return load_window_material(conn, work_id, a, b)[0]
|
||||
|
||||
|
||||
def load_known(conn, work_id):
|
||||
@ -149,16 +160,20 @@ def load_known(conn, work_id):
|
||||
|
||||
返回 (name_map: {名字: (draft_id, 型, 摘要)}, presence: {(型,名字): set(章)})"""
|
||||
name_map = {}
|
||||
for did, payload in conn.execute(
|
||||
"""SELECT id, draft_payload FROM muse_knowledge_draft
|
||||
for did, entity_type, name, brief, aliases in conn.execute(
|
||||
"""SELECT id,
|
||||
draft_payload->>'type',
|
||||
draft_payload->>'名称',
|
||||
COALESCE(draft_payload->>'一句话摘要',''),
|
||||
COALESCE(draft_payload->'别名','[]'::jsonb)
|
||||
FROM muse_knowledge_draft
|
||||
WHERE tenant_id=%s AND work_id=%s AND source_type=%s AND deleted=FALSE""",
|
||||
(TENANT, work_id, SOURCE_TYPE)).fetchall():
|
||||
t, nm = payload.get("type", ""), (payload.get("名称") or "").strip()
|
||||
brief = payload.get("一句话摘要", "")
|
||||
t, nm = entity_type or "", (name or "").strip()
|
||||
if nm:
|
||||
name_map[nm] = (did, t, brief)
|
||||
for al in payload.get("别名", []) or []:
|
||||
if al and al.strip():
|
||||
for al in aliases or []:
|
||||
if isinstance(al, str) and al.strip():
|
||||
name_map[al.strip()] = (did, t, brief)
|
||||
for cn, al in conn.execute(
|
||||
"SELECT canonical_name, alias FROM example_upgrade_alias "
|
||||
@ -195,7 +210,7 @@ def observe_prompt(contracts, title, a, b, text, onstage):
|
||||
2) 已知实体新信息:清单中实体在本窗的实质新信息(境界变化/性格显露/重大经历/立场转变),一条 ≤60 字观察点。没有实质新信息的不要报。
|
||||
3) 纯出场:清单中实体本窗出现但无实质新信息的,只报名称+出场章。
|
||||
纪律:一次性龙套(单章无名或仅路过)不报进新名字;实体判据与字段以合同为准,无证据不填;不脑补。数值(战力/指数/排名等)必须正文原样出现才可写,禁止推算或编造。组织改组/合并产生的新组织是**新实体**(走新名字),不是旧组织的别名。「疑似别名指向」只在确为同一实体改名/化名时填。
|
||||
里程碑纪律(凡填「演变历程」字段必守):每条是对象 {{"章": 正文原样出现的真实章号(整数如 420,跨多章连续事件用区间字符串如 "420-423"), "台阶": "进化到什么+靠什么关键事件的一句话(≤40 字,只说跃迁+关键事件,不展开过程)", "周期": 登场/成长/高光/退场/结局 之一}};**必须用真实章号,禁止 [窗N] 窗号、禁止"本窗/近期/前段"这类相对指代**(卡会脱离运行环境被单独阅读);抽不出完整对象时至少给 {{"章","台阶"}},别整条丢;当前态字段(品阶/能力与限制/摘要等)只写"现在什么样"的干净值,历史进化流水一律进「演变历程」、不许塞进当前态字段。**体系级纪律(power_system/faction 这类体系/组织卡必守)**:其「演变历程」只收**体系级**变化(代际更替/规则改写/整体兴衰/版图重划);某台具体机甲的战斗、某名成员的晋升这类**个体单位事件**绝不写进体系/组织卡,只记进各自实体卡(无卡则走新名字或纯出场),防止体系卡被单位流水账污染。
|
||||
里程碑纪律(凡填「演变历程」字段必守):每条是对象 {{"章": 正文原样出现的真实章号(整数如 420,跨多章连续事件用区间字符串如 "420-423"), "台阶": "进化到什么+靠什么关键事件的一句话(≤40 字,只说跃迁+关键事件,不展开过程)", "周期": 登场/成长/高光/退场/结局 之一, "证据": "所标章节正文原样连续短句(8–30字)"}};**证据必须逐字来自所填章号对应正文,系统会机械核验,错章、缺证据或改写证据均拒收入库;证据只用于校验,不写入卡体**。必须用真实章号,禁止 [窗N] 窗号、禁止"本窗/近期/前段"这类相对指代(卡会脱离运行环境被单独阅读);无法给出章号与原文证据时不要输出该里程碑;当前态字段(品阶/能力与限制/摘要等)只写"现在什么样"的干净值,历史进化流水一律进「演变历程」、不许塞进当前态字段。**体系级纪律(power_system/faction 这类体系/组织卡必守)**:其「演变历程」只收**体系级**变化(代际更替/规则改写/整体兴衰/版图重划);某台具体机甲的战斗、某名成员的晋升这类**个体单位事件**绝不写进体系/组织卡,只记进各自实体卡(无卡则走新名字或纯出场),防止体系卡被单位流水账污染。
|
||||
|
||||
【六型字段合同】
|
||||
{render_entity_contracts(contracts, ENTITY_TYPES)}
|
||||
@ -220,7 +235,7 @@ def update_prompt(contracts, title, a, b, text, cards_with_obs):
|
||||
return f"""【功能指令(parse-book 作品面升格·卡增量更新)】
|
||||
下列每张卡给出「当前卡全文」与「本窗观察点」。对照本窗正文,**只输出需要变更的字段**:
|
||||
- 覆写类字段(性格底色/说话方式/当前状态等标量):**必须输出该字段完整的新全量值**——旧值里仍然成立的信息要保留进新值,禁止只写"新增…"式增量(那会把旧信息抹掉);
|
||||
- 里程碑字段(演变历程):每当实体发生境界/代际/形态/能力的跃迁,或到达登场/高光/退场/结局节点,**追加**一条里程碑对象 {{"章": 真实章号(整数如 420 或跨章区间字符串 "420-423"), "台阶": "进化到什么+靠什么关键事件的一句话(≤40 字,只说跃迁+关键事件,不展开过程)", "周期": 登场/成长/高光/退场/结局 之一}}——只输出本窗**新增**里程碑(不重抄旧条目,不输出 _win 等内部键);章必须是正文原样章号、**禁 [窗N] 与"本窗/近期"相对指代**;抽不出完整对象时至少给 {{"章","台阶"}},别整条丢;并入观察材料里的别名/判重「初卡材料」时,其中「登场」条目**若本卡演变历程已有登场里程碑则不再重复追加**,其余台阶照常判断追加;**体系级纪律**:power_system/faction 这类体系/组织卡的演变历程只收体系级变化(代际更替/规则改写/整体兴衰/版图重划),某台具体机甲的战斗、某名成员的晋升这类个体单位事件绝不写进体系/组织卡;
|
||||
- 里程碑字段(演变历程):每当实体发生境界/代际/形态/能力的跃迁,或到达登场/高光/退场/结局节点,**追加**一条里程碑对象 {{"章": 真实章号(整数如 420 或跨章区间字符串 "420-423"), "台阶": "进化到什么+靠什么关键事件的一句话(≤40 字,只说跃迁+关键事件,不展开过程)", "周期": 登场/成长/高光/退场/结局 之一, "证据": "所标章节正文原样连续短句(8–30字)"}}——只输出本窗**新增**里程碑(不重抄旧条目,不输出 _win 等内部键);证据必须逐字来自所填章号对应正文,系统机械核验后丢弃证据字段,错章、缺证据或改写证据均拒收;章必须是正文原样章号、**禁 [窗N] 与"本窗/近期"相对指代**;无法给出章号与证据时不要输出该里程碑;并入观察材料里的别名/判重「初卡材料」时,其中「登场」条目**若本卡演变历程已有登场里程碑则不再重复追加**,其余台阶照常判断追加;**体系级纪律**:power_system/faction 这类体系/组织卡的演变历程只收体系级变化(代际更替/规则改写/整体兴衰/版图重划),某台具体机甲的战斗、某名成员的晋升这类个体单位事件绝不写进体系/组织卡;
|
||||
- 其他追加类字段(成长弧线=未来计划、演变轨迹等数组):只输出本窗新增条目(不要重抄旧条目,不要自己加 [窗N] 前缀,系统会加);
|
||||
- **当前态字段干净纪律**:品阶/能力与限制/当前状态/一句话摘要等只写"现在是什么样"的全量当前值——历史进化流水一律进「演变历程」里程碑,**绝不把成长史塞进当前态字段**(这是老卡把升级线塞进「戏剧作用/流转计划/跨体系换算」污染字段语义的病根,务必避免);
|
||||
- 没有变化的字段不要输出;整卡无实质变化则不输出该卡;
|
||||
@ -390,12 +405,128 @@ def _infer_lifecycle(text):
|
||||
return "成长"
|
||||
|
||||
|
||||
def _clean_milestone(item, win_no):
|
||||
def _milestone_chapters(value):
|
||||
"""把单章或闭区间章号转为整数列表;含糊格式直接返回空列表。"""
|
||||
|
||||
if isinstance(value, int) and not isinstance(value, bool) and value > 0:
|
||||
return [value]
|
||||
if not isinstance(value, str):
|
||||
return []
|
||||
match = re.fullmatch(r"\s*(\d+)\s*(?:[-~—–至]\s*(\d+)\s*)?", value)
|
||||
if not match:
|
||||
return []
|
||||
start = int(match.group(1))
|
||||
end = int(match.group(2) or start)
|
||||
if start <= 0 or end < start or end - start > 50:
|
||||
return []
|
||||
return list(range(start, end + 1))
|
||||
|
||||
|
||||
def _normalize_evidence_text(value):
|
||||
"""证据比对忽略排版空白和宽窄差异,但保留正文字符与标点。"""
|
||||
|
||||
return re.sub(r"\s+", "", unicodedata.normalize("NFKC", str(value or "")))
|
||||
|
||||
|
||||
def _milestone_evidence_matches(item, chapter_texts):
|
||||
"""验证短原文证据确实存在于里程碑声明的章节正文。"""
|
||||
|
||||
if not isinstance(item, dict) or not isinstance(chapter_texts, dict):
|
||||
return False
|
||||
evidence = _normalize_evidence_text(item.get("证据"))
|
||||
if not 8 <= len(evidence) <= 30:
|
||||
return False
|
||||
chapters = _milestone_chapters(item.get("章"))
|
||||
if not chapters or any(chapter not in chapter_texts for chapter in chapters):
|
||||
return False
|
||||
return any(
|
||||
evidence in _normalize_evidence_text(chapter_texts[chapter])
|
||||
for chapter in chapters
|
||||
)
|
||||
|
||||
|
||||
def _walk_milestone_items(value, path="$"):
|
||||
"""稳定遍历模型输出中的里程碑对象,并生成可回填的 JSON 路径。"""
|
||||
|
||||
if isinstance(value, dict):
|
||||
if "章" in value and ("台阶" in value or "阶" in value):
|
||||
yield path, value
|
||||
return
|
||||
for key, child in value.items():
|
||||
yield from _walk_milestone_items(child, f"{path}.{key}")
|
||||
elif isinstance(value, list):
|
||||
for index, child in enumerate(value):
|
||||
yield from _walk_milestone_items(child, f"{path}[{index}]")
|
||||
|
||||
|
||||
def repair_missing_milestone_evidence(
|
||||
model_output,
|
||||
*,
|
||||
title,
|
||||
a,
|
||||
b,
|
||||
text,
|
||||
chapter_texts,
|
||||
call,
|
||||
):
|
||||
"""为本窗缺证据里程碑补原文短引;窗外重抄项不进入修复调用。"""
|
||||
|
||||
candidates = []
|
||||
by_ref = {}
|
||||
for ref, item in _walk_milestone_items(model_output):
|
||||
if _milestone_evidence_matches(item, chapter_texts):
|
||||
continue
|
||||
chapters = _milestone_chapters(item.get("章"))
|
||||
if not chapters or any(chapter not in chapter_texts for chapter in chapters):
|
||||
continue
|
||||
candidates.append(
|
||||
{
|
||||
"ref": ref,
|
||||
"章": item.get("章"),
|
||||
"台阶": item.get("台阶") or item.get("阶"),
|
||||
}
|
||||
)
|
||||
by_ref[ref] = item
|
||||
if not candidates:
|
||||
return 0
|
||||
|
||||
prompt = f"""【功能指令(parse-book 里程碑证据修复)】
|
||||
下列候选里程碑缺少可机械验证的证据。只为确实被正文支持的候选返回一条所标章节中的原文连续短句(8–30字);不得改章号、不得改台阶、不得概括或改写原文。找不到逐字证据的候选不要返回。
|
||||
|
||||
【输出规则(只输出一个 JSON 对象)】
|
||||
{{"证据修复":[{{"ref":"候选ref原样回传","章":489,"证据":"正文原样连续短句"}}]}}
|
||||
|
||||
━━━ 候选里程碑 ━━━
|
||||
{json.dumps(candidates, ensure_ascii=False)}
|
||||
|
||||
━━━ 《{title}》第 {a}–{b} 章正文 ━━━
|
||||
{text}
|
||||
"""
|
||||
repaired, _ = call(prompt, ("证据修复",))
|
||||
count = 0
|
||||
for row in repaired.get("证据修复") or []:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
ref = str(row.get("ref") or "")
|
||||
item = by_ref.get(ref)
|
||||
if item is None or _milestone_chapters(row.get("章")) != _milestone_chapters(item.get("章")):
|
||||
continue
|
||||
item["证据"] = row.get("证据")
|
||||
if _milestone_evidence_matches(item, chapter_texts):
|
||||
count += 1
|
||||
else:
|
||||
item.pop("证据", None)
|
||||
return count
|
||||
|
||||
|
||||
def _clean_milestone(item, win_no, *, chapter_texts=None, require_evidence=False):
|
||||
"""规范化单个里程碑为 {章,台阶,周期,_win};垃圾/空台阶返回 None。
|
||||
- dict 入参:取 章/台阶/周期;台阶剥前缀+剥尾残;缺章从台阶抽内嵌章号兜底;缺/非法周期启发式推断。
|
||||
- 字符串入参(模型降级输出或存量迁移):整串当台阶,抽内嵌章号当章,推断周期。
|
||||
- _win 盖当前窗号,仅作撤销溯源(undo_window 按它删本窗新增),不参与展示与排序。
|
||||
降级保底(设计稿 §九 风险2 + 拍板#1):抽不出完整对象也退成"章号+一句话"最小对象,绝不整条丢。"""
|
||||
if require_evidence and not _milestone_evidence_matches(item, chapter_texts):
|
||||
return None
|
||||
if isinstance(item, dict):
|
||||
step = _strip_tail(_strip_prefix(str(item.get("台阶") or item.get("阶") or "")))
|
||||
ch = item.get("章")
|
||||
@ -419,7 +550,7 @@ def _clean_milestone(item, win_no):
|
||||
return {"章": ch, "台阶": step, "周期": cycle, "_win": win_no}
|
||||
|
||||
|
||||
def _merge_milestones(old, items, win_no):
|
||||
def _merge_milestones(old, items, win_no, *, chapter_texts=None, require_evidence=False):
|
||||
"""合并里程碑数组:去重按台阶内容、排序按真实章号。返回 (merged, rejected)。
|
||||
old 中已有对象保留其原 _win(不被本窗覆盖);新对象由 _clean_milestone 盖当前 win_no。
|
||||
rejected 为被判垃圾的原始条目,交调用方留审计(对齐字符串路径的垃圾拦截)。"""
|
||||
@ -427,7 +558,12 @@ def _merge_milestones(old, items, win_no):
|
||||
seen = {str(m.get("台阶", "")).strip() for m in kept}
|
||||
rejected = []
|
||||
for it in (items if isinstance(items, list) else [items]):
|
||||
m = _clean_milestone(it, win_no)
|
||||
m = _clean_milestone(
|
||||
it,
|
||||
win_no,
|
||||
chapter_texts=chapter_texts,
|
||||
require_evidence=require_evidence,
|
||||
)
|
||||
if not m:
|
||||
rejected.append(it)
|
||||
continue
|
||||
@ -471,7 +607,16 @@ def _debut_milestone(milestones, brief, chaps, win_no):
|
||||
return [debut] + list(milestones or [])
|
||||
|
||||
|
||||
def merge_card(conn, draft_id, win_no, changes, alias_new, valid_keys=None):
|
||||
def merge_card(
|
||||
conn,
|
||||
draft_id,
|
||||
win_no,
|
||||
changes,
|
||||
alias_new,
|
||||
valid_keys=None,
|
||||
*,
|
||||
chapter_texts=None,
|
||||
):
|
||||
"""按 5.1 三类规则合并变更字段:数组/白名单=追加(剥模型自带前缀+同文去重后带窗号),
|
||||
标量=覆写留审计(增量式假全量拦截转追加——抽检#4 信息回退病)。
|
||||
valid_keys:该型合同的合法字段 key 集——越合同 key 裁剪留审计(窗29实测模型把
|
||||
@ -525,7 +670,13 @@ def merge_card(conn, draft_id, win_no, changes, alias_new, valid_keys=None):
|
||||
# 不走下面处理字符串条目([窗N] 前缀)的老路径。迟到窗追加无害:对象自带章号,
|
||||
# 乱序由 _chapter_sort_key 排序纠正,故不设水位闸(与字符串追加同策略)。
|
||||
base = fields.get(k) if isinstance(fields.get(k), list) else ([fields[k]] if fields.get(k) else [])
|
||||
merged, rejected = _merge_milestones(base, v, win_no)
|
||||
merged, rejected = _merge_milestones(
|
||||
base,
|
||||
v,
|
||||
win_no,
|
||||
chapter_texts=chapter_texts,
|
||||
require_evidence=True,
|
||||
)
|
||||
for rj in rejected:
|
||||
# 垃圾/空台阶里程碑拒收留审计(对齐字符串路径的垃圾拦截守卫)
|
||||
conn.execute(
|
||||
@ -618,7 +769,7 @@ def merge_card(conn, draft_id, win_no, changes, alias_new, valid_keys=None):
|
||||
(draft_id, payload.get("_work_id") or 0, win_no, TENANT))
|
||||
|
||||
|
||||
def new_card(conn, work_id, win_no, ent, milestone_types=None):
|
||||
def new_card(conn, work_id, win_no, ent, milestone_types=None, *, chapter_texts=None):
|
||||
"""立初卡:payload 全字段以旧值=NULL 入审计(G7,错认拆回可还原初始态)。
|
||||
milestone_types(洞②):含「演变历程」字段的型集合(据库内合同动态判定)——该型立卡时若抽取结果
|
||||
无登场里程碑,机械兜底补一条登场(保成长型实体登场即有生命周期起点);None/空集则不触发兜底。
|
||||
@ -641,7 +792,13 @@ def new_card(conn, work_id, win_no, ent, milestone_types=None):
|
||||
if k in MILESTONE_FIELDS:
|
||||
# 里程碑字段(演变历程):初卡即走对象合并(登场/首个进化台阶),去重排序;
|
||||
# 初卡尚无 draft_id 无法留审计,垃圾条目直接过滤(与下方字符串路径初卡同策略)。
|
||||
merged, _ = _merge_milestones([], v, win_no)
|
||||
merged, _ = _merge_milestones(
|
||||
[],
|
||||
v,
|
||||
win_no,
|
||||
chapter_texts=chapter_texts,
|
||||
require_evidence=True,
|
||||
)
|
||||
fields0[k] = merged
|
||||
continue
|
||||
if not isinstance(v, list):
|
||||
@ -1072,12 +1229,21 @@ def run(work_id, max_windows, max_calls, model, redo_window, semantic_on):
|
||||
try:
|
||||
# ── 读1(短连接):窗正文 + 判重底册,读完即关(三段式红线:连接不跨 LLM/嵌入调用存活)──
|
||||
with psycopg.connect(DSN) as conn:
|
||||
text = load_window_text(conn, work_id, a, b)
|
||||
text, chapter_texts = load_window_material(conn, work_id, a, b)
|
||||
name_map, presence = load_known(conn, work_id)
|
||||
onstage = prescan(name_map, text) # ⓪ 机械预扫纯内存,无连接
|
||||
# ── 算1(无连接):① 实体观察 M3 调用 + 输出清洗,全程不持连接 ──
|
||||
obs, usage = call(observe_prompt(contracts, title, a, b, text, onstage),
|
||||
("新名字", "已知实体新信息", "纯出场"))
|
||||
repair_missing_milestone_evidence(
|
||||
obs,
|
||||
title=title,
|
||||
a=a,
|
||||
b=b,
|
||||
text=text,
|
||||
chapter_texts=chapter_texts,
|
||||
call=call,
|
||||
)
|
||||
# 模型输出防御(深空窗5实测:列表元素偶为裸字符串,.get 直接炸)——统一只留 dict 元素
|
||||
for k in ("新名字", "已知实体新信息", "纯出场"):
|
||||
obs[k] = [x for x in (obs.get(k) or []) if isinstance(x, dict)]
|
||||
@ -1148,7 +1314,14 @@ def run(work_id, max_windows, max_calls, model, redo_window, semantic_on):
|
||||
(work_id, canon, nm, win_no, TENANT))
|
||||
continue
|
||||
if verdict == "chain": # 前身后继:仍立卡,串链关系记候选审计
|
||||
did = new_card(conn, work_id, win_no, ent, milestone_types)
|
||||
did = new_card(
|
||||
conn,
|
||||
work_id,
|
||||
win_no,
|
||||
ent,
|
||||
milestone_types,
|
||||
chapter_texts=chapter_texts,
|
||||
)
|
||||
new_ids.append(did)
|
||||
name_map[nm] = (did, ent.get("型", ""), ent.get("一句话摘要", ""))
|
||||
# 只记候选提示、不自动写「前身/后继」字段——避免误串,链接由人工/后续确认落字段
|
||||
@ -1161,7 +1334,14 @@ def run(work_id, max_windows, max_calls, model, redo_window, semantic_on):
|
||||
json.dumps({"对方卡号": did2, "对方名称": nm2},
|
||||
ensure_ascii=False), TENANT))
|
||||
continue
|
||||
did = new_card(conn, work_id, win_no, ent, milestone_types)
|
||||
did = new_card(
|
||||
conn,
|
||||
work_id,
|
||||
win_no,
|
||||
ent,
|
||||
milestone_types,
|
||||
chapter_texts=chapter_texts,
|
||||
)
|
||||
new_ids.append(did)
|
||||
name_map[nm] = (did, ent.get("型", ""), ent.get("一句话摘要", ""))
|
||||
else: # 单章龙套 → 留档(G4)
|
||||
@ -1200,6 +1380,15 @@ def run(work_id, max_windows, max_calls, model, redo_window, semantic_on):
|
||||
raise
|
||||
print(f"[宽容] 窗{win_no} 更新批缺键按空批放行: {str(e)[:80]}", file=sys.stderr)
|
||||
upd = {"更新": []}
|
||||
repair_missing_milestone_evidence(
|
||||
upd,
|
||||
title=title,
|
||||
a=a,
|
||||
b=b,
|
||||
text=text,
|
||||
chapter_texts=chapter_texts,
|
||||
call=call,
|
||||
)
|
||||
valid = {d for d, _, _ in cards}
|
||||
# 每卡按其型的合同 key 集校验(+一句话摘要),越合同 key 裁剪留审计
|
||||
did2keys = {d: {f["key"] for f in contracts.get(p.get("type"), {}).get("字段", [])}
|
||||
@ -1208,7 +1397,8 @@ def run(work_id, max_windows, max_calls, model, redo_window, semantic_on):
|
||||
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("别名新增"),
|
||||
valid_keys=did2keys.get(u["draft_id"]))
|
||||
valid_keys=did2keys.get(u["draft_id"]),
|
||||
chapter_texts=chapter_texts)
|
||||
# ⑤ 关系增量(核心角色=本窗有更新的 character + 在场 character,≤8)
|
||||
char_cards = []
|
||||
seen = set()
|
||||
|
||||
@ -115,6 +115,57 @@ def test_clean_milestone_guard():
|
||||
check("clean-截断前先推全文周期", m["周期"] == "高光")
|
||||
check("clean-抽后仍截至80", len(m["台阶"]) == 80)
|
||||
|
||||
# 新抽取里程碑必须用短原文证据把章号绑定到对应正文;证据只用于校验,不写入卡体。
|
||||
chapter_texts = {
|
||||
488: "安若雪率领亲卫继续突进,环星防御火力全面展开。",
|
||||
489: "安若雪与加特朗激战,同步率飙至100%,击碎能量屏障。",
|
||||
}
|
||||
verified = pu._clean_milestone(
|
||||
{
|
||||
"章": 489,
|
||||
"台阶": "同步率飙至100%击碎加特朗屏障",
|
||||
"周期": "高光",
|
||||
"证据": "同步率飙至100%,击碎能量屏障",
|
||||
},
|
||||
84,
|
||||
chapter_texts=chapter_texts,
|
||||
require_evidence=True,
|
||||
)
|
||||
check(
|
||||
"clean-证据绑定正确章",
|
||||
verified is not None and verified["章"] == 489 and "证据" not in verified,
|
||||
)
|
||||
wrong_chapter = pu._clean_milestone(
|
||||
{
|
||||
"章": 488,
|
||||
"台阶": "同步率飙至100%击碎加特朗屏障",
|
||||
"周期": "高光",
|
||||
"证据": "同步率飙至100%,击碎能量屏障",
|
||||
},
|
||||
84,
|
||||
chapter_texts=chapter_texts,
|
||||
require_evidence=True,
|
||||
)
|
||||
check("clean-错章证据拒收", wrong_chapter is None)
|
||||
missing_evidence = pu._clean_milestone(
|
||||
{"章": 489, "台阶": "同步率飙至100%", "周期": "高光"},
|
||||
84,
|
||||
chapter_texts=chapter_texts,
|
||||
require_evidence=True,
|
||||
)
|
||||
check("clean-缺证据拒收", missing_evidence is None)
|
||||
short_evidence = {"章": 488, "台阶": "继续突进", "周期": "成长", "证据": "安若雪率领亲卫"}
|
||||
check(
|
||||
"clean-不足8字证据拒收",
|
||||
not pu._milestone_evidence_matches(short_evidence, chapter_texts),
|
||||
)
|
||||
long_quote = "证" * 31
|
||||
long_evidence = {"章": 489, "台阶": "长证据", "周期": "成长", "证据": long_quote}
|
||||
check(
|
||||
"clean-超过30字证据拒收",
|
||||
not pu._milestone_evidence_matches(long_evidence, {489: f"前缀{long_quote}后缀"}),
|
||||
)
|
||||
|
||||
|
||||
# ── ④ 归并材料 _merge_material(洞②:演变历程完整不截、其余字段截断)──
|
||||
def test_merge_material():
|
||||
@ -177,6 +228,92 @@ def test_int_chaps():
|
||||
check("intchaps-空输入", pu._int_chaps([]) == set() and pu._int_chaps(None) == set())
|
||||
|
||||
|
||||
def test_load_known_projection():
|
||||
"""判重底册只投影索引字段,禁止跨网搬运每张卡的完整 payload。"""
|
||||
|
||||
class Result:
|
||||
def __init__(self, rows):
|
||||
self.rows = rows
|
||||
|
||||
def fetchall(self):
|
||||
return self.rows
|
||||
|
||||
class Conn:
|
||||
def __init__(self):
|
||||
self.queries = []
|
||||
|
||||
def execute(self, query, params):
|
||||
self.queries.append(query)
|
||||
if "muse_knowledge_draft" in query:
|
||||
return Result([(1, "character", "张三", "主角", ["阿三"])])
|
||||
if "example_upgrade_alias" in query:
|
||||
return Result([("张三", "老张")])
|
||||
return Result([("character", "李四", 7)])
|
||||
|
||||
conn = Conn()
|
||||
name_map, presence = pu.load_known(conn, 8)
|
||||
check("known-正名投影", name_map["张三"] == (1, "character", "主角"))
|
||||
check("known-payload别名投影", name_map["阿三"] == name_map["张三"])
|
||||
check("known-别名表投影", name_map["老张"] == name_map["张三"])
|
||||
check("known-presence保留", presence[("character", "李四")] == {7})
|
||||
first_query = conn.queries[0]
|
||||
check(
|
||||
"known-禁搬完整payload",
|
||||
"SELECT id, draft_payload FROM" not in first_query
|
||||
and "draft_payload->>'名称'" in first_query,
|
||||
)
|
||||
|
||||
|
||||
def test_repair_milestone_evidence():
|
||||
"""只修本窗缺证据里程碑;未来重抄项不能借修复调用混回卡体。"""
|
||||
|
||||
output = {
|
||||
"更新": [
|
||||
{
|
||||
"draft_id": 1,
|
||||
"变更字段": {
|
||||
"演变历程": [
|
||||
{"章": 489, "台阶": "同步率飙至100%", "周期": "高光"},
|
||||
{"章": 493, "台阶": "未来治疗事件", "周期": "高光"},
|
||||
]
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
chapter_texts = {488: "防线开启。", 489: "安若雪同步率飙至100%,击碎能量屏障。"}
|
||||
seen = {}
|
||||
|
||||
def fake_call(prompt, required_keys):
|
||||
seen["prompt"] = prompt
|
||||
seen["required_keys"] = required_keys
|
||||
return (
|
||||
{
|
||||
"证据修复": [
|
||||
{
|
||||
"ref": "$.更新[0].变更字段.演变历程[0]",
|
||||
"章": 489,
|
||||
"证据": "同步率飙至100%,击碎能量屏障",
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
)
|
||||
|
||||
repaired = pu.repair_missing_milestone_evidence(
|
||||
output,
|
||||
title="深空之影",
|
||||
a=488,
|
||||
b=489,
|
||||
text="## 第488章\n防线开启。\n## 第489章\n安若雪同步率飙至100%,击碎能量屏障。",
|
||||
chapter_texts=chapter_texts,
|
||||
call=fake_call,
|
||||
)
|
||||
milestones = output["更新"][0]["变更字段"]["演变历程"]
|
||||
check("repair-本窗证据补齐", repaired == 1 and "证据" in milestones[0])
|
||||
check("repair-未来重抄不送修复", "证据" not in milestones[1] and "未来治疗事件" not in seen["prompt"])
|
||||
check("repair-固定输出键", seen["required_keys"] == ("证据修复",))
|
||||
|
||||
|
||||
# ── ⑥ 三个提示词含关键纪律语句(洞②登场必须 / 洞③≤40字 + 体系级 / 洞②归并去重)──
|
||||
def test_prompts_disciplines():
|
||||
c = _fake_contracts()
|
||||
@ -184,12 +321,14 @@ def test_prompts_disciplines():
|
||||
check("observe-登场必须硬约束", "必须给「演变历程」的首条登场里程碑" in obs)
|
||||
check("observe-台阶≤40字", "≤40 字" in obs)
|
||||
check("observe-体系级纪律", "体系级纪律" in obs)
|
||||
check("observe-里程碑要求原文证据", '"证据"' in obs and "正文原样连续短句" in obs)
|
||||
|
||||
upd = pu.update_prompt(c, "测试书", 1, 12, "正文占位",
|
||||
[(10, {"type": "power_system", "名称": "生物机甲"}, ["观察点"])])
|
||||
check("update-台阶≤40字", "≤40 字" in upd)
|
||||
check("update-体系级纪律", "体系级纪律" in upd)
|
||||
check("update-登场归并去重", "若本卡演变历程已有登场里程碑则不再重复追加" in upd)
|
||||
check("update-里程碑要求原文证据", '"证据"' in upd and "正文原样连续短句" in upd)
|
||||
|
||||
rel = pu.relation_prompt(c, "测试书", 1, 12, "正文占位",
|
||||
[(10, {"名称": "张三"}), (11, {"名称": "李四"})], [])
|
||||
@ -207,6 +346,7 @@ def test_prompts_disciplines():
|
||||
if __name__ == "__main__":
|
||||
for fn in (test_classify_new_name, test_debut_milestone, test_clean_milestone_guard,
|
||||
test_merge_material, test_build_embed_text_type_fix, test_int_chaps,
|
||||
test_load_known_projection, test_repair_milestone_evidence,
|
||||
test_prompts_disciplines):
|
||||
fn()
|
||||
print(f"\n全部离线自测通过:{_passed} 项(未连库、未发任何网络/嵌入/LLM 调用)")
|
||||
|
||||
@ -55,7 +55,7 @@ disable-model-invocation: true
|
||||
- planner、detector、judge 子进程统一受 `--timeout-seconds` 限制,默认 300 秒;任一超时分别落盘 `planner_timeout`、`detector_timeout`、`judge_timeout`,不得继续进入后续阶段或标记 `completed`。
|
||||
- `scripts/write_report.py`:从 `run_result.json` 生成独立严格 schema 的安全摘要,只接受受限标识符、枚举、数字、短安全摘要和 SHA-256;不会读取候选正文,也不会把候选路径以外的原始响应写入报告。
|
||||
|
||||
真实作品运行前必须先从权威来源取得不可变授权快照。当前 work=8 已有 `research_only` 且 `allowedPurpose=["offline_evaluation"]` 的有效记录;真实 489 dry-run 因安若雪卡含错标目标事件被 `invalid_snapshot` 阻断,尚未进入 planner。其他作品缺记录时继续保持 `blocked_authorization`,不得伪造为 `licensed`。
|
||||
真实作品运行前必须先从权威来源取得不可变授权快照。当前 work=8 已有 `research_only` 且 `allowedPurpose=["offline_evaluation"]` 的有效记录;升格入口增加里程碑章号与所标章节原文短引的机械绑定并重抽第 84 窗后,真实 489 dry-run 已达到 `ready`、内容泄露发现为 0。首次模型执行因外部 Opus 网关 `503 No available accounts` 在第一臂 planner 前失败,未产生候选且费用为 0;通道恢复后必须完整重跑三臂。其他作品缺记录时继续保持 `blocked_authorization`,不得伪造为 `licensed`。
|
||||
|
||||
## 正文 A/B/C 回放
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# 升格卡改造设计(P0 补骨架记里程碑 · P1 串链语义判重)
|
||||
|
||||
- 版本:v1(2026-07-16 设计稿,供创始人评审,未实现、未动数据、未提交)
|
||||
- 版本:v2(2026-07-20 已实现;新增里程碑章号原文证据绑定)
|
||||
- 目标读者:创始人 + 后续实现的子代理
|
||||
- 边界:只改**参考书作品面升格卡**(`source_type='upgrade_book'`)的字段骨架、抽取提示词、判重方式、上下文裁剪标注;不碰范式卡、不碰主仓。概念定义对齐 `../design-docs/`(专题-06、架构-02)与源方案 `docs/2026-07-14-参考书作品面数据入库方案.md`,本稿是对该方案 §五「实体卡生命周期」的一次修订,不自立门户。
|
||||
- 一句话结论:给机甲/武器/力量体系这些型补上一条"从登场到结局"的**演变历程**明细,用**真实章号**当索引(不用会变的运行时窗号),并把这条上万字的明细**只给一致性检查看、不给续写看**(续写只看当前态和一句话梗概);再用**语义判重**把改了名、换了型的同一条成长线并回一起、串成前后链。
|
||||
@ -18,7 +18,7 @@
|
||||
- **第一步(先补骨架、记里程碑)**:给机甲/武器/力量体系这些型补上"演变历程"字段,把每一次进化台阶记成一条条里程碑,不再被覆写抹平、不再塞错字段。
|
||||
- **第二步(再串链、上语义判重)**:加"前身/后继"把散落的碎卡串成一条进化链;判重从"只比名字字符串"升级到"比语义",治改名和跨型漏并。
|
||||
|
||||
两个硬要求贯穿全程:**里程碑用真实章号索引(不用窗号)**;**卡片分两层给——续写只看当前态,一致性检查才看全历史**。
|
||||
三个硬要求贯穿全程:**里程碑用真实章号索引(不用窗号)**;**章号必须由所标章节原文短引机械证明**;**卡片分两层给——续写只看当前态,一致性检查才看全历史**。
|
||||
|
||||
---
|
||||
|
||||
@ -98,7 +98,7 @@ flowchart TB
|
||||
|
||||
提示词纪律要加两条硬约束:
|
||||
|
||||
1. 每条里程碑**必须带真实章号**(正文里原样出现的"第 X 章"),**禁止 [窗N] 窗号、禁止"本窗/近期/前段"这类相对指代**——因为卡会脱离运行环境被单独阅读。
|
||||
1. 每条里程碑**必须带真实章号**,并临时附一段来自所标章节正文的连续短引;系统按章读取原文做逐字机械核验,错章、缺证据或改写证据均拒收入库。**禁止 [窗N] 窗号、禁止"本窗/近期/前段"这类相对指代**——因为卡会脱离运行环境被单独阅读。证据只用于写入前校验,校验后删除,不进入正式卡体。
|
||||
2. 生命周期标记从固定枚举里选:**登场 / 成长 / 高光 / 退场 / 结局**。这一条直接治"铁头机甲摘要跳到已退役、中间态全丢"——有了登场和退场节点,一台机甲什么时候来的、什么时候谢幕,一目了然。
|
||||
|
||||
### 4.3 合并逻辑要跟着改
|
||||
@ -122,19 +122,21 @@ flowchart TB
|
||||
|
||||
### 5.2 里程碑对象结构(每条演变历程条目长这样)
|
||||
|
||||
推荐用**极简三字段结构化对象**,既能按章号排序/定位,又不会因字段太多让模型抽崩:
|
||||
正式卡体使用**极简三字段结构化对象**,既能按章号排序/定位,又不会因字段太多让模型抽崩。抽取模型输出时临时增加第四个 `证据` 字段,写入前机械核验并删除:
|
||||
|
||||
```json
|
||||
{
|
||||
"章": 420,
|
||||
"台阶": "V代编队首战:暗星帝国出动十台生物机甲配合亡灵号,扭转多瑙星海战场局势",
|
||||
"周期": "高光"
|
||||
"周期": "高光",
|
||||
"证据": "所标章节正文中的连续短句"
|
||||
}
|
||||
```
|
||||
|
||||
- **章**:绝对章号,用于索引与排序。单章事件填整数(`420`);跨多章的连续事件填区间字符串(`"420-423"`)。**这是"用绝对章号索引"的落点**。
|
||||
- **台阶**:一句话讲清"进化到什么 + 靠什么事件"。刻意把"结果 + 经过"合成一句,不再往下拆细字段,避免模型输出格式崩。
|
||||
- **周期**:生命周期枚举,取值 `登场 / 成长 / 高光 / 退场 / 结局`。
|
||||
- **证据**:仅存在于抽取传输层,必须逐字出现在「章」所指正文中;机械核验后删除,正式卡仍保持三字段。
|
||||
|
||||
这个结构是有真实依据的:库里关系卡「苏铭×林初雨」早期就自发用过 `{状态, 章区间: "Ch.21", 转折事件}` 这种带章号的结构化格式,证明模型抽得出、章号索引可行。三字段是"结构化可查询"和"模型稳定"之间的最优平衡点。
|
||||
|
||||
@ -211,7 +213,7 @@ flowchart TB
|
||||
| `meta/schemas/faction.yaml` `location.yaml` `event.yaml` | 同上(建议同批补,避免二次改;见拍板点) | schema |
|
||||
| `meta/schemas/character.yaml` | 语义对齐:明确「成长弧线」=未来计划,已发生台阶归「演变历程」(是否给 character 也加演变历程见拍板点) | schema |
|
||||
| `meta/schemas/character_relation.yaml` | 「演变轨迹」说明里"章区间"沿用真实章号(本就如此),确认 aiContext 保持 `true` | schema |
|
||||
| `.claude/skills/parse-book/scripts/parse_upgrade.py` | ①`APPEND_FIELDS` 加「演变历程」②observe/update 提示词改为记台阶+生命周期+真实章号、禁窗号相对指代、当前态字段保持干净 ③合并/立卡/撤销逻辑适配里程碑对象(去重按内容、排序按章号)④判重步接语义近邻+M3 终判(P1) | prompt + 逻辑 |
|
||||
| `.claude/skills/parse-book/scripts/parse_upgrade.py` | ①`APPEND_FIELDS` 加「演变历程」②observe/update 提示词改为记台阶+生命周期+真实章号+原文证据、禁窗号相对指代、当前态字段保持干净 ③合并/立卡/撤销逻辑适配里程碑对象(证据绑定、去重按内容、排序按章号)④判重步接语义近邻+M3 终判(P1) | prompt + 逻辑 |
|
||||
| `.claude/skills/db/scripts/seed_schemas.py` | 不改脚本;schema YAML 改完后**需重跑一次**把新字段和 aiContext 灌进库(本稿不跑,实施时跑) | 种子 |
|
||||
| `.claude/skills/read-context/SKILL.md` | Layer 2 知识卡裁剪说明里,点名"演变历程完整层仅一致性检查可见、续写不给"(机制已支持,补一句说明) | skill 文档 |
|
||||
| `.claude/skills/detect/SKILL.md` | 检查项示例表补一行"演变历程 → 演变连续性/跳级穿帮"(实际自动生成,文档补例) | skill 文档 |
|
||||
@ -245,7 +247,7 @@ flowchart TB
|
||||
### 风险
|
||||
|
||||
1. **条目从字符串变对象,合并逻辑改动面不小**。`parse_upgrade.py` 现有一大套针对字符串条目的守卫(剥前缀、剥尾残、拦垃圾、拆粘连、窗序归位),全要跟着改造成处理里程碑对象;存量卡还是字符串,迁移要兼容。→ 缓解:分两步落,先补字段和提示词(新卡受益),再做存量迁移;对象结构压到三字段降低崩坏面。
|
||||
2. **模型抽结构化对象比抽字符串更容易格式崩**。→ 缓解:三字段极简;保留降级——抽不出完整对象就退化成"章号 + 一句话"最小对象,别整条丢。
|
||||
2. **模型可能漏证据或给出错章证据**。→ 缓解:卡体仍保持三字段;模型输出临时附证据,缺失时只对当前窗候选发一次有界证据修复调用;最终仍无法由所标章节原文证明的条目拒收并留审计。完整性不能优先于章号真实性。
|
||||
3. **语义判重增加调用量和跨型误并风险**。→ 缓解:0.60 阈值(实测校准) + 同型才自动并 + 跨型仅提示;判重近邻可像 v6 说的那样批量做、不逐条烧。
|
||||
4. **存量迁移精度损失**:无内嵌章号的条目只能靠窗映射到 12 章宽区间。→ 接受为存量固有局限,新卡不受影响。
|
||||
5. **改 schema 后必须重跑种子**,否则库里字段合同和 YAML 不一致,抽取会按旧合同把新字段裁掉。→ 实施清单里钉死"改 YAML 后重跑 `seed_schemas.py`"这一步。
|
||||
@ -257,13 +259,14 @@ flowchart TB
|
||||
- **一致性检查自动加项**:对一章跑 `detect`,确认检查清单里自动多了"演变连续性"这一项。
|
||||
- **语义判重召回**:造一个改名样例(影杀者 / IV 代纯机械机甲·影杀者),确认语义判重能召回并由 M3 判为同一实体或前身后继。
|
||||
- **章号无窗号残留**:迁移后全库扫一遍,确认演变类字段里不再有 [窗N] 前缀。
|
||||
- **章号证据真绑定**:用跨章窗口重抽验证,错标章节的里程碑必须拒收;正确章节原文短引通过后,正式卡只保留章 / 台阶 / 周期,不残留证据字段。
|
||||
|
||||
---
|
||||
|
||||
## 十、需要创始人拍板的点
|
||||
|
||||
> **✅ 已拍板(2026-07-17,创始人 4 项 + 主代理定 #4)——实现以此为准,下方原始理由留档:**
|
||||
> 1. 里程碑格式 = **结构化对象(章 / 台阶 / 周期)**,配降级(抽不出完整对象退成"章号 + 一句话",不整条丢)。
|
||||
> 1. 里程碑卡体格式 = **结构化对象(章 / 台阶 / 周期)**;抽取传输临时增加原文证据,机械核验后删除。无法证明所标章号的条目拒收并留审计,不做猜测性降级。
|
||||
> 2. 补齐范围 = **五型全补**:item / power_system / faction / location / event。
|
||||
> 3. character = **一起改**:给人物也加「演变历程」、「成长弧线」回归"未来计划"本义,连带迁移 200+ 张存量人物卡。
|
||||
> 4. 演变概括 = **独立字段**(主代理定:摘要管当前态、概括管轨迹,职责清晰)。
|
||||
|
||||
@ -14,11 +14,14 @@
|
||||
- 已验证:replay-eval 127 个离线测试全部通过;fake runner 已跑通三臂 planner、真正不识别臂卡内容的逐候选 detector、双 judge 反序盲评、子进程超时失败关闭、稳定性门和去盲矩阵。合成 fixture 不代表参考作品评测结果。
|
||||
- 已新增并验证:`audit_leakage.py` 对公共快照和三臂卡注入区执行内容级事实审计;`load_reference_work.py` 通过 PostgreSQL 只读事务组装仓库外临时配置,候选卡标记为 `eval_draft`,不进入生产检索。
|
||||
- 授权表已应用,work=8 已有一条有效的 `research_only + offline_evaluation` 不可变快照;原文件、document SHA-256 和 `raw-file-v1` 来源版本三方一致,重验时间为 2026-08-18。
|
||||
- 已执行真实 work=8 / as_of=488 / target=489 dry-run:正确卡选择为安若雪与圣蒂曼行星,placebo 为同型近体量的伊蕾莉雅与鹦鹉螺号;未调用模型。
|
||||
- 首次真实 dry-run 暴露内容审计假阴性:安若雪卡把第 489 章“加特朗 / 同步率 100%”事件错标为第 488 章,旧审计只做完整字符串包含而错误放行。现已增加原子事件子句、多强锚点和目标子句覆盖率门,真实样本被正确阻断为 `invalid_snapshot`,不生成 manifest、不进入 planner。
|
||||
- 当前状态:**授权与真实装配已通,目标样本因上游卡章号错标被内容门阻断,模型实跑未开始**。必须先修正或隔离泄露来源,再执行三臂模型回放。
|
||||
- 已执行真实 work=8 / as_of=488 / target=489 dry-run:正确卡选择为安若雪与圣蒂曼行星,placebo 为同型近体量的伊蕾莉雅与鹦鹉螺号。重抽后活动地点卡由旧 ID `12383` 变为 `21881`,评测卡选择按活动卡重登记;卡 ID 漂移只影响运行配置,不改变冻结点、卡型或评测变量。
|
||||
- 首次真实 dry-run 暴露内容审计假阴性:安若雪卡把第 489 章“加特朗 / 同步率 100%”事件错标为第 488 章,旧审计只做完整字符串包含而错误放行。现已增加原子事件子句、多强锚点和目标子句覆盖率门;同时在升格抽取入口增加“章号 + 所标章节原文短引”机械绑定,缺证据、错章证据和改写证据均拒收入库,证据字段校验后丢弃。
|
||||
- 已重抽 work=8 第 84 窗(487–489 章):20 次调用、21 张卡更新;机械门拒绝 19 条无法由所标章节正文支撑的里程碑,其中包含错标为第 488 章的加特朗 / 100% 同步率事件。正式卡未写入临时证据字段,目标泄露扫描为 0。
|
||||
- 修复后重新装配并运行真实 dry-run:授权有效,三臂公共输入哈希一致,仅卡注入分区不同,`leakageAudit.findingCount=0`,状态为 `ready`。
|
||||
- 已尝试启动三臂模型实跑,但第一臂 planner 前被 Claude 推理网关 `503 No available accounts` 阻断;随后极小 Opus 连通性探测再次得到相同 503。两次均为 0 输入 token、0 输出 token、0 费用,未产生候选细纲,后两臂、detector 和双 judge 均未执行。
|
||||
- 当前状态:**授权、真实装配、上游章号证据绑定和真实 dry-run 已通;模型实跑仅被外部 Opus 通道阻塞**。通道恢复后必须从完整三臂重新执行,不能复用这次未完成的第一臂,也不能换模型污染变量控制。
|
||||
- `.claude/skills/llm/scripts/test_quota.py` 已统一到当前 `$24/6000` 契约:预算场景使用 `$24.5`,调用上限场景使用 `6000`,并增加策略常量断言。
|
||||
- 当前允许的结论是“授权、真实数据装配、冻结、变量控制、内容级泄露阻断、候选结构门、detector/judge 双评编排和安全摘要机制已通”;不能说细纲智能体或知识卡已通过。489 样本需先清除泄露来源,430/550 样本仍待执行。
|
||||
- 当前允许的结论是“授权、真实数据装配、冻结、变量控制、上游里程碑证据绑定、内容级泄露阻断、候选结构门、detector/judge 双评编排和安全摘要机制已通”;不能说细纲智能体或知识卡已通过。489 样本等待 Opus 通道恢复后执行,430/550 样本仍待执行。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user