feat(prompt): 用户描述先过措辞门禁与适配器 - #321
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
| # 不往提示词里加任何部位词、也不要求用户改写措辞。反过来把默认设成非双足,会让占多数的 | ||
| # 人形角色被要求把"手臂"改写成"前肢 / 尾",那些词进提示词就是让模型凭空长出对应部位。 | ||
| # 不自动识别:那要调模型,而认错是静默的 —— 错误的体型只在下一次付费生成的画面上显形。 | ||
| stance: CharacterStance = CharacterStance.BIPED |
There was a problem hiding this comment.
[P1] 将角色体型从生产请求传入 CharacterCard
当前唯一的生产构造点 ActionTaskExecutor._produce_action 仍只传 name/desc,而 API、CharacterActionInput 和持久化的 CharacterData 也没有 stance 来源,因此所有真实请求都会落到这里的 BIPED 默认值。结果是四足/蛇形角色在生产链路中永远不会触发本 PR 新增的 STANCE_MISMATCH,像“raises the left arm”仍会进入付费调用并生成错误肢体。请把显式体型字段贯穿请求/角色数据到 CharacterCard,或在启用该规则前提供另一个可靠来源。
| _NEGATION = ( | ||
| "no", "not", "never", "without", "avoid", "avoids", "avoiding", | ||
| "don't", "doesn't", "isn't", "aren't", "won't", "cannot", | ||
| "不要", "不能", "不许", "禁止", "避免", "勿", |
There was a problem hiding this comment.
[P2] 覆盖常见中文和英文否定式
否定门禁只列了少数完整短语,常见输入如 向前走,不转身 和 can't turn around 当前都会得到空 issue 并继续进入模型;这正是本规则要阻止的“否定极性丢失后名词/动作被 latch”路径。请补齐常见否定形式,尤其是中文 不+动词 与英文 can't/弯引号 contraction,并添加这些实际句式的回归用例。
| "then", "after that", "afterwards", "followed by", "next,", "and finally", | ||
| "然后", "接着", "紧接着", "之后", "再", "最后", "先", "收势", | ||
| ) | ||
| _ARM_WORDS = ("arm", "arms", "elbow", "hand", "hands", "手臂", "胳膊", "手肘") |
There was a problem hiding this comment.
[P2] 识别中文“手”类体型冲突措辞
非双足门禁覆盖了 手臂/胳膊/手肘,却漏掉更常见的 手/左手/双手;例如四足角色描述 举起左手 不会命中 _ARM_WORDS,会直接进入付费 i2v,而英文等价句会被拒绝。请用能覆盖这些中文组合的匹配规则,并避免简单加入单字后对无关词造成误报。
|
|
||
| def __init__(self, code: PromptRejectCode, detail: str) -> None: | ||
| super().__init__(f"这段描述跑不出可用产物({code.value}):{detail}") | ||
| self.code = code |
There was a problem hiding this comment.
[P2] 让 PromptRejectCode 穿过异步任务边界
这里虽然保存了结构化 code,但生产调用方 run_action_task 用宽泛的 except Exception 捕获后只持久化 str(exc),GenerationTaskOut 也只有 error_message。因此 server 实际无法按 code 选文案或区分可修改的 4xx 输入错误与引擎 5xx,除非重新解析异常字符串,正好违背这个端口的设计目的。请在任务失败结果中单独序列化拒绝码(或专门捕获 PromptRejected 并完成映射)。
Refs #309
改了什么
用户那句动作描述以前是原样进提示词骨架的,现在先过一道措辞门禁 + 一层零模型的适配器,拦在付费调用之前。
prompt/lint.py:纯函数措辞门禁,只依赖标准库。八类规则,每类对应一条模型侧机制(否定式 / 特效名词 / 装备形状先验 / 冲击词 / 亚阈值微动 / 持物无身体锚 / 装备名词),报错按机制写而不是"命中禁词"——收到它的人要据此改自己的描述。prompt/adapter.py:规则版适配器RuleBasedPromptAdapter,零模型、零成本、确定性。跑门禁、error 级当场拒、把那句话嵌进已验证的骨架、追加单主体与构图后缀。ports/__init__.py:AdaptedPrompt/PromptAdapterPort/PromptRejected/PromptRejectCode。拒绝理由带 code,server 据此选文案,不用去 parse 异常消息。models/character.py:CharacterStance枚举 +CharacterCard.stance,默认biped。prompt/custom.py:抽出build_custom_body(不含循环性尾句),两条尾句转公开。strategy/concrete.py:CUSTOM 分支接适配器。为什么
#309 的根因是动作描述在一条路径上有适配、在另一条路径上直传,而两条路径对入参的语义要求不同。这个 PR 先把"适配"这件事做成一个有协议、可测、可复用的组件,并在视频路线的 custom 分支上接通。
几条判定的机制:
negative_prompt,模型不处理否定极性、只把名词 latch 进画面,"不要 X" 等于点名要 X。所以本 PR 走的是加正向约束(单主体 + 整身入画 + 纯色背景)而不是补负向词。kind="still"时多阶段标记直接拒,并强制追加"单一冻结瞬间"。同一句话对kind="i2v"是合法的,不能一刀切。stance参与判定;每个非双足成员在适配器里都有自己那套部位说法,否则拒绝理由只能说"不行"、给不出改法。两条工程约束:
except PromptRejected: raise排在宽泛的except之前。它是ValueError子类,放后面永远轮不上,那样一段必然出坏产物的描述会被"回退到旧骨架"接住,照样把钱花掉。PromptRejected的任何异常)只丢掉那层改写,回退到原有build_custom_prompt,不打死整条生成线。ActionSpec.cyclic),适配器入参里没有,替调用方猜一条会把一次性动作首尾闭环,而帧数时长全对、看不出来。怎么验证的
backend/tests/test_prompt_adapter.py,30 个用例,全部离线零成本:PromptRejectCode都可达、都带机制说明;两条规则同时命中时两条都列出来CharacterCard.stance确实传到了适配器;四足角色写"手臂"端到端被拒、双足同一句放行本地
backend/下:uv run ruff check .→ All checks passeduv run lint-imports→ 2 kept, 0 brokenuv run pytest -q→ 731 passed, 14 skipped范围说明
只含后端。
ports/__init__.py、models/character.py、strategy/concrete.py这几个文件同期还有别的改动(成色台账字段、攻击原型分支),那些不在本 PR 里。