动作视频/生图背景色链路升级 + 阿里云抠图接入 #75

Merged
kdletters merged 48 commits from feat/video-BGfilter into master 2026-07-10 18:22:17 +08:00
Owner

主要改动

  1. 阿里云通用抠图接入(新增 platform-matting crate)

手搓阿里云 VIAPI 通用抠图接口(ACS3-HMAC-SHA256 签名),跨区输入走临时桶通道。
图片抠图升级为三档降级:BgFilter → 阿里云 → 本地键色兜底。
动作视频逐帧抠图改用阿里云,remove_editor_generated_green_screen_background 降为最后兜底。
2. 背景色不再写死绿色,改多背景色自动决策

动作视频背景色和生图一样从候选调色板自动选,选项继承。
3. 视觉背景色决策(修"蓝撞蓝")

让 LLM 看参考图选背景色(gpt-4o-mini 视觉模型,路由到 VectorEngine 视觉客户端),修复原纯文本盲选。
4. 背景色候选硬过滤器(新增 editor_screen_background_filter)

按参考图前景配色算 Lab 危险质量 + 皮肤专属三判据(ΔE 距离 / 色调投影 / RGB 分离)剔除撞色候选,LLM 只在安全集合里审美选。露肤角色自动收敛到冷区。新增"灰竹绿"候选补齐安全弧绿色段。
5. 源图合成到背景色(修视频背景变白)

透明源图先填成选定实色再发给 Ark,视频背景确定性等于抠图键色,不再赌模型服从提示词。
6. screenColorHex 结构化记录 + 死代码清理

四条链路统一把实际背景色写进 generation_inputs,修复生图链路"抠图背景色"字段从未生效的旧逻辑。
清理前端抠绿 + qwenSprite 死代码,移除一次性探针示例。

主要改动 1. 阿里云通用抠图接入(新增 platform-matting crate) 手搓阿里云 VIAPI 通用抠图接口(ACS3-HMAC-SHA256 签名),跨区输入走临时桶通道。 图片抠图升级为三档降级:BgFilter → 阿里云 → 本地键色兜底。 动作视频逐帧抠图改用阿里云,remove_editor_generated_green_screen_background 降为最后兜底。 2. 背景色不再写死绿色,改多背景色自动决策 动作视频背景色和生图一样从候选调色板自动选,选项继承。 3. 视觉背景色决策(修"蓝撞蓝") 让 LLM 看参考图选背景色(gpt-4o-mini 视觉模型,路由到 VectorEngine 视觉客户端),修复原纯文本盲选。 4. 背景色候选硬过滤器(新增 editor_screen_background_filter) 按参考图前景配色算 Lab 危险质量 + 皮肤专属三判据(ΔE 距离 / 色调投影 / RGB 分离)剔除撞色候选,LLM 只在安全集合里审美选。露肤角色自动收敛到冷区。新增"灰竹绿"候选补齐安全弧绿色段。 5. 源图合成到背景色(修视频背景变白) 透明源图先填成选定实色再发给 Ark,视频背景确定性等于抠图键色,不再赌模型服从提示词。 6. screenColorHex 结构化记录 + 死代码清理 四条链路统一把实际背景色写进 generation_inputs,修复生图链路"抠图背景色"字段从未生效的旧逻辑。 清理前端抠绿 + qwenSprite 死代码,移除一次性探针示例。
lhk229 added 9 commits 2026-07-08 22:49:42 +08:00
- 新增 platform-matting crate:手搓 SegmentCommonImage ACS3 签名调用、
  VIAPI 临时桶上传、分辨率守卫(缩图送抠 + alpha 回贴原图)
- api-server 接入 GENARRATIVE_ALIYUN_MATTING_* 配置与 MattingClient
- 画布生成图片抠图链改为 BgFilter → 阿里云 → 本地键色三档降级

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 动画生成请求新增 screenColor,继承生图入口的 auto 决策行为,
  提示词从写死绿幕改为纯色背景条款(LLM 决策 11 色)
- 后端抽帧抠图改为逐帧阿里云优先(并发 3 保序),单帧失败降级
  本地键色算法(使用决策出的背景色)
- 清理 legacy 纯绿幕常量与提示词

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 删除无调用方的前端像素抠绿链(chromaKey.ts 及
  characterAssetWorkflowModel 中的视频采帧/抠绿函数)
- 删除已被 server-rs 提示词实现取代的 qwenSprite 共享骨架

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
修复动作视频背景色的三类真实事故并加确定性防线:

1. 视觉背景色决策:让 LLM 看参考图选背景色(gpt-4o-mini 视觉模型,
   路由到 VectorEngine 视觉客户端),修复原纯文本盲选导致的"蓝撞蓝"。

