一张 AI 图片从"看起来不错"走向"可以交付",中间往往隔着十几次微小但关键的判断:产品表冠是否与真实结构一致,人物手指有没有穿模,主视觉能不能同时适配 16:9、4:5 和 9:16,扩图后光线是否连续,放大时材质纹理有没有被模型重新发明。传统文生图工作流擅长从零探索,却不擅长保护已经正确的部分。商业生产真正缺少的不是更多随机候选,而是一套能描述"基于哪张图、修改哪里、执行什么动作、得到哪个版本"的控制系统。

Midjourney 的 Vary、Vary Region、Zoom Out、Pan、Reroll、Upscale 等二次操作,可以被看作一组图像变换算子。每个算子接收父图与上下文,创建一个新的结果节点;节点之间形成 Action Graph,而不是一条不断覆盖文件的直线。customId 则把上游消息中的具体动作与这个图连接起来。它更接近一次交互能力的句柄,不是供业务系统自由拼接的 Prompt,也不应被假定为可逆的 Seed 数据包。
本文从 Action Graph、事件溯源与能力协商的角度,重新解释 Midjourney V6/v6.1 时代成熟起来的二次编辑范式。版本信息需要动态看待:当前产品可能已经提供更新的生成模型,但部分 Vary Region、Pan 等兼容路径仍可能由 V6.1 能力承担;Upscale 的名称、倍率和结果语义也会随模型、入口与任务变化。因此,文中涉及 Latent Inpainting、Outpainting 的部分采用扩散模型通用原理解释,不声称掌握 Midjourney 未公开的模型源码;工程实现则坚持"上游实际返回什么能力,WebUI 才展示什么能力"。
一、控制力的真正单位不是按钮,而是可回放的 Action Graph
单次生成可以写成 (x=G(c,z,v)):文本条件 (c)、随机变量 (z) 与模型版本 (v) 共同决定结果 (x)。问题在于,商业修改通常只改变需求向量中的一个分量。例如客户只要求"把银色表带改为黑色陶瓷",重新执行完整生成却可能同时改变表盘、人物、镜头和背景。只要模型重新采样整幅图,所有已通过审阅的视觉决策就重新暴露在随机性下。
Action Graph 用节点和边重新定义了工作过程。节点保存一张结果图及其生成上下文,边表示对父节点执行的动作:VARY_SUBTLE、VARY_REGION、PAN_RIGHT、ZOOM_1_5X、UPSCALE_SUBTLE 等。父节点保持不可变,一个父图可以派生多个分支。审阅者选择的不是"最后上传的文件",而是图中的某个被批准节点。
#mermaid-svg-jB9jjZXA3XucJmTV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jB9jjZXA3XucJmTV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jB9jjZXA3XucJmTV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jB9jjZXA3XucJmTV .error-icon{fill:#552222;}#mermaid-svg-jB9jjZXA3XucJmTV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jB9jjZXA3XucJmTV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jB9jjZXA3XucJmTV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jB9jjZXA3XucJmTV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jB9jjZXA3XucJmTV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jB9jjZXA3XucJmTV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jB9jjZXA3XucJmTV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jB9jjZXA3XucJmTV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jB9jjZXA3XucJmTV .marker.cross{stroke:#333333;}#mermaid-svg-jB9jjZXA3XucJmTV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jB9jjZXA3XucJmTV p{margin:0;}#mermaid-svg-jB9jjZXA3XucJmTV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jB9jjZXA3XucJmTV .cluster-label text{fill:#333;}#mermaid-svg-jB9jjZXA3XucJmTV .cluster-label span{color:#333;}#mermaid-svg-jB9jjZXA3XucJmTV .cluster-label span p{background-color:transparent;}#mermaid-svg-jB9jjZXA3XucJmTV .label text,#mermaid-svg-jB9jjZXA3XucJmTV span{fill:#333;color:#333;}#mermaid-svg-jB9jjZXA3XucJmTV .node rect,#mermaid-svg-jB9jjZXA3XucJmTV .node circle,#mermaid-svg-jB9jjZXA3XucJmTV .node ellipse,#mermaid-svg-jB9jjZXA3XucJmTV .node polygon,#mermaid-svg-jB9jjZXA3XucJmTV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jB9jjZXA3XucJmTV .rough-node .label text,#mermaid-svg-jB9jjZXA3XucJmTV .node .label text,#mermaid-svg-jB9jjZXA3XucJmTV .image-shape .label,#mermaid-svg-jB9jjZXA3XucJmTV .icon-shape .label{text-anchor:middle;}#mermaid-svg-jB9jjZXA3XucJmTV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jB9jjZXA3XucJmTV .rough-node .label,#mermaid-svg-jB9jjZXA3XucJmTV .node .label,#mermaid-svg-jB9jjZXA3XucJmTV .image-shape .label,#mermaid-svg-jB9jjZXA3XucJmTV .icon-shape .label{text-align:center;}#mermaid-svg-jB9jjZXA3XucJmTV .node.clickable{cursor:pointer;}#mermaid-svg-jB9jjZXA3XucJmTV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jB9jjZXA3XucJmTV .arrowheadPath{fill:#333333;}#mermaid-svg-jB9jjZXA3XucJmTV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jB9jjZXA3XucJmTV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jB9jjZXA3XucJmTV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jB9jjZXA3XucJmTV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jB9jjZXA3XucJmTV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jB9jjZXA3XucJmTV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jB9jjZXA3XucJmTV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jB9jjZXA3XucJmTV .cluster text{fill:#333;}#mermaid-svg-jB9jjZXA3XucJmTV .cluster span{color:#333;}#mermaid-svg-jB9jjZXA3XucJmTV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jB9jjZXA3XucJmTV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jB9jjZXA3XucJmTV rect.text{fill:none;stroke-width:0;}#mermaid-svg-jB9jjZXA3XucJmTV .icon-shape,#mermaid-svg-jB9jjZXA3XucJmTV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jB9jjZXA3XucJmTV .icon-shape p,#mermaid-svg-jB9jjZXA3XucJmTV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jB9jjZXA3XucJmTV .icon-shape .label rect,#mermaid-svg-jB9jjZXA3XucJmTV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jB9jjZXA3XucJmTV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jB9jjZXA3XucJmTV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jB9jjZXA3XucJmTV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Initial Prompt
Contact Sheet
Selected Master
Vary Region: 修正表冠
Vary Subtle: 探索灯光
Pan Up: 标题留白
Zoom Out: 横版母图
Upscale Subtle
Upscale Creative 对照组
9:16 Approved
16:9 Review
这种结构带来四项生产能力。第一是回退:扩图失败时可以回到局部修正后的父节点,不必重新生成初图。第二是并行:同一母版可以同时派生横版、竖版和方图。第三是审计:每次修改都有动作、输入、时间和费用。第四是可比较:系统可以把兄弟节点并排展示,判断哪种 Upscale 或哪次 Mask 更有效。

