框架: README§九批6-7全线放量记录+compact恢复锚点;固化salvage/rewash脚本入仓

This commit is contained in:
zizi 2026-07-15 17:08:40 +08:00
parent f9eddbb06b
commit fd1628084d
3 changed files with 241 additions and 0 deletions

View File

@ -0,0 +1,129 @@
#!/usr/bin/env python3
"""存量回洗(一次性,抽检 2026-07-15 后):
追加字段前缀堆叠/[本窗]残留清洗 + 同文去重34+9 张卡
关系卡顶层演变轨迹迁移进 字段.演变轨迹双份分裂修复
出场章全量重建卡名+别名 机械预扫全书正文 AI比增量准
脏别名清理备忘录行/括号注释/错认两行橡树妖/雷行天下
占位值清理字段值含续写不可见的删字段
"""
import json
import re
import sys
import psycopg
DSN = ("postgresql://root:f6710e2d0294eb1c10e26a805a64bc54@100.64.0.8:5433/muse-example"
"?keepalives=1")
TENANT = 1
PREFIX_RE = re.compile(r"^(?:\[[窗本][^\]]{0,6}\]\s*)+") # 含 [窗本窗] 等模型自造变体
WIN_NUMS = re.compile(r"\[窗(\d+)\]")
# 7 型合同合法 key越合同 key 清洗用,运行时从库加载)
VALID_KEYS = {}
def load_valid_keys(conn):
for (sk, snap) in conn.execute(
"""SELECT s.schema_key, v.field_contract_snapshot FROM muse_meta_schema s
JOIN muse_meta_schema_version v ON v.id=s.active_version_id
WHERE s.tenant_id=1 AND s.schema_key IN
('character','location','item','faction','power_system','event','character_relation')"""):
VALID_KEYS[sk] = {f["key"] for f in snap.get("特有字段", [])} | {"一句话摘要", "演变轨迹"}
def clean_list(items):
"""剥堆叠前缀+同文去重;窗号取原条目最内层(最后一个),无窗号保持裸条目。"""
out, seen = [], set()
for it in items:
s = str(it)
core = PREFIX_RE.sub("", s).strip()
if not core or core in seen:
continue
seen.add(core)
nums = WIN_NUMS.findall(s[:len(s) - len(core)])
out.append(f"[窗{nums[-1]}] {core}" if nums else core)
return out
def rebuild_presence(chapters, names):
"""全书逐章扫描名字集合 → 出场章号列表。"""
hits = set()
for order_no, text in chapters:
if any(nm in text for nm in names):
hits.add(order_no)
return sorted(hits)
def main():
stats = {"前缀清洗卡": 0, "关系迁移卡": 0, "出场章重建卡": 0, "删别名行": 0, "删占位字段": 0}
with psycopg.connect(DSN) as conn:
load_valid_keys(conn)
for work_id in (4, 8):
chapters = conn.execute(
"""SELECT c.order_no, b.content_text FROM muse_content_chapter c
JOIN muse_content_block b ON b.chapter_id=c.id AND b.deleted=FALSE
WHERE c.tenant_id=%s AND c.work_id=%s AND c.deleted=FALSE
ORDER BY c.order_no""", (TENANT, work_id)).fetchall()
cards = conn.execute(
"""SELECT id, draft_payload FROM muse_knowledge_draft
WHERE tenant_id=%s AND work_id=%s AND source_type='upgrade_book'
AND deleted=FALSE""", (TENANT, work_id)).fetchall()
for did, p in cards:
changed = False
fields = p.get("字段") or {}
# ② 关系卡顶层演变迁移
if p.get("type") == "character_relation" and "演变轨迹" in p:
legacy = p.pop("演变轨迹")
base = fields.get("演变轨迹") or []
fields["演变轨迹"] = (base if isinstance(base, list) else [base]) + \
(legacy if isinstance(legacy, list) else [legacy])
p["字段"] = fields
changed = True
stats["关系迁移卡"] += 1
# ①⑤ 数组前缀清洗 + 占位字段删除 + 越合同畸形 key 清洗窗29黄蜂针实测
vk = VALID_KEYS.get(p.get("type"))
for k in list(fields.keys()):
if vk is not None and k not in vk:
del fields[k]
changed = True
stats["删越合同key"] = stats.get("删越合同key", 0) + 1
continue
v = fields[k]
if isinstance(v, str) and "续写不可见" in v:
del fields[k]
changed = True
stats["删占位字段"] += 1
elif isinstance(v, list):
nv = clean_list(v)
if nv != v:
fields[k] = nv
changed = True
stats["前缀清洗卡"] += 1
# ③ 出场章全量重建(实体卡;关系卡无出场章语义)
if p.get("type") != "character_relation":
names = {p.get("名称", "")} | set(p.get("别名") or [])
names = {n for n in names if n and len(n) >= 2} # 单字名误命中太多,跳过
if names:
nv = rebuild_presence(chapters, names)
if nv and nv != p.get("出场章"):
p["出场章"] = nv
changed = True
stats["出场章重建卡"] += 1
if changed:
conn.execute(
"UPDATE muse_knowledge_draft SET draft_payload=%s, updater='rewash' WHERE id=%s",
(json.dumps(p, ensure_ascii=False), did))
# ④ 脏别名:备忘录/括号/超长 + 抽检定点两行错认
r = conn.execute(
"""UPDATE example_upgrade_alias SET deleted=TRUE, updater='rewash'
WHERE tenant_id=%s AND deleted=FALSE AND (
alias ~ '[())。,:]' OR length(alias) > 12
OR canonical_name ~ '[())。,:]'
OR alias IN ('橡树妖','雷行天下'))""", (TENANT,))
stats["删别名行"] = r.rowcount
conn.commit()
print(json.dumps(stats, ensure_ascii=False))
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,111 @@
#!/usr/bin/env python3
"""顽固敏感章分段抢救(创始人 2026-07-15「定点处理遗留」
原理内容安全拦截多为全文综合触发把章正文对半拆成两段各自独立走
scaffold 抽取 M3M2.7deepseek 降级链机械合并两半结果后走原守卫入库
每半的细纲上限=半章正文 5%合并后自然满足全章 5% 上限ingest 校验不变
任一半降级链仍全败 该章保持 failed不硬磕不无限烧额度
用法salvage --work-id N salvage --all跑全部 failed
"""
import json
import sys
import click
import psycopg
sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent))
sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parents[2] / "llm" / "scripts"))
from parse_llm import (m3_json, scaffold_prompt, ingest, SensitiveHardStop, # noqa: E402
DSN, TENANT)
MODEL = "MiniMax-M3"
def salvage_chapter(conn, work_id, title, ch, ch_title, text, prev):
"""对半拆分两段抽取→机械合并→守卫入库。返回 (是否成功, 说明)。
极短章<1500 不拆半细纲 60 字下限×2 段会超过正文本身星环#97 实测
比例 113.7% 被守卫退回整章单发重试一次"""
# 感言/公告章(极短+作者口吻):无正文可拆,诚实标注直接入库,零 LLM星环#97 实测 116 字感言)
plain = text.strip()
if len(plain) < 500 and any(w in plain for w in ("作者", "感言", "请假", "推荐票", "月票", "补更", "上架")):
ok, out = ingest("scaffold", work_id, ch,
{"outline": "(作者感言/公告章,无正文内容)", "entities": [], "hints": []})
return ok, "感言章标注入库" if ok else out[-120:]
if len(text) < 1500:
halves = [text]
else:
mid = len(text) // 2
# 对半点对齐段落边界(往后找最近换行,避免句子拦腰斩)
cut = text.find("\n", mid)
cut = cut if cut != -1 else mid
halves = [text[:cut], text[cut:]]
outlines, ents, hints = [], {}, []
n = len(halves)
for i, half in enumerate(halves, 1):
seg_note = f"(第{i}/{n}段)" if n > 1 else ""
p = scaffold_prompt(title, ch, f"{ch_title}{seg_note}", half, prev)
try:
data, _ = m3_json(p, MODEL, ("outline", "entities", "hints"))
except SensitiveHardStop:
return False, f"{i}/2段降级链仍全败"
outlines.append(str(data.get("outline") or "").strip())
for e in data.get("entities") or []:
if isinstance(e, dict):
nm = (e.get("名称") or e.get("name") or "").strip()
if nm and nm not in ents:
ents[nm] = e
hints.extend(h for h in (data.get("hints") or []) if isinstance(h, dict))
outline = "".join(o for o in outlines if o)
# 合并后超 8% 硬顶(降级模型偶超标,星环#1664 实测 11%):按「;」从尾机械砍段到 7% 留余量
import re as _re
wc = len(_re.sub(r"\s", "", text))
cap = max(60, int(wc * 0.07))
while len(_re.sub(r"\s", "", outline)) > cap and "" in outline:
outline = outline.rsplit("", 1)[0]
merged = {"outline": outline,
"entities": list(ents.values()), "hints": hints}
ok, out = ingest("scaffold", work_id, ch, merged)
return ok, out[-120:]
@click.command()
@click.option("--work-id", type=int, default=0, help="只跑指定书0=全部)")
@click.option("--limit", type=int, default=0, help="最多抢救几章0=不限)")
def main(work_id, limit):
with psycopg.connect(DSN) as conn:
cond = "AND t.work_id=%s" % work_id if work_id else ""
rows = conn.execute(f"""
SELECT t.work_id, w.title, c.order_no, c.title, b.content_text
FROM example_parse_task t
JOIN muse_content_chapter c ON c.id=t.chapter_id
JOIN muse_content_block b ON b.chapter_id=c.id AND b.deleted=FALSE
JOIN muse_content_work w ON w.id=t.work_id
WHERE t.scaffold_status='failed' AND c.deleted=FALSE {cond}
ORDER BY t.work_id, c.order_no""").fetchall()
click.echo(f"待抢救 failed 章:{len(rows)}")
saved = failed = 0
for wid, title, ch, ch_title, text in rows[:limit or None]:
# 前文实体名清单(复用章级压缩口径)
with psycopg.connect(DSN) as conn:
prev = [e for (es,) in conn.execute(
"""SELECT s.entities FROM example_parse_scaffold s
JOIN muse_content_chapter c ON c.id=s.chapter_id
WHERE s.tenant_id=%s AND s.work_id=%s AND c.order_no<%s AND s.deleted=FALSE""",
(TENANT, wid, ch)).fetchall() for e in es]
try:
ok, note = salvage_chapter(conn, wid, title, ch, ch_title, text, prev)
except Exception as e: # 单章异常不挡后续章
ok, note = False, f"异常: {str(e)[:100]}"
if ok:
saved += 1
click.echo(f" ✓ 救回 {title}#{ch}")
else:
failed += 1
click.echo(f" ✗ 仍败 {title}#{ch}: {note}")
click.echo(f"=== 抢救收尾:救回{saved} / 仍败{failed} ===")
if __name__ == "__main__":
main()