2. 背景色候选硬过滤器(新增 editor_screen_background_filter):
   按参考图前景配色算 Lab 危险质量 + 皮肤专属三判据(ΔE 距离、
   色调投影、RGB 分离)剔除撞色候选,LLM 只在安全集合里审美选。
   露肤角色自动收敛到冷区。新增"灰竹绿"候选补齐安全弧绿色段。

3. 源图合成到背景色:透明源图先填成选定实色再发给 Ark,视频背景
   确定性等于抠图键色,不再赌模型服从提示词(修复背景变白)。

4. screenColorHex 结构化记录:四条链路统一把实际背景色写进
   generation_inputs,并修复生图链路"抠图背景色"字段从未生效的旧逻辑。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 视觉模型 gpt-4o-mini → gpt-5-mini。
- gpt-5-mini 是推理模型:视觉决策走 Responses 协议并设 reasoning_effort=low,
  否则默认档会把 token 预算全烧在推理上、返回空答案;纯文本路径仍走 ChatCompletions。
- max_tokens 96 → 768,给 low 档推理(实测 320~384 token)留足余量。

已真机验证:/responses 端点网关可用,暖肤色主体正确选中冷色背景。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
给 gpt-5-mini low 推理档再留余量(实测推理 320~384 token),
降低复杂图触顶后走空响应兜底的概率。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lhk229 requested review from kdletters 2026-07-08 22:50:12 +08:00
lhk229 added 7 commits 2026-07-09 12:33:02 +08:00
# Conflicts:
#	server-rs/crates/api-server/src/config.rs
EditorCharacterAnimationGenerateRequest 新增 screen_color 后,
shared-contracts 的 camelCase 契约测试初始化漏补该字段,导致
`cargo test -p shared-contracts --no-run` 编译失败(P0,阻塞 PR 合入)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BgFilter 目前跑 CPU 推理,单次抠图耗时较长,超时必须 120s(见文档)。
合并 web/master 的「收紧超时保护」时该常量被误解成 45s,改回 120s。
web/master 新增的熔断器配置(失败阈值/冷却)保留不变。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
架构要求外部供应商调用失败必须进 OTLP + tracking_event
external_api_call_failure,但 BgFilter 和阿里云通用抠图失败原来只
warn! 后静默兜底,生产故障在审计链路里不可见(P1)。

- external_api_audit:新增 ExternalApiAuditContext 与统一入口
  record_matting_external_api_failure(复用 record_external_api_failure,
  自动填 status_class/retryable);is_retryable_external_api_failure 去掉
  cfg(test) 门供生产复用,内部 StatusCode 换字面量避开测试专用导入。
- 三处失败点接入:BgFilter 请求失败、生图链路阿里云兜底失败、
  动作视频逐帧阿里云兜底失败(带 frame_index)。
- user/profile/request_id 从各入口 caller / owner+project+request_context
  透传进兜底函数,沿用现有 external_api_audit 上下文映射。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
角色动作抽帧的正式契约已改(screenColor=auto 视觉决策 + 硬过滤 +
源图合成到选定背景色 + 抽帧优先阿里云、失败降级本地键色),但权威
文档仍写着 legacy #00FF00 + 本地 editor_green_screen(P1 文档失同步)。

- 后端数据契约文档、前端图片画布 MVP 文档:更新角色动作抽帧描述到
  新契约;顺带把 BgFilter 默认超时 45000ms 修正为 120000ms(与代码
  一致,CPU 推理必须留足)。
- decision-log 追加 2026-07-09 决策条目记录本次契约变更。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
apply_editor_screen_background_decision_to_generation_inputs 之前会往
generationInputs.fields 追加「抠图背景色」条目,把内部自动决策/兜底细节
暴露给用户,违反「用户可见输入快照不记录抠图背景色/抠图模型」的产品契约(P2)。

- 去掉 fields 注入,只保留顶层内部可追踪元数据 screenColorHex(不进用户快照)。
- 删除因此变成死代码的 format_editor_screen_background_decision_input 及其 import。
- 重写两个单测:断言 fields 不被注入/不被修改,只写 screenColorHex。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
代码审查发现 4 项问题,本次一并处理:

- 抠图输入"上海地域自有 OSS 上传"分支整体删除:我们没有上海地域
  OSS,region marker 判定永远为假,该分支任何环境都不会触发,且其
  matting-input/ 中间对象无清理会造成存储泄漏(若将来启用)。抠图
  输入统一走 VIAPI 官方临时桶(1 天自动过期,天然无泄漏)。
