14 KiB
Prompt Optimize 与生成链路经验总结
更新日期:2026-04-19
这轮暴露出的高价值规律
- 显式选择必须压过隐式上下文
当用户已经明确选择了模型时,不能再偷偷继承旧的 provider/profile。
否则 UI 显示是 A,实际请求却跑到 B,最后问题会表现成“模型名被改写”“请求走错适配器”“参数形态不对”。
落地原则:
- 只要有显式
modelId + modelRef,请求路由就以它为准 - 只有在显式选择缺失时,才允许回退到 preset / legacy route
modelId和modelRef必须一起校验,不能允许旧 ref 残留
- 草稿态只能覆盖“它真的有值”的部分
这轮“重新生成视频没带参考图”本质上不是弹窗没透传,而是空草稿把默认值吃掉了。
draft 存在,不代表 draft.images 就应该覆盖系统默认参考图。
落地原则:
- 草稿字段要按“非空覆盖”处理,而不是“对象存在就整体覆盖”
- 对数组字段尤其要区分:
undefined、[]、[有效值] - 默认参考图这类系统推导值,应该作为二级回退保留
建议判断顺序:
- 非空草稿值
- 当前页面可推导默认值
- 最终兜底空值
- 嵌套弹窗默认挂顶层,不挂父弹窗内容区
Prompt Optimize 的 open 状态已经变成 true,但用户看不到,根因是它被 portal 到父弹窗内部,吃到了父容器的 overflow: hidden 和层叠上下文。
落地原则:
- 二级弹窗、确认框、轻量配置弹窗默认挂到
document.body - 只有在明确需要“跟随父容器裁剪/定位”时,才挂到父节点
- 如果日志显示 open=true 但肉眼不可见,优先查 portal root / overflow / z-index,而不是先怀疑状态管理
- 真正值钱的修复是收敛判断规则,不是追加特判
这轮几个问题表面不同:
- 模型名串 provider
- 视频重生不带参考图
- 优化弹窗 open 了但看不见
本质都属于“状态来源优先级不清”。
如果只在单点加 if,短期能过,但后面会在别的入口复发。
更稳的做法是统一规则:
- 显式输入优先于隐式上下文
- 非空用户态优先于系统默认态
- 顶层浮层优先于局部裁剪容器
后续开发检查清单
每次新增生成入口、草稿恢复、弹窗嵌套时,先过这 5 个问题:
- 当前请求到底由哪个
modelId / modelRef驱动? - 有没有旧 ref、旧 provider 被复用?
- 草稿字段为空时,是否错误覆盖了默认值?
- 二级弹窗挂载到了哪里?父容器是否会裁剪?
- 这次修复是在补规则,还是在补特判?
结论
这轮最该记住的不是某一个 bug,而是三条优先级:
- 显式选择 > 隐式路由
- 非空草稿 > 系统默认
- 顶层 portal > 父容器挂载
后面继续做生成链路和弹窗能力时,只要这三条不乱,很多“看起来离谱”的问题会直接消失。
2026-04-26:提示词优化弹窗 WinBox 化与控制区收敛
这轮把“提示词优化”从普通 Dialog 改为 WinBoxWindow 承载,并连续调整了内容布局、底部控制条和历史入口。暴露出的经验不是“换一个弹窗组件”这么简单,而是浮动窗口里表单、控制区、历史数据的边界要更清楚。
1. WinBox 只负责窗口能力,业务内容必须保持 React 管理
第三方窗口库容易让内容挂载、关闭回调和 React 状态脱节。项目里已有 WinBoxWindow 封装,业务组件应该复用它,而不是直接 new WinBox。
落地原则:
- 标题、关闭、拖拽、缩放交给
WinBoxWindow - 表单内容仍由 React 渲染到 WinBox body portal
- 关闭时继续走原有
handleClose,保证 abort、草稿、模式状态被清理 open=false时不挂载窗口内容,避免输入栏常驻时提前加载 WinBox 和历史面板
2. 弹窗内容区和底部控制区要分层
这轮多次调整后,比较稳定的结构是:
- 内容区:只放当前提示词、补充要求、优化结果
- 底部控制区:放输出模式、模型选择、主操作按钮
- WinBox 标题栏:只放窗口标题和窗口控制按钮
不要把模型选择、说明文字、取消按钮都塞进内容区。内容区越纯,textarea 的自适应高度越容易做,滚动条也越少。
3. 主输入框吃剩余高度,辅助输入框固定高度
弹窗中间出现大片空白时,不应该靠缩小窗口或写死两个 textarea 的高度解决。更稳的是让主输入框所在 section 变成 flex 容器里的可伸缩项。
落地原则:
- “当前提示词”是主输入,
flex: 1,textarea 跟随剩余高度撑开 - “补充要求”是辅助输入,固定一个稳定高度
- 有优化结果时再切成左右分栏,结果草稿 textarea 吃右侧剩余高度
- 控制区固定在底部,不参与内容区滚动
4. 模型选择优先复用 AI 输入框的紧凑展示
提示词优化弹窗里的模型选择不是配置表单,而是生成前的轻量控制。它应该使用 AI 输入框同款的 minimal 展示,而不是大号 form 下拉。
落地原则:
- 使用
ModelDropdown的 minimal 形态:来源图标、#、短模型名、健康状态、下箭头 - 输出模式和模型选择放在同一控制组
- 主按钮保持最右,用户视线和点击路径更稳定
- WinBox 右上角已有关闭按钮时,不再重复放“取消”按钮
5. 历史入口要区分数据语义
“当前提示词”可以复用全局提示词历史,因为它本质上仍是可生成的 prompt。“补充要求”则是优化指令,不应该混进全局提示词历史。
落地原则:
- 当前提示词历史:复用
usePromptHistory - 补充要求历史:使用独立 key,小容量、去重、按时间倒序
- 只在点击“开始优化”时写历史,不在输入过程中持续写
- 历史展示要有上限,避免长列表导致弹窗卡顿
- localStorage 读写必须
try/catch,失败不能影响优化主流程
6. 回归检查清单
后续再改提示词优化弹窗时,先检查:
- WinBox 关闭是否仍会 abort 正在进行的优化请求?
open=false时是否避免不必要挂载?- 当前提示词 textarea 是否吃掉内容区剩余高度?
- 输出模式、模型选择、生成按钮是否仍在底部控制区?
- 当前提示词历史和补充要求历史是否仍然分开?
- 历史列表是否有限制,是否避免每次输入都写存储?
- 目标单测和 drawnix typecheck 是否通过?
2026-04-26:内置提示词预览瘦身与 tips 侧向展示
这轮优化提示词预览时,核心问题不是“预览样式不好看”,而是内置提示词把大量图片和视频样片放进 public,导致构建和部署都要携带这些静态资源。内置提示词的价值应该来自文本本身,预览只应该来自用户真实生成过的结果。
1. 内置提示词不应该绑定重媒体预览
apps/web/public/prompt-examples 里的内置图片、视频会直接进入 Web 静态资源体积,尤其视频样片会显著放大产物。默认提示词越多,资源目录越容易变成不可见的构建负担。
落地原则:
- 内置提示词只维护文本、分类和基础描述
- 不再通过数组下标或静态路径给内置提示词补预览
- 预览图、视频封面、poster 只来自用户生成历史
- 删除内置预览资源时同步删除引用逻辑和测试假设
2. 预览来源要表达数据语义
提示词列表里“有预览”应该表示用户已经生成过可复用结果,而不是系统塞了一个演示样片。否则用户会误以为内置提示词自带可用素材,也会让测试绑定到静态资源路径。
落地原则:
- 用户历史记录有
imageUrl / previewUrl / posterSrc时才生成previewExamples - 内置提示词没有生成结果时展示文本 tips
- 单测校验“用户生成结果才有预览”的语义,不校验某个静态资源路径
- 不用 index 映射补默认预览,避免提示词删减或排序后串图
3. 内置提示词要少而实用
提示词入口不是素材库,内置项太多会增加选择成本。默认列表应该覆盖高频生成场景,让用户一眼能改、能用、能出结果。
落地原则:
- 每类保留少量高质量提示词
- 文案尽量包含主体、风格、画面/输出约束
- 删除泛泛而谈、只适合演示、不容易直接复用的项
- 后续新增内置提示词时优先问:它是否比用户自己输入更省时间?
4. tips 应该在列表右侧展示
没有预览时,tips 如果覆盖在列表内部,会挡住后续选项,尤其在长列表或窄面板里更明显。提示信息应该辅助选择,而不是抢占选择路径。
落地原则:
- 无预览的提示词 tips 默认从列表项右侧弹出
- tips 使用稳定宽度和换行,避免撑开列表项
- hover 只改变浮层,不改变列表布局高度
- 面板宽度不足时再考虑边界兜底,而不是回到覆盖列表内容
5. 回归检查清单
后续再改提示词预览和内置提示词时,先检查:
- Web 静态资源目录是否新增了非必要图片或视频?
- 内置提示词是否又和静态预览路径产生绑定?
- 用户生成历史是否仍能展示 image / video / poster 预览?
- 删除或重排内置提示词后,是否不会出现串图?
- 无预览 tips 是否展示在列表右侧,且不挡住其他选项?
- 目标单测、drawnix typecheck、
git diff --check是否通过?
2026-04-26:提示词优化公共能力与知识库模板化
这轮把散落在 AI 输入框、图片/视频工具、PPT、爆款音乐里的“提示词优化”收敛成公共服务和公共按钮。真正的经验不是多抽一个组件,而是“同一个动作在不同业务场景里有不同优化语义”,公共能力必须同时保留统一调用方式和场景差异。
1. 公共能力要收敛入口,不要抹平场景
提示词优化看起来都是一个 Sparkles 按钮,但图片、视频、音频、Agent、PPT 公共提示词、PPT 单页提示词、音乐创作的优化方向完全不同。如果只用 type=image/video/audio 这类粗粒度类型,后续会把历史、提示词、埋点和默认模式都搅在一起。
落地原则:
- 使用稳定的
scenarioId表达入口语义,例如ai-input.agent、ppt.common、music.create-song type表达优化模型语义,historyType表达历史分桶,二者不能强行合并- PPT 公共提示词可以是视觉优化语义,但历史必须进入
ppt-common - 公共按钮只负责打开弹窗和回填,场景差异放到 registry/service
2. 可编辑系统提示词要放知识库正文,不放 metadata
提示词优化模板是用户会长期编辑的内容,不应该塞进代码常量或列表 metadata。列表元数据适合 title、directoryId、sourceUrl、tags;模板正文应该走知识库正文存储,避免列表加载时把大文本一起拉出来。
落地原则:
- 模板笔记通过
sourceUrl=aitu://prompt-optimization/<scenarioId>绑定,避免只靠标题匹配 - 用户非空正文优先,内置模板只兜底
- 目录或笔记缺失时自动恢复,空正文回填默认模板
- 知识库打开时补齐全部默认模板,不能只在用户首次点击某个场景时懒创建一篇
- 不把大提示词放进
KBNoteMeta.metadata,避免知识库列表和搜索出现不必要内存压力
3. 模板变量不能成为唯一输入来源
用户可能误删 {{originalPrompt}} 或 {{requirements}},如果服务完全依赖模板变量,优化请求会丢掉真正输入。模板可编辑,但原始提示词和补充要求必须由系统固定追加,保证请求总有权威输入块。
落地原则:
- 支持模板变量:
{{originalPrompt}}、{{requirements}}、{{language}}、{{mode}}、{{scenarioName}} - 渲染模板后始终追加固定输入块
- 固定输入块里明确“原始提示词”“补充要求”“输出要求”
- 单测覆盖“自定义模板不含变量时仍包含原始输入”
4. React 组件里避免 HMR 半更新暴露裸变量
这轮出现过 scenarioId is not defined。源码已解构 props,但浏览器热更新时可能加载到半旧变换结果。对跨多入口的公共弹窗,props 字段最好从 props 显式取值,避免 refactor 时出现裸变量引用残留。
落地原则:
- 公共弹窗 props 多、兼容旧调用时,优先
props入参后显式取字段 - 新增可选 props 时跑真实入口,不只跑单测
- HMR 出现变量未定义时,先确认浏览器加载的变换版本,再用更稳的写法消除裸变量
- 兼容旧调用时,
scenarioId可选,type仍作为 fallback
5. 结果区不要做双层胶囊
优化结果草稿本质是一个可编辑 textarea,不需要再包一层带边框和底色的结果卡。双层边框会让用户误以为有两个可编辑区域,也会让左右输入框上沿不齐。
落地原则:
- 结果区外层只负责布局,不再加边框、底色和内边距
- textarea 自身承担可编辑边界
- “回填”属于提交当前编辑成果,放在“开始优化”右侧;没有优化结果草稿时也可回填当前提示词
- “用结果继续优化”才属于结果操作,继续保留在底部结果操作区,避免挤占结果标题行
6. 回归检查清单
后续再改提示词优化公共能力时,先检查:
- 新入口是否有独立
scenarioId,而不是复用过粗的type type、historyType、defaultMode是否按场景分开配置- 知识库「提示词优化」目录打开后是否能看到全部默认模板
- 用户改过的非空模板是否不会被覆盖
- 空模板、缺失模板、缺失目录是否能自动恢复
- 模板删除变量后,最终请求是否仍包含原始提示词和补充要求
- AI 输入框、图片/视频工具、PPT 公共/单页、音乐创作入口是否都能打开公共按钮并回填
- 结果区是否只有 textarea 自身边框,左右输入框上沿是否对齐
- 目标单测、OpenSpec validate、
git diff --check是否通过?