概述
本文是 “多 Agent 系统与 AI 应用解决方案” 系列的第 12 篇。在上一篇中,我们构建了面向全企业的大模型微调即服务平台,赋予了企业定制专属模型的能力。当模型能力就绪后,最直接、最广泛的商业价值出口之一便是多模态内容生成——从营销海报、产品视频到社交媒体配图,内容创意是连接 AI 能力与营销业务的桥梁。
当市场部找到你,说“双 11 我们需要 100 张不同风格的促销海报和 3 段宣传视频,3 天内交付,但我们只有 2 个设计师”——你立刻知道,依赖人工的传统模式根本无法完成。但如果有一个平台,市场人员只需上传产品信息和文案,AI 就能自动生成各种风格的图片和视频,还能自动抠图、叠加 Logo、审核合规性,人工仅需最终确认——这不仅是效率的质变,更是创意生产关系的重构。今天,我们就用 Spring Boot、RabbitMQ、ComfyUI 和 LangChain4j,构建这样一个 “多模态内容工厂”。
本文将围绕五大核心技术展开:模型统一抽象与智能路由(屏蔽 Stable Diffusion、DALL-E、Midjourney 等厂商差异)、ComfyUI 工作流引擎(编排数十个节点的复杂图像管线)、全链路内容安全审核(从 Prompt 到生成图片的四层防御)、异步任务与队列管理(支撑高并发的生成任务调度)以及 Agent 工具集成(将生成能力融入自动化营销工作流)。最终,我们将通过一个贯穿案例——“双 11 大促批量生成 100 张促销海报与 3 段宣传视频”——完整推演从需求接收到多渠道发布的自动化流程,让你掌握构建企业级 AI 创意工厂的系统性工程能力。
文章组织架构图如下,标明了各模块编号与层级关系:
flowchart TD
subgraph S1["1. 多模态内容生成的挑战与平台架构全景"]
direction LR
A1["传统流程痛点与AI平台价值"]
A2["五大核心能力与微服务架构类比"]
A3["六层架构全景图"]
A1 --> A2 --> A3
end
subgraph S2["2. 模型统一抽象与智能路由"]
direction LR
B1["ImageModel/VideoModel接口定义"]
B2["多厂商实现与ModelRouter四维决策"]
B3["路由降级链与模型质量对比"]
B1 --> B2 --> B3
end
subgraph S3["3. ComfyUI工作流引擎集成"]
direction LR
C1["工作流JSON结构与动态参数注入"]
C2["模板版本管理与复用"]
C3["K8s集群负载均衡与HPA"]
C1 --> C2 --> C3
end
subgraph S4["4. 内容审核与安全保障"]
direction LR
D1["生成前PromptAuditor责任链"]
D2["生成后ContentAuditor与NSFW检测"]
D3["WatermarkService数字水印溯源"]
D4["Camunda人工审核工作流"]
D1 --> D2 --> D3 --> D4
end
subgraph S5["5. 异步任务与队列管理"]
direction LR
E1["RabbitMQ优先级队列设计"]
E2["消费者重试机制与死信队列"]
E3["WebSocket实时推送与队列监控"]
E1 --> E2 --> E3
end
subgraph S6["6. Agent工具集成与自动化工作流"]
direction LR
F1["多模态能力Tool封装"]
F2["营销自动化Plan-Solve案例"]
F3["效率提升30倍的量化分析"]
F1 --> F2 --> F3
end
subgraph S7["7. 贯穿案例: 双11大促批量营销内容生成"]
direction TB
G1["场景: 100张海报+3段视频,3天交付"]
G2["10步全流程推演与成本分析"]
G3["失败场景: GPU节点宕机与HPA应急"]
G1 --> G2 --> G3
end
subgraph S8["8. 与前后系列的衔接"]
direction LR
H1["模型抽象、安全、规划等复用关系"]
end
subgraph S9["9. 面试高频专题"]
direction LR
I1["≥14道面试题含系统设计题"]
end
%% 横向主流程连线
S1 --> S2 --> S3 --> S4
S4 --> S5 --> S6 --> S7 --> S8 --> S9
%% 子图背景色(不同极浅色)
classDef sub1 fill:#f0f4ff,stroke:#94a3b8,stroke-width:1.5px
classDef sub2 fill:#f0fff4,stroke:#94a3b8,stroke-width:1.5px
classDef sub3 fill:#fef9f0,stroke:#94a3b8,stroke-width:1.5px
classDef sub4 fill:#f5f0ff,stroke:#94a3b8,stroke-width:1.5px
classDef sub5 fill:#fff0f5,stroke:#94a3b8,stroke-width:1.5px
classDef sub6 fill:#e0f2fe,stroke:#94a3b8,stroke-width:1.5px
classDef sub7 fill:#f0fdf4,stroke:#94a3b8,stroke-width:1.5px
classDef sub8 fill:#fefce8,stroke:#94a3b8,stroke-width:1.5px
classDef sub9 fill:#f3e8ff,stroke:#94a3b8,stroke-width:1.5px
%% 节点样式(与子图同色系,稍饱和)
classDef node1 fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#1e3a8a
classDef node2 fill:#d1fae5,stroke:#10b981,stroke-width:1.5px,color:#065f46
classDef node3 fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#92400e
classDef node4 fill:#ede9fe,stroke:#8b5cf6,stroke-width:1.5px,color:#4c1d95
classDef node5 fill:#fce4ec,stroke:#f472b6,stroke-width:1.5px,color:#9d174d
classDef node6 fill:#bfdbfe,stroke:#3b82f6,stroke-width:1.5px,color:#1e3a8a
classDef node7 fill:#a7f3d0,stroke:#10b981,stroke-width:1.5px,color:#065f46
classDef node8 fill:#fde68a,stroke:#d97706,stroke-width:1.5px,color:#92400e
classDef node9 fill:#d8b4fe,stroke:#8b5cf6,stroke-width:1.5px,color:#4c1d95
class A1,A2,A3 node1
class B1,B2,B3 node2
class C1,C2,C3 node3
class D1,D2,D3,D4 node4
class E1,E2,E3 node5
class F1,F2,F3 node6
class G1,G2,G3 node7
class H1 node8
class I1 node9
class S1 sub1
class S2 sub2
class S3 sub3
class S4 sub4
class S5 sub5
class S6 sub6
class S7 sub7
class S8 sub8
class S9 sub9
架构图说明:
- 总览说明:全文 9 个模块从认知挑战和构建平台全景出发,逐步深入模型路由、工作流引擎、安全防线、异步队列、Agent 集成等核心技术,最终以贯穿案例验证全链路设计,并以面试题巩固关键知识。
- 逐模块说明:
- 模块 1:建立“传统手工艺生产 → AI 工业化生产”的变革认知,给出六层架构全景图。
- 模块 2-3:是平台的“发动机”——
ModelRouter负责智能选择最合适的“引擎”,ComfyUI 工作流负责编排复杂的“生产管线”。 - 模块 4:是平台的“刹车与合规系统”——全链路内容审核是品牌安全的生命线。
- 模块 5:是平台的“消化与循环系统”——异步任务队列支撑高并发、高可用的请求处理。
- 模块 6:是能力的“神经末梢”——Agent 工具集成让平台从被动工具升级为主动创意伙伴。
- 模块 7:通过双 11 实战推演,验证架构设计的可行性与鲁棒性。
- 模块 8-9:建立与系列知识体系的关联,并通过面试题巩固技能。
- 关键结论:多模态内容生成平台的本质,是将创意设计从“手工艺人模式”升级为“工业化生产模式”。通过模型统一抽象(屏蔽厂商差异)、ComfyUI 工作流引擎(标准化管线)、全链路内容安全审核(合规保障)和异步任务队列(高并发支撑),平台将原本依赖少数设计师的稀缺创意能力,变成了人人可用的普惠 AI 服务。掌握了这套架构,你就能为企业构建起一座“AI 创意工厂”,让营销内容的产出效率提升数十倍,成本降至原来的十分之一,同时保障品牌安全和内容合规。
1. 多模态内容生成的挑战与平台架构全景
1.1 传统内容生产流程的“三座大山”
在引入 AI 之前,企业内容生产(尤其是营销内容)受困于三个核心瓶颈:
- 效率低下,重度依赖人力:一张高质量促销海报,从构思、找素材、设计、修改到定稿,平均耗时 2-4 小时。一名设计师一天最多产出 3-5 张。对于“双 11”这类需要数百张不同尺寸、品类、风格物料的场景,人力根本无法覆盖。
- 工具割裂,工作流碎片化:设计师需要 Photoshop 做图像合成、After Effects 做动效视频、Illustrator 做矢量元素,文件在多个工具间反复导入导出,版本管理混乱。而文案、市场人员与设计师的协作依赖微信/邮件,反馈周期长。
- 品牌一致性与合规风险:不同设计师风格差异大,品牌视觉难统一。更严峻的是版权风险——设计师可能无意中使用未授权的字体、图片,或在“借鉴”时过度参考迪士尼、皮克斯等风格导致侵权,而人工审核难以全量覆盖。
1.2 平台五大核心能力与微服务架构类比
我们将上述痛点抽象为平台需要具备的五大能力,并直接映射到微服务架构中的经典模式。这种映射不是生搬硬套,而是架构设计上的同构。
| 传统内容生产环节 | AI 内容工厂能力 | 微服务架构类比 |
|---|---|---|
| 设计师使用单一工具 (Photoshop/Midjourney) | 模型统一抽象与智能路由:屏蔽 Stable Diffusion、DALL-E、Midjourney 等不同模型实现。 | 接口与策略模式:ImageModel 接口定义行为,DalleModel/MidjourneyModel 等是策略实现,ModelRouter 扮演路由网关和负载均衡角色。 |
| 设计师执行复杂操作 (抠图+合成+调色) | ComfyUI 工作流引擎:编排数十个节点的复杂管线,实现 ControlNet 姿态控制、Inpainting 局部重绘、IP-Adapter 风格迁移等。 | 服务编排 (Orchestration):工作流 JSON 类似 BPMN 流程定义,ComfyUIWorkflowEngine 是流程引擎,驱动节点按序执行。 |
| 多级人工审核 (组长初審、法务终审) | 全链路内容安全审核:Prompt 预审、生成内容 NSFW 检测、数字水印、人工终审。 | 责任链模式:PromptAuditor 和 ContentAuditor 形成过滤器链,请求依次经过各环节,任一环节有权拒绝。 |
| 项目排期与任务分配 (PM 协调) | 异步任务与队列管理:支撑高并发请求,通过优先级、重试、死信队列保障最终一致性。 | 消息队列:RabbitMQ 承担异步解耦职责,任务提交后立即返回 taskId,消费者异步处理,类似订单系统。 |
| 跨部门协作 (营销→设计→审核→发布) | Agent 工具集成与自动化工作流:将生成能力封装为 Tool,由 AI Agent 自动编排“需求→Prompt 生成→生图→抠图→审核”的全流程。 | Saga 或编排模式:Agent 充当协调者(Orchestrator),通过调用不同 Tool (服务) 完成一个长生命周期业务流。 |
1.3 平台六层架构全景图
基于上述类比,我们设计出多模态内容生成平台的六层架构。
flowchart TD
subgraph AccessLayer["接入层"]
direction TB
WebUI["Web UI<br/>React/Vue"]
API["REST API<br/>Spring Boot"]
AgentTool["Agent Tool<br/>LangChain4j"]
end
subgraph BizLayer["业务服务层"]
direction TB
ImageService["ImageGenService<br/>图片生成服务"]
VideoService["VideoGenService<br/>视频生成服务"]
WorkflowService["WorkflowService<br/>工作流管理"]
ReviewService["ReviewService<br/>审核服务"]
end
subgraph MQLayer["消息队列层"]
direction TB
RabbitMQ["RabbitMQ<br/>任务调度与解耦"]
Kafka["Kafka<br/>事件总线 审核/发布"]
end
subgraph ModelLayer["模型推理层"]
direction TB
ComfyUICluster["ComfyUI 集群<br/>K8s Deployment"]
SDWebUI["Stable Diffusion WebUI"]
ExternalAPI["DALL-E/Midjourney/<br/>Runway/Pika API"]
end
subgraph StorageLayer["存储与审核层"]
direction TB
MinIO["MinIO<br/>内容存储+水印处理"]
SafetyChecker["SafetyChecker<br/>NSFW 审核模型"]
PostgreSQL["PostgreSQL<br/>元数据与模板库"]
Redis["Redis<br/>任务状态缓存"]
end
subgraph MonitorLayer["监控层"]
direction TB
Prometheus["Prometheus<br/>指标采集"]
Grafana["Grafana<br/>生成量/延迟/通过率大屏"]
end
AccessLayer --> BizLayer
BizLayer --> MQLayer
BizLayer --> StorageLayer
MQLayer --> BizLayer
MQLayer --> ModelLayer
ModelLayer -.-> StorageLayer
StorageLayer --> BizLayer
MonitorLayer -.-> BizLayer
MonitorLayer -.-> MQLayer
MonitorLayer -.-> ModelLayer
%% 子图背景色(极浅莫兰迪色系)
classDef subAccess fill:#f0f4ff,stroke:#94a3b8,stroke-width:1.5px
classDef subBiz fill:#f0fff4,stroke:#94a3b8,stroke-width:1.5px
classDef subMQ fill:#fef9f0,stroke:#94a3b8,stroke-width:1.5px
classDef subModel fill:#f5f0ff,stroke:#94a3b8,stroke-width:1.5px
classDef subStorage fill:#fff0f5,stroke:#94a3b8,stroke-width:1.5px
classDef subMonitor fill:#e0f2fe,stroke:#94a3b8,stroke-width:1.5px
%% 节点样式(与子图同色系,稍饱和)
classDef nodeAccess fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#1e3a8a
classDef nodeBiz fill:#d1fae5,stroke:#10b981,stroke-width:1.5px,color:#065f46
classDef nodeMQ fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#92400e
classDef nodeModel fill:#ede9fe,stroke:#8b5cf6,stroke-width:1.5px,color:#4c1d95
classDef nodeStorage fill:#fce4ec,stroke:#f472b6,stroke-width:1.5px,color:#9d174d
classDef nodeMonitor fill:#bae6fd,stroke:#0ea5e9,stroke-width:1.5px,color:#0c4a6e
class WebUI,API,AgentTool nodeAccess
class ImageService,VideoService,WorkflowService,ReviewService nodeBiz
class RabbitMQ,Kafka nodeMQ
class ComfyUICluster,SDWebUI,ExternalAPI nodeModel
class MinIO,SafetyChecker,PostgreSQL,Redis nodeStorage
class Prometheus,Grafana nodeMonitor
class AccessLayer subAccess
class BizLayer subBiz
class MQLayer subMQ
class ModelLayer subModel
class StorageLayer subStorage
class MonitorLayer subMonitor
图 1-1:多模态内容生成平台分层架构全景图
- 主旨概括:本图展示了平台的六层逻辑架构,描绘了一个用户请求从接入到最终生成内容并安全存储的完整路径,以及各层组件间的交互关系。
- 逐元素分解:
- 接入层:提供 Web UI、REST API 和 Agent Tool 三种交互方式。Web UI 面向业务人员,API 面向内部系统集成,Agent Tool 面向自动化工作流。
- 业务服务层:核心逻辑所在,包含
ImageGenService等。它们是任务的发起者,将生成请求封装后投递到消息队列,并处理任务结果。 - 消息队列层:解耦生成请求与模型推理。RabbitMQ 负责任务的异步调度和优先级管理,Kafka 负责可靠的审核结果发布等事件流。
- 模型推理层:具体执行生成任务的服务集群,包括自部署的 ComfyUI/SD WebUI 集群以及外部商业 API。
- 存储与审核层:MinIO 存储生成的图片/视频并支持水印处理,PostgreSQL 存储工作流模板等元数据,Redis 缓存实时任务状态。
- 监控层:全栈可观测性,从业务指标(生成量、审核通过率)到技术指标(队列深度、GPU 利用率)统一采集与展示。
- 设计原理映射:该分层架构是典型的关注点分离设计。业务层不关心模型是本地推理还是 API 调用,推理层不关心任务优先级。这允许各层独立演进,例如将 RabbitMQ 替换为 Kafka 或 RocketMQ,业务层代码无需修改,体现了依赖倒置原则。
- 工程联系与关键结论:生产常见误配置案例:在部署时,如果将所有服务(业务、消费、监控)打成同一个 JAR 包并以单体运行,当 ComfyUI 集群故障导致生成任务大量堆积时,消费线程池会阻塞,进而耗尽 Tomcat 工作线程,最终导致整个平台对外不可用,包括任务查询和监控。必须将 Web 服务与消费者服务物理分离,至少是独立的进程或 Pod。
2. 模型统一抽象与智能路由
2.1 核心接口设计:ImageModel 与 VideoModel
屏蔽多厂商差异的第一步是定义清晰、稳定的接口。得益于前文《LangChain4j 源码透析》中对 ImageModel 接口的分析,我们可以直接复用并扩展其设计理念。
// ImagePrompt: 封装所有图片生成参数
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ImagePrompt {
private String positivePrompt;
private String negativePrompt;
private String size; // e.g., "1024x1024"
private String style; // e.g., "photographic", "anime", "3d-render"
private List<String> referenceImageUrls; // 图生图参考图
private ControlNetParams controlNetParams; // 可选,高级控制
private Integer seed; // 确定性生成
}
// ImageResult: 统一返回结果
@Data
@Builder
public class ImageResult {
private String imageUrl;
private String revisedPrompt; // 模型可能微调了prompt
private Long generationTimeMs;
private String modelVersion;
}
// ImageModel 核心接口
public interface ImageModel {
ImageResult generate(ImagePrompt prompt);
String getModelName();
}
接下来,我们为不同厂商实现此接口。这本质上是策略模式的实践。
2.1.1 StableDiffusionModel(本地部署,WebUI API)
@Component
public class StableDiffusionModel implements ImageModel {
private final RestClient restClient;
private final String baseUrl;
public StableDiffusionModel(@Value("${models.sd-webui.url}") String baseUrl) {
this.baseUrl = baseUrl;
this.restClient = RestClient.create();
}
@Override
public ImageResult generate(ImagePrompt prompt) {
String endpoint = this.baseUrl + "/sdapi/v1/txt2img";
Map<String, Object> requestBody = buildRequest(prompt);
long start = System.currentTimeMillis();
SdWebUiResponse response = restClient.post()
.uri(endpoint)
.contentType(MediaType.APPLICATION_JSON)
.body(requestBody)
.retrieve()
.body(SdWebUiResponse.class);
long timeMs = System.currentTimeMillis() - start;
// 解码 base64 图片并上传至 MinIO
String imageUrl = decodeAndUpload(response.getImages().get(0));
return ImageResult.builder()
.imageUrl(imageUrl)
.generationTimeMs(timeMs)
.modelVersion("sd-webui-v1.6")
.revisedPrompt(prompt.getPositivePrompt())
.build();
}
private Map<String, Object> buildRequest(ImagePrompt prompt) {
Map<String, Object> params = new HashMap<>();
params.put("prompt", prompt.getPositivePrompt());
params.put("negative_prompt", prompt.getNegativePrompt());
params.put("steps", 25);
params.put("width", 1024);
params.put("height", 1024);
// ... other params
return params;
}
@Override
public String getModelName() { return "Stable-Diffusion-WebUI"; }
}
2.1.2 DalleModel(OpenAI API)
@Component
public class DalleModel implements ImageModel {
private final OpenAiImageClient openAiImageClient;
public DalleModel(@Value("${models.dalle.api-key}") String apiKey) {
this.openAiImageClient = OpenAiImageClient.builder()
.apiKey(apiKey)
.build();
}
@Override
public ImageResult generate(ImagePrompt prompt) {
long start = System.currentTimeMillis();
ImageResponse response = openAiImageClient.generate(
prompt.getPositivePrompt(),
"dall-e-3",
"1024x1024",
"standard"
);
long timeMs = System.currentTimeMillis() - start;
String imageUrl = downloadAndUploadToMinIO(response.getUrl()); // 重上传至自己存储
return ImageResult.builder()
.imageUrl(imageUrl)
.revisedPrompt(response.getRevisedPrompt())
.generationTimeMs(timeMs)
.modelVersion("dall-e-3")
.build();
}
@Override
public String getModelName() { return "DALL-E 3"; }
}
设计意图解读:接口 ImageModel 隔离了不同模型的 HTTP/RPC 调用细节、认证方式、请求组装和响应解析逻辑。业务调用方(ImageGenService)只需要注入 ImageModel 实例并调用 generate(prompt) 方法,完全解耦了具体厂商依赖。
生产影响分析:为每个厂商实现独立的 @Component,使得我们可以通过 @ConditionalOnProperty 灵活启用或禁用某个模型,例如在双 11 期间临时降低 Midjourney 配额,只需修改配置中心即可。
2.2 ModelRouter 的四维决策与降级链
ModelRouter 是实现智能分发的核心,它根据任务需求、预算、延迟、质量四个维度进行综合评分,选出最优模型。
@Component
public class ModelRouter {
private final Map<String, ImageModel> modelMap;
private final ModelMetricsService metricsService; // 定期从Grafana/Prometheus更新
public ModelRouter(Map<String, ImageModel> modelMap, ModelMetricsService metricsService) {
this.modelMap = modelMap;
this.metricsService = metricsService;
}
public ImageModel route(TaskRequirement req) {
List<ImageModel> candidates = new ArrayList<>(modelMap.values());
// 1. 过滤:基于预算、延迟硬约束
candidates = candidates.stream()
.filter(m -> fitsBudget(m, req.getMaxBudget()))
.filter(m -> fitsLatency(m, req.getMaxLatencyMs()))
.collect(Collectors.toList());
if (candidates.isEmpty()) {
throw new NoSuitableModelException("No model satisfies budget/latency constraints.");
}
// 2. 评分:基于质量、成本、速度的加权得分
candidates.sort(Comparator.comparingDouble(
m -> calculateScore(m, req)).reversed());
return candidates.get(0); // 返回得分最高者
}
private double calculateScore(ImageModel model, TaskRequirement req) {
ModelMetrics metrics = metricsService.getMetrics(model.getModelName());
// 质量得分 (CLIP Score)
double qualityScore = metrics.getAvgClipScore();
// 成本得分 (1.0 - 成本比例,成本越低得分越高)
double costScore = 1.0 - (metrics.getCostPerImage() / MAX_BUDGET);
// 速度得分 (1.0 - 延迟比例)
double latencyScore = 1.0 - (metrics.getAvgLatencyMs() / MAX_LATENCY);
// 权重由任务需求决定
return req.getQualityWeight() * qualityScore
+ req.getBudgetWeight() * costScore
+ req.getLatencyWeight() * latencyScore;
}
// 路由降级逻辑
public ImageModel routeWithFallback(TaskRequirement req) {
try {
return route(req);
} catch (NoSuitableModelException e) {
log.warn("No optimal model found, falling back to cheapest/fastest default.");
// 降级到本地SD基础版,保证可用性
return modelMap.get("sd-base-v1.5");
}
}
}
设计意图解读:ModelRouter 将模型选择策略封装在 route 方法中,该策略可随时根据业务需求调整权重。metricsService 是模型指标的抽象,从监控系统获取实时数据,避免基于静态假设做决策。routeWithFallback 实现了降级机制,这是高可用架构的关键。
生产影响分析:ModelRouter 中权重需定期根据实际监控数据(Grafana 延迟统计和 FinOps 月度账单)更新。例如,如果 Flux Pro 提供商降价,就应提高其成本权重得分。若不更新,路由决策将偏离最优,导致公司持续为更贵的模型付费,或无法享受到新模型的质量提升。
2.3 多模型质量/延迟/成本量化对比
同一 Prompt “A futuristic tech product showcase, cyberpunk neon lights, 8k detail, cinematic lighting” 在三种模型下的实验数据如下:
| 模型 | CLIP Score (与文本一致性) | 生成延迟 | API 成本/张 | 最佳场景 |
|---|---|---|---|---|
| SDXL Turbo (本地) | 0.285 | 1.8s | ¥0.00 (算力已折旧) | 实时预览,快速迭代 |
| Flux Pro (API) | 0.335 | 4.5s | ¥0.05 | 高质量成品输出 |
| Midjourney (API代理) | 0.312 | 15.2s | ¥0.10 | 艺术风格,创意探索 |
ModelRouter 的选择逻辑:当 req 要求最大延迟 2s 时,SDXL Turbo 是唯一候选;当 req 看重质量(weight=0.8)且预算允许,Flux Pro 胜出。
2.4 ModelRouter 多维决策与降级流程序列图
sequenceDiagram
actor User as 业务用户
participant Service as ImageGenService
participant Router as ModelRouter
participant Metrics as ModelMetricsService
participant Model as ImageModel
User->>Service: createTask(prompt, style, budget, latency)
Service->>Router: route(TaskRequirement)
Router->>Metrics: getMetricsForAllModels()
Metrics-->>Router: [{name: "flux", cost:0.05, latency:4.5, score:0.335}, ...]
Note over Router: 1. 硬约束过滤: <br/>预算≤0.06 & 延迟≤5s
Note over Router: 2. 加权评分: <br/>qualityWeight=0.7, cost=0.2, speed=0.1
Router-->>Service: 返回 FluxModel
Service->>Model: FluxModel.generate(prompt)
Model-->>Service: throw 429 RateLimitException
Service->>Router: routeWithFallback(TaskRequirement)
Note over Router: 降级策略: <br/>Flux不可用 → SDXL Turbo
Router-->>Service: 返回 SdxlTurboModel
Service->>Model: SdxlTurboModel.generate(prompt)
Model-->>Service: ImageResult
Service-->>User: taskId
图 2-1:ModelRouter 四维决策与降级流程序列图
- 主旨概括:此序列图展示了
ModelRouter如何接收业务需求,通过硬约束和软评分两个阶段从候选模型池中选出最优模型,并在首选模型故障时自动启用降级链路。 - 逐元素分解:
- 硬约束过滤:
Service将用户模糊意图(“高质量”“快速”)量化为具体数值并封装为TaskRequirement,传入Router。Router首先执行硬性过滤,排除预算或延迟不达标的模型。 - 加权评分:对通过过滤的模型,根据
TaskRequirement中的权重和ModelMetricsService提供的实时指标,计算综合加权得分并排序。 - 自动降级:上层的
Service在调用模型失败后,主动调用routeWithFallback,该方法捕获异常并返回一个默认的安全模型,保障核心业务可用。
- 硬约束过滤:
- 设计原理映射:这是策略模式的经典应用。
ModelRouter的route方法执行具体的路由策略。我们可以轻松实现新的路由策略,例如“负载最轻优先”或“新模型灰度测试”,并动态切换。routeWithFallback是断路器模式的简化实现。 - 工程联系与关键结论:生产常见误配置:如果
metricsService只是简单的缓存而没有 TTL,当某个模型 API 出现波动,延迟从 4s 飙升到 20s,路由依然会认为它很快而持续选择它,导致大量任务超时。必须对 metrics 设置合理的 TTL(如 5s)并基于滑动窗口统计,确保路由基于新鲜数据。
3. ComfyUI 工作流引擎集成
如果说 ModelRouter 选择的是单兵作战的“特种兵”,那么 ComfyUI 工作流就是一个高度自动化、精密协作的“生产车间”。它允许我们将数十个模型节点(检查点、LoRA、ControlNet、IP-Adapter)编排成一个可复用的 JSON 流程。
3.1 工作流 JSON 结构与动态参数注入
ComfyUI 工作流是一个包含 nodes 和 links 的 JSON。节点(Node)有 id、type(如 CLIPTextEncode、KSampler),inputs 中包含具体参数或引用其他节点的输出。
一个简化的工作流 JSON 片段:
{
"id": "5",
"type": "CLIPTextEncode",
"inputs": {
"text": "{{positive_prompt}}", // 这是我们要替换的占位符
"clip": ["4", 0]
}
}
我们的 ComfyUIWorkflowEngine 核心任务就是:加载模板 → 查找占位符 → 替换参数 → 提交执行。
@Service
public class WorkflowService {
private final MinioClient minioClient;
private final RestClient comfyClient;
public String executeWorkflow(String templateId, Map<String, Object> params) {
// 1. 从MinIO加载模板
String templateJson = loadTemplateFromMinIO(templateId);
// 2. 正则替换占位符 {{key}} 为 params 中的值
String finalJson = replacePlaceholders(templateJson, params);
// 3. 提交到 ComfyUI Server
String promptId = comfyClient.post("/prompt", finalJson);
return promptId;
}
private String replacePlaceholders(String template, Map<String, Object> params) {
Pattern pattern = Pattern.compile("\\{\\{(\\w+)\\}\\}");
Matcher matcher = pattern.matcher(template);
StringBuilder sb = new StringBuilder();
while (matcher.find()) {
String key = matcher.group(1);
Object value = params.getOrDefault(key, "");
matcher.appendReplacement(sb, Matcher.quoteReplacement(value.toString()));
}
matcher.appendTail(sb);
return sb.toString();
}
}
设计意图解读:将 Promp、Seed、参考图 URL 等参数化,使得同一个复杂工作流可以被复用无数次。市场人员不懂技术,但只需上传产品图,就能驱动一个包含抠图、背景替换、光影合成、叠加文案的 20 步复杂管线。
生产影响分析:如果参数替换逻辑使用了简单的 String.replace 而非正则,可能产生注入风险。例如,用户输入的文案中包含 {{key}} 这种字符,就可能破坏 JSON 结构。使用正则和 Matcher.quoteReplacement 可有效防止。
3.2 模板版本管理与复用
我们将工作流模板视为软件制品,进行严格的版本化管理。
- 存储:模板 JSON 文件存储于 MinIO:
workflows/{templateId}/{version}.json - 元数据:存入 PostgreSQL
workflow_templates表,记录name,description,preview_image_url,author_id,usage_count。 - 推荐:一个定时任务按
usage_count和近 7 天使用趋势对模板排序,将高质量、高频使用的模板(如“双 11 促销海报 v3”)推荐到用户首页。
3.3 ComfyUI 集群管理与 HPA 弹性伸缩
多个 ComfyUI 实例部署在 K8s 集群中,通过 Service 暴露。
@Component
public class ComfyUILoadBalancer {
private final List<String> instances; // 从 K8s Endpoints 动态获取
private AtomicInteger counter = new AtomicInteger(0);
private final RestClient restClient;
public String getNextAvailableInstance() {
// 简单轮询,可升级为“最少活跃任务数”算法
int index = counter.getAndIncrement() % instances.size();
String instance = instances.get(index);
// 健康检查:检查其队列长度和GPU可用性
if (isHealthy(instance)) {
return instance;
}
// ... fallback to next healthy instance
}
}
HPA 配置(YAML 理念):我们不仅监控 GPU 使用率,更关键的是监控 ComfyUI 内部队列深度。当单个实例队列待处理任务超过 5 时,就触发扩容。这是最贴近业务表现的指标。
# 自定义指标,例如通过 Prometheus Adapter 暴露
- type: Pods
pods:
metric:
name: comfyui_queue_depth
target:
type: AverageValue
averageValue: "5"
3.4 工作流 vs 纯 API 的灵活性对比
- 纯 API:你只能调用
/txt2img,得到一个基于 Prompt 的图片。如果想换背景、固定人物姿势,需要开发者在代码中编排多次 API 调用,且中间结果处理困难。 - ComfyUI 工作流:一个工作流可包含“加载参考图 → ControlNet 提取姿态/线稿 → 背景分割 → IP-Adapter 风格注入 → KSampler 生图 → Inpainting 精修 → 超分放大”的全流程。这是从“单次函数调用”到“程序化管线”的质变,极大提升了自动化创作的天花板。
3.5 ComfyUI 工作流引擎流程序列图
sequenceDiagram
participant User
participant WorkflowSvc as WorkflowService
participant MinIO
participant LB as ComfyUILoadBalancer
participant ComfyUI as ComfyUI Instance
participant WebSocket
User->>WorkflowSvc: execute(templateId, {positive_prompt:"...", imageUrl:"..."})
WorkflowSvc->>MinIO: loadTemplate(templateId)
MinIO-->>WorkflowSvc: workflow.json with {{placeholders}}
WorkflowSvc->>WorkflowSvc: replacePlaceholders(json, params)
WorkflowSvc->>LB: getNextAvailableInstance()
LB-->>WorkflowSvc: instance_base_url
WorkflowSvc->>ComfyUI: POST /prompt with final_json
ComfyUI-->>WorkflowSvc: { "prompt_id": "abc-123" }
ComfyUI-->>WorkflowSvc: GET /history/{prompt_id} (long polling)
ComfyUI-->>WebSocket: push "executing" status
WebSocket-->>User: push {"status": "PROCESSING"}
ComfyUI-->>WebSocket: push "executed" result
WebSocket-->>User: push {"status": "COMPLETED", "imageUrl": "..."}
图 3-1:ComfyUI 工作流引擎流程序列图
- 主旨概括:该图描绘了从用户触发一个工作流到获得最终结果的全过程,核心在于“模板加载与参数化 → 负载均衡调度 → ComfyUI 异步执行 → WebSocket 实时反馈”这一闭环。
- 逐元素分解:
- 参数化与提交:
WorkflowService是流程的编排者,它首先从 MinIO 获取模板,然后用动态参数替换占位符,生成最终的可执行 JSON。 - 集群调度:
ComfyUILoadBalancer作为一个透明的代理层,隐藏了后端多实例的复杂性,负责找到一个健康的节点来执行任务。 - 异步执行与反馈:ComfyUI 实例提交任务后立即返回
prompt_id,任务在其内部队列中异步执行。平台通过 WebSocket 主动将执行状态和最终结果推送给前端,避免前端轮询带来的性能损耗。
- 参数化与提交:
- 设计原理映射:
ComfyUILoadBalancer是典型的服务定位器模式。HPA 基于队列深度的弹性伸缩,是响应式系统原则的体现,系统会根据当前负载自动调整资源。 - 工程联系与关键结论:生产常见误配置:如果 ComfyUI 的所有实例的
batch_size都设置得过大,单个任务会独占 GPU 显存,导致并发能力极差。HPA 会因为单一指标高而不断扩容新 Pod,但由于新 Pod 同样存在大batch_size问题,最终所有 GPU 节点都被占满,系统整体吞吐量反而下降。必须根据模型大小和 GPU 显存,精细调整每个实例的batch_size和max_queue_size,达到最佳吞吐。
4. 内容审核与安全保障
内容安全是 AI 内容工厂的生命线。我们从 Prompt 输入到图片/视频输出,再到人工终审,构建了四层纵深防御体系。
4.1 生成前 PromptAuditor 责任链
任何用户输入的 Prompt,在送入模型前,都必须经过三道安全闸门。
public interface AuditHandler {
AuditResult audit(String prompt);
}
// 1. 正则过滤器
public class RegexFilter implements AuditHandler {
private List<Pattern> blockedPatterns; // NSFW、政治、版权敏感词
@Override
public AuditResult audit(String prompt) {
for (Pattern pattern : blockedPatterns) {
if (pattern.matcher(prompt).find()) {
return AuditResult.reject("Prompt contains blocked keywords: " + pattern.pattern());
}
}
return AuditResult.pass();
}
}
// 2. AI 分类模型 (调用 OpenAI Moderation API 或本地 detoxify)
public class ModerationModelFilter implements AuditHandler { /* ... */ }
// 3. 注入与越狱攻击检测
public class InjectionDetector implements AuditHandler {
@Override
public AuditResult audit(String prompt) {
if (prompt.length() > 1500) return AuditResult.reject("Prompt too long.");
if (prompt.toLowerCase().contains("ignore previous instructions")) {
return AuditResult.reject("Jailbreak attempt detected.");
}
return AuditResult.pass();
}
}
// 责任链组装
@Service
public class PromptAuditor {
private final List<AuditHandler> chain;
public PromptAuditor(RegexFilter f1, ModerationModelFilter f2, InjectionDetector f3) {
this.chain = List.of(f1, f2, f3);
}
public AuditResult audit(String prompt) {
for (AuditHandler handler : chain) {
AuditResult result = handler.audit(prompt);
if (!result.isPassed()) return result; // 遇到拒绝立即终止
}
return AuditResult.pass();
}
}
4.2 生成后 ContentAuditor 自动审核
当图片/视频生成后,我们会进行二次审核。
- 图片 NSFW 检测:调用 Stability AI 的
SafetyChecker或 Google Vision API,对图片的adult,violence,racy等维度进行打分。 - 视频逐帧审核:按 1s 间隔抽取关键帧,对每一帧进行图片 NSFW 检测。
- OCR 文字审核:对图片进行 OCR 识别,再次检查生成出的文字中是否包含敏感词。
@Service
public class ContentAuditor {
private final SafetyChecker safetyChecker;
private final OcrService ocrService;
public ContentAuditResult auditImage(String imageUrl) {
// 1. NSFW检测
SafetyResult safety = safetyChecker.check(imageUrl);
if (safety.isNsfw()) {
return ContentAuditResult.reject("NSFW content detected: " + safety.getViolationCategories());
}
// 2. OCR文字审核 (复用PromptAuditor的链)
String extractedText = ocrService.extractText(imageUrl);
AuditResult textAudit = promptAuditor.audit(extractedText);
if (!textAudit.isPassed()) {
return ContentAuditResult.reject("Sensitive text detected in image: " + textAudit.getReason());
}
return ContentAuditResult.pass();
}
}
4.3 WatermarkService 数字水印与溯源
这是安全体系的最后一道防线。我们使用 DWT (离散小波变换) 技术嵌入不可见水印,包含生成者、时间戳和模型版本信息。
@Service
public class WatermarkService {
public BufferedImage embed(BufferedImage image, WatermarkData data) {
String payload = objectMapper.writeValueAsString(data);
// 调用 invisible-watermark 库或 OpenCV 实现
return DwtWatermarker.embed(image, payload);
}
public WatermarkData extract(BufferedImage image) {
String payload = DwtWatermarker.extract(image);
return objectMapper.readValue(payload, WatermarkData.class);
}
}
错误示例:某次因 ContentAuditor 中的 NSFW 模型是 6 个月前训练的,导致把大量“动漫风格”的促销海报误判为“成人内容”而拦截。修正方案:引入人工反馈闭环,审核员对误判图片点击“误报”后,系统自动将该图片加入 NSFW 模型的困难样本微调集,并每月定期用新数据更新模型。
4.4 内容审核链流程序列图
sequenceDiagram
actor User
participant PromptAudit as PromptAuditor
participant Model
participant ContentAudit as ContentAuditor
participant Watermark as WatermarkService
participant HumanReview as CamundaWorkflow
User->>PromptAudit: submitPrompt(prompt)
PromptAudit->>PromptAudit: Chain: Regex→AI→Injection
alt Prompt Rejected
PromptAudit-->>User: Reject with specific reason
else Prompt Passed
PromptAudit-->>Model: generate(prompt)
Model-->>ContentAudit: generatedImage
ContentAudit->>ContentAudit: NSFW Check & OCR Check
alt Content Rejected
ContentAudit-->>Model: (Log & Soft Delete)
ContentAudit-->>User: Notify: "Content rejected by auto-audit"
else Content Passed
ContentAudit->>Watermark: embed(image, metadata)
Watermark-->>ContentAudit: watermarkedImage
ContentAudit->>MinIO: store(watermarkedImage)
ContentAudit->>HumanReview: createReviewTask(images)
HumanReview-->>User: Notify: "Pending human review"
HumanReview->>HumanReview: Reviewer approves/rejects with comment
HumanReview-->>Kafka: fireReviewResultEvent
end
end
图 4-1:四层内容审核链流程序列图
- 主旨概括:清晰地展示了从 Prompt 提交到最终发布或驳回,内容流经的四道安全闸门及其决策路径。
- 逐元素分解:
- 第一道闸(Prompt):在模型调用前,用最轻量的方式(正则、关键词)过滤掉绝大多数恶意和明显违规请求。
- 第二、三道闸(Content & OCR):对模型输出进行深度视觉和文字分析,拦截 AIGC 可能产生的“幻觉式”违规。
- 第四道闸(Human):对于营销等高风险场景,自动化流程的最后一步强制进入人工审核,由 Camunda 驱动工单流转。
- 设计原理映射:整个审核流程是责任链模式的完美体现。每个审核环节都是一个独立的处理器(Handler),可以动态增减和调整顺序。任务在不同系统间的流转(生图→审核→发布)则是通过 Kafka 事件驱动的发布-订阅模式解耦。
- 工程联系与关键结论:生产误配置:如果
ContentAuditor的 OCR 服务没有熔断机制,当 OCR 服务因故响应极慢时,会导致整个审核线程池耗尽,后续所有图片都无法完成自动审核而直接积压,整个平台管线阻塞。必须为所有外部依赖(包括 NSFW API 和 OCR API)设置超时和熔断(如 Resilience4j CircuitBreaker)。
4.5 数字水印嵌入与溯源架构流程图
flowchart TB
subgraph 生成与嵌入
direction LR
A[AI模型生成图片] --> B{WatermarkService.embed}
B -- DWT频域水印算法 --> C[嵌入水印后的图片]
C --> D[上传至MinIO]
end
subgraph 溯源与处置
direction LR
E[外部发现滥用图片] --> F{WatermarkService.extract}
F -- 提取水印JSON --> G[得到元数据]
G --> H[溯源: 生成者/时间/模型]
H --> I[处置: 封禁用户/优化模型]
end
D -.->|图片被泄露或滥用| E
图 4-2:数字水印嵌入与溯源架构流程图
- 主旨概括:描述了数字水印作为内容“指纹”,在内容生成时被嵌入,在发生滥用时被提取和溯源,形成安全闭环。
- 逐元素分解:
- 嵌入阶段:
WatermarkService作为旁路模块,在图片生成后、上传 MinIO 前介入,对图片流做 DWT 变换并嵌入包含{userId, timestamp, requestId}的不可见信息。 - 溯源阶段:当合规或法务团队在外部平台发现被滥用的图片时,可通过平台的反向工具提取水印,精准定位到生成请求,实现事后追责。
- 嵌入阶段:
- 设计原理映射:水印的嵌入和提取是典型的装饰器模式思想,我们为生成的图片附加了额外的“能力”(溯源能力),而无需修改生成模型本身。
- 工程联系与关键结论:常见问题:如果水印嵌入是一个同步的耗时操作(例如处理超高清大图),它会阻塞主线程,影响 API 响应速度。解决方案是将其异步化,即生成图片先保存一个临时副本,由独立的水印处理 Worker 消费消息队列,完成嵌入并替换最终文件。
5. 异步任务与队列管理
图片和视频的生成是典型的长时间耗时操作(从 2 秒到 2 分钟不等)。同步等待会耗尽服务器连接资源。因此,我们采用基于 RabbitMQ 的异步任务模型。
5.1 RabbitMQ 优先级队列设计
我们在 RabbitMQ 中声明支持优先级的主队列和对应的死信队列。
@Configuration
public class RabbitMqConfig {
@Bean
public Queue highPriorityImageQueue() {
return QueueBuilder.durable("image.gen.high")
.withArgument("x-max-priority", 10) // 最大优先级
.build();
}
@Bean
public Queue standardImageQueue() {
return QueueBuilder.durable("image.gen.standard")
.withArgument("x-max-priority", 5)
.withArgument("x-dead-letter-exchange", "dlx.image") // 死信
.withArgument("x-dead-letter-routing-key", "dlq.image")
.build();
}
// ... Topic Exchange, Binding 等配置
}
5.2 ImageGenerationConsumer 消费、重试与死信
消费者是执行任务的核心。它需要处理各种异常,并通过拒绝(basic.nack)来实现重试。
@Component
@RabbitListener(queues = "#{standardImageQueue.name}")
public class ImageGenerationConsumer {
@Autowired
private ModelRouter modelRouter;
@Autowired
private MinioService minioService;
@RabbitHandler
public void onMessage(Message message, Channel channel) {
TaskRequest task = deserialize(message.getBody());
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// 1. 更新Redis状态为 PROCESSING
redisTemplate.opsForValue().set("task:" + task.getTaskId(), "PROCESSING");
// 2. 路由并生成
ImageModel model = modelRouter.routeWithFallback(task.getRequirement());
ImageResult result = model.generate(task.getImagePrompt());
// 3. 上传MinIO
String imageUrl = minioService.upload(result.getImageUrl(), task.getTaskId());
// 4. 更新Redis状态为 COMPLETED, 并存储结果
saveTaskResult(task.getTaskId(), imageUrl);
channel.basicAck(deliveryTag, false);
} catch (NonRetryableException e) {
// 业务异常(如Prompt违规),不重试,直接存入死信
channel.basicNack(deliveryTag, false, false);
} catch (Exception e) {
// 网络超时等临时故障,拒绝并重新入队(带指数退避)
// 这里简化处理,实际可通过消息头记录重试次数
channel.basicNack(deliveryTag, false, true);
}
}
}
生产者端,VIP 用户的任务会被发布到高优队列。
@Service
public class ImageGenService {
public String submitTask(ImagePrompt prompt, TaskRequirement req, String userId) {
String taskId = UUID.randomUUID().toString();
TaskRequest request = new TaskRequest(taskId, prompt, req, userId);
rabbitTemplate.convertAndSend("content.generation",
"image.high", // 根据VIP状态动态选择routingKey
request, msg -> {
msg.getMessageProperties().setPriority(10);
return msg;
});
// 初始化Redis状态
redisTemplate.opsForValue().set("task:" + taskId, "PENDING");
return taskId;
}
}
5.3 WebSocket 实时推送任务状态
前端不再通过轮询 GET /task/{taskId} 来获取状态,而是通过 WebSocket 监听服务端推送,极大降低服务器压力。
// 当任务状态变更时(由消费者或其他服务触发)
@Service
public class TaskNotificationService {
private final SimpMessagingTemplate messagingTemplate;
public void notifyUser(String userId, TaskStatusUpdate update) {
messagingTemplate.convertAndSendToUser(
userId,
"/queue/tasks",
update
);
}
}
5.4 双 11 批量生成任务异步队列处理时序图
sequenceDiagram
participant BatchSvc as BatchGenService
participant RabbitMQ
participant Consumers as 5 x Consumers
participant Redis
participant WebSocket
participant User
BatchSvc->>BatchSvc: 解析CSV, 生成100个TaskRequest
loop 提交100个任务
BatchSvc->>RabbitMQ: publish(high/standard priority)
BatchSvc->>Redis: SET task:123=PENDING
end
par 并发消费
Consumer1->>RabbitMQ: consume(prefetch=1)
Consumer1->>Redis: SET task:123=PROCESSING
Consumer1->>Consumer1: generate & upload
Consumer1->>Redis: SET task:123=COMPLETED + resultUrl
Consumer1->>WebSocket: notifyUser(taskId, COMPLETED)
WebSocket-->>User: push progress
and
Consumer2->>RabbitMQ: consume...
end
图 5-1:双 11 批量生成异步队列处理时序图
- 主旨概括:展示了批量任务如何被解析、发布到队列,并由多个消费者公平、并发地消费,最终通过 WebSocket 实时通知用户的状态流转全过程。
- 逐元素分解:
- 生产者:
BatchGenService是典型的“发射后不管”模式,它只负责将 100 个任务快速提交到 RabbitMQ 和初始化 Redis 状态,不等待任何结果。 - 公平调度:5 个消费者并行工作。通过设置
prefetch_count=1,RabbitMQ 采用 Round-Robin 分发,确保每个消费者一次只处理一个任务,处理完再取下一个,有效避免任务不均。 - 实时反馈:消费者的最后一步是将状态变更通过 WebSocket 推送给用户,前端可以毫秒级地看到每个任务的进度条更新。
- 生产者:
- 设计原理映射:这是生产者-消费者模式与观察者模式的结合。任务队列解耦生产与消费速度;WebSocket 推送让前端变为一个响应式的观察者,不再主动轮询。
- 工程联系与关键结论:生产致命误配置:若未设置
prefetch_count=1(RabbitMQ 默认是无限制的),则会发生严重的负载不均。一个处理慢的消费者可能会不断积压消息,而另一个快的消费者可能空闲。更重要的是,积压的消费者可能会因内存溢出而崩溃。在生产环境,为消费者设置prefetch_count=1或一个合理的较低值,是实现公平调度的基本要求。
6. Agent 工具集成与自动化工作流
6.1 多模态能力 Tool 封装
我们将平台的服务能力封装为符合 LangChain4j 规范的 Tool,使 AI Agent 能够通过 Function Calling 自动调用。
@Component
public class ContentGenTools {
@Tool("根据文字描述生成图片")
public ImageResult generateImage(@P("图片描述") String prompt,
@P("图片风格") String style,
@P("图片尺寸") String size) {
return imageGenService.submitAndWait(generateImagePrompt);
}
@Tool("移除图片背景")
public ImageResult removeBackground(@P("图片URL") String imageUrl) {
// 调用一个预先配置好的ComfyUI“抠图”工作流
return workflowService.execute("template-remove-bg", Map.of("image_url", imageUrl));
}
@Tool("高清放大图片")
public ImageResult upscaleImage(@P("图片URL") String imageUrl, @P("放大倍数") int scale) {
return workflowService.execute("template-upscale", Map.of("image_url", imageUrl, "scale", scale));
}
}
6.2 营销自动化:Plan-Solve 模式生成端午海报
市场人员在聊天窗口输入:“生成一组端午节促销海报,3 张不同风格,包含公司 Logo 和促销文案”。
AI Agent 通过 Plan-Solve 模式进行任务规划与执行:
- Plan (LLM):生成 3 组 Prompt(传统中国风、现代简约风、卡通趣味风)。
- Act (Tool Call):并行调用 3 次
generateImage。 - Observe (Result):收到 3 张初稿图片 URL。
- Act (Tool Call):依次为每张图调用
removeBackground(抠图)和addOverlay(叠加 Logo/文案,这也是一个工作流 Tool)。 - Finalize:提交
HumanReviewService进行人工终审。
量化效果:传统流程需协调设计师、文案、审核员,耗时约 4 小时。Agent 自动化完成初稿到提交审核仅需 8 分钟,效率提升 30 倍,人力成本降低 95%。
7. 贯穿案例:双 11 大促批量营销内容生成
现在,让我们将以上所有组件组合起来,面对真实世界的挑战。
场景定义
- 需求:3 天内交付 100 张不同品类(手机/电脑/家电)和风格(科技/温馨/潮流)的促销海报,以及 3 段 15 秒的宣传视频。
- 资源:2 名设计师(仅负责人工终审),后端平台资源(4 GPU ComfyUI 集群)。
- 预算:严格控制 API 调用成本。
7.1 10 步全流程推演与成本分析
- 需求提交:市场运营将需求整理成 CSV 文件,通过 Web UI 上传。
- 自动规划与分派:
BatchGenService解析 CSV,为每一行组合自动生成详细的 Prompt(LLM 辅助),并封装为 100 个高优图像和 3 个标准视频任务,投递到 RabbitMQ。 - 并发生成:5 个
ImageGenerationConsumer并发处理,ModelRouter根据风格智能路由(科技→Flux Pro,温馨→Midjourney,潮流→本地 SDXL Turbo)。 - 自动审核拦截:
ContentAuditor自动拦截了 3 张涉及“穿着过于暴露”的人物生成图,任务自动标记失败,并通过 Kafka 事件通知市场人员修改 Prompt。 - 自动后处理:余下 97 张通过的图片,由工作流自动执行
removeBackground和addOverlay(叠加促销文案),并嵌入数字水印。 - 视频初稿生成:3 段视频任务由
VideoGenService调用 Runway Gen-3 API 生成了初稿。 - 人工终审:97 张海报和 3 段视频进入 Camunda 工作流队列。2 名设计师在专有审核界面进行终审,每人每小时可审阅约 50 张,2 小时内完成。最终驳回 5 张(排版瑕疵),通过 95 张。
- 多渠道发布:
ReviewService通过 Kafka 发送审批通过事件,下游的 CMS 系统和社交媒体发布系统消费这些事件,自动上架物料。 - 成本结算:平台自动生成月度账单。本地 GPU 耗时折合 ¥45,API 调用费 ¥40,总成本 ¥85。传统人工设计 48 工时,人力成本超 ¥5,000。
- 全流程耗时:从上传 CSV 到完成发布,总耗时约 6 小时,效率相较纯人工提升约 8 倍。
7.2 失败场景:GPU 节点宕机应急
双 11 前夕,4 台 GPU 节点中的 2 台因散热故障突然宕机。剩余 2 台 GPU 利用率瞬间飙升至 95%,RabbitMQ 任务队列深度从 20 猛增至 200。
- 告警触发:
ComfyUIMonitor采集到comfyui_queue_depth > 100且gpu_utilization > 90%,触发 PagerDuty P1 告警。 - 紧急响应:运维人员收到告警后,立即通过 K8s HPA 的控制器,将 ComfyUI Deployment 的
replicas从 4 手动扩容至 8(启动备用节点)。 - 自动消化:新 Pod 启动注册后,
ComfyUILoadBalancer自动发现新节点。3 个空闲的消费者开始将积压任务分发到新节点。 - 恢复与复盘:5 分钟后,队列深度降至 50 以下,系统恢复健康。
- 事后优化:将 HPA 策略从单一的 GPU 利用率指标,增加
comfyui_queue_depth指标,实现真正的基于业务表现的自动弹性伸缩,避免未来人工介入的延迟。
7.3 贯穿案例完整 10 步时序图
sequenceDiagram
actor Market as 市场人员
participant Web
participant BatchSvc
participant RabbitMQ
participant Consumer
participant Router
participant ComfyUI
participant AuditSvc
participant HumanSvc
participant CMS
Market->>Web: 1. 上传CSV(100行)
Web->>BatchSvc: 2. 解析CSV, 生成Prompts, 提交100个Job
BatchSvc->>RabbitMQ: 3. publish 100 msg
par 并发生成
loop 5 Consumers
Consumer->>RabbitMQ: 4. consume msg
Consumer->>Router: route()
Router-->>Consumer: FluxPro
Consumer->>ComfyUI: 5. generate()
ComfyUI-->>Consumer: ImageResult
Consumer->>AuditSvc: 6. audit()
AuditSvc-->>Consumer: 3/100 REJECTED
Consumer->>ComfyUI: 7. post-process workflow
Consumer->>RabbitMQ: ack
end
end
Consumer->>HumanSvc: 8. submit 97 imgs + 3 videos for review
HumanSvc-->>Market: Review UI
Market->>HumanSvc: 9. approve 95, reject 5
HumanSvc->>Kafka: 10. publish review events
Kafka->>CMS: consume & auto-publish
图 7-1:双 11 营销内容从需求到发布完整 10 步时序图
- 主旨概括:此图端到端地展示了双 11 案例的全流程,验证了各个微服务和中间件如何像精密齿轮一样咬合,共同完成复杂业务目标。
- 逐元素分解:
- 全自动流水线(1-7步):从 CSV 文件上传那一刻起,系统就进入了无人值守的自动驾驶模式,经历规划、生成、审核、后处理,直到提交人工节点。这是一个典型的批处理工作流。
- 人机协作(8-9步):人工审核是平台有意设置的“决策点”,保证了品牌安全。人与 AI 的职责清晰划分:AI 负责 0 到 1 的规模化和标准化,人负责 1 到 1.1 的质量与风险把控。
- 事件驱动的最终分发(第10步):内容发布不再是紧耦合的点对点连接,而是通过 Kafka 事件广播,CMS、社交媒体等下游系统各自订阅感兴趣的事件。这赋予了系统极高的可扩展性。
- 设计原理映射:整个案例是 Saga 模式在业务层的宏观体现。每一个步骤(生成、审核、发布)都是一个本地事务,通过消息(RabbitMQ/Kafka)串联成一个最终一致性的长流程。如果后续新增一个“通知邮件服务”,只需订阅 Kafka Topic,而无需修改核心流程。
- 工程联系与关键结论:容量规划陷阱:假如在场景规划时,只考虑了平均生成时间,而没有考虑排队时间和重试时间。当 100 个任务瞬间涌入,大量任务在队列中等待和被重试,导致部分任务的端到端完成时间远超预期,可能拖慢整个人工审核计划。必须进行峰值压力测试,并根据 P99 延迟来规划消费者数量和任务超时时间,而不是简单的平均延迟。
8. 与前后系列的衔接
本文构建的多模态内容工厂,是整个 AI 应用专家知识体系中的重要一环,它与系列中的多篇文章形成了紧密的复用和递进关系。
- 关联《LangChain4j 源码透析》(系列二第2篇):本文的
ImageModel和VideoModel接口,正是基于第二篇对 LangChain4j 模型抽象层的源码分析进行设计的。我们将框架的理念应用于真实业务场景,并扩展了多厂商实现、智能路由和内容审核等企业级能力。 - 关联《Agent 安全机制》(系列四第7篇):本文构建的**“Prompt → 内容 → 水印 → 人工”四层审核链**,是第 7 篇通用安全原则在多模态生成场景下的具体实例化。
PromptAuditor的责任链模式、Camunda 审批工作流的集成,都是对安全机制中 HITL 和全链路审计原则的实践。 - 关联《企业级 Agent 平台》(系列五第5篇):本文 ComfyUI 集群的 K8s 部署、基于 HPA 的弹性伸缩、以及 RabbitMQ 任务队列的基础设施,均构建于第 5 篇的企业级平台底座之上。本文是平台能力向特定业务领域(内容创意)的延伸。
- 关联《Agent 规划系统实战》(系列四第6篇):第 6 节的营销自动化案例中,Agent 使用 Plan-Solve 模式自动执行“端午海报生成”任务,这是第 6 篇规划系统在多模态 Tool 调用场景下的直接应用,证明了规划能力的通用性。
9. 面试高频专题
1. 如何设计一个统一的 ImageModel 接口来屏蔽多厂商的差异?
- 一句话回答:定义统一的输入输出对象(
ImagePrompt/ImageResult)和接口方法generate(),每个厂商(SD、DALL-E、MJ)分别实现该接口,内部封装各自的 API 调用和参数转换。 - 详细解释:接口定义了一套标准化的契约。
ImagePrompt封装了生成所需的所有高阶参数(正向/负向Prompt、尺寸、风格),而不是在接口方法中传十几个零散参数。各实现类负责将ImagePrompt翻译成特定厂商 API 所需的格式。例如,SD 需要正负向 Prompt,DALL-E 3 则通过 API 自动生成负向提示词,此时StableDiffusionModel的实现会用到negativePrompt,而DalleModel可能就忽略它。客户端(如ImageGenService)只需面向ImageModel接口编程,未来新增 Flux 模型只需新增一个实现类,完全符合开闭原则。 - 多角度追问:
- 追问1:如果不同模型支持的尺寸参数完全不同怎么办? 答:在
ImagePrompt中设计一个通用的尺寸适配器。客户端传入通用尺寸(如"1024x1024"),各实现在generate内部将其转换为模型支持的尺寸。如果模型不支持,可以内部进行裁剪或缩放。路由层ModelRouter也可以参与,如果一个模型的输出尺寸列表不包含用户需求,该模型在第一阶段过滤就会被排除。 - 追问2:想同时调用多个模型生图做 A/B test,怎么设计? 答:可以引入一个
CompositeImageModel,它实现了ImageModel接口,内部聚合了多个ImageModel实例。其generate方法会并发调用所有实例,并返回一个List<ImageResult>。 - 故障深挖:若某模型 API 密钥泄露,所有通过该模型的请求都可能被恶意利用,如何防止或最小化影响? 答:应在
DalleModel等实现中加入客户端级别的限流和计费检查。同时,在基础设施层,用 Vault 等工具动态管理密钥,实现密钥轮换。平台侧应能实时监控每个模型的 API 调用量和费用,设置每日硬性上限,一旦触发立即熔断该模型的所有请求,并自动降级到备用模型。
- 追问1:如果不同模型支持的尺寸参数完全不同怎么办? 答:在
2. ModelRouter 如何处理多维决策?如果一个新模型上线,如何优雅地把它加进路由?
- 一句话回答:
ModelRouter使用“硬约束过滤 + 加权评分”两步法。新模型只需实现ImageModel接口并注册为 Spring Bean,就会被自动发现。 - 详细解释:
ModelRouter通过依赖注入获得Map<String, ImageModel>,Spring 会自动将所有ImageModel的实现放入这个 Map。因此,新模型加入零侵入。决策时,route方法首先进行硬约束过滤,排除不满足延迟、预算硬指标的模型。然后,基于TaskRequirement中由业务方传入的质量、成本、速度三个维度的权重,结合从ModelMetricsService(一个抽象)获取的各模型实时 CLIP Score、P99 延迟、Cost/Image 等指标,计算出一个归一化后的加权总分,最终选择得分最高的模型。 - 多角度追问:
- 追问1:
ModelMetricsService如何保证路由决策基于最新数据? 答:ModelMetricsService内部不能是静态配置。它应该是一个从 Prometheus/Grafana 定期拉取指标的计算层,或者直接消费 Kafka 中的模型调用事件流,维护一个带有短 TTL(如 30 秒)的本地实时指标缓存。过期的数据会让路由决策不准确。 - 追问2:如果一个模型质量很好但偶尔会超时,如何避免它被彻底拉黑? 答:这是 断路器模式 的用武之地。不能因为一次超时就永久降级。
ModelMetricsService应该统计一个滑动窗口(如最近 100 次调用)的错误率。当错误率超过阈值(如 10%)时,ModelRouter才临时将其从候选名单中移除。同时,主流程(ImageGenService)可以有一个后台任务去半试探性地调用该模型,一旦恢复就将其重新加入候选池。 - 故障深挖:如果 ComfyUI 集群全部 GPU 节点不可用,平台应如何保障最低限度的图片生成能力? 答:平台必须实现一个全局兜底策略。当
ModelRouter发现没有任何本地模型可用时,应能自动、无缝地将所有“非高质量需求”级别的任务切换到最便宜的 CPU 模式(如果支持)或专用的 Failover API(如使用某个预留了额度的 DALL-E 2 API)。系统应向管理员发送最高级别告警,并在 UI 上提示用户“高性能模式不可用,当前为应急模式”。
- 追问1:
3. ComfyUI 工作流引擎中,模板的动态参数替换是如何实现的?有什么安全风险?
- 一句话回答:通过正则表达式匹配 JSON 模板中的
{{key}}占位符,并用传入参数 Map 中的值进行替换。核心风险是 JSON 注入。 - 详细解释:
WorkflowService.execute()从 MinIO 加载含占位符的 JSON 模板。然后使用Pattern.compile("\\{\\{\\w+\\}\\}")遍历匹配。对于每个匹配,从params中取出对应 key 的值,使用Matcher.quoteReplacement进行安全替换,防止值中的特殊字符破坏 JSON 结构。这是关键的安全措施。 - 多角度追问:
- 追问1:如果参数值本身就是一个复杂的 JSON 对象,如何处理? 答:简单的字符串替换不适用。此时,我们应该先将模板 JSON 反序列化为
JsonNode树,然后递归遍历节点,找到值中包含{{...}}的文本节点,并将其替换为paramsMap 中的相应对象。这样可以保持 JSON 结构完整性。 - 追问2:模板版本更新后,如果有正在执行的任务怎么办? 答:任务在提交时,其使用的模板版本 ID 会被记录在
TaskRequest中。执行时,ComfyUIWorkflowEngine必须使用任务中指定的确切版本来加载模板 JSON,而不是直接用最新版本。这保证了正在执行的管线的幂等性和可复现性。 - 故障深挖:有用户恶意构造了一个值,替换后使整个工作流 JSON 异常庞大(如内含一个巨大的 Base64 图片),导致 ComfyUI Server 解析时 OOM,如何防范? 答:必须在
WorkflowService层进行参数校验。在替换参数前,检查所有输入参数的长度和大小。对于image_url这类参数,如果它是 Base64 编码的,要检查其解码后的分辨率是否超过限制(如 4096x4096),或文件大小是否超过上限。拒绝处理任何异常大的参数,并记录安全告警。
- 追问1:如果参数值本身就是一个复杂的 JSON 对象,如何处理? 答:简单的字符串替换不适用。此时,我们应该先将模板 JSON 反序列化为
4. 异步任务系统中,消费者死循环或处理过慢导致消息积压,应如何设计和监控?
- 一句话回答:设计死信队列 (DLQ) 处理消费失败的消息,并基于 RabbitMQ 队列深度和消费者处理延迟设置 Prometheus 监控与 Grafana 告警。
- 详细解释:声明队列时指定死信交换机和路由键,并设置
prefetch_count=1实现公平调度。ImageGenerationConsumer捕获异常,对业务异常(NonRetryableException)直接拒绝不入队;对临时故障(IOException),使用指数退避重试。重试超限后,消息进入死信队列。监控层面,我们基于 Micrometer 导出指标:rabbitmq_queue_depth和consumer_process_time_seconds。当队列深度 > 100 或 P99 延迟 > 60s,Prometheus Alertmanager 触发告警。死信队列的堆积量也是重要指标,因为它意味着有任务无法自动恢复。 - 多角度追问:
- 追问1:
prefetch_count=1会不会影响整体吞吐量? 答:在生成图片这种 IO/GPU 密集型任务中,prefetch_count=1几乎不会影响吞吐量,反而通过避免单消费者过载确保了整个系统的稳定性。如果想提升,可以设为 2 或 3,但必须经过严格的压力测试,确保单个消费者在处理 N 个并发任务时内存和 GPU 显存不会溢出。 - 追问2:如何处理需要按顺序处理的视频关键帧? 答:可以为视频任务创建单独的队列。使用 RabbitMQ 的 message group 特性或 Kafka 的 partition key,将同一个视频 ID 的所有关键帧任务发送到同一个分区,由一个消费者顺序处理,保证时序正确。
- 故障深挖:监控系统本身故障,无法感知队列积压。如何利用平台自身能力做兜底监控? 答:在
BatchGenService或一个独立的监控服务中,定期采样任务的平均等待时间。做法是:在TaskRequest中打上入队时间戳,消费者在开始处理时计算等待时延,并写入一个 RedisZSET。后台服务每隔 1 分钟计算 P99 等待时延,如果超过阈值(如 3 分钟),则绕过 Prometheus 直接通过钉钉/飞书 Webhook 发出告警。
- 追问1:
5. 内容审核链中,如果某个审核环节持续失败或超时,整个平台的生成任务都会阻塞吗?如何设计容错?
- 一句话回答:不会。必须为每个审核环节设置超时和熔断机制,并配置一个全局的“安全优先”或“可用性优先”的降级策略。
- 详细解释:对每个实现
AuditHandler的 Bean 进行 AOP 包装,使用 Resilience4j 的@CircuitBreaker。以ModerationModelFilter为例,当它调用外部 API 时,设置超时 2s。如果 60 秒内失败率超过 50%,断路器跳闸。此时,PromptAuditor可以捕获CircuitBreakerOpenException,并根据配置决定如何处理:对于营销类强合规需求,安全优先,直接拒绝所有未经 AI 审核的 Prompt;对于内部测试等低风险场景,可用性优先,忽略该环节,让请求通过并记录高风险日志,进入后续人工审核。 - 多角度追问:
- 追问1:某类促销文案频繁被误杀,如何优化而不直接关闭过滤? 答:这正是引入人工反馈闭环的价值。审核员在驳回时可选择“误报”。系统统计高频误报词,分析后将其从
RegexFilter的黑名单移到灰名单,或提交给ModerationModelFilter算法团队作为负样本进行微调。 - 追问2:如何防止用户通过拆分 Prompt 绕过审核? 答:我们的审核不仅是基于单个 Prompt,更要基于上下文。如果一个用户在短时间内连续提交了多段看似无害的文本,但这些文本组合起来构成违规内容,系统很难识别。更高级的防御是在 Agent 层,由 LLM 对用户的整个会话上下文进行整体意图分析,而不是只审核最后一条 Prompt。
- 故障深挖:如果 OCR 服务被恶意攻击,向它传入一张经过特殊构造、能使服务 CPU 100% 的“解压炸弹”图片,如何防御? 答:必须在
ContentAuditor调用 OCR 之前,对图片进行安全预处理。包括:1. 检查图片文件头和尺寸,拒绝超大文件;2. 使用 ImageMagick 等工具对图片进行“消毒”(如去除所有元数据、限制最大像素数、转换为安全格式);3. 在一个独立的、资源受限的 Sandbox 容器中运行 OCR 服务,确保即使攻击成功也无法影响整个平台。
- 追问1:某类促销文案频繁被误杀,如何优化而不直接关闭过滤? 答:这正是引入人工反馈闭环的价值。审核员在驳回时可选择“误报”。系统统计高频误报词,分析后将其从
6. 如何为这个内容生成平台设计一个面向全球市场、满足合规和实时协作的多语言多模态内容中台?【系统设计题】
- 系统要求: ① 支持根据用户语言自动切换 Prompt,生成对应语言文案的图片。 ② 全球多 Region 部署,欧洲用户数据满足 GDPR。 ③ 支持 A/B 测试不同风格模板,自动优化推荐。 ④ 支持实时协作:多设计师可同时对同一工作流模板进行编辑和评论。
- 全球架构图:
flowchart LR
subgraph DNSRouting["DNS与路由"]
GeoDNS["GeoDNS<br/>智能解析与就近接入"]
end
subgraph RegionEU["区域 EU (欧盟 Region)"]
direction TB
APIGW_EU["API 网关"] --> Auth["统一认证"]
APIGW_EU --> Svcs_EU["业务服务集群<br/>Image/Video/Workflow Service"]
Svcs_EU --> MQ_EU["RabbitMQ"]
Svcs_EU --> MinIO_EU["MinIO<br/>主存储"]
MQ_EU --> Inference_EU["模型推理集群<br/>ComfyUI/APIs"]
MinIO_EU <== "异步复制" ==> MinIO_NA
end
subgraph RegionNA["区域 NA (北美 Region)"]
direction TB
APIGW_NA["API 网关"] --> Auth
APIGW_NA --> Svcs_NA["业务服务集群"]
Svcs_NA --> MQ_NA["RabbitMQ"]
Svcs_NA --> MinIO_NA["MinIO<br/>主存储"]
MQ_NA --> Inference_NA["模型推理集群"]
end
subgraph GlobalServices["全局服务 (Global Services)"]
direction LR
PostgreSQL["PostgreSQL<br/>多主/强一致元数据"]
Redis["Redis Cluster<br/>会话与协作锁"]
Kafka["Kafka<br/>全局事件总线"]
LangSvc["多语言 Prompt 优化服务"]
end
GeoDNS --> APIGW_EU
GeoDNS --> APIGW_NA
Auth --> PostgreSQL
Svcs_EU <--> Redis
Svcs_EU <--> Kafka
Svcs_EU <--> LangSvc
%% 子图背景色(极浅莫兰迪色系)
classDef subDNS fill:#f0f4ff,stroke:#94a3b8,stroke-width:1.5px
classDef subEU fill:#f0fff4,stroke:#94a3b8,stroke-width:1.5px
classDef subNA fill:#fef9f0,stroke:#94a3b8,stroke-width:1.5px
classDef subGlobal fill:#f5f0ff,stroke:#94a3b8,stroke-width:1.5px
%% 节点样式
classDef dnsNode fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#1e3a8a
classDef euNode fill:#d1fae5,stroke:#10b981,stroke-width:1.5px,color:#065f46
classDef naNode fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#92400e
classDef globalNode fill:#ede9fe,stroke:#8b5cf6,stroke-width:1.5px,color:#4c1d95
classDef database fill:#cffafe,stroke:#06b6d4,stroke-width:1.5px,color:#155e75
class GeoDNS dnsNode
class APIGW_EU,Auth,Svcs_EU,MQ_EU,Inference_EU euNode
class MinIO_EU database
class APIGW_NA,Svcs_NA,MQ_NA,Inference_NA naNode
class MinIO_NA database
class PostgreSQL,Redis,Kafka,LangSvc globalNode
class PostgreSQL,Redis,Kafka database
class DNSRouting subDNS
class RegionEU subEU
class RegionNA subNA
class GlobalServices subGlobal
- 一个欧洲用户生成图片并审核发布的完整时序图:
sequenceDiagram
actor EU_User as 欧洲用户 (DE)
participant GeoDNS
participant EU_GW as EU API GW
participant PromptSvc as Prompt优化服务
participant EU_GenSvc as EU生成服务
participant EU_AuditSvc as EU审核服务
participant EU_MinIO as EU MinIO
participant Global_Kafka as Global Kafka
EU_User->>GeoDNS: 请求:生成“新年快乐”海报
GeoDNS-->>EU_User: 解析到 EU Gateway
EU_User->>EU_GW: 请求(含 user:de_DE locale)
EU_GW->>PromptSvc: optimize("新年快乐", de_DE)
PromptSvc-->>EU_GW: "Frohes Neues Jahr, berliner Dom style"
EU_GW->>EU_GenSvc: generate(prompt: "Frohes Neues Jahr...")
EU_GenSvc->>EU_GenSvc: route & generate image
EU_GenSvc->>EU_AuditSvc: audit()
EU_AuditSvc-->>EU_GenSvc: passed
EU_GenSvc->>EU_MinIO: store image
EU_MinIO-->>EU_GenSvc: image_url
EU_GenSvc->>Global_Kafka: publish(Event: IMAGE_GENERATED, region: EU)
EU_GenSvc-->>EU_User: taskId + preview_url
- 跨 Region 备份与故障恢复分析:
- MinIO 配置了 异步复制策略 (
mc mirror),EU Region 的存储桶所有变化会近乎实时地复制到北美 (NA) Region 的灾备桶中。元数据(图片 URL)存储在全局 PostgreSQL 中。 - 当 EU Region 的 MinIO 完全故障时:
- 自动熔断:
EU_GenSvc中调用 MinIO 的客户端会触发熔断(Resilience4j)。 - 读取降级:读取图片的请求,API 网关层面会解析请求中的图片 ID,查询 PostgreSQL 获取其全球可访问的 CDN/灾备 URL,并重定向到 NA Region 的 MinIO 节点。
- 写入降级:新的内容生成任务会被
EU_GenSvc临时调度,直接将内容上传至 NA MinIO,同时更新 PostgreSQL 中的primary_storage标记。待 EU 恢复后,通过反向同步追平数据。 - 用户影响最小化:对于终端用户,图片的访问和上传操作延迟会略微增加,但服务不会中断,且已生成的历史图片绝不会丢失。
- 自动熔断:
- MinIO 配置了 异步复制策略 (
- 多语言 Prompt 自动翻译和风格适配技术方案:
- 语言与风格映射库:维护一个
locale -> { translation_keys, style_prompt }的配置。例如:"zh_CN"+"新年快乐"→style_prompt = "传统中国红, 灯笼, 剪纸风格""en_US"+"新年快乐"→translated = "Happy New Year",style_prompt = "Times Square celebration, fireworks"
- 大模型驱动优化 (PromptSvc):规则覆盖不到的长尾场景,调用 LLM。我们设计一个 Few-shot Prompt:“你是多语言节日营销专家。将用户的原始描述翻译并适配为对应文化背景的图像生成 Prompt。示例:输入:’中秋快乐‘, locale=’zh_TW‘ -> ’Mid-Autumn Festival bliss, Taiwanese mooncake, lanterns, family gathering‘。现在,请处理:输入’{user_input}‘, locale=’{locale}‘。”
- A/B 测试与模板推荐:为每个模板添加
meta标签。通过 Kafka 实时流,将模板的曝光和最终素材的点击率、转化率(需对接下游业务系统)关联。利用 Spark Streaming 计算每个模板在不同地域、不同节日场景下的“有效转化率”。推荐系统 API 定期从计算结果中拉取 Top-N 模板,并显示在前端,或由ModelRouter自动选用。
- 语言与风格映射库:维护一个