框架: parse_llm试拆首轮对症四改——细纲绝对字数上限+超标压缩重试+source机械回填+归型五型均衡纪律(15章实测:scaffold全过/craft偏科待验)

This commit is contained in:
zizi 2026-07-13 16:14:38 +08:00
parent ee372f7ea2
commit 7be76b9ebb

View File

@ -10,6 +10,7 @@
"""
import json
import pathlib
import re
import subprocess
import sys
@ -54,12 +55,15 @@ CONTRACTS = {
def scaffold_prompt(title, ch, ch_title, text, prev_entities):
ents = json.dumps(prev_entities, ensure_ascii=False) if prev_entities else "(第一章,为空)"
# 绝对字数上限比抽象比例对 LLM 更可执行(首轮实测 M3 按比例会写到 9.8%19%
wc = len(text.replace("\n", "").replace(" ", ""))
cap = max(60, int(wc * 0.05))
return f"""【muse 创作实验台·拆书 2b·脚手架 pass】
{IDENTITY}
功能指令(parse-book 逐章内环)
对参考书{title} {ch} {ch_title}正文附后
1) 逆推本章细纲章目标/关键事件/出场角色/伏笔动作(··)/章末钩子字数=章正文的 35%3000字章100150硬顶 8%超标会被校验脚本退回细纲是结构骨架不是缩写复述
对参考书{title} {ch} {ch_title}正文附后 {wc}
1) 逆推本章细纲章目标/关键事件/出场角色/伏笔动作(··)/章末钩子**细纲全文不得超过 {cap} **章正文的 5%超标会被校验脚本机械退回细纲是结构骨架不是缩写复述用短语与分号不写完整句子
2) 抽实体增量脚手架级索引{ENTITY_CRITERIA}
判重下方是前文已收录实体不重报除非本章给出新身份则在一句话摘要里并入
@ -83,7 +87,8 @@ def patterns_prompt(title, ch, ch_title, text, outline, existing_cards):
{json.dumps(CONTRACTS, ensure_ascii=False)}
纪律
- 宁缺毋滥一章 03 张为常态只收本章表现突出可跨书复用的写法平庸章可出 0
- 归型走判据五型之外一律不出卡
- 归型走判据五型之外一律不出卡**先按判据定 type再只用该 type 字段表里的中文 key fields混用他型字段=机械拒卡** trope 公式步骤不得出现在 craft 卡里
- **五型都是候选不要把一切归成 craft**整场武力对抗combat整场情绪戏emotion拍卖/谈判/审讯等场景公式scene_pattern跨章复用的情节公式tropecraft 只留给单点装置删去它场景仍成立type 值必须与 fields 所用字段表同型
- **脱敏红线**只写抽象结构与手法归纳严禁抄录原文15 连续字与原文重合=校验脚本机械拒卡卡名与字段值用主角/强敌/导师等抽象指代**不得出现书内专名**人名/地名/机甲名/组织名专名只允许出现在 source.anchor
- fields 按该型字段合同的中文 key 没证据的 key 省略不编造
- 判重下方已积累卡名录不与之重复立同义卡
@ -176,14 +181,24 @@ def main(work_id, from_, to, model):
# pass1 脚手架断点续跑done 跳过)
if s_st != "done" or outline is None:
try:
data, usage = m3_json(scaffold_prompt(title, ch, ch_title, text, prev), model,
("outline", "entities"))
p1 = scaffold_prompt(title, ch, ch_title, text, prev)
data, usage = m3_json(p1, model, ("outline", "entities"))
total_in += usage.get("prompt_tokens", 0)
total_out += usage.get("completion_tokens", 0)
ok, out = ingest("scaffold", work_id, ch, data)
# 比例超标是 M3 高频病:带压缩指令重试 1 次(首轮实测 10/15 章超标)
if not ok and "细纲比例" in out:
cap = max(60, int(len(re.sub(r"\s", "", text)) * 0.05))
data, usage = m3_json(
p1 + f"\n\n【重试】上次细纲 {len(data['outline'])} 字超标被退回。"
f"压缩到 {cap} 字以内:只留章目标/关键事件/伏笔动作/钩子,删掉一切修饰与过程描述。",
model, ("outline", "entities"))
total_in += usage.get("prompt_tokens", 0)
total_out += usage.get("completion_tokens", 0)
ok, out = ingest("scaffold", work_id, ch, data)
click.echo(f" {out}")
if not ok:
continue # 比例超标等已记 task failed重跑本命令补
continue # 已记 task failed重跑本命令补
outline = data["outline"]
except RuntimeError as e:
click.echo(f" #{ch} 脚手架 M3 失败: {e}")
@ -195,6 +210,13 @@ def main(work_id, from_, to, model):
("cards",))
total_in += usage.get("prompt_tokens", 0)
total_out += usage.get("completion_tokens", 0)
# source 机械回填book/chapter 是调用侧确定数据,不依赖 LLM 自觉
#(实测 M3 偶发 source=null 整批被拒anchor 是内容性字段,缺时用 brief 兜底
for c in data.get("cards") or []:
src = c.get("source") or {}
c["source"] = {"book": src.get("book") or title,
"chapter": src.get("chapter") or f"{ch}{ch_title}",
"anchor": src.get("anchor") or c.get("brief") or ""}
_, out = ingest("patterns", work_id, ch, data)
click.echo(f" {out}")
except RuntimeError as e: