Import Human Voice Rewrite Demo (PRD v1.1) + Dockerfile for Coolify

This commit is contained in:
Jeamee
2026-08-24 16:49:57 +08:00
commit d6f70b02f4
16 changed files with 3580 additions and 0 deletions
+228
View File
@@ -0,0 +1,228 @@
"""E2E stub server: deterministic LLM payloads per workflow stage.
Injects a dispatcher into main_mod.get_client so the real FastAPI app,
pydantic validation, evidence sanitization and the frontend run unchanged.
Run on the same port the real server uses (index.html fetches /api/* same-origin).
"""
import uvicorn
import main as main_mod
ANALYZE = {
"essay_summary": "文章以一支静止的铅笔开头,描述作者遇到一个无法立即解决的问题,并从中学会了耐心,最后回到书桌前继续面对。",
"prompt_alignment": {"prompt_intent": "", "current_alignment": "", "optional_opportunity": ""},
"paragraphs": [
{
"id": "p1",
"original_text": "The pencil in my hand was still. I wanted to find the answer.",
"natural_meaning_zh": "我手里的铅笔静止不动,我想要找到答案。",
"semantic_anchor": "铅笔静止暗示思考停滞,内心渴望答案。",
"optional_content_opportunity": "",
},
{
"id": "p2",
"original_text": "I met a problem that I could not solve right away.",
"natural_meaning_zh": "我遇到了一个无法立刻解决的问题。",
"semantic_anchor": "明确点出问题的存在,且无法立即解决。",
"optional_content_opportunity": "",
},
{
"id": "p3",
"original_text": "I learned to be patient.",
"natural_meaning_zh": "我学会了耐心。",
"semantic_anchor": "核心教训是耐心,但未说明如何学到。",
"optional_content_opportunity": "",
},
{
"id": "p4",
"original_text": "Now, I sit at my desk again.",
"natural_meaning_zh": "现在,我再次坐在书桌前。",
"semantic_anchor": "回到起点,暗示重新面对问题,但未说明结果。",
"optional_content_opportunity": "",
},
],
}
DIAGNOSE = {
"overall_diagnosis": "文章极短,但每一段都呈现典型的生成式写作模式:抽象陈述、缺乏具体细节、结构过于简化和对称。",
"patterns": [
{
"pattern_id": "P02",
"name": "Explicit Lesson",
"category": "growth",
"affected_paragraphs": ["p3"],
"evidence": ["I learned to be patient."],
"why_ai_like": "总结式结尾是生成模型最顺手的收束方式。",
"human_impact": "直接说教,读者看不到具体如何学会。",
"transformation_rule": "Lesson -> Change in Judgment",
},
{
"pattern_id": "P08",
"name": "Circular Ending",
"category": "structure",
"affected_paragraphs": ["p4"],
"evidence": ["Now, I sit at my desk again."],
"why_ai_like": "首尾呼应结构容易讨巧。",
"human_impact": "只剩结构对称,没有新信息。",
"transformation_rule": "Close on a Concrete Action",
},
],
"paragraph_briefs": [
{
"paragraph_id": "p1",
"confirmed_meaning": "铅笔静止暗示思考停滞,内心渴望答案。",
"rewrite_goal": "把静止的铅笔转化为具体的场景与内心活动。",
"ai_focus": "抽象陈述;缺乏具体细节",
"must_preserve": [],
"annotations": [
{
"pattern_id": "P01",
"category": "rhetoric",
"kind_label": "修辞包装",
"title": "象征开头",
"evidence": ["The pencil in my hand was still."],
"observation": "用静止铅笔暗示停滞,但没给任何具体场景。",
"rewrite_action": "补一个真实瞬间:笔停在纸上的位置、当时我在做什么。",
"why_ai_like": "象征开头是生成模型高频起笔方式。",
}
],
"context_hint": "上一段已完成 0 个反射,这一段只需建立画面。",
"scaffold": "The pencil stayed in my hand because ______.",
"reference_snippet": "The pencil had stopped halfway across the page.",
},
{
"paragraph_id": "p2",
"confirmed_meaning": "明确点出问题的存在,且无法立即解决。",
"rewrite_goal": "把问题具体化,写出具体内容和无法解决的原因。",
"ai_focus": "抽象陈述",
"must_preserve": [],
"annotations": [
{
"pattern_id": "P01",
"category": "rhetoric",
"kind_label": "修辞包装",
"title": "泛化问题",
"evidence": ["a problem that I could not solve"],
"observation": "问题本身是空洞的泛指。",
"rewrite_action": "写出问题具体是什么。",
"why_ai_like": "泛化名词让生成模型无需真实素材。",
}
],
"context_hint": "上一段已建立画面,这一段写具体困境。",
"scaffold": "The problem was ______.",
"reference_snippet": "",
},
{
"paragraph_id": "p3",
"confirmed_meaning": "核心教训是耐心,但未说明如何学到。",
"rewrite_goal": "把耐心转化为具体行为,并保留 secret code。",
"ai_focus": "总结式说教",
"must_preserve": ["secret code"],
"annotations": [
{
"pattern_id": "P02",
"category": "growth",
"kind_label": "抽象总结",
"title": "直接点题",
"evidence": ["I learned to be patient."],
"observation": "用一句总结构束教训。",
"rewrite_action": "写出耐心在行为上如何体现,保留 secret code。",
"why_ai_like": "教训总结是生成模型收束习惯。",
}
],
"context_hint": "上一段已写具体困境,这一段写困境中的一次具体选择。",
"scaffold": "Each day I ______ instead of ______.",
"reference_snippet": "",
},
{
"paragraph_id": "p4",
"confirmed_meaning": "回到起点,暗示重新面对问题,但未说明结果。",
"rewrite_goal": "把回到起点转化为具体行动,暗示改变但不总结。",
"ai_focus": "结构对称",
"must_preserve": [],
"annotations": [
{
"pattern_id": "P08",
"category": "structure",
"kind_label": "结构路标",
"title": "首尾呼应",
"evidence": ["Now, I sit at my desk again."],
"observation": "用呼应结构收尾,没有新内容。",
"rewrite_action": "写一个具体的下一步动作。",
"why_ai_like": "首尾对称让文章看起来完整。",
}
],
"context_hint": "上一段已写具体选择,这一段以行动收尾,不写总结。",
"scaffold": "This time, I ______.",
"reference_snippet": "",
},
],
"optional_suggestion": "若有真实素材,可补当时问题发生的具体场景。",
}
RECHECK_PASS = {
"status": "pass",
"checked_rewrite_version": "rv_1",
"global_checks": {
"semantic_preservation": "pass",
"voice_consistency": "pass",
"pattern_reduction": "pass",
"new_pattern": "pass",
"coherence": "pass",
"reference_copying": "pass",
"word_limit": "pass",
},
"revision_targets": [],
}
SCAFFOLD = {"paragraph_id": "p1", "scaffold": "The pencil stayed in my hand because ______."}
REFERENCE = {"paragraph_id": "p1", "starter": "The pencil had stopped halfway across the page.", "reference_snippet": "The pencil had stopped halfway across the page."}
RECHECK_REVISION = {
"status": "revision_required",
"checked_rewrite_version": "rv_1",
"global_checks": {
"semantic_preservation": "pass",
"voice_consistency": "pass",
"pattern_reduction": "pass",
"new_pattern": "pass",
"coherence": "pass",
"reference_copying": "pass",
"word_limit": "pass",
},
"revision_targets": [
{
"paragraph_id": "p3",
"blocking_issue": "新版本仍是 Explicit Lesson:直接总结学到了什么。",
"evidence": ["gradually came to realize"],
"single_revision_goal": "不要总结我学到了什么,写判断怎么变化。",
}
],
}
class StubCompleter:
def complete_json(self, system, user, validate=None):
# NOTE: system prompts share a _TASK_CHAIN prefix (contains 全文复检 etc.)
# so dispatch must use stage-unique keywords, most specific first.
if "给我一个写作起点" in system:
return SCAFFOLD
if "Level 2" in system:
return REFERENCE
if "Human Voice Diagnosis" in system:
return DIAGNOSE
if "最终阶段" in system:
# mockFillFail injects revision-case wording; detect it to serve the
# revision_required path, otherwise the golden-path pass response.
# NOTE: cannot use "gradually came to realize" — previous_revision_target
# is embedded in the prompt (with its evidence quoting that phrase), so it
# matches on every resubmission even when the current text is fixed.
if "uncertainty is an important part of growth" in user:
return RECHECK_REVISION
return RECHECK_PASS
return ANALYZE
main_mod.get_client = lambda: StubCompleter()
if __name__ == "__main__":
uvicorn.run(main_mod.app, host="127.0.0.1", port=8000, log_level="warning")