From 37d508ffb0309a62ed712697220479e4553e81db Mon Sep 17 00:00:00 2001 From: zizi Date: Wed, 19 Aug 2026 21:05:27 +0800 Subject: [PATCH] =?UTF-8?q?chore(workspace):=20=E5=86=99=E7=AB=A0=E8=8A=82?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=20step2=20/=20step2a=20=E6=8E=A5=E5=85=A5?= =?UTF-8?q?=E4=BA=BA=E6=84=9F=E4=BA=94=E6=8A=80=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit step2_write_chapter.py 在 sys.path 加入 prevent-ai-flavor / diagnose-ai-flavor,新增「1.5) 人感前置预防」环节——生成前调用 build_prevention_contract(负约束+声音账)冻结进 WriterContext,并把 diagnosis 列为 shadow 候选的伴随检测。step2a_retrofit_ch1_context.py 相应补回退补写。 脚本只做拼装与持久化触发,五技能合同与 hard gate 由各自 skill 把守。 人感体系资产层仍由 humanization/ 维护(example 自治);本脚本只消费。 --- docs/write-chapter/step2_write_chapter.py | 35 ++++++++++++++++++- .../step2a_retrofit_ch1_context.py | 12 ++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/docs/write-chapter/step2_write_chapter.py b/docs/write-chapter/step2_write_chapter.py index 067b720..0bf306b 100644 --- a/docs/write-chapter/step2_write_chapter.py +++ b/docs/write-chapter/step2_write_chapter.py @@ -40,6 +40,8 @@ for sub in ( "access-database/scripts", "check-content-consistency/scripts", "decide-candidate/scripts", + "prevent-ai-flavor/scripts", + "diagnose-ai-flavor/scripts", ): p = str(SKILLS / sub) if p not in sys.path: @@ -47,6 +49,10 @@ for sub in ( from db import connect, DSN # noqa: E402 from assemble_writer_context import assemble_context # noqa: E402 +from prevent_ai_flavor import ( # noqa: E402 + PreventionContractError, build_prevention_contract, persist_prevention, render_writer_constraints, +) +from diagnose_ai_flavor import run_diagnosis, persist_diagnosis # noqa: E402 from retrieve_writer_sources import ( # noqa: E402 ProductionCardIndexRepository, FrozenProseRepository, RetrievalError, build_retrieval_plan, retrieve_writer_sources, load_confirmed_fine_outline, @@ -287,6 +293,19 @@ def main(): print(f"目标第{target}章(asOf={as_of});细纲已读,基线 {got}," f"基线总字数 {sum(len(item['text']) for item in recent_chapters)}") + # 1.5) 人感前置预防:规则/声音账先形成合同,再冻结进 WriterContext。 + try: + humanization_contract = build_prevention_contract( + f"work:{WORK_ID}", load_database=True + ) + humanization_contract["writer_constraints"] = render_writer_constraints(humanization_contract) + prevention_receipt = persist_prevention(humanization_contract) + except PreventionContractError as exc: + raise SystemExit(f"人感前置预防合同失败:{exc}") from exc + print(f"人感前置预防:约束 {len(humanization_contract['writer_constraints'])} 条," + f"规则库 {humanization_contract['built_from']['rule_library_version']}," + f"run_id={prevention_receipt['run_id']}") + # 2) 检索计划 + 执行(生产仓储;新书无卡诚实返空) token_budget = {"maxContextChars": 200000} plan = build_retrieval_plan( @@ -324,7 +343,7 @@ def main(): narrative_state=narrative_state, recent_chapters=recent_chapters, output_contract=output_contract, token_budget=token_budget, pattern_references=pattern_references, style_constraints=style_constraints, - generated_at=generated_at, + humanization_contract=humanization_contract, generated_at=generated_at, evidence_strategy="production_dual_evidence") writer_context = assembled["context"] (ARTIFACTS / f"{run_id}-writer-context.json").write_text( @@ -364,6 +383,20 @@ def main(): persist_call=persist_llm_event) receipts_by_version[candidate_version] = receipt candidates_by_version[candidate_version] = candidate + # 人感技能 3:每个候选先做只读诊断并自动落质量账;不在这里改正文。 + try: + deai_artifact = run_diagnosis( + candidate["candidateBody"], work_ref=f"work:{WORK_ID}", + chapter_ref=f"chapter:{target}", mode="Audit", + ) + _dump(ARTIFACTS / f"{run_id}-ai-flavor-diagnosis-v{candidate_version}.json", deai_artifact) + persist_diagnosis(deai_artifact, text=candidate["candidateBody"]) + except Exception as exc: + raise PipelineError( + "AI_FLAVOR_DIAGNOSIS_FAILED", "候选 AI 味诊断或落库失败", + details={"errorType": type(exc).__name__, "message": str(exc)}, + ) from exc + print(f"AI 味诊断(v{candidate_version}):发现 {len(deai_artifact['findings'])} 条") return candidate except WriterAdapterError as exc: if exc.code != "candidate_length_out_of_range" or retry == MAX_ATTEMPTS: diff --git a/docs/write-chapter/step2a_retrofit_ch1_context.py b/docs/write-chapter/step2a_retrofit_ch1_context.py index 105be92..648ca35 100644 --- a/docs/write-chapter/step2a_retrofit_ch1_context.py +++ b/docs/write-chapter/step2a_retrofit_ch1_context.py @@ -22,6 +22,7 @@ for sub in ( "write-next-chapter/scripts", "record-run-evidence/scripts", "access-database/scripts", + "prevent-ai-flavor/scripts", ): p = str(SKILLS / sub) if p not in sys.path: @@ -29,6 +30,9 @@ for sub in ( from db import connect, DSN # noqa: E402 from assemble_writer_context import assemble_context # noqa: E402 +from prevent_ai_flavor import ( # noqa: E402 + PreventionContractError, build_prevention_contract, persist_prevention, render_writer_constraints, +) from retrieve_writer_sources import ( # noqa: E402 ProductionCardIndexRepository, FrozenProseRepository, build_retrieval_plan, retrieve_writer_sources, @@ -117,6 +121,12 @@ def main(): authorization_snapshot = {"snapshotId": "auth-work12-production-v1", "allowedPurpose": "production_generation", "verifiedAt": generated_at, "sourceVersion": "v1"} + try: + humanization_contract = build_prevention_contract(f"work:{WORK_ID}", load_database=True) + humanization_contract["writer_constraints"] = render_writer_constraints(humanization_contract) + persist_prevention(humanization_contract) + except PreventionContractError as exc: + raise SystemExit(f"人感前置预防合同失败:{exc}") from exc assembled = assemble_context( run_id=run_id, attempt=1, mode="production", purpose="production", quality_policy_version="writer-production-v1", work_id=WORK_ID, @@ -125,7 +135,7 @@ def main(): retrieval_plan=plan, retrieval_result=retrieval, fine_outline=fine_outline, narrative_state=narrative_state, recent_chapters=[], output_contract=output_contract, token_budget=token_budget, - pattern_references=[], generated_at=generated_at, + pattern_references=[], humanization_contract=humanization_contract, generated_at=generated_at, evidence_strategy="production_dual_evidence") (SCRIPT_DIR / "artifacts").mkdir(exist_ok=True) (SCRIPT_DIR / "artifacts" / f"{run_id}-writer-context.json").write_text(