fix(prompt): 写死动作不再丢弃用户写的动作细节 - #839
Conversation
前端把用户的一句自由文本拆成两半发过来:action_type 选哪条已调好的管线, custom_prompt 说这次具体要什么。后端只读了前一半 —— 后一半进了 CharacterCard.desc,而视频路线不读 desc,于是用户写的东西一个字都没进提示词。 任务照常成功、照常扣费、帧数时长成色全对,唯一能察觉的方式是看产物。 生产 124/124 条非 custom 任务全中。 ActionSpec 新增 detail,叠在模板之上(模板定运动拓扑,细节说这次要什么), 插在构图约束之前以保住构图约束是收口句这条不变量。 lint 新增 on_template:subthreshold 与 unanchored_prop 两条的机制都要求 "这句话是画面里唯一的运动",叠在模板上时不成立,照查会把"走路时手臂轻微摆动" 判成弱指令。其余各条(否定式/特效名词/装备形状先验/体型不匹配)与叠加无关,照查。 拿 124 条真实提示词回放:放开前 47% 会被拒,按机制分流后 19%, 剩下的全是 negation(19) 与 hazard_noun(5) —— 这两条本来就该拒。 Refs 1024XEngineer#838
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
| return f"{body} {REFERENCE_FIDELITY_LOCK} {SINGLE_SUBJECT_FRAMING}" | ||
| clause = detail.strip() | ||
| head = f"{body} {clause}" if clause else body | ||
| return f"{head} {REFERENCE_FIDELITY_LOCK} {SINGLE_SUBJECT_FRAMING}" |
There was a problem hiding this comment.
[P2] Bump the persisted prompt version
with_framing now changes every walk, jump, idle, and attack prompt by inserting the user detail clause, but windup_ai_engine.prompt.PROMPT_VERSION remains v2. CharacterGenerator persists that constant in every GeneratedAction, so outputs created before and after this PR will carry the same version even though their prompts and generation behavior differ; this defeats the documented prompt-version ledger and prevents reliable quality comparisons or migration decisions. Increment PROMPT_VERSION in the same change.
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## main #839 +/- ##
=======================================
Coverage 86.47% 86.47%
=======================================
Files 297 297
Lines 22355 22379 +24
Branches 3402 3402
=======================================
+ Hits 19331 19352 +21
- Misses 2074 2076 +2
- Partials 950 951 +1
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 1 file with indirect coverage changes 🚀 New features to boost your workflow:
|
Closes #838
问题
前端把用户的一句自由文本拆成两半发过来:
action_type选哪条已调好的管线,custom_prompt说这次具体要什么(quick-start planner 的提示词原文:"让生成复用已有优化管线" + "必须把动作单独写入 actionPrompt")。后端只读了前一半。后一半进了
CharacterCard.desc—— 而视频路线不读 desc,那条注释自己写着「角色身份由母版图像承载,不读 name / desc」。于是用户写的东西一个字都没进提示词,任务照常成功、照常扣费、帧数时长成色全对。生产 124/124 条非 custom 任务全中:
去重后 walk 有 39 种、idle 有 15 种不同文本——这些是用户真写的,不是前端套话。
用户报障的那条(#564)就是这么来的:史莱姆角色 + "炸开分裂成小史莱姆",实际发出去的是 thrust 模板「弓步蓄力、腰侧收招、直线突刺、收回架式」,模型照着画了个金发人形武者。不是模型幻觉,是我们发错了提示词。
改了什么
ActionSpec.detail—— 用户那句细节,叠在模板之上。与custom_action分工:那个是整个动作的内容(custom 时没有模板),这个是对模板动作的补充说明。两个都填会炸。叠加而不是替换。 模板定运动拓扑(走路要腿交替、跳跃要腾空),那正是前端把这句话分类到这个
action_type的理由;替换掉等于把这次生成降级成 custom。位置由两条约束夹定:插在模板正文之后、构图约束之前。
_framing那份注释把SINGLE_SUBJECT_FRAMING定成收口句,提示词适配器的契约靠它确认公共约束没被绕过 —— 贴在它后面就是悄悄废掉那道检查。lint(on_template=...)—— 这是本 PR 里唯一一处放宽判据,理由是机制不适用:subthresholdunanchored_propprops and not _hits(text,"body"),而模板正是那句 bodynegationhazard_nounshape_priorstance_mismatch部署影响(先看这段再决定合不合)
拿生产那 124 条真实提示词回放了一遍:
那 24 条今天是静默成功的(描述被丢掉、出一段通用动画),合了之后会变成 4xx 并告诉用户怎么改。方向上这是对的(下一步就是付费调用),但它是用户可见的行为变化 —— 举个真实例子:
"角色进行走动动作…仅参考人物本身,不参考或添加其他角色"会被 negation 拒掉,理由是这条通路没有 negative_prompt、否定句里的名词反而会被 latch 进画面。如果不想一次到位,我可以再提一个改动:细节句命中 negation / hazard 时只丢细节、照旧出模板动画,同时给用户回一条 warning。但那还是"用户的话被丢了",只是不再是静默的。 我倾向直接拒。
验证
_detail_clause恒返回空串(= 回到静默丢弃)→ 6 红ruff check .通过;export_openapirc=0 且 openapi.json 无漂移;lint-imports2 kept / 0 broken;pytest -q --cov=packages1895 passed, 14 skipped。两处遗留(另开,不在本 PR)
stance从没被采集过。windup_character.character_data96/96 个角色只有outfits/version/templates;input_payload558/558 没传stance。所以stance_mismatch这道门禁在生产中永远不触发 —— 本 PR 让细节句走了这道门,但在前端采集 stance 之前它是不可达的,别拿它当"非人形角色已修好"。build_attack_prompt缺省THRUST对非人形角色是错的。 它的注释只回避了「断言持械」(提示词库把某个原型角色的装备写死成默认值,导致所有角色被加上斗篷与武器(Refs #171) #195),没回避「断言人形」:弓步、后脚承重、腰侧收招、直线突刺、收回架式,一个史莱姆没有这些部位。要治根得先有 ①。