- platform-matting:segment_image_to_transparent_png 去掉恒为 None
  的 accessible_url 参数,简化提交分支;修正"生产建议用上海自有
  OSS"的误导注释。
- editor_green_screen:删除不可达的 #00FF00 绿幕特判分支(legacy
  绿幕色产生点已删、调色板无此色)。
- segment_smoke example:默认参数里的个人路径改为用法提示 + 退出。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lhk229 added 1 commit 2026-07-09 14:31:10 +08:00
lhk229 added 2 commits 2026-07-09 15:39:48 +08:00
保留 BgFilter 上游真实 HTTP 状态与超时标记

保留阿里云抠图上游状态、超时与错误摘要

补充审计分类单测覆盖 429 与 timeout 场景
更新候选色数量为 12 个

同步角色动作多色背景与阿里云优先抠帧说明
lhk229 added 1 commit 2026-07-09 16:36:37 +08:00
区分抠图传输失败与上游 HTTP 状态

补齐 BgFilter 与阿里云抠图失败 latencyMs

同步阿里云抠图配置与角色动作背景色文档口径
lhk229 added 1 commit 2026-07-09 17:16:41 +08:00
补充 VIAPI 临时桶 OSS V1 签名例外

同步 BgFilter 默认超时为 180000ms
lhk229 added 2 commits 2026-07-09 17:31:10 +08:00
response.chunk() 在 HTTP 状态已成功后中断,属于「读 body 时链路断裂」
的传输层故障,但原先只抛 BAD_GATEWAY+message,外部 API 失败审计取不到
transport/timeout/upstreamStatus,回落 Some(502) → statusClass 5xx,
漏掉了 transport 场景。

- 新增 editor_image_removal_body_read_error:timeout 走 504、其余 502,
  统一带 transport/timeout/latencyMs/rawExcerpt,审计正确归类为 transport。
- read_editor_image_removal_response_bytes 加 request_started_at,BgFilter
  与 birefnet 两条读体路径都透传,chunk 失败可算出 latencyMs。
- BgFilter 空 body 补 latencyMs,与 Aliyun 兜底口径对齐。
- 补两个回归测试覆盖 body-read 传输故障与 timeout 分类。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
record_matting_external_api_failure 计算 retryable 时把 connect(transport)
参数硬编码为 false,导致 DNS / 连接重置 / body-read 中断这类 status_code=None、
timeout=false 的传输失败落在 statusClass=transport 却 retryable=false,与
"transport failures actionable" 语义冲突,误导告警 / 重试分析。

- status_code=None 恰好等价于 statusClass=transport,据此派生 has_transport_error
  传入 retryable 计算,无需改签名或调用点。
- 抽出可测的 build_matting_external_api_failure_draft。
- 补回归测试:非超时 transport → transport ∧ retryable=true;上游 4xx → retryable=false。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lhk229 force-pushed feat/video-BGfilter from 6e4e9ea318 to 00ce3a275c 2026-07-09 17:31:10 +08:00 Compare
lhk229 added 1 commit 2026-07-09 17:38:04 +08:00
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
lhk229 added 5 commits 2026-07-09 21:53:24 +08:00
circuit open 时直接 return 本地键色扣除,使同函数后面的阿里云兜底不可达;
BgFilter 一旦连续失败触发 cooldown,整段冷却期从「BgFilter→阿里云→本地」
双供应商降级退化成只剩低质量本地扣色。

- 抽出统一兜底链 fallback_editor_screen_background_removal(阿里云→本地),
  熔断打开与 BgFilter 调用失败两条路径共用:熔断只跳过 BgFilter HTTP 调用本身,
  仍先试阿里云。阿里云失败审计逻辑原样搬入 helper,零行为改动。
- 日志 tag 改为 editor_bgfilter_circuit_open_fallback_to_aliyun_matting,如实反映。
- 结构测试升级为 in-order 断言,钉死「熔断→fallback→请求→fallback」与
  helper 内「阿里云→本地」顺序,防止回退成直接本地扣色。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
四条外部生成端点(生图 / 图标 / UI 素材 / 角色动画)后端都接收
screen_color,但公开 OpenAPI 一个都没声明,且 additionalProperties:false
会挡掉客户端传该字段。三条生图的漂移在 master 已存在,本分支给动画请求
新增该字段后又多一处,一并补齐。

- EditorImageGenerationRequest / EditorIconSpritesheetGenerationRequest /
  EditorUiDesignAssetExtractionRequest / EditorCharacterAnimationGenerationRequest
  各加可选 screenColor(nullable string,不进 required,additionalProperties 保留)。