用变更预算约束生成自由度
每个 Action 都应有"变更预算"。设目标修改区域占比为 (r),允许的语义漂移为 (d),新增画布占比为 (e)。Vary Region 的理想特征是 (r) 小、Mask 外 (d) 低;Zoom Out 的 (e) 较高,但原图主体的 (d) 应受限;Reroll 则允许全局高自由度。团队可以依据缺陷类型选择预算最小的动作,而不是习惯性重掷。
例如,手表表冠缺陷只占画面 2%,优先局部重绘;人物、产品和布光都正确,只缺上方标题区域,优先向上 Pan;整体镜头选错,局部修改面积虽小也无济于事,应回到 Reroll。动作选择的本质是控制被重新采样的信息量。被保护的信息越多,前期审阅成果越不容易丢失。
"精准控制"仍不是像素级绝对不变。生成式 Upscale 可能补充不存在的纹理,局部重绘的编码和解码也可能让 Mask 外产生细微差异。合理验收应把一致性分成主体身份、产品几何、空间布局、色彩光照与像素感知五层。人物面部用特征相似度判断,产品用关键边缘和尺寸比例判断,布局用边界框位移判断,背景用感知距离判断。只有业务关键层保持稳定,Action 才算成功。
Seed 只能帮助复现,不能代替状态
图像 Seed 一致性控制常被过度解释。Seed 是随机过程的重要输入,但相同 Seed 不等于跨版本、跨入口、跨参数获得相同图像;模型权重、默认设置、个性化配置或预处理变化,都可能改变结果。更不能只保存 Seed 而丢失父图、Prompt、版本和动作上下文。Action Graph 的状态快照必须覆盖完整条件,Seed 只是其中一项可选证据。
商业系统应为每个节点生成内容哈希和谱系摘要,例如 sha256(asset) + parentNodeId + actionType + inputHash + capabilityVersion。这样,即使外部图片链接过期,仍能通过本地资产和事件记录还原操作来源。生成式视觉的控制力最终体现为"能否解释和回放",而不是界面上有多少按钮。
二、从 Discord Component 到 customId:把动作句柄当作能力令牌
Discord 消息可以携带组件行和按钮。按钮面向用户展示可读标签,同时携带供交互回调使用的 custom_id。点击发生时,平台结合消息、组件、用户和会话上下文处理请求。对 Web 端集成而言,真正有用的不是模拟用户重新输入命令,而是捕获任务结果上实际存在的组件,把它们翻译为内部 Action。