View File

@ -237,6 +237,7 @@ flowchart LR
- **B2 全本拆书·限流分批2026-07-14 创始人拍板:暂停→分批,每批 ≤3000 次请求、批间隔 5 小时)**:首夜 5 路并行拆至 425544/书(合计 ~2570 章落库)后按指令暂停。**限流纪律**:请求含重试保守全计,每批按 ~2700 章排(章级=每章 1 次调用+少量病症重试;窗级出卡=每窗 58 次);批间隔 ≥5 小时,宁晚勿早。**批1 定时失败→09:09 脱离补跑**sleep 18000 定时挂在 Claude Code 后台任务上,上会话退出被带走→06:40 未开跑、零请求(额度未亏);改 nohup+disown 脱离会话补跑五路深空→594/机动→673/超神→1455/星环→1722/机战→827,共 2700 章,断点续跑跳已完成章,日志 /tmp/muse-b1-*.log。**跨会话根因未除**:本地后台长任务随 Claude Code 会话消亡,彻底可靠需放 mini-desktop tmux/nohup待定;`pkill -f parse_llm` 模式串会连带杀定时器。**批2**=机战章级收口→2901+四书窗大纲续切+余量开出卡;批3+=出卡/终检/审核/样张,每批开跑前按预算精排。**shell 教训**:定时批命令 `A && B & C &` 中 C 不等 A`&` 分组陷阱),必须 `A && { B & C & wait; }`——首挂曾致 4 书抢跑约 1 分钟,当场杀掉,烧个位数请求。**章级完成后的续跑序**:窗大纲续切(`window --to 末章`——机动已有 win1-37/深空 win1-36/其余至 50,续跑自动从缺口接;换窗参数才需清窗行)→ 分型出卡 `cards --redo 不带`(断点续跑跳已出窗)→ `check` 终检 → `review` 审核 → `export` 样张呈报。
- **B2 停跑·缓存与敏感优化前置2026-07-14 创始人拍板)**:创始人点破**限次数是果不是因**——每次请求 token 太贵→同配额可用次数缩水→被迫连砍额度(4000→3000→1000→500);省 token=省次数一根链先前把两者当并列选项是错判。停跑时批2实发280(机动16/深空43/超神81/星环67/机战73)**其中66次(23%)被上游内容安全 new_sensitive(1026) 拦截空耗**(机动/星环暴力战争情节,每敏感章重试3次)。双重漏水:①缓存没吃到(scaffold_prompt/window_cards_prompt 把 title/章号/cap/batch_note 变量前置,破坏 read-context 规定的"稳定度递减吃前缀缓存")②敏感空耗23%。**优化落地前不重启批次**,三前置(均省次数):①缓存重排(固定规则前置、正文/章号/大纲尾置;窗级合同+金标准纪律上千字是最大靶,拆书自拼消息不受"阶段一子代理不能共享缓存"豁免)②拆书补敏感换模型/跳过机制(清洗侧已有先例)③读 token 明细验 M3+New-API 真支持缓存(usage 带 *_tokens_details 疑含 cached_tokens,需1次诊断)。方案待出给创始人。
- **B2 优化落地·批3重启2026-07-14 实测纠正预判)**:①**真凶是前文实体膨胀,不是缓存**——章级 prompt 把前文实体全档(型+名+摘要)累积重发,深空557章2851实体≈15万token/章占输入97%、随章号雪崩;缓存只缓存"每次一样的固定前缀"、救不了每章都变的实体清单,此路错判。②**压缩(主力)**:实体索引全档json→名字清单(判重只需名字),每章16万→2万token砍86%(深空500实测14万→1.95万,细纲/实体/线索质量正常),同预算次数×7-8,直达创始人目标2-3000。③**缓存诊断**:M3经New-API自动缓存恒命中128token(其内部模板,与我方内容无关);换 Anthropic /v1/messages+cache_control 仍 cache_creation=0(M3上游不支持显式缓存);M2.7/deepseek=0——要吃缓存须换模型,但压缩后缓存仅省固定前缀1k小头,不值当;提示词已缓存重排(固定前置)备将来换模型。④**敏感降级链**:撞 new_sensitive→MiniMax-M2.7→deepseek-v4-flash→全失败硬停,实测 M2.7 救回深空/机动密集敏感章(深空连测3章皆敏感)。**批3=500次自停闸**(wrapper PID精确kill+计数 grep [llm]|[敏感] 含敏感失败请求防超支,哨兵盯收尾)。**教训**:创始人两次纠偏单一归因(省钱=省次数是一根链;缓存/压缩/检索是多重方向非二选一)。检索召回(名字清单再压)留后续。
- **批6-7 全线放量+质量闭环(2026-07-15,compact恢复锚点)**批6(2400闸→1211章,敏感救回423,超神/星环/机战大步收口)。批7(创始人放量5000,中途令牌余额见底403中断→充值恢复)**章级 7344/7345 收官**(顽固59章分段抢救救回57、感言章#97/坏章#811诚实标注入库;唯一余量见库);fable5抽检(事实4.4/合同3.8/生长2.8/归并4.0,判"需修后再放量")→**4高2中全修+两轮存量回洗**(前缀去重/出场章全书重扫重建312卡/关系落点统一57卡/覆写回退拦截/越合同key裁剪38处/别名准入);定点修复7调用全过(4卡audit旧值合并/黄蜂针净化剥雷行天下/泰加+白色游魂2对并卡)。**关键机制修复(勿回退)**:max_tokens=512000(创始人拍板不设限;New-API按max_tokens预扣费,余额须≥并发×$0.154否则403)+chat()撞模型上限自适应降档(M2.7上限196608实测)+细纲守卫60字绝对豁免+升格failed窗重跑先撤销+连续2窗失败才停书+模型输出str防御。**compact时在跑(nohup脱离会话,勿重启)**:升格路(机动52+/80窗→深空116窗,日志/tmp/muse-b7-upgrade.log)+出卡双路(5书窗大纲续切→分型出卡,/tmp/muse-b7-cards[AB].log)+全局守护闸4700(/tmp/muse-b7-cards-main.log出STOP行=收尾)。跑完续跑序:check终检→review审核→export样张+升格样张(机动全书+深空末段压测数据)呈报创始人。固化脚本:parse_salvage.py(顽固章分段抢救)/parse_rewash.py(存量回洗)/parse_upgrade.py(作品面升格)。待创始人:样张过目、B3检索验证、B5确认门。
- **批3-5 分批拆(2026-07-14,创始人逐批给额度)**批3(501次→322章)、批4(1000次→572章,529升5%)、**批5 收尾(2000次自停零超支→1133章:星环250/机战510/超神373;M2.7救回281章,0硬停,529降至1.7%)**。压缩+敏感降级实测有效。**章级全局 5500/7345=75%**:机动673✓/深空593(仅#143)/超神1438(差17)/星环1194/机战1602。**待办**:批6待创始人发额度(剩余约1845章≈2100次可全收口章级)、深空#143顽固敏感(备用模型疑也撞,单独攻)、机动/深空章级已完成可进窗大纲+出卡。续跑序:章级完→window切窗大纲→cards分型出卡→check终检→review审核→export样张。
- **参考书作品面数据入库2026-07-14 创始人指令+同日认可开工)**:「这几本书本身也是被导入的作品,对应的数据也都需要入库」——**方案 v6 定稿并已批准:`docs/2026-07-14-参考书作品面数据入库方案.md`**。四轮独立评审+创始人五条反馈换核,核心机制=**全实体统一生长**(废v4「前15全卡/轻卡」分层——创始人质疑成立):一实体一卡(唯一键=作品×型×归一名×范围,范围恒「本书私有」),每窗「机械预扫在场实体→实体观察→判重(留档/别名→嵌入近邻→AI终判)→卡更新(只对有新信息,批≤6张)→关系增量」,字段三类演进(底色覆写留审计/演进追加带窗号/当前态保最新);两阶段合并:拆书期候选卡就地累积,创作期走变更提案(draft表entity_id/proposed_changes/snapshot三列原生支持);正文窗直抽(3-5万字/窗,同窗调用连发吃缓存);防膨胀双设计(索引=窗内机械预扫命中集不随书涨/观察输出超12新名或15章对半分段)。**5张升格工作表已建**(`db/ddl/94`:别名/出场留档/窗状态/卡水位/覆写审计)。作品面抽取估3000-4800次,**总额闸5000封顶**,试跑校准;全链从2026-07-14起约5900-8400次。**下一步**:首书机动风暴~10窗+深空末段3窗压测出样张→创始人过目→放量。M3无思考确认(实测reasoning_tokens=0,中位out=331),维持不开(质量已达标,思考按输出计费烧次数,深推理单点用强模型)。
- **未决**:①机动残留变体与感言章处理方式②跨书同功母卡归并=放量后公共面课题③S5 参数 A/BM3 输出方差根治口④S6 pacing 书级抽取(等整本拆完,依赖全本+作品面入库)。