package com.nanri.aiimage.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.ArrayList; import java.util.List; @Data @ConfigurationProperties(prefix = "aiimage.similar-asin") public class SimilarAsinProperties { private String cozeBaseUrl = "https://api.coze.cn"; private String cozeWorkflowPath = "/v1/workflow/run"; private String cozeWorkflowHistoryPath = "/v1/workflows/{workflow_id}/run_histories/{execute_id}"; private String cozeWorkflowId = "7635328462404583478"; private String cozeToken = ""; private List cozeCredentials = new ArrayList<>(); private int cozeCredentialStripeSize = 0; /** * P0-1:单次提交 Coze 工作流的 row 数量。 * 历史值 10,在含 puzzle 多图行的场景下频繁触发 720712008 * "node executed out of limit: 1000"。降到 3 以避免节点上限被打爆。 * 出现持续 720712008 时还会被 P1-1 滑窗自适应再降到 1。 * 不影响 AppearancePatentProperties 的同名值。 */ private int cozeBatchSize = 3; /** * img_switch=false 时单次提交 Coze 的 row 数。 * 不走图片检测时工作流压力小,恢复到 10 行一批以提高吞吐;开启图片检测时仍使用 cozeBatchSize。 */ private int cozeTextOnlyBatchSize = 10; private int cozeConnectTimeoutMillis = 10000; private int cozeReadTimeoutMillis = 60000; private int cozePollIntervalMillis = 30000; private int cozePollTimeoutMillis = 1800000; private int staleTimeoutMinutes = 30; private String staleFinalizeCron = "0 */2 * * * *"; /** * 同一 credential 两次提交之间的最小间隔(毫秒)。 * 历史值硬编码 30000(持锁 sleep),导致单凭证仅 2 batch/分钟。 * 几千行任务场景下成为提交吞吐瓶颈,下调到 5000ms 并改为锁外冷却。 * 出现 Coze 限流加重时可通过 AIIMAGE_SIMILAR_ASIN_COZE_SUBMIT_MIN_INTERVAL_MILLIS 调高。 */ private long cozeSubmitMinIntervalMillis = 5000L; /** * 末尾零头 batch 的强制 flush 阈值(分钟):当不足 cozeBatchSize 的零头 row * 长时间挂着(Python 慢回传)时触发提交。 * 任务级实测:345 行 / 4h 总耗时中,约 2-3 小时是 batch 永远凑不满 batchSize 在等下一波回传, * 把阈值从 15 调到 1:最多 60s 后 1-2 行也强制提交,让 Coze 提交侧持续进票, * 总耗时降到与 Python 回传节奏接近。配合 cozeBatchSize=3、cozeSubmitMinIntervalMillis=5000, * 实际不会触发 Coze 限流。出现限流加重再调回 5/10。 */ private int cozeFlushPendingMinutes = 1; /** * 同 batch retry + split retry 共享的最大重试次数。原硬编码 5。 */ private int cozeSubmitMaxRetryCount = 5; /** * 图片嵌入下载线程池大小。原 SimilarAsinImageEmbedder.DOWNLOAD_POOL_SIZE = 8。 * P2-10:1000+ 行 ×3 列图片场景下,pool=16 仍是 assemble 阶段瓶颈(实测下载 244s/918s), * 提到 32 配合 retry=2、global deadline 显著拉低尾延迟; * 受 2GB 堆约束,单图缩略图维持 300KB 以内,整体内存峰值 ≈ 32 * 300KB ≈ 10MB。 */ private int imageDownloadPoolSize = 32; /** * 单张图片下载超时(秒)。 * P2-10:放宽到 8 + retry=1 在快源(aiproxy/m.media-amazon)下没问题, * 但慢源(cbu01.alicdn)会一直挂 8s 才进入 retry,整体串行时间放大。 * 调到 5s + retry=2,让慢源更早重试新连接,单图最坏耗时 ≈ 5s * (1+2) = 15s。 */ private int imageDownloadTimeoutSeconds = 5; /** * assemble 阶段 taskImageCache 的字节上限。 * 默认 256MB:5000 行 × 3 列 × 平均 100KB = 1.5GB 远超 2GB 堆, * 用 BoundedImageCache 按字节累计 LRU 淘汰避免爆堆。 * 由于 embed() 写完即 remove(),活跃图片字节通常 ≤ 100MB,仅在极端 prefetch 领先场景才会触发淘汰。 * 出现淘汰过频影响命中率时可上调到 512MB;2GB 堆约束下不建议超过 768MB。 */ private long imageCacheMaxBytes = 256L * 1024L * 1024L; /** * 是否在 Coze 请求 parameters 中附带 api_key 字段。 * 默认 true:线上 Coze 工作流将该字段视为必填,缺失会得到 4000 * "Missing required parameters";前端传入的 api_key 必须透传到 coze。 * 仅在工作流明确不再需要 api_key 时,可通过环境变量 * AIIMAGE_SIMILAR_ASIN_COZE_INCLUDE_LEGACY_API_KEY=false 关闭。 */ private boolean cozeIncludeLegacyApiKey = true; /** * 是否使用旧的 item 字段顺序 {asin, sku, url, target_urls, title}。 * 默认 false:当前实现使用 {asin, url, target_urls, title, sku}。 * 出现兼容问题时可通过 AIIMAGE_SIMILAR_ASIN_COZE_USE_LEGACY_ITEM_ORDER=true * 切回旧顺序进行回归对比。 */ private boolean cozeUseLegacyItemFieldOrder = false; /** * 是否启用 P0-3 merge 增量缓冲:每个 batch DONE 时仅缓冲 cozeRows, * 不立即合并到 chunk;finalize 前一次性按 chunkScopeHash 分组合并, * 把 OSS chunk 读写从 1000+ 次降到 chunk 数量级。 * 仅作用于"正常 poll DONE"路径;失败 batch / 单 batch 任务 / 其他 * 11 个 mergeCozeRowsIntoChunk 调用点保留原立即 merge 行为。 * 出现问题时可通过 AIIMAGE_SIMILAR_ASIN_COZE_RESULT_BUFFER_ENABLED=false * 一键回滚到老路径。 */ private boolean cozeResultBufferEnabled = true; /** * P0-4:单 credential 抢 Coze 提交锁的最长等待时间(毫秒)。 * 原硬编码 1000ms,在高并发 split retry 时大量抛 "Coze submit throttle lock timeout" * 并把整批行 markFailed。应与 cozeSubmitMinIntervalMillis(5000ms)保持 1.5-2 倍关系, * 默认 10000ms 给抢锁更多时间。 */ private long cozeSubmitLockWaitMillis = 10000L; /** * P0-4:抢 Coze 提交锁失败后下次重试间隔(毫秒)。 * 原硬编码 500ms,会在指数退避算法中作为基础值(500/1000/2000/4000ms 上限 4000)。 */ private long cozeSubmitLockRetryDelayMillis = 500L; @Data public static class CozeCredential { private String name; private String workflowId; private String token; } }