代码中经常使用 customId 作为字段名,而上游原始结构可能采用 custom_id。无论命名如何,它都应按不透明值处理。业务服务不应该假设字符串某一段是任务 ID、某一段是图片索引,也不应该通过替换字符构造另一个动作。即使当前格式看似可读,上游仍可随时改变编码、校验或过期规则。解析可见标签用于分类,保存完整句柄用于执行,这是两条不同的数据通道。
把 customId 称为能力令牌,是因为它代表"在特定上下文中可以尝试执行某个动作",而不是永久资源地址。它可能依赖来源消息、账号权限、模型版本、任务状态和时间窗口。攻击者若获得有效句柄并绕过归属校验,可能操作不属于自己的任务。因此真实值应加密存储,浏览器只接收内部 actionId,日志只保留不可逆指纹。
json
{
"nodeId": "node_01K8J2",
"parentNodeId": "node_01K8FQ",
"source": {
"provider": "midjourney-adapter",
"messageId": "msg_938472",
"jobId": "job_a91f"
},
"capabilitySnapshot": {
"observedAt": "2026-08-03T04:00:00Z",
"modelRoute": "mj-v6.1-compatible",
"actions": [
{
"actionId": "action_pan_up_01",
"kind": "PAN",
"label": "Pan Up",
"secretRef": "vault://mj-actions/action_pan_up_01",
"fingerprint": "sha256:5ec2..."
}
]
}
}
新图为什么能操作,旧图为什么常常不能
新任务完成时,回调处理器通常能同时获得结果资源和 components。只要此时保存组件快照,WebUI 就能展示 Action。旧图片若只保存了图片 URL、文件或 Prompt,没有保存原始消息与动作句柄,集成系统就不知道上游允许它执行什么。像素文件描述结果内容,却不携带平台授权和交互上下文。
因此,"旧图没有二次按钮"常是数据谱系问题,而非算法绝对不能编辑。恢复时应按证据逐层查找:本地原始回调、消息归档、供应方任务查询接口、平台支持的历史 Job 恢复方式。若上游重新返回了组件,就建立新的能力快照;若没有,就不能猜测 customId。可选择把图片导入支持外部资源的 Editor,或作为参考图启动新任务,但这已是新的工作流,不应伪装成原任务原地续接。
还要区分"平台原生旧图"和"任意上传图片"。某些 Vary Region、Pan 或 Zoom 路径只对特定来源、版本或分辨率开放;网站 Editor 对外部图片的支持范围也可能变化。前端应显示明确的不可用原因,例如"缺少动作快照""当前版本不支持""需要导入 Editor",而不是统一提示"系统错误"。
Action 能力必须形成时间快照
模型与产品会演进。同一个 UPSCALE 在不同版本中可能意味着不同倍率、尺寸和生成自由度;一个任务返回的按钮也可能与另一个任务不同。系统不能只维护一张全局能力表,而应在每个结果节点上保存 capabilitySnapshot。用户看到的是"这张图现在可以做什么",不是产品宣传页上曾经出现过什么。
能力快照至少包含动作原始标签、归一化类型、输入要求、来源版本、观测时间和过期状态。对于未知标签,先保存为 UNKNOWN 并禁用执行;通过适配器规则升级后再开放。这样,上游新增 Make Square、调整 Zoom 或更换 Upscale 名称时,系统不会把未知动作误映射成危险操作。
三、把 Vary Region 视为一份局部变更合同:Mask、隐空间与边界守恒
Vary Region 的价值不只是"能涂一下重画",而是让用户提交一份空间变更合同:Mask 内允许重新生成,Mask 外应尽量保持,边界区域负责让新旧内容自然连接。网站 Editor 中的 Erase 与 Discord 侧 Vary Region 在交互名称上可能不同,但都体现了这种局部约束思想。启用 Remix 后,用户还可以为选区提供更聚焦的新 Prompt。

通用 Latent Inpainting 可用一个简化过程解释。原图 (x) 经编码器得到 (z_0=E(x)),Mask (M) 被缩放到隐空间。生成分支按照新条件去噪,参考分支保留原图在相同噪声阶段的信息,每一步再执行混合:
z_t=M\\odot z_t\^{edit}+(1-M)\\odot z_t\^{source}
当 (M=1) 时允许编辑,当 (M=0) 时倾向保留来源。真实闭源系统是否逐步采用这一公式无法从界面确认,但它准确表达了扩散式局部重绘的通用目标:在编辑自由度和外部一致性之间建立空间门控。
Mask 需要主体区、过渡环和保护区
生产系统可以把 Mask 拆成三层。主体区是必须改变的像素;过渡环包含边缘、阴影、反射和遮挡关系;保护区是不得改变的关键内容。修正手表表冠时,主体区覆盖错误表冠,过渡环包含表壳边缘与金属反射,保护区则包括表盘刻度、Logo 承载面和人物手腕。如果只涂表冠内部,新的金属光线难以接入表壳;若覆盖整个手表,产品几何又可能被模型重构。
Mask 半径不应使用固定像素。可按短边设置膨胀比例 (r_d=\alpha\min(W,H)),羽化宽度 (r_f=\beta\min(W,H))。产品硬边的 (\beta) 应较小,头发、烟雾和织物则需要更宽过渡。系统可以在前端预览主体区与过渡环,并在提交前提示选区过小、触碰保护区或包含过多画面。
局部 Prompt 同样是一份变更合同。初图 Prompt 描述完整场景,局部 Prompt 只需说明新对象和必须延续的属性。修表冠可以写:single black ceramic crown, same watch case geometry, matching rim light, realistic product photography。若把模特、背景、镜头和广告风格全部重复一遍,模型可能重新解释无关区域。需要修改多个缺陷时,逐个执行并分别验收,比一个巨大 Mask 更容易定位失败原因。
用双区域指标判断是否成功
局部重绘至少有两个相反目标:Mask 内变化要足够,Mask 外变化要尽量小。可定义:
S=\\lambda_1 Q_{inside}-\\lambda_2 D_{outside}-\\lambda_3 E_{boundary}-\\lambda_4 D_{identity}
其中 (Q_{inside}) 衡量目标是否完成,(D_{outside}) 是 Mask 外感知漂移,(E_{boundary}) 衡量接缝,(D_{identity}) 衡量人物或产品身份变化。只比较整图相似度会掩盖问题:选区很小,即使完全改错,整图相似度仍可能很高;只看选区又可能忽略模型把人物面部一起改变。
工程上可以组合人工检查、LPIPS、SSIM、特征嵌入和边缘连续性。对产品图,再增加轮廓 IoU、关键尺寸比和 Logo 保护区差异;对人物图,增加面部特征与手部关键点检查。自动指标用于筛查,不应替代最终视觉审核,因为"语义正确但视觉不自然"和"指标相似但产品结构错误"都可能发生。
与 ControlNet、SDXL、IP-Adapter 和 Flux.1 Redux 的关系
| 技术路线 | 核心控制输入 | 适合的问题 | 主要风险 |
|---|---|---|---|
| Midjourney Vary Region | 平台图像、选区、可选 Remix Prompt | 快速局部修补与风格延续 | 参数不可见,依赖 Action 与版本能力 |
| SDXL Inpainting | 原图、Mask、Prompt、Seed、去噪强度 | 私有化、可复现的批量局部修改 | 节点和模型治理复杂 |
| ControlNet Inpaint | Mask 加边缘、深度、姿态等结构条件 | 产品轮廓、人体姿态、建筑线条 | 多条件冲突会产生伪影 |
| IP-Adapter | 参考图特征与文本 | 人物、物体或风格一致性 | 参考权重过强会限制编辑 |
| Flux.1 Redux 类流程 | 图像参考条件 | 变体探索与语义延续 | 实际能力依赖模型和节点实现 |
这些方案可以组合而不是互斥。Midjourney 适合快速建立视觉母版;当任务要求严格产品结构、同一人物批量场景或本地数据隔离时,可以把选中的母版送入 ControlNet、Stable Diffusion XL(SDXL)Inpainting 或 IP-Adapter 管线。系统架构层应把"局部编辑"作为统一能力,把具体模型视为可路由执行器,并用统一评测比较各路线的图生图一致性。这样才能按风险、成本和可复现性选择工具,而不是让所有问题依赖单一按钮。
四、Zoom、Pan 与 Reroll:用几何坐标系理解 Outpainting
Outpainting 与 Inpainting 的区别在于:前者需要生成原画布之外的内容。Zoom Out 向四周扩展上下文,Pan 沿特定方向增加画布,Make Square 将长边保持为基准并补足短边。它们不是普通缩放或裁切,因为新增区域包含模型生成的信息。对于商业版式,外绘承担的是"把已经批准的主体放进新叙事空间"的任务。

