
构建AI人脸生成SaaS平台Vue前端与.NET后端集成Qwen-Image-Edit-F2P最近在做一个挺有意思的项目帮一个创意团队搭建一个AI人脸生成平台。他们想做一个SaaS服务让用户能在线生成各种风格的人像从写实肖像到动漫角色都能搞定。核心需求很明确前端要好看好用后端要稳定能扛住流量还要能无缝调用底层的AI模型。我们最终敲定的技术栈是前端用Vue 3后端用.NET CoreAI推理引擎则通过ComfyUI来调度Qwen-Image-Edit-F2P模型。这个组合听起来可能有点“混搭”但实际跑下来在开发效率、性能和维护性上找到了一个不错的平衡点。今天就来聊聊在这种前后端分离的架构下怎么把大模型推理服务安全、高效地集成进来并且能从容应对可能的高并发场景。1. 为什么选择这个技术栈在项目启动前我们对比过几种方案。全栈JavaScriptNode.js React固然统一但在处理复杂后台任务和与Python生态的AI服务交互时总觉得.NET Core的健壮性和C#的强类型更有优势。而Vue 3的响应式和组合式API对于构建动态、交互复杂的图像生成界面又特别顺手。Qwen-Image-Edit-F2P这个模型是我们技术选型的核心。它本身是一个功能强大的图像编辑模型我们主要利用其人脸生成和风格转换的能力。但它不像一些提供标准HTTP API的云服务我们需要一个“翻译官”和“调度员”来跟它沟通。这就是ComfyUI出场的时候了。你可以把ComfyUI想象成一个可视化的AI工作流编排器。我们把Qwen模型的调用逻辑包括加载模型、预处理输入、执行推理、后处理输出都封装成了一个可复用的ComfyUI工作流。后端.NET服务只需要告诉ComfyUI“执行A号工作流这是用户给的描述词和风格参数”然后等着拿结果就行。这种解耦让后端逻辑变得非常清晰。所以整体的数据流是这样的用户在Vue前端操作 - 请求发送到.NET后端API - .NET后端将任务放入队列并调用ComfyUI服务 - ComfyUI执行工作流调用Qwen模型 - 生成结果返回给后端 - 后端更新任务状态前端获取结果并展示。2. Vue 3前端构建用户友好的生成工坊前端的目标是降低用户的使用门槛把复杂的AI生成过程包装成一个直观、愉悦的操作界面。我们基于Vue 3和Element Plus组件库来搭建。2.1 核心交互界面设计主界面我们分成了三个主要区域左侧的生成控制面板中间的实时预览画布以及右侧的历史成果画廊。在控制面板里最重要的就是Prompt输入框。我们并没有只做一个简单的文本框。为了提升生成质量我们设计了一个“提示词助手”组件。当用户输入“一个微笑着的亚洲女性职业装”时助手会浮出建议比如“是否需要补充发色、光线环境如‘办公室柔光’、细节描述如‘精致的耳环’”。这背后是一个轻量级的语义联想服务能有效引导用户输入更丰富、模型更容易理解的描述。风格选择我们做成了可视化卡片。除了常见的“写实照片”、“二次元动漫”、“油画肖像”、“科幻CG”等风格我们还允许团队运营人员后台配置“风格融合”参数。比如可以创建一个“复古漫画”风格它实际上是70%的“美式漫画”加30%的“做旧质感”参数组合。用户点击卡片对应的风格参数就会传递给后端。2.2 状态管理与实时反馈图像生成是个耗时操作良好的状态管理和反馈至关重要。我们使用Pinia来管理全局状态核心是一个generationTaskStore。// stores/generationTask.js import { defineStore } from pinia export const useGenerationTaskStore defineStore(generationTask, { state: () ({ currentTaskId: null, taskQueue: [], // 排队中的任务 activeTasks: {}, // 执行中的任务 { taskId: { status, progress, previewUrl? } } completedGallery: [] // 已完成的作品 }), actions: { async submitGeneration(prompt, style) { const taskInfo { prompt, style, timestamp: Date.now() } this.taskQueue.push(taskInfo) // 调用后端API const response await fetch(/api/generate/submit, { method: POST, body: JSON.stringify(taskInfo) }) const { taskId } await response.json() this.currentTaskId taskId this.activeTasks[taskId] { status: queued, progress: 0 } this.startPolling(taskId) // 开始轮询任务状态 }, async startPolling(taskId) { const poll async () { const res await fetch(/api/task/status/${taskId}) const data await res.json() this.activeTasks[taskId] data if (data.status completed) { this.completedGallery.unshift({ id: taskId, imageUrl: data.resultUrl, prompt: data.prompt, style: data.style }) delete this.activeTasks[taskId] } else if (data.status failed) { console.error(Generation failed:, data.error) delete this.activeTasks[taskId] } else if (data.status processing) { // 继续轮询 setTimeout(poll, 1500) } } poll() } } })在UI上我们为每个进行中的任务在预览区显示一个进度条和占位图。当后端支持时甚至可以接收并展示低分辨率的实时预览图ComfyUI工作流中可以插入预览节点这能极大提升用户的等待体验。3. .NET Core后端稳健的API与任务调度中枢后端是整个平台的大脑负责业务逻辑、任务调度、与ComfyUI通信以及用户计费。我们采用经典的控制器-服务-仓储分层架构。3.1 API设计与用户鉴权我们使用JWT进行用户鉴权。每个生成请求都需要携带有效的Token。GenerationController主要暴露两个关键接口// Controllers/GenerationController.cs [ApiController] [Route(api/[controller])] [Authorize] // 需要登录 public class GenerationController : ControllerBase { private readonly IGenerationService _generationService; [HttpPost(submit)] public async TaskIActionResult SubmitGenerationTask([FromBody] GenerationRequest request) { // 1. 验证用户权限和配额 var userId User.FindFirst(ClaimTypes.NameIdentifier)?.Value; var canProceed await _generationService.CheckUserQuotaAsync(userId); if (!canProceed) { return BadRequest(Insufficient quota or credit.); } // 2. 创建生成任务 var taskId await _generationService.CreateGenerationTaskAsync(userId, request); // 3. 将任务推入队列 _generationService.EnqueueTask(taskId); return Ok(new { taskId }); } [HttpGet(task/status/{taskId})] public async TaskIActionResult GetTaskStatus(string taskId) { var status await _generationService.GetTaskStatusAsync(taskId); return Ok(status); } } public class GenerationRequest { public string Prompt { get; set; } public string Style { get; set; } // 其他参数如分辨率、生成数量等 }3.2 任务队列与ComfyUI服务集成这是后端最核心的部分。我们不能让每个HTTP请求都直接、同步地去调用ComfyUI那样会阻塞请求且无法控制并发。我们引入了后台任务队列这里以Hangfire为例也可用Azure Service Bus、RabbitMQ等。// Services/GenerationService.cs public class GenerationService : IGenerationService { private readonly IComfyUIService _comfyUIService; private readonly IBackgroundJobClient _backgroundJobClient; private readonly ITaskStatusRepository _taskStatusRepo; public void EnqueueTask(string taskId) { // 使用Hangfire将任务排入后台队列 _backgroundJobClient.EnqueueGenerationService(s s.ProcessGenerationTaskAsync(taskId)); } [AutomaticRetry(Attempts 2)] // 失败自动重试2次 public async Task ProcessGenerationTaskAsync(string taskId) { // 1. 从数据库获取任务详情 var task await _taskStatusRepo.GetTaskAsync(taskId); await _taskStatusRepo.UpdateStatusAsync(taskId, processing, 10); // 2. 准备ComfyUI工作流所需的参数 var workflowApiRequest new ComfyUIWorkflowRequest { Prompt task.Prompt, StyleParams GetStyleParameters(task.Style), Seed new Random().Next() }; // 3. 调用ComfyUI服务执行工作流 var result await _comfyUIService.ExecuteWorkflowAsync(face_generation_workflow.json, workflowApiRequest); // 4. 处理结果 if (result.Success) { // 将生成的图片从ComfyUI服务器保存到我们的对象存储如Azure Blob Storage、S3 var imageUrl await SaveImageToStorageAsync(result.Images[0]); await _taskStatusRepo.UpdateStatusAsync(taskId, completed, 100, imageUrl); // 5. 扣减用户配额 await DeductUserQuotaAsync(task.UserId); } else { await _taskStatusRepo.UpdateStatusAsync(taskId, failed, 0, error: result.ErrorMessage); } } }IComfyUIService是对ComfyUI HTTP API的封装。ComfyUI启动后会暴露一个API服务器我们可以通过向其发送一个定义好的工作流JSON并替换其中特定节点的参数来触发执行。// Services/ComfyUIService.cs public class ComfyUIService : IComfyUIService { private readonly HttpClient _httpClient; private readonly string _comfyUIServerUrl; public async TaskComfyUIExecutionResult ExecuteWorkflowAsync(string workflowTemplateName, ComfyUIWorkflowRequest request) { // 1. 加载工作流模板JSON文件 var workflowJson await LoadWorkflowTemplateAsync(workflowTemplateName); // 2. 动态替换工作流中特定节点的输入参数 // 例如找到prompt节点将其text属性替换为request.Prompt var populatedWorkflow PopulateWorkflowParameters(workflowJson, request); // 3. 调用ComfyUI的API执行工作流 var executePayload new { prompt populatedWorkflow }; var response await _httpClient.PostAsJsonAsync(${_comfyUIServerUrl}/prompt, executePayload); // 4. 轮询或通过WebSocket获取执行结果 var result await WaitForExecutionCompletionAsync(response); return result; } }3.3 高并发与稳定性考虑当用户量上来高并发请求是必然的。我们做了以下几层防护队列缓冲所有任务先入队列由后台工作进程按顺序或优先级消费避免瞬间压垮ComfyUI服务。ComfyUI服务池单个ComfyUI实例处理能力有限尤其依赖GPU。我们部署了多个ComfyUI实例后端服务通过简单的负载均衡策略如轮询将任务分发到不同的实例上实现水平扩展。用户配额与限流在API网关或应用层对每个用户/每个IP的请求频率进行限制。结合计费系统确保资源被合理使用。异步与状态分离生成任务完全异步化。用户提交后立即返回一个taskId然后通过轮询或WebSocket来获取进度和结果。这样前端连接不会长时间挂起后端也更灵活。4. 安全与计费考量在SaaS平台中安全和商业化是重中之重。安全方面输入净化对用户输入的Prompt进行严格的过滤和审查防止注入攻击或生成不当内容。输出审查生成的图片在返回给用户前会经过一个轻量级的审核服务可以是另一个AI模型或规则引擎确保内容符合规范。API安全除了JWT鉴权所有对ComfyUI服务的内部调用也使用API密钥或IP白名单进行保护。数据隔离确保用户A无法访问用户B的生成历史和结果图片。计费方面 我们设计了基于“信用点”的计费模型。生成一张标准分辨率的图片消耗1个点高清或批量生成消耗更多。用户注册赠送点数用完需购买套餐。每次任务成功后GenerationService会调用计费服务扣减点数。所有消费记录清晰可查便于用户对账和平台运营。5. 部署与运维整个系统部署在云上采用容器化部署。前端Vue应用构建为静态文件托管在对象存储或CDN上。后端.NET Core API服务打包成Docker镜像运行在Kubernetes集群中可以根据负载自动伸缩。ComfyUI服务每个实例也打包成包含模型文件的Docker镜像运行在具有GPU能力的节点上。通过Kubernetes的Horizontal Pod Autoscaler可以根据队列长度自动增减实例。数据库与队列使用云托管的数据库和消息队列服务减少运维负担。监控方面我们集成了应用性能监控工具来追踪API响应时间、任务队列长度、ComfyUI实例的健康状态和GPU利用率确保问题能及时发现和定位。整个项目做下来感觉就像搭积木把Vue的灵活界面、.NET的稳健后台、ComfyUI的AI工作流编排能力还有Qwen模型的生成能力一块块地拼成了一个能实际运转的产品。最大的挑战其实不在单一技术而在于如何让这些异构的组件顺畅、可靠地协同工作并且要考虑到未来的用户增长和功能扩展。目前这个架构运行得挺平稳它把复杂的AI生成任务变成了一个用户可以简单操作的服务。如果你也在考虑构建类似的AI应用希望我们这套前后端分离、通过队列调度集成ComfyUI的思路能给你一些参考。当然每家的具体需求和资源都不一样你可以在这个基础上调整比如用不同的前端框架或者换一种任务队列的实现方式。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。