- 描述如实:传色板 hex 指定,auto/null/省略由服务端自动决策;删掉动画那条
  原描述里"通常继承源角色图背景色"的不实表述(继承未实现)。
- 后端行为零改动,纯契约对齐。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
避免临时 OSS 签名 URL 进入日志和审计 metadata

补充结果下载错误脱敏单测
原实现只按边长缩放,未缩放时直接上传原始 bytes 却固定声明 image/png:
小尺寸大体积 PNG、JPEG/WebP 原图、或异常位深 PNG 可能被上游 4xx 拒绝,
静默降级到低质量本地扣色。按阿里云 SegmentCommonImage 输入限制归一化:

- 格式:统一 to_rgba8 + encode_rgba_png,永远产出 8 位 RGBA PNG,消除 MIME
  名不副实与 8/16/64 位 PNG / 非 PNG 不受支持的问题。
- 大小:MAX_INPUT_BYTES=3MB,编码后超限则等比降尺寸重编码,直到达标。
- 分辨率:最长边 ≤1999 保留;新增每边 >32 的短路,过小图直接 InvalidRequest
  让调用方走本地兜底,省一次注定 4xx 的往返。
- URL:object 叶子名 ASCII 过滤,保证 ImageURL 不含中文/非 ASCII。
- 抽出 normalize_matting_input_png(_within),alpha 上采样按实际上传尺寸是否
  变化判定(覆盖边长缩 + 体积缩两种情况),补 3 个单测。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lhk229 added 1 commit 2026-07-09 22:00:44 +08:00
lhk229 added 1 commit 2026-07-10 10:51:28 +08:00
OSS 签名类错误(如 SignatureDoesNotMatch)会在错误体里回显 StringToSign
(含 x-oss-security-token 明文)、StringToSignBytes(十六进制)和 SignatureProvided。
原实现把该错误体裸拼进 MattingError 消息,随后经 aliyun_matting 进入
external_api_call_failure 审计的 errorMessage/rawExcerpt 与 OTLP 日志,
泄漏 STS 临时凭证。

- 上传非 2xx 分支改用 sanitize_oss_upload_error_body:剥掉 StringToSign /
  StringToSignBytes / SignatureProvided 三个元素,并把已知 security_token
  在正文任何位置的出现替换为 ***,保留 <Code> 供诊断后再记录。
- 新增 redact_xml_element(无正则依赖)与单测覆盖脱敏效果。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lhk229 added 5 commits 2026-07-10 15:20:30 +08:00
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
无源图路径此前继承 state.llm_client()(Ark/豆包),选色能力弱、且线上从未
调用过 VectorEngine。新增独立常量 EDITOR_SCREEN_BACKGROUND_TEXT_LLM_MODEL,
让无图决策也走 VectorEngine gpt-5-mini(Responses 协议 + reasoning=low),与
有图视觉档区分、便于各自调参;gpt5 客户端未配置时才降级回默认文本客户端。
附两个 #[ignore] 真机联调测试,验证有图/无图两条路径真实调用 VectorEngine。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
生图 / 动作生视频 / 图标 spritesheet / UI 提取四条链路此前都在扣费前跑 gpt-5-mini
背景色决策,导致用户余额不足或生成注定失败时仍白发一次决策,平台白付 token。

改为:预扣前只做颜色无关的算价 / 校验 / settings(动画用默认色占位算价,与队列
路径一致),把决策及颜色相关的 prompt / 合成 / generation_inputs 搬进
execute_billable_asset_operation_with_cost 的闭包,闭包把决策结果带出供后续抠图
与落库使用。语义:余额不足则决策不跑(平台零成本);决策失败则闭包返 Err 走失败
退款(用户不损失泥点)。四条 _for_owner 函数同时覆盖 worker / inline / agent /
external-API 所有入口。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
实测 gpt-5-mini(经 VectorEngine /v1/responses)对图片输入远比阿里云通用抠图
宽松:纯色图到 5000×5000、噪声图到 base64 请求体 34MB 仍全部 HTTP 200,App
真实源角色图(≤2048px、几 MB)稳落安全区。据此记入 pitfalls,避免误给带图
背景色决策链路补一套阿里云抠图专属的图片归一化。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
decision-log 新增 2026-07-10 条目,确立「所有用背景色决策的生成链路,决策必须
在余额校验+预扣泥点之后发起」这一长期规则,并记录无图决策改走独立文本档
gpt-5-mini。定价方案文档补一句:背景色决策本身是上游调用,同样在预扣之后发起,
与「预扣失败不得继续调用上游」原则一致。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lhk229 added 1 commit 2026-07-10 15:30:39 +08:00
同步后端架构文档的阿里云优先兜底口径

更新开发运维文档中的BgFilter排障日志名

更新图片画布技术方案的熔断降级说明
lhk229 added 1 commit 2026-07-10 15:47:17 +08:00
lhk229 added 1 commit 2026-07-10 15:57:02 +08:00
response.bytes() 直接分配任意大小响应,作为 BgFilter 降级路径且逐帧动画可并发多次
调用,异常/恶意上游会造成 API 进程内存压力。复用 BgFilter 路径的做法:
- 下载:先按 Content-Length 拒绝,再流式 chunk 累加,超 32MB 立即中断
- 解码:ImageReader + Limits(边长 ≤8192、max_alloc 上限)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lhk229 added 8 commits 2026-07-10 17:22:20 +08:00
代码默认已改 180s(14a5cde9d,适配 2K 图),但 env.example 与 deploy/provision
脚本仍写 45000,会被注入生产 env 覆盖默认值,2K BgFilter 仍可能超时。
- 两份 api-server.env.example: 45000 -> 180000
- deploy/provision 脚本改用 ensure_env_value_migrates_old_default,
  把存量机器的旧默认 45000 迁移到 180000(key 缺失则直接写 180000)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
两处在缩图前直接 image::load_from_memory:背景色过滤的参考图(请求体 data URL)
与阿里云抠图源图。缺省 image::Limits 无尺寸上限、仅 512MiB alloc 兜底,12MB 请求体
即可构造巨幅压缩图在解码阶段撑爆内存,逐帧动画并发时叠加。
改用带 Limits 的 ImageReader(边长 ≤8192、alloc ≤128MiB),与结果解码同口径。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
InvalidConfig/InvalidRequest/Sign 都是发请求前的本地预检失败(未配置、图片解码失败、
尺寸过小、签名构造失败),此时根本没调用阿里云。旧映射统一默认 externalCallAttempted=true、
transport=true,把本地失败写成可重试的 transport 供应商故障审计。
现按 MattingError 变体区分:仅 Upstream 保留原上游失败语义,本地预检标记
externalCallAttempted=false / transport=false,调用方据此跳过外部失败审计。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
aliyun_matting_enabled 默认 true,但 env 模板/校验器从未提供
GENARRATIVE_ALIYUN_MATTING_ACCESS_KEY_*,标准部署里 build_matting_client 因缺 key
返回 None,BgFilter 失败直接本地去背,与三级兜底设计口径不符。
- 两份 api-server.env.example 补 matting 段(enabled/endpoint/AccessKey 占位/timeout)
- check-api-server-env.mjs 增可选告警:enabled 但缺 AccessKey 时提示兜底层被跳过
- 开发运维文档补记该层需配 AccessKey 才生效

密钥与 OSS/SMS/token 一致,只留空占位由运维填,不写进 provision/deploy 脚本。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-2x 与 2026-07-03 两条历史决策仍写「BgFilter 失败/熔断直接本地兜底」,
与当前 BgFilter→阿里云通用抠图→本地 的三级实现冲突。按日志惯例追加 2026-07-10
更正指针,不改写历史正文。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
外部供应商协议归属应留在 platform-* 层。此前 api-server 从中文错误字符串
contains("timeout") / find("HTTP ") 反推超时、HTTP 状态与 transport,违反分层。
- MattingError::Upstream 改为结构化 UpstreamFailure,携带 timeout/transport/
  upstream_status,在错误发生处(reqwest is_timeout、HTTP 状态、响应体不可用)直接捕获
- 新增 external_call_attempted/is_timeout/is_transport/upstream_status 访问器
- 20 处 Upstream 构造点改用 upstream_transport_error/upstream_http_error/
  upstream_response_error,分类语义不变
- aliyun_matting.rs 删除三个字符串解析 helper,直接读结构化分类;审计 JSON 键
  (timeout/transport/upstreamStatus/externalCallAttempted)完全不变,无功能损失
- 分类单测下沉到 platform-matting,BFF 侧改测映射

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
为gpt-5-mini背景色决策记录OTLP和失败事件

透传角色、图标、UI提取和动作生成的审计上下文

补充失败分类测试和后端契约说明
lhk229 added 1 commit 2026-07-10 18:04:32 +08:00
记录segModel仅限内部兼容使用,不作为用户或外部 OpenAPI 字段

补充BgFilter内存与并发约束的排障说明
kdletters approved these changes 2026-07-10 18:20:45 +08:00
kdletters merged commit 5c9b5ef69e into master 2026-07-10 18:22:17 +08:00
kdletters deleted branch feat/video-BGfilter 2026-07-10 18:22:17 +08:00
Sign in to join this conversation.