Merge branch 'master' into fix/agc-canvas-json
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 6m49s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 7m3s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m21s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m56s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 6m50s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m1s
Project CI / Frontend tests (pull_request) Successful in 4m38s
Project CI / Repository checks (pull_request) Successful in 3m43s
Project CI / Backend tests (pull_request) Successful in 8m30s
Project CI / AI game creator shell web tests (pull_request) Successful in 4m7s
Project CI / Native shell tests (pull_request) Successful in 25m34s

This commit is contained in:
2026-09-17 13:05:33 +08:00
25 changed files with 790 additions and 67 deletions
@@ -151,7 +151,7 @@ Authorization: Bearer <internal-token>
- 当前部署只有一个配置内私有 OSS bucket,因此请求只传 `sourceObjectKey`,子 worker 从自身 OSS 配置取 bucket 并生成短期签名 URL。
- 如果未来确实支持多个 bucket,新增字段也必须由服务端 allowlist 校验;不能接受调用方提供任意下载 URL。
- `backgroundMode` 只允许 `flat / complex``segModel` 继续沿用当前 `birefnet / anime-seg` allowlistcomplex 固定使用当前参数组合。
- `screenColor` 只对 flat 必填;complex 不得误接 flat 参数,两种模式的熔断状态必须隔离
- `screenColor` flat 下可省略,也可传 `auto``#RRGGBB`;省略或 `auto` 由 BgFilter 自动识别。complex 不得携带背景色,两种模式的熔断状态必须隔离。生成角色、图集等既有链路继续传已确定的背景色
- `maxQueueWaitMs``callBudgetMs` 都是相对预算,不是跨机器绝对时间。前者从 admission 起约束排队阶段(worker 还会用 §5.2 的动态估计对其取 min);后者从取得 provider permit 起计时,覆盖签名、两次 attempt、结果校验和响应构造。`callBudgetMs` 是父侧按 `N / est` 公式算出的“配置指纹”,仅作核对:worker 始终以自己按同一公式派生的值执行,不一致时不拒绝请求,而是记录 warn 日志并递增漂移指标。发布调优 N / est 时新旧进程共存的瞬态漂移因此不会误伤在途任务;持久性漂移的硬拦截由部署脚本的共享 env 对齐校验承担。
- JSON body 设置很小的固定上限;源图字节不进入该 JSON。
@@ -0,0 +1,104 @@
# AGC 抠图模式与背景色透传方案
## 目标
主站编辑器保持现有前端行为(继续使用 `complex`),同时扩展 External v1 抠图接口和 AGC 客户端,使客户端可以选择 `complex` / `flat`,并把 `screenColor` 原样交给 BgFilter。`complex` 用语义分割识别前景,`flat` 用于纯色背景抠图;确定背景为纯色时优先使用 `flat``auto` 的背景色识别完全由下游服务负责,主站不读取图片、不调用模型决策颜色、不生成颜色兜底值。
## 当前 BgFilter 契约
已登录生产服务器核对 `/root/BGfilter`,当前代码版本为 `f1a0833`,运行进程为 `python -m uvicorn app:app --host 0.0.0.0 --port 6006 --workers 1 --no-access-log`。服务契约为 `POST /remove-background` multipart
- `background_mode`:可选,`flat``complex`
- `screen_color`:可选,支持 `#RRGGBB``auto` 或省略;省略/`auto` 时由 BgFilter 从图片边框自动检测;
- `complex` 模式忽略 `screen_color`
- 自动检测失败由 BgFilter 返回 400。
- 若配置 `BGFILTER_AUTH_TOKEN`,必须发送 `X-Genarrative-Image-Token`;缺失或错误返回 401;未配置时该接口不在服务层做 token 校验。
主站向 BgFilter 发送 `#RRGGBB` 时保留 `#``auto` 也原样发送,不做所谓的“hex 转换”。
服务器行为:`screen_color``auto` 或省略时由服务自动检测;`#RRGGBB` 用作指定背景色。`background_mode` 缺省在 BgFilter 侧为 `flat`,因此主站必须为 External v1 旧请求显式归一化为 `complex`,不能把下游服务的默认值直接当成主站默认值。
## External v1 请求契约
接口保持:
```text
POST /api/external/v1/editor/images/background-removals
```
新增可选字段:
| 字段 | 取值 | 缺省/行为 |
| --- | --- | --- |
| `backgroundMode` | `complex``flat` | 不填按 `complex`,保证旧客户端兼容 |
| `screenColor` | `auto``#RRGGBB` | 不填则不向 BgFilter 发送该字段 |
组合规则:
1. 不传新增字段:按 `complex` 执行。
2. `complex` 不允许传 `screenColor`,返回 400。
3. `flat` 可以传具体 `#RRGGBB``auto`,也可以省略颜色。
4. 模式和颜色严格按原值校验;非法值、空字符串、前后空格和大写 `AUTO` / `FLAT` 返回 400。十六进制颜色的字母允许大小写。
5. 主站只做格式和组合校验;`auto` 不在主站解析,直接转发给 BgFilter。
6. HTTP 请求中的 `null` 视同省略;模式省略时提供非 null 颜色同样违反 complex 约束。客户端 MCP 可选参数应省略,不传 null。
格式或组合错误在入队前返回 400;BgFilter 自动检测失败发生在异步执行阶段,任务通过既有失败状态收口,不把已接受的 202 改成同步 400,不启动其他抠图方式兜底。
OpenAPI 与客户端工具的对外说明只描述模式用途、参数约束和调用方可见的结果;provider 选择与兜底策略保留在内部技术方案中,不写入对外 description。
主站前端继续不传新增字段,因此用户行为不变。
## AGC 客户端改动
`agc_remove_background` 增加可选参数:
```json
{
"sourceLocalAssetId": "...",
"assetName": "...",
"backgroundMode": "flat",
"screenColor": "auto"
}
```
客户端保留旧参数调用;新字段不填时不改变旧调用语义。客户端不读取图片、不自动选色、不把 `auto` 改写为具体颜色,使用原有 Bearer 认证、幂等键和队列返回模型。
工具 schema、桥接参数校验和随包 `agc-client-projection` Skill/契约说明必须保持一致。模式与颜色属于请求意图,必须参与客户端幂等指纹;同一图片与名称的不同模式不能复用同一次请求。缺省 complex 且没有颜色时保留既有指纹。主站在默认值归一化之前计算 External 请求指纹,缺失的新字段不序列化,避免旧请求重放发生冲突。
## 实施任务
### 任务一:冻结 BgFilter 契约
记录服务器已支持的模式、颜色格式、自动检测和错误行为。不得把 SSH 地址、服务器凭据写入客户端或公开契约。
### 任务二:更新主站 DTO 与 OpenAPI
为 External v1 和内部任务 DTO 增加可选字段,更新 `docs/openapi/genarrative-external-v1.openapi.json`,写明默认值、组合约束和 400 响应。
### 任务三:更新主站归一化与队列
缺省模式归一化为 `complex``complex + screenColor` 拒绝;`flat` 允许颜色、省略或 `auto`。队列保存字段,worker 始终发送 `background_mode`,仅在调用方提供颜色时发送 `screen_color`,值原样透传。
### 任务四:更新 AGC 客户端
增加参数 schema、请求体字段和本地校验,更新 Skill、projection contract 与测试。旧客户端请求必须继续有效。
### 任务五:联调与验收
覆盖旧请求、`flat + auto``flat + #RRGGBB``flat` 不传颜色、`complex``complex + screenColor` 和非法值;使用真实 BgFilter 验证 multipart 字段及自动检测错误传播。
## 依赖、发布与回滚
先发布兼容的新主站,再发布支持新参数的 AGC 客户端。主站前端无需发布改动。若联调失败,客户端可回退为只传旧字段,主站仍按 `complex` 处理;主站回滚时不改变旧字段语义。
## 验收证据
2026-09-16 实测:
- 主站 `cargo test -p api-server background_removal`:36 项通过,覆盖非法请求入队前拒绝、缺省 complex、队列参数保留、旧请求指纹、父侧内部 RPC 和 provider multipart。
- `cargo test -p api-server bgfilter`:52 项通过,包括 flat 的 auto/省略/具体颜色以及既有生成链路。
- `exported_openapi_json_contains_external_editor_routes_and_security` 契约测试通过。
- 客户端 `agent::direct_tools_mcp::tests` 18 项、`agent::skill_pack::tests` 4 项与抠图幂等指纹测试通过;Skill manifest 内容指纹已同步。主站与客户端 rustfmt、文档索引、编码及 diff 检查通过。
- 真实 BgFilter(版本 `f1a0833`):使用进程内凭据串行请求 `flat + auto`、flat 省略颜色、`flat + #CFEFFF`、complex;四组均返回 200、512×512 RGBA PNGalpha 范围均为 0255。
- 无纯色背景的随机噪声图片使用 flat + auto 返回 400,确认自动识别失败要求调用方提供颜色。测试没有修改服务器代码或配置。
- 本地 `npm run dev:api-server` 已尝试,但当前配置指向的 SpacetimeDB 不可连接,服务停留在启动恢复重试,`/healthz` 未通过;已结束本次启动。完整登录客户端 → 主站持久化队列 → 结果回写的运行时验收尚未完成,不能用真实 BgFilter 的独立测试代替。没有部署本次主站或客户端代码。