设原图画布为 (\Omega=0,W\times0,H),新画布为 (\Omega')。Zoom 系数 (s>1) 时,可以把原图映射到新画布中心的 (1/s) 尺度位置,理论新尺寸近似 (sW\times sH)。新增面积相对原图为 (s^2-1)。因此 1.5x Zoom 会新增约 125% 的画布面积,2x Zoom 新增约 300%。扩展幅度越大,需要模型创造的内容越多,中心语义被重新解释的风险也越高。
Pan 则改变锚点。向右 Pan 时,原图通常保留在新画布左侧,右侧区域成为生成区;向上 Pan 时,原图向下锚定,上方获得新空间。对视觉设计而言,Pan 的方向应服务于视线和排版:人物看向右侧,可向右扩出叙事空间;竖版海报需要标题区,可向上扩展连续背景。若连续左右来回 Pan,模型可能逐轮引入不一致的透视和重复对象。
Outpainting 接缝不是后处理小问题
生成区与原图之间通常需要一条重叠带。它提供纹理频率、光线方向、景深和透视线索。如果生成 Mask 从边界外严格开始,系统看不到足够的过渡信息,容易产生天空色阶断裂、墙面纹理错位或地平线跳变;如果重叠带过宽,已批准内容又会被改写。重叠比例应按场景调整:平滑背景可以较窄,头发、树叶和复杂建筑需要更宽上下文。
扩图完成后要比较保护区。产品主体、人物面部和既有 Logo 区应做差异检查,新增区域则检查重复主体、虚构文字、透视中断与光影冲突。对于产品广告,模型扩出的表带卡扣、传感器或按钮可能并不存在,不能因为画面合理就当作真实结构。高风险产品信息应由原始摄影或三维资产锁定,生成模型只补环境。
Make Square 也应通过能力元数据执行。横图通常在上下补齐为正方形,竖图通常在左右补齐,但具体锚点和参数取决于当前入口。前端不能自己把"Make Square"解释为居中裁切或拉伸。真正的 Action 语义来自上游为这张图返回的按钮与约束。
Reroll 和 Vary 不属于局部编辑
Reroll 允许重新探索全局结果,适合主体、镜头和构图整体不成立的情况。Vary 在父图附近生成候选,适合方向已确定但需要比较表情、光线或细节。Vary Region 只针对明确选区,Zoom/Pan 针对画布,Upscale 针对输出尺寸和细节策略。把这些动作混用,会使修改预算失控。
可以建立简单决策表:全局语义错误选 Reroll;整体正确但希望探索邻近解选 Vary;局部缺陷面积小且结构影响有限选 Vary Region;画幅不足选 Zoom/Pan;分辨率不足选 Upscale。如果缺陷虽然小,却涉及产品核心结构或人物身份,仍应评估回到更早节点,不能只按面积机械判断。
2x、4x 与生成式放大的版本陷阱
Upscale 需要区分传统插值、生成式细节增强和连续倍率编排。当前某些官方路径以 Subtle、Creative 区分保真与创造,两者可能只提供两倍边长;旧版、第三方节点或特定任务可能出现不同选项。WebUI 上写着 4x,不代表所有 Midjourney 任务都原生支持 4x,也不代表结果无损。
如果 4x 实际由两次 2x 组成,Action Graph 应产生两个节点,因为第二次放大会基于第一次生成的新细节继续推断。系统需要分别记录费用、耗时和漂移指标。产品结构、文字和人物面部通常优先 Subtle;概念艺术的织物、岩石和环境纹理可以建立 Creative 对照分支。放大模式应由业务风险决定,而不是由最大倍率决定。
五、多轮精修真正难在状态治理:版本、并发、成本与可解释性
设计师说"回到换表带之前的横版,再把顶部多扩一点",这句话同时引用了父节点、分支、动作顺序和版式。如果系统只有若干图片 URL,它无法可靠执行。多轮精修的首要工程任务是建立不可变资产与事件历史,而不是堆叠更多生成接口。
每个节点应保存原始资产、内容哈希、缩略图、父节点、Prompt 快照、模型路由、Action 输入、能力版本、创建人和审核状态。每条边保存动作类型、内部 Action ID、上游句柄引用、提交时间、完成时间、费用和错误分类。结果图片必须下载到受控资产存储,外部 CDN 地址只能作为来源,不能作为永久主键。
状态机要能抵抗异步世界
每个 Task 都由内部任务 ID、上游 Job 和父节点共同定位。Action 任务可以采用:DRAFT -> READY -> SUBMITTED -> QUEUED -> RUNNING -> SUCCEEDED | FAILED | EXPIRED | CANCELLED。Vary Region 在 READY 前还可能经历 WAITING_MASK 与 WAITING_PROMPT。状态转换需要单调约束,迟到的 RUNNING 回调不能覆盖 SUCCEEDED;同一上游事件重复送达时,事件消费者必须去重。
用户双击、页面重试和网关超时都可能造成重复提交。系统应根据 tenantId + parentNodeId + actionId + inputHash 建立幂等键。第一次调用创建 Job,后续相同调用返回已有 Job。不同 Mask 或不同 Prompt 的请求可以成为兄弟分支,而不是互相覆盖。这样既避免重复费用,也保留了探索自由度。
并发编辑还需要分支语义。两名设计师可以分别从同一母版创建横版与竖版,但不能都把自己的结果写成唯一"当前图片"。批准状态应独立于生成状态:节点生成成功不等于通过审核。最终交付通过 approvedNodeId 或发布清单引用具体节点,任何未批准分支都不能悄悄覆盖母版。
成本应该归因到成功交付
只统计调用次数会鼓励无效生成。更有意义的指标包括:首次候选通过率、每个批准资产的 Action 数、Vary Region 首次成功率、Mask 外漂移率、扩图接缝失败率、P50/P95 任务时延、重试放大系数、人工评审分钟数和单位交付成本。
例如一个活动最终交付三种版式,经历 12 次初图、6 次局部重绘、4 次扩图和 3 次放大。系统不应报告"生成 25 张,效率很高",而应报告"每个批准版式平均消耗 8.3 个任务,其中局部重绘重试占 32% 成本"。如果表冠修复长期失败,应引入产品结构控制或人工合成,而不是继续投入 Reroll。
能力版本比模型名称更重要
生产记录不能只写"Midjourney V6"。同一模型在网站、Discord、不同分辨率和不同时间点上可能返回不同 Action。应保存可验证的能力事实,例如 supportsVaryRegion=true、upscaleModes=[subtle,creative]、panDirections=[up,down,left,right],并附来源任务和观测时间。模型名称用于解释,能力快照用于执行。
当能力漂移时,旧节点仍按历史快照展示,但执行前需要重新验证句柄状态;新节点则采用新规则。这样可以避免一次适配器升级让历史按钮全部改变含义。可解释性的核心不是宣称系统永远不变,而是准确记录"当时观察到了什么、现在还能不能执行"。
六、WebUI Action 编译器:从上游组件到安全的可视化工作台
可靠的 WebUI 不应把供应方组件原样透传给浏览器。更合适的设计是 Action 编译器:输入上游消息组件、任务上下文和适配器规则,输出内部标准动作;浏览器只看到内部动作和表单约束,真实 customId 留在服务端密钥存储。部署在 https://178.nz/yinc 的前端状态映射节点可以用于渲染本次任务实际返回的放大、变体、局部重绘、Zoom、Pan 与 Reroll 操作,但它不承担句柄保管和上游授权。
Provider Adapter Queue Event Store Action API WebUI Designer Provider Adapter Queue Event Store Action API WebUI Designer #mermaid-svg-ZYiOIpWm7r1B85uT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZYiOIpWm7r1B85uT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZYiOIpWm7r1B85uT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZYiOIpWm7r1B85uT .error-icon{fill:#552222;}#mermaid-svg-ZYiOIpWm7r1B85uT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZYiOIpWm7r1B85uT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZYiOIpWm7r1B85uT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZYiOIpWm7r1B85uT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZYiOIpWm7r1B85uT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZYiOIpWm7r1B85uT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZYiOIpWm7r1B85uT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZYiOIpWm7r1B85uT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZYiOIpWm7r1B85uT .marker.cross{stroke:#333333;}#mermaid-svg-ZYiOIpWm7r1B85uT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZYiOIpWm7r1B85uT p{margin:0;}#mermaid-svg-ZYiOIpWm7r1B85uT .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZYiOIpWm7r1B85uT text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ZYiOIpWm7r1B85uT .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ZYiOIpWm7r1B85uT .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ZYiOIpWm7r1B85uT .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ZYiOIpWm7r1B85uT .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ZYiOIpWm7r1B85uT #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ZYiOIpWm7r1B85uT .sequenceNumber{fill:white;}#mermaid-svg-ZYiOIpWm7r1B85uT #sequencenumber{fill:#333;}#mermaid-svg-ZYiOIpWm7r1B85uT #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ZYiOIpWm7r1B85uT .messageText{fill:#333;stroke:none;}#mermaid-svg-ZYiOIpWm7r1B85uT .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZYiOIpWm7r1B85uT .labelText,#mermaid-svg-ZYiOIpWm7r1B85uT .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ZYiOIpWm7r1B85uT .loopText,#mermaid-svg-ZYiOIpWm7r1B85uT .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ZYiOIpWm7r1B85uT .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ZYiOIpWm7r1B85uT .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ZYiOIpWm7r1B85uT .noteText,#mermaid-svg-ZYiOIpWm7r1B85uT .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ZYiOIpWm7r1B85uT .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZYiOIpWm7r1B85uT .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZYiOIpWm7r1B85uT .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZYiOIpWm7r1B85uT .actorPopupMenu{position:absolute;}#mermaid-svg-ZYiOIpWm7r1B85uT .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ZYiOIpWm7r1B85uT .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZYiOIpWm7r1B85uT .actor-man circle,#mermaid-svg-ZYiOIpWm7r1B85uT line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ZYiOIpWm7r1B85uT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 选择 Action 与 MaskactionId + input + idempotencyKey校验租户、父节点、能力快照ACTION_REQUESTEDenqueue internalJobId解密 opaque customIdprogress/result eventsWebSocket/SSE projection新节点与可用 Action
标准动作契约
标准层只归一化稳定语义,不丢弃供应方原始信息。以下 TypeScript 结构把前端视图和服务端秘密分开:
ts
type ActionKind =
| "VARY"
| "VARY_REGION"
| "ZOOM"
| "PAN"
| "REROLL"
| "UPSCALE"
| "UNKNOWN";
interface ActionCapability {
actionId: string;
nodeId: string;
kind: ActionKind;
label: string;
enabled: boolean;
requirements: {
mask?: boolean;
editablePrompt?: boolean;
direction?: "UP" | "DOWN" | "LEFT" | "RIGHT";
scale?: number;
};
observedAt: string;
ruleVersion: string;
}
interface ActionCredential {
actionId: string;
tenantId: string;
provider: string;
sourceMessageId: string;
encryptedCustomId: string;
fingerprint: string;
expiresAt?: string;
}
编译器先验证组件结构,再根据 provider + route + ruleVersion 做映射。标签相同不代表动作相同,标签不同也可能属于同一核心类型。无法确认的动作归入 UNKNOWN,默认禁用并生成告警;不能通过字符串中是否含有 U1、V2 等片段猜测行为。对 Zoom 1.5x/2x、Custom、Make Square 和 Upscale 2x/4x,都应以当前组件和明确参数为准。
API、事件与投影
执行接口可以设计为 POST /v1/nodes/{nodeId}/actions/{actionId}:execute。请求体只包含 Mask 资产 ID、局部 Prompt、方向或倍率等标准输入,并携带 Idempotency-Key。API 校验节点归属、Action 是否属于该节点、状态是否可执行、输入是否满足 Schema,然后写入 ACTION_REQUESTED 事件并返回内部 Job。
适配器异步提交上游交互,回调统一转化为 JOB_QUEUED、JOB_PROGRESS、ASSET_RECEIVED、CAPABILITIES_OBSERVED、JOB_FAILED 等事件。投影器从事件构建前端状态;投影损坏时可以重放,WebSocket 断开时可以从游标续传。这样,UI 的"处理中 62%"与"可继续 Pan"都来自事件证据,而不是前端猜测。
事件去重优先使用上游事件 ID;若上游没有稳定事件 ID,可组合消息 ID、状态、资产哈希和时间窗。资源收到后必须完成下载、哈希、格式验证、安全扫描和缩略图生成,才能把节点标记为 SUCCEEDED。只有远程 URL 而没有受控资产,不应进入可交付状态。
安全、权限与审计
浏览器永远不接触真实 customId、账号令牌或会话凭证。Action API 必须同时校验租户、项目、节点和操作人权限,防止通过枚举内部 ID 操作他人任务。Mask 上传要限制 MIME、像素数、文件体积与解码资源;回调端点要验证来源或签名并设置重放窗口;日志只记录 fingerprint、动作类型、状态和耗时。
服务还要遵守平台条款与授权边界。Midjourney API WebUI 封装不应把非公开、非授权的交互方式包装成"官方 API"。适配器层需要可替换,以便在授权渠道、产品入口或条款变化时切换实现。系统错误分类应区分鉴权失败、句柄过期、能力不兼容、限流、回调丢失、资产下载失败和内容审核失败,因为只有限流等短暂错误适合自动重试。

前端交互不是把所有按钮排成一行
工作台应根据创作阶段组织 Action。候选评审区提供 Reroll 与 Vary;局部工具进入画布模式,显示 Mask、羽化和保护区;版式工具用方向箭头、比例预览和安全线呈现 Pan/Zoom;导出阶段才显示 Upscale。每次执行前展示父节点缩略图、动作摘要和预计影响范围,完成后自动进入父子对比,而不是把用户丢回图片列表。
对于不可用动作,前端应说明原因:能力快照不存在、句柄已过期、当前节点不是平台原生资源、分辨率不支持或等待上一步完成。一个安静但准确的禁用状态,比点击后返回模糊错误更能降低操作成本。
七、闭环实战:用 Action Graph 制作智能手表多渠道发布视觉
本次实战目标不是生成一张"未来感手表图",而是交付同一产品、同一人物、同一灯光体系下的三套素材:1:1 电商主图、4:5 信息流海报和 9:16 竖屏开屏。产品硬约束包括圆形黑色表盘、单一右侧表冠、黑色陶瓷表带和无虚构文字;视觉约束包括冷白轮廓光、深灰背景与真实皮肤。

第一步:生成结构正确的母版
初始 Prompt 聚焦产品结构与人物动作:
text
premium smartwatch campaign, close-up of an athlete checking a round black
smartwatch on the left wrist, one crown on the right side of the watch case,
black ceramic strap, accurate product proportions, cool white rim lighting,
dark graphite studio background, realistic skin and fabric, clean composition,
no extra watch, no letters, no logo, commercial product photography --ar 1:1
候选评审先看结构,再看美感。检查表盘是否为圆形、表冠数量和方向是否正确、表带是否连贯、手腕是否自然、产品是否被遮挡。只有结构通过的候选才能成为 MASTER_SQUARE_01。如果四张图都把表冠生成在错误位置,应修正 Prompt 或 Reroll,而不是选一张光影漂亮的错误产品继续投入。
保存母版时同步保存资产哈希、Prompt、模型路由、消息 ID 和能力快照。系统为其显示实际可用的 Vary、Vary Region、Zoom、Pan 与 Upscale,而不是从全局菜单添加不存在的功能。
第二步:用 Vary Region 修正表冠
假设母版的表冠形状过大。Mask 主体区覆盖表冠,过渡环包含相邻表壳和高光,保护区覆盖表盘边缘与刻度。局部 Prompt 使用 small single black ceramic crown, flush with the same watch case, physically accurate reflection。不要把整块手表全部擦除,否则模型可能重新设计表盘。
结果验收分成 Mask 内和 Mask 外。Mask 内检查表冠大小、数量、位置和反射;边界检查金属与陶瓷过渡;Mask 外检查表盘、表带、手腕和人物是否变化。通过的节点命名为 PRODUCT_FIX_01,未通过的兄弟节点保留失败原因,例如 extra_crown、case_deformed 或 identity_drift,为后续统计提供数据。
第三步:清理手腕与表带连接
从 PRODUCT_FIX_01 创建第二次局部编辑,只修复表带穿入皮肤的问题。Mask 覆盖连接点、少量皮肤和表带边缘,Prompt 使用 watch strap resting naturally above the wrist, clear separation, realistic contact shadow。把两个缺陷拆开处理,可以避免修好表冠后又被一次大选区破坏。
若连续两次局部重绘仍无法保持产品结构,应停止在该分支上消耗预算,切换到带结构条件的 SDXL Inpainting 或 ControlNet 流程,或者用真实产品渲染进行合成。Action Graph 允许保留已经正确的人物和背景,同时把高风险产品区域交给更可控的工具。
第四步:创建三个版式分支
1:1 母版直接进入轻量构图检查。4:5 分支可以向上 Pan,增加标题和卖点安全区;9:16 分支先向上、再根据人物位置少量向下或横向扩展。每个方向只执行一次并评审,不做左右来回的无目的 Pan。
扩图 Prompt 使用 continuous dark graphite studio background, same rim light, clean negative space, no new objects, no text。验收新增区域是否出现第二只手表、随机文字、异常肢体或光线断层,同时比较原产品保护区。标题安全线只存在于 WebUI 叠加层,不进入生成资产。
4:5、9:16 和 1:1 是从同一通过节点派生的兄弟分支。不要先把方图连续扩成竖图,再把竖图扩成横图,因为每一轮生成都会累积误差。需要 16:9 时,应从 PRODUCT_FIX_01 独立执行 Zoom 或水平 Pan。
第五步:建立 Upscale 对照实验
产品交付优先建立 Subtle 分支,尽量保持表冠、表盘边缘和表带结构。Creative 只作为对照,用于观察织物、皮肤与背景纹理是否更自然。系统以父子对比显示 100% 局部图,并计算产品轮廓、人物特征和保护区差异。
如果节点只返回两倍放大,就只执行两倍;如果某个集成节点提供 4x,需要确认是原生动作还是连续两次 2x。连续放大时,每一步都建立节点并验收,不能只保存最终图。真实品牌 Logo、界面文字和健康数据最后由确定性设计工具添加,避免生成式 Upscale 改写字符。
第六步:审核、发布与复盘
最终发布清单明确引用三个批准节点,例如 APPROVED_1X1、APPROVED_4X5、APPROVED_9X16。每个节点关联无字母版、带字成稿、色彩空间、目标像素、审核人和生成谱系。设计师可以从任一交付稿回到表冠修正节点,查看扩图和放大的全部分支。
复盘不统计"生成了多少张漂亮图片",而统计每个交付版式的有效成本。记录初图候选数、局部编辑次数、扩图重试、放大模式、失败分类和人工分钟数。如果 9:16 分支频繁生成多余对象,应优化扩图 Prompt 与保护区;如果 Creative Upscale 经常改变产品轮廓,就在产品类别中默认隐藏该模式。
这套流程的价值在于把经验转化为规则。下一次制作耳机、相机或运动鞋广告时,可以复用"结构母版、局部修复、独立版式分支、保真放大、确定性排版"的管线,而不是依赖某位设计师记住几十次点击顺序。
八、从 Visual UI 走向约束画布:二次编辑的下一阶段
Midjourney Action 的长期意义,不是让 Command Line 旁边多出一排按钮,而是把生成式图像改造成有状态、可分支、可回退的编辑过程。customId 提供一次具体动作的上游句柄,Action Graph 保存结果之间的因果关系,能力快照说明某个节点实际支持什么,事件状态机则确保异步执行可以被追踪和恢复。
下一代 Visual UI 会更接近约束画布。用户不只涂抹 Mask,还会锁定人物身份、产品轮廓、透视线、光源方向、品牌色和文字安全区。系统把这些约束编译为 Inpainting、Outpainting、ControlNet、参考图适配或传统合成步骤,并按任务风险选择执行器。Prompt 仍然重要,但不再是唯一控制接口。
Canvas 还会改变协作方式。设计师标注空间意图,产品经理批准结构与卖点区域,品牌团队锁定资产,系统记录每次动作与审核。模型版本变化时,团队可以选择只重放某个分支,而不是重新生成全部素材。质量评估也从单张审美分数转向变更合同:该改的是否改对,不该改的是否保持,新增区域是否符合业务事实。
真正成熟的 AIGC 视觉生产线不会承诺随机性消失,而是把随机性关进可管理的边界。探索阶段允许 Reroll 和 Vary 提供多样性,精修阶段用 Mask、Pan 和能力快照收紧自由度,交付阶段用保真放大、确定性文字和审核清单锁定结果。每一步都有父节点、证据、成本和退路,生成式图像才能从一次性的视觉惊喜,变成可以稳定复用的工程资产。