diff --git a/.claude/skills/parse-book/scripts/parse_outline.py b/.claude/skills/parse-book/scripts/parse_outline.py index 2950d89..d429292 100644 --- a/.claude/skills/parse-book/scripts/parse_outline.py +++ b/.claude/skills/parse-book/scripts/parse_outline.py @@ -14,7 +14,26 @@ import click import psycopg sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2] / "llm" / "scripts")) -from llm import chat, extract_json # noqa: E402 +from llm import chat, extract_json, SensitiveError # noqa: E402 + +# 敏感降级链(与 parse_llm 同款;窗大纲输入含正文,批7实测撞 new_sensitive 直接崩进程) +FALLBACK_MODELS = ["MiniMax-M2.7", "deepseek-v4-flash"] + + +def chat_degrade(prompt, model): + """chat 带敏感降级:主模型被拦依次换备选;全链被拦原样抛 SensitiveError 交上层跳窗。""" + chain = [model] + FALLBACK_MODELS + for mi, m in enumerate(chain): + try: + content, usage = chat(prompt, model=m) + if m != model: + print(f"[敏感降级成功] 本条由 {m} 产出", file=sys.stderr) + return content, usage + except SensitiveError: + if mi < len(chain) - 1: + print(f"[敏感] {m} 拒绝,降级到 {chain[mi + 1]}", file=sys.stderr) + continue + raise DSN = ("postgresql://root:f6710e2d0294eb1c10e26a805a64bc54@100.64.0.8:5433/muse-example" "?keepalives=1&keepalives_idle=15&keepalives_interval=5&keepalives_count=3") @@ -121,8 +140,13 @@ def _do_window(conn, work_id, title, win_no, chs, model, done): cap = max(200, int(wc * 0.004)) outlines = "\n".join(f"第{no}章《{ct}》:{o}" for no, ct, _, o in chs) text = "\n\n".join(f"【第{no}章 | {ct}】\n{t}" for no, ct, t, _ in chs) - content, usage = chat(WINDOW_PROMPT.format( - title=title, a=chs[0][0], b=chs[-1][0], wc=wc, cap=cap, outlines=outlines, text=text), model=model) + try: + content, usage = chat_degrade(WINDOW_PROMPT.format( + title=title, a=chs[0][0], b=chs[-1][0], wc=wc, cap=cap, outlines=outlines, text=text), model) + except SensitiveError as e: + # 全链被拦:跳过本窗继续后续窗(窗大纲窗间独立,不必停书),重跑自动补 + click.echo(f"win-{win_no:02d} 敏感全链被拦,跳过(重跑补): {str(e)[:80]}") + return data = extract_json(content) ol = (data.get("outline") or "").strip() if not ol: