【2026实战】AIGC应用与智能体开发全栈指南:从Ollama私有化部署到星图AI平台架构深度解析

📌 核心要点:本文系统梳理AIGC应用开发与智能体构建的完整技术栈,涵盖大模型私有化部署、文生图工程化落地、实时通信架构设计、分布式并发控制等核心能力,并以"星图AI"平台为实战载体,提供可直接落地的代码方案与架构思考。
一、写在前面:为什么2026年你必须掌握AIGC全栈能力
三年前,我还在纠结要不要学Prompt Engineering;三年后的今天,企业招聘JD里写的是"能独立完成大模型私有化部署+智能体编排+多模态生成管线"。变化之快,远超预期。
这不是贩卖焦虑。我带过的团队里,去年只会调API的同事今年已经在简历上写"精通LangChain编排"了,而真正拿到高薪offer的,是那些能把模型部署到内网、把ComfyUI工作流封装成微服务、用WebSocket把生成结果实时推给前端的人。
本文不写空泛的概念科普。我要做的事情是:把一套完整的AIGC应用开发技术栈拆解成可执行的技术方案,每个环节都给出能跑的代码和踩过的坑。
二、技术全景:从模型到产品的完整链路
在动手之前,先建立全局认知。一个AIGC应用从底层模型到用户可感知的产品,至少经过以下层次:
#mermaid-svg-oCAN6D4iFEe3g3zv{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oCAN6D4iFEe3g3zv .error-icon{fill:#552222;}#mermaid-svg-oCAN6D4iFEe3g3zv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oCAN6D4iFEe3g3zv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oCAN6D4iFEe3g3zv .marker.cross{stroke:#333333;}#mermaid-svg-oCAN6D4iFEe3g3zv svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oCAN6D4iFEe3g3zv p{margin:0;}#mermaid-svg-oCAN6D4iFEe3g3zv .edge{stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .section–1 rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section–1 path,#mermaid-svg-oCAN6D4iFEe3g3zv .section–1 circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section–1 polygon,#mermaid-svg-oCAN6D4iFEe3g3zv .section–1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section–1 text{fill:#ffffff;}#mermaid-svg-oCAN6D4iFEe3g3zv .node-icon–1{font-size:40px;color:#ffffff;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-edge–1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-depth–1{stroke-width:17;}#mermaid-svg-oCAN6D4iFEe3g3zv .section–1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled circle,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:lightgray;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:#efefef;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-0 rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section-0 path,#mermaid-svg-oCAN6D4iFEe3g3zv .section-0 circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section-0 polygon,#mermaid-svg-oCAN6D4iFEe3g3zv .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section-0 text{fill:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .node-icon-0{font-size:40px;color:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-depth-0{stroke-width:14;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled circle,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:lightgray;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:#efefef;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-1 rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section-1 path,#mermaid-svg-oCAN6D4iFEe3g3zv .section-1 circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section-1 polygon,#mermaid-svg-oCAN6D4iFEe3g3zv .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section-1 text{fill:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .node-icon-1{font-size:40px;color:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-depth-1{stroke-width:11;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled circle,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:lightgray;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:#efefef;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-2 rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section-2 path,#mermaid-svg-oCAN6D4iFEe3g3zv .section-2 circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section-2 polygon,#mermaid-svg-oCAN6D4iFEe3g3zv .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section-2 text{fill:#ffffff;}#mermaid-svg-oCAN6D4iFEe3g3zv .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-depth-2{stroke-width:8;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled circle,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:lightgray;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:#efefef;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-3 rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section-3 path,#mermaid-svg-oCAN6D4iFEe3g3zv .section-3 circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section-3 polygon,#mermaid-svg-oCAN6D4iFEe3g3zv .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section-3 text{fill:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .node-icon-3{font-size:40px;color:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-depth-3{stroke-width:5;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled circle,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:lightgray;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:#efefef;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-4 rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section-4 path,#mermaid-svg-oCAN6D4iFEe3g3zv .section-4 circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section-4 polygon,#mermaid-svg-oCAN6D4iFEe3g3zv .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section-4 text{fill:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .node-icon-4{font-size:40px;color:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-depth-4{stroke-width:2;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled circle,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:lightgray;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:#efefef;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-5 rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section-5 path,#mermaid-svg-oCAN6D4iFEe3g3zv .section-5 circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section-5 polygon,#mermaid-svg-oCAN6D4iFEe3g3zv .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section-5 text{fill:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .node-icon-5{font-size:40px;color:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-depth-5{stroke-width:-1;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled circle,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:lightgray;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:#efefef;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-6 rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section-6 path,#mermaid-svg-oCAN6D4iFEe3g3zv .section-6 circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section-6 polygon,#mermaid-svg-oCAN6D4iFEe3g3zv .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section-6 text{fill:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .node-icon-6{font-size:40px;color:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-depth-6{stroke-width:-4;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled circle,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:lightgray;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:#efefef;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-7 rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section-7 path,#mermaid-svg-oCAN6D4iFEe3g3zv .section-7 circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section-7 polygon,#mermaid-svg-oCAN6D4iFEe3g3zv .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section-7 text{fill:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .node-icon-7{font-size:40px;color:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-depth-7{stroke-width:-7;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled circle,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:lightgray;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:#efefef;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-8 rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section-8 path,#mermaid-svg-oCAN6D4iFEe3g3zv .section-8 circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section-8 polygon,#mermaid-svg-oCAN6D4iFEe3g3zv .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section-8 text{fill:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .node-icon-8{font-size:40px;color:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-depth-8{stroke-width:-10;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled circle,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:lightgray;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:#efefef;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-9 rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section-9 path,#mermaid-svg-oCAN6D4iFEe3g3zv .section-9 circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section-9 polygon,#mermaid-svg-oCAN6D4iFEe3g3zv .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section-9 text{fill:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .node-icon-9{font-size:40px;color:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-depth-9{stroke-width:-13;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled circle,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:lightgray;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:#efefef;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-10 rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section-10 path,#mermaid-svg-oCAN6D4iFEe3g3zv .section-10 circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section-10 polygon,#mermaid-svg-oCAN6D4iFEe3g3zv .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section-10 text{fill:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .node-icon-10{font-size:40px;color:black;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .edge-depth-10{stroke-width:-16;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled circle,#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:lightgray;}#mermaid-svg-oCAN6D4iFEe3g3zv .disabled text{fill:#efefef;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-root rect,#mermaid-svg-oCAN6D4iFEe3g3zv .section-root path,#mermaid-svg-oCAN6D4iFEe3g3zv .section-root circle,#mermaid-svg-oCAN6D4iFEe3g3zv .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-oCAN6D4iFEe3g3zv .section-root text{fill:#ffffff;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-root span{color:#ffffff;}#mermaid-svg-oCAN6D4iFEe3g3zv .section-2 span{color:#ffffff;}#mermaid-svg-oCAN6D4iFEe3g3zv .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-oCAN6D4iFEe3g3zv .edge{fill:none;}#mermaid-svg-oCAN6D4iFEe3g3zv .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-oCAN6D4iFEe3g3zv :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
AIGC应用全栈
模型层
大语言模型 LLM
扩散模型 Diffusion
视频生成模型
部署层
Ollama 本地推理
vLLM 集群推理
ComfyUI 工作流引擎
服务层
WebSocket 实时通信
RESTful API
分布式任务调度
业务层
复式记账系统
数据流分析引擎
智能体编排
客户端层
Retrofit 声明式调用
Web 前端
移动端
这张思维导图对应了本文后续所有章节的展开顺序。我刻意把"业务层"放在中间而不是顶层——因为技术最终服务于业务,复式记账、数据流分析这些看似传统的工程能力,恰恰是AIGC平台商业闭环的关键。1
三、Ollama私有化部署:把大模型装进自己的服务器
3.1 为什么必须私有化
公有云API有三个绕不开的问题:数据合规(客户合同里白纸黑字写着"数据不出内网")、成本失控(日均百万token调用量下,API费用会吃掉全部利润)、延迟不可控(跨境网络抖动直接导致用户体验崩塌)。
私有化部署不是"锦上添花",而是企业级AIGC应用的生存底线。
3.2 Ollama安装与模型管理
Ollama目前(2026年8月)已经是本地/私有化部署LLM的事实标准。安装极其简单:
# Linux 一键安装
curl -fsSL https://ollama.com/install.sh | sh
# 验证安装
ollama –version
# 输出: ollama version is 0.8.x
# 拉取模型(以 Qwen3 为例)
ollama pull qwen3:32b
# 查看已下载模型
ollama list
# 运行交互式对话
ollama run qwen3:32b
💡 小贴士:32B参数模型在FP16精度下约需64GB显存。如果只有24GB显卡(如RTX 4090),使用量化版本:ollama pull qwen3:32b-q4_K_M,显存占用降至约20GB,推理速度损失控制在15%以内。
3.3 REST API接入
Ollama默认在11434端口暴露HTTP API,这是后端服务集成的入口:
# 文本生成(非流式)
curl http://localhost:11434/api/generate -d '{
"model": "qwen3:32b",
"prompt": "用一句话解释什么是复式记账法",
"stream": false,
"options": {
"temperature": 0.7,
"num_ctx": 4096
}
}'
3.4 Java后端集成Ollama
在Spring Boot中,我推荐用WebClient做非阻塞调用:
@Service
public class OllamaService {
private final WebClient webClient;
public OllamaService(WebClient.Builder builder) {
this.webClient = builder
.baseUrl("http://localhost:11434")
.build();
}
/**
* 流式生成文本,逐token推送给前端
*/
public Flux<String> streamGenerate(String prompt, String model) {
Map<String, Object> body = Map.of(
"model", model,
"prompt", prompt,
"stream", true
);
return webClient.post()
.uri("/api/generate")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(body)
.retrieve()
.bodyToFlux(OllamaResponse.class)
.map(OllamaResponse::getResponse)
.doOnError(e -> log.error("Ollama调用异常", e));
}
}
// 响应DTO
@Data
public class OllamaResponse {
private String model;
@JsonProperty("response")
private String response;
private boolean done;
}
⚠️ 注意:生产环境中,baseUrl必须通过配置中心(如Nacos)管理,而非硬编码。当Ollama实例水平扩展时,还需要在前面加一层Nginx做负载均衡,按模型名称路由到不同节点。
3.5 ChatBot与知识库:RAG私有化方案
单纯的LLM只能做通用对话。企业场景需要知识库增强,即RAG(Retrieval-Augmented Generation)架构:
#mermaid-svg-XIDOcJrxNXHYskP6{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XIDOcJrxNXHYskP6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XIDOcJrxNXHYskP6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XIDOcJrxNXHYskP6 .error-icon{fill:#552222;}#mermaid-svg-XIDOcJrxNXHYskP6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XIDOcJrxNXHYskP6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XIDOcJrxNXHYskP6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XIDOcJrxNXHYskP6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XIDOcJrxNXHYskP6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XIDOcJrxNXHYskP6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XIDOcJrxNXHYskP6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XIDOcJrxNXHYskP6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XIDOcJrxNXHYskP6 .marker.cross{stroke:#333333;}#mermaid-svg-XIDOcJrxNXHYskP6 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XIDOcJrxNXHYskP6 p{margin:0;}#mermaid-svg-XIDOcJrxNXHYskP6 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-XIDOcJrxNXHYskP6 .cluster-label text{fill:#333;}#mermaid-svg-XIDOcJrxNXHYskP6 .cluster-label span{color:#333;}#mermaid-svg-XIDOcJrxNXHYskP6 .cluster-label span p{background-color:transparent;}#mermaid-svg-XIDOcJrxNXHYskP6 .label text,#mermaid-svg-XIDOcJrxNXHYskP6 span{fill:#333;color:#333;}#mermaid-svg-XIDOcJrxNXHYskP6 .node rect,#mermaid-svg-XIDOcJrxNXHYskP6 .node circle,#mermaid-svg-XIDOcJrxNXHYskP6 .node ellipse,#mermaid-svg-XIDOcJrxNXHYskP6 .node polygon,#mermaid-svg-XIDOcJrxNXHYskP6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XIDOcJrxNXHYskP6 .rough-node .label text,#mermaid-svg-XIDOcJrxNXHYskP6 .node .label text,#mermaid-svg-XIDOcJrxNXHYskP6 .image-shape .label,#mermaid-svg-XIDOcJrxNXHYskP6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-XIDOcJrxNXHYskP6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XIDOcJrxNXHYskP6 .rough-node .label,#mermaid-svg-XIDOcJrxNXHYskP6 .node .label,#mermaid-svg-XIDOcJrxNXHYskP6 .image-shape .label,#mermaid-svg-XIDOcJrxNXHYskP6 .icon-shape .label{text-align:center;}#mermaid-svg-XIDOcJrxNXHYskP6 .node.clickable{cursor:pointer;}#mermaid-svg-XIDOcJrxNXHYskP6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XIDOcJrxNXHYskP6 .arrowheadPath{fill:#333333;}#mermaid-svg-XIDOcJrxNXHYskP6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XIDOcJrxNXHYskP6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XIDOcJrxNXHYskP6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XIDOcJrxNXHYskP6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XIDOcJrxNXHYskP6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XIDOcJrxNXHYskP6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XIDOcJrxNXHYskP6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XIDOcJrxNXHYskP6 .cluster text{fill:#333;}#mermaid-svg-XIDOcJrxNXHYskP6 .cluster span{color:#333;}#mermaid-svg-XIDOcJrxNXHYskP6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XIDOcJrxNXHYskP6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XIDOcJrxNXHYskP6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-XIDOcJrxNXHYskP6 .icon-shape,#mermaid-svg-XIDOcJrxNXHYskP6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XIDOcJrxNXHYskP6 .icon-shape p,#mermaid-svg-XIDOcJrxNXHYskP6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XIDOcJrxNXHYskP6 .icon-shape .label rect,#mermaid-svg-XIDOcJrxNXHYskP6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XIDOcJrxNXHYskP6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XIDOcJrxNXHYskP6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XIDOcJrxNXHYskP6 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
离线索引
文档上传
文本分块
Embedding向量化
Milvus/PGVector
用户提问
向量检索
命中知识片段?
拼接Prompt
直接LLM生成
Ollama推理
返回答案+来源引用
关键代码——文档分块与向量化:
@Service
public class KnowledgeBaseService {
private final EmbeddingClient embeddingClient; // 调用本地Embedding模型
private final VectorStore vectorStore; // PGVector实现
/**
* 文档入库:分块 -> 向量化 -> 存储
*/
public void ingestDocument(MultipartFile file) throws IOException {
String rawText = extractText(file); // PDF/Word解析
// 按512token分块,重叠64token保证语义连贯
List<String> chunks = TextSplitter.split(rawText, 512, 64);
for (String chunk : chunks) {
float[] embedding = embeddingClient.embed(chunk);
vectorStore.store(chunk, embedding, Map.of(
"source", file.getOriginalFilename(),
"ingest_time", Instant.now().toString()
));
}
}
/**
* 语义检索:返回Top-K相关片段
*/
public List<String> retrieve(String query, int topK) {
float[] queryVec = embeddingClient.embed(query);
return vectorStore.similaritySearch(queryVec, topK);
}
}
✅ 建议:分块策略直接影响检索质量。对于技术文档,按段落自然边界切分优于固定长度切分;对于合同类长文本,建议按条款编号做结构化切分。
四、Stable Diffusion与ComfyUI:文生图的工程化落地
4.1 从"能跑"到"能上线"的距离
很多教程教你在本地跑通Stable Diffusion WebUI,但从玩具到生产级服务,中间隔着:
| 并发 | 单张图排队 | 多GPU并行+队列调度 |
| 延迟 | 无所谓 | P99 < 30s |
| 可控性 | 手动调参 | API参数化+预设工作流 |
| 监控 | 无 | 生成耗时/失败率/显存水位 |
| 计费 | 无 | 按次/按分辨率/按模型 |
4.2 ComfyUI作为工作流引擎
ComfyUI的核心价值不是"又一个SD前端",而是把图像生成管线抽象为有向无环图(DAG)。每个节点(加载模型、CLIP编码、采样、VAE解码)都是可编排的原子操作。
#mermaid-svg-LuFWbMNse0VUIeWQ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LuFWbMNse0VUIeWQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LuFWbMNse0VUIeWQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LuFWbMNse0VUIeWQ .error-icon{fill:#552222;}#mermaid-svg-LuFWbMNse0VUIeWQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LuFWbMNse0VUIeWQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LuFWbMNse0VUIeWQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LuFWbMNse0VUIeWQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LuFWbMNse0VUIeWQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LuFWbMNse0VUIeWQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LuFWbMNse0VUIeWQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LuFWbMNse0VUIeWQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LuFWbMNse0VUIeWQ .marker.cross{stroke:#333333;}#mermaid-svg-LuFWbMNse0VUIeWQ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LuFWbMNse0VUIeWQ p{margin:0;}#mermaid-svg-LuFWbMNse0VUIeWQ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-LuFWbMNse0VUIeWQ .cluster-label text{fill:#333;}#mermaid-svg-LuFWbMNse0VUIeWQ .cluster-label span{color:#333;}#mermaid-svg-LuFWbMNse0VUIeWQ .cluster-label span p{background-color:transparent;}#mermaid-svg-LuFWbMNse0VUIeWQ .label text,#mermaid-svg-LuFWbMNse0VUIeWQ span{fill:#333;color:#333;}#mermaid-svg-LuFWbMNse0VUIeWQ .node rect,#mermaid-svg-LuFWbMNse0VUIeWQ .node circle,#mermaid-svg-LuFWbMNse0VUIeWQ .node ellipse,#mermaid-svg-LuFWbMNse0VUIeWQ .node polygon,#mermaid-svg-LuFWbMNse0VUIeWQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LuFWbMNse0VUIeWQ .rough-node .label text,#mermaid-svg-LuFWbMNse0VUIeWQ .node .label text,#mermaid-svg-LuFWbMNse0VUIeWQ .image-shape .label,#mermaid-svg-LuFWbMNse0VUIeWQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-LuFWbMNse0VUIeWQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LuFWbMNse0VUIeWQ .rough-node .label,#mermaid-svg-LuFWbMNse0VUIeWQ .node .label,#mermaid-svg-LuFWbMNse0VUIeWQ .image-shape .label,#mermaid-svg-LuFWbMNse0VUIeWQ .icon-shape .label{text-align:center;}#mermaid-svg-LuFWbMNse0VUIeWQ .node.clickable{cursor:pointer;}#mermaid-svg-LuFWbMNse0VUIeWQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LuFWbMNse0VUIeWQ .arrowheadPath{fill:#333333;}#mermaid-svg-LuFWbMNse0VUIeWQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LuFWbMNse0VUIeWQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LuFWbMNse0VUIeWQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LuFWbMNse0VUIeWQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LuFWbMNse0VUIeWQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LuFWbMNse0VUIeWQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LuFWbMNse0VUIeWQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LuFWbMNse0VUIeWQ .cluster text{fill:#333;}#mermaid-svg-LuFWbMNse0VUIeWQ .cluster span{color:#333;}#mermaid-svg-LuFWbMNse0VUIeWQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LuFWbMNse0VUIeWQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LuFWbMNse0VUIeWQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-LuFWbMNse0VUIeWQ .icon-shape,#mermaid-svg-LuFWbMNse0VUIeWQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LuFWbMNse0VUIeWQ .icon-shape p,#mermaid-svg-LuFWbMNse0VUIeWQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LuFWbMNse0VUIeWQ .icon-shape .label rect,#mermaid-svg-LuFWbMNse0VUIeWQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LuFWbMNse0VUIeWQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LuFWbMNse0VUIeWQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LuFWbMNse0VUIeWQ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Checkpoint Loader
CLIP Text Encode正向提示词
CLIP Text Encode负向提示词
KSampler采样器
VAE Decode
Save Image
在生产环境中,我通过ComfyUI的API模式(–listen 0.0.0.0 –port 8188)将其作为无状态推理节点,由Java后端负责任务编排:
# ComfyUI API调用示例(Python客户端)
import json
import requests
COMFYUI_URL = "http://gpu-node-01:8188"
def submit_generation(prompt: str, width: int, height: int, steps: int = 30):
"""提交文生图任务到ComfyUI"""
workflow = json.load(open("workflow_api.json"))
# 动态注入参数
workflow["6"]["inputs"]["text"] = prompt # 正向提示词节点
workflow["5"]["inputs"]["width"] = width
workflow["5"]["inputs"]["height"] = height
workflow["3"]["inputs"]["steps"] = steps
resp = requests.post(f"{COMFYUI_URL}/prompt", json={
"prompt": workflow,
"client_id": "java-backend-001"
})
return resp.json()["prompt_id"] # 返回任务ID用于轮询
def poll_result(prompt_id: str):
"""轮询生成结果"""
resp = requests.get(f"{COMFYUI_URL}/history/{prompt_id}")
data = resp.json()
if prompt_id in data:
outputs = data[prompt_id]["outputs"]
# 提取生成图片路径
for node_id, output in outputs.items():
if "images" in output:
return output["images"][0]
return None
4.3 星图AI的多模态生成矩阵
星图AI平台支持的生成能力远不止文生图:
#mermaid-svg-A2Hzfv5APXLGf8zj{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-A2Hzfv5APXLGf8zj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-A2Hzfv5APXLGf8zj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-A2Hzfv5APXLGf8zj .error-icon{fill:#552222;}#mermaid-svg-A2Hzfv5APXLGf8zj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-A2Hzfv5APXLGf8zj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-A2Hzfv5APXLGf8zj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-A2Hzfv5APXLGf8zj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-A2Hzfv5APXLGf8zj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-A2Hzfv5APXLGf8zj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-A2Hzfv5APXLGf8zj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-A2Hzfv5APXLGf8zj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-A2Hzfv5APXLGf8zj .marker.cross{stroke:#333333;}#mermaid-svg-A2Hzfv5APXLGf8zj svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-A2Hzfv5APXLGf8zj p{margin:0;}#mermaid-svg-A2Hzfv5APXLGf8zj .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-A2Hzfv5APXLGf8zj .cluster-label text{fill:#333;}#mermaid-svg-A2Hzfv5APXLGf8zj .cluster-label span{color:#333;}#mermaid-svg-A2Hzfv5APXLGf8zj .cluster-label span p{background-color:transparent;}#mermaid-svg-A2Hzfv5APXLGf8zj .label text,#mermaid-svg-A2Hzfv5APXLGf8zj span{fill:#333;color:#333;}#mermaid-svg-A2Hzfv5APXLGf8zj .node rect,#mermaid-svg-A2Hzfv5APXLGf8zj .node circle,#mermaid-svg-A2Hzfv5APXLGf8zj .node ellipse,#mermaid-svg-A2Hzfv5APXLGf8zj .node polygon,#mermaid-svg-A2Hzfv5APXLGf8zj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-A2Hzfv5APXLGf8zj .rough-node .label text,#mermaid-svg-A2Hzfv5APXLGf8zj .node .label text,#mermaid-svg-A2Hzfv5APXLGf8zj .image-shape .label,#mermaid-svg-A2Hzfv5APXLGf8zj .icon-shape .label{text-anchor:middle;}#mermaid-svg-A2Hzfv5APXLGf8zj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-A2Hzfv5APXLGf8zj .rough-node .label,#mermaid-svg-A2Hzfv5APXLGf8zj .node .label,#mermaid-svg-A2Hzfv5APXLGf8zj .image-shape .label,#mermaid-svg-A2Hzfv5APXLGf8zj .icon-shape .label{text-align:center;}#mermaid-svg-A2Hzfv5APXLGf8zj .node.clickable{cursor:pointer;}#mermaid-svg-A2Hzfv5APXLGf8zj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-A2Hzfv5APXLGf8zj .arrowheadPath{fill:#333333;}#mermaid-svg-A2Hzfv5APXLGf8zj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-A2Hzfv5APXLGf8zj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-A2Hzfv5APXLGf8zj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A2Hzfv5APXLGf8zj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-A2Hzfv5APXLGf8zj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A2Hzfv5APXLGf8zj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-A2Hzfv5APXLGf8zj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-A2Hzfv5APXLGf8zj .cluster text{fill:#333;}#mermaid-svg-A2Hzfv5APXLGf8zj .cluster span{color:#333;}#mermaid-svg-A2Hzfv5APXLGf8zj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-A2Hzfv5APXLGf8zj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-A2Hzfv5APXLGf8zj rect.text{fill:none;stroke-width:0;}#mermaid-svg-A2Hzfv5APXLGf8zj .icon-shape,#mermaid-svg-A2Hzfv5APXLGf8zj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A2Hzfv5APXLGf8zj .icon-shape p,#mermaid-svg-A2Hzfv5APXLGf8zj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-A2Hzfv5APXLGf8zj .icon-shape .label rect,#mermaid-svg-A2Hzfv5APXLGf8zj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A2Hzfv5APXLGf8zj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-A2Hzfv5APXLGf8zj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-A2Hzfv5APXLGf8zj :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
输出
生成管线
输入模态
文本
图像
姿势骨架
视频
文生图
图生图
姿势生图
文生视频
图生视频
画质增强
高清图像
生成视频
💡 小贴士:姿势生图(Pose-to-Image)在电商虚拟试衣、数字人生成场景中需求极大。实现上通常依赖ControlNet的OpenPose预处理器,将骨架图作为条件输入注入扩散过程。
五、WebSocket双端消息通信:实时推送的工程实践
5.1 为什么AIGC平台必须用WebSocket
图像生成动辄需要10~60秒。如果前端用HTTP轮询,一是浪费带宽,二是用户体验割裂。WebSocket提供全双工、持久连接,服务端生成完毕后主动推送,体验丝滑。
5.2 Spring Boot WebSocket服务端实现
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired
private GenerationHandler generationHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(generationHandler, "/ws/generation")
.setAllowedOrigins("*"); // 生产环境务必限制来源
}
}
@Component
public class GenerationHandler extends TextWebSocketHandler {
// 维护用户ID -> Session映射
private final ConcurrentHashMap<String, WebSocketSession> sessions
= new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) {
String userId = extractUserId(session);
sessions.put(userId, session);
log.info("WebSocket连接建立: userId={}", userId);
}
/**
* 点对点推送:向指定用户发送生成结果
*/
public void sendToUser(String userId, GenerationResult result) {
WebSocketSession session = sessions.get(userId);
if (session != null && session.isOpen()) {
try {
String json = ObjectMapperUtil.toJson(result);
session.sendMessage(new TextMessage(json));
} catch (IOException e) {
log.error("推送失败: userId={}", userId, e);
}
}
}
/**
* 广播推送:向所有在线用户发送系统通知
*/
public void broadcast(SystemNotice notice) {
String json = ObjectMapperUtil.toJson(notice);
sessions.values().forEach(session -> {
if (session.isOpen()) {
try {
session.sendMessage(new TextMessage(json));
} catch (IOException e) {
log.warn("广播推送失败", e);
}
}
});
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
String userId = extractUserId(session);
sessions.remove(userId);
log.info("WebSocket连接关闭: userId={}, status={}", userId, status);
}
}
5.3 消息协议设计
消息体必须有统一结构,否则前后端联调会变成噩梦:
{
"type": "GENERATION_PROGRESS",
"taskId": "gen_20260830_abc123",
"payload": {
"progress": 0.72,
"stage": "sampling",
"eta_seconds": 8
},
"timestamp": 1756540800000
}
GPU推理节点
Java后端
用户浏览器
GPU推理节点
Java后端
用户浏览器
#mermaid-svg-vevQc0JVK1eZ2Ra7{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vevQc0JVK1eZ2Ra7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .error-icon{fill:#552222;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .marker.cross{stroke:#333333;}#mermaid-svg-vevQc0JVK1eZ2Ra7 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vevQc0JVK1eZ2Ra7 p{margin:0;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-vevQc0JVK1eZ2Ra7 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-vevQc0JVK1eZ2Ra7 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-vevQc0JVK1eZ2Ra7 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .sequenceNumber{fill:white;}#mermaid-svg-vevQc0JVK1eZ2Ra7 #sequencenumber{fill:#333;}#mermaid-svg-vevQc0JVK1eZ2Ra7 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .messageText{fill:#333;stroke:none;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .labelText,#mermaid-svg-vevQc0JVK1eZ2Ra7 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .loopText,#mermaid-svg-vevQc0JVK1eZ2Ra7 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-vevQc0JVK1eZ2Ra7 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .noteText,#mermaid-svg-vevQc0JVK1eZ2Ra7 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .actorPopupMenu{position:absolute;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-vevQc0JVK1eZ2Ra7 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-vevQc0JVK1eZ2Ra7 .actor-man circle,#mermaid-svg-vevQc0JVK1eZ2Ra7 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-vevQc0JVK1eZ2Ra7 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
loop
[生成过程中]
WebSocket连接 /ws/generation
连接确认
HTTP POST 提交生成任务
分发任务到GPU队列
WS推送 {type: TASK_ACCEPTED}
进度回调
WS推送 {type: PROGRESS, payload: {progress: 0.5}}
生成完毕,返回图片URL
WS推送 {type: COMPLETED, payload: {imageUrl: "…"}}
前端渲染图片
⚠️ 注意:WebSocket连接是有状态的。当服务多实例部署时,用户A连在实例1上,但任务回调打到了实例2——消息就丢了。解决方案:用Redis Pub/Sub做跨实例消息转发,或者用Sticky Session(不推荐,影响扩缩容)。
六、复式记账法:AIGC平台的资源审计基石
6.1 为什么AIGC平台需要复式记账
很多人觉得记账是财务的事,跟AI平台没关系。错了。
星图AI平台每天处理数万次生成请求,每次请求消耗不同量的GPU时间、不同模型的推理成本、不同用户的配额。谁用了什么资源、花了多少、剩余多少——这是一笔笔必须精确记录的"账"。
单式记账(只记"支出10元")无法追溯资金流向。复式记账的核心原则:有借必有贷,借贷必相等。
6.2 领域模型设计
#mermaid-svg-9wOOQt0VDIazQXtM{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9wOOQt0VDIazQXtM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9wOOQt0VDIazQXtM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9wOOQt0VDIazQXtM .error-icon{fill:#552222;}#mermaid-svg-9wOOQt0VDIazQXtM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9wOOQt0VDIazQXtM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9wOOQt0VDIazQXtM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9wOOQt0VDIazQXtM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9wOOQt0VDIazQXtM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9wOOQt0VDIazQXtM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9wOOQt0VDIazQXtM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9wOOQt0VDIazQXtM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9wOOQt0VDIazQXtM .marker.cross{stroke:#333333;}#mermaid-svg-9wOOQt0VDIazQXtM svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9wOOQt0VDIazQXtM p{margin:0;}#mermaid-svg-9wOOQt0VDIazQXtM g.classGroup text{fill:#9370DB;stroke:none;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-9wOOQt0VDIazQXtM g.classGroup text .title{font-weight:bolder;}#mermaid-svg-9wOOQt0VDIazQXtM .cluster-label text{fill:#333;}#mermaid-svg-9wOOQt0VDIazQXtM .cluster-label span{color:#333;}#mermaid-svg-9wOOQt0VDIazQXtM .cluster-label span p{background-color:transparent;}#mermaid-svg-9wOOQt0VDIazQXtM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9wOOQt0VDIazQXtM .cluster text{fill:#333;}#mermaid-svg-9wOOQt0VDIazQXtM .cluster span{color:#333;}#mermaid-svg-9wOOQt0VDIazQXtM .nodeLabel,#mermaid-svg-9wOOQt0VDIazQXtM .edgeLabel{color:#131300;}#mermaid-svg-9wOOQt0VDIazQXtM .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-9wOOQt0VDIazQXtM .label text{fill:#131300;}#mermaid-svg-9wOOQt0VDIazQXtM .labelBkg{background:#ECECFF;}#mermaid-svg-9wOOQt0VDIazQXtM .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-9wOOQt0VDIazQXtM .classTitle{font-weight:bolder;}#mermaid-svg-9wOOQt0VDIazQXtM .node rect,#mermaid-svg-9wOOQt0VDIazQXtM .node circle,#mermaid-svg-9wOOQt0VDIazQXtM .node ellipse,#mermaid-svg-9wOOQt0VDIazQXtM .node polygon,#mermaid-svg-9wOOQt0VDIazQXtM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9wOOQt0VDIazQXtM .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-9wOOQt0VDIazQXtM g.clickable{cursor:pointer;}#mermaid-svg-9wOOQt0VDIazQXtM g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-9wOOQt0VDIazQXtM g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-9wOOQt0VDIazQXtM .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-9wOOQt0VDIazQXtM .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-9wOOQt0VDIazQXtM .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-9wOOQt0VDIazQXtM .dashed-line{stroke-dasharray:3;}#mermaid-svg-9wOOQt0VDIazQXtM .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-9wOOQt0VDIazQXtM #compositionStart,#mermaid-svg-9wOOQt0VDIazQXtM .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9wOOQt0VDIazQXtM #compositionEnd,#mermaid-svg-9wOOQt0VDIazQXtM .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9wOOQt0VDIazQXtM #dependencyStart,#mermaid-svg-9wOOQt0VDIazQXtM .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9wOOQt0VDIazQXtM #dependencyStart,#mermaid-svg-9wOOQt0VDIazQXtM .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9wOOQt0VDIazQXtM #extensionStart,#mermaid-svg-9wOOQt0VDIazQXtM .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9wOOQt0VDIazQXtM #extensionEnd,#mermaid-svg-9wOOQt0VDIazQXtM .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9wOOQt0VDIazQXtM #aggregationStart,#mermaid-svg-9wOOQt0VDIazQXtM .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9wOOQt0VDIazQXtM #aggregationEnd,#mermaid-svg-9wOOQt0VDIazQXtM .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9wOOQt0VDIazQXtM #lollipopStart,#mermaid-svg-9wOOQt0VDIazQXtM .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9wOOQt0VDIazQXtM #lollipopEnd,#mermaid-svg-9wOOQt0VDIazQXtM .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9wOOQt0VDIazQXtM .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-9wOOQt0VDIazQXtM .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9wOOQt0VDIazQXtM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9wOOQt0VDIazQXtM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9wOOQt0VDIazQXtM :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
contains 1..*
references
Account
+Long id
+String accountCode
+String accountName
+AccountType type
+BigDecimal balance
+addEntry(Entry entry)
JournalEntry
+Long id
+String transactionId
+LocalDateTime occurredAt
+String description
+List<EntryLine> lines
+validate() : boolean
EntryLine
+Long id
+Long journalEntryId
+Long accountId
+Direction direction
+BigDecimal amount
«enumeration»
AccountType
ASSET
LIABILITY
REVENUE
EXPENSE
EQUITY
«enumeration»
Direction
DEBIT
CREDIT
6.3 核心实现
@Service
@Transactional
public class BookkeepingService {
private final AccountRepository accountRepo;
private final JournalEntryRepository journalRepo;
/**
* 记录一笔GPU推理消费
* 借:用户消费(费用增加)
* 贷:用户余额(资产减少)
*/
public void recordInferenceCost(String userId, BigDecimal cost, String model) {
String txId = UUID.randomUUID().toString();
JournalEntry entry = new JournalEntry();
entry.setTransactionId(txId);
entry.setOccurredAt(LocalDateTime.now());
entry.setDescription("模型推理消费: " + model);
// 借方:费用账户
EntryLine debit = new EntryLine();
debit.setAccountId(getAccountId("EXPENSE_GPU_" + model));
debit.setDirection(Direction.DEBIT);
debit.setAmount(cost);
// 贷方:用户余额账户
EntryLine credit = new EntryLine();
credit.setAccountId(getAccountId("USER_BALANCE_" + userId));
credit.setDirection(Direction.CREDIT);
credit.setAmount(cost);
entry.setLines(List.of(debit, credit));
// 校验:借方总额 == 贷方总额
if (!entry.validate()) {
throw new BookkeepingException("借贷不平衡,拒绝入账");
}
journalRepo.save(entry);
// 更新账户余额
accountRepo.debit(debit.getAccountId(), cost);
accountRepo.credit(credit.getAccountId(), cost);
}
}
✅ 建议:所有记账操作必须在同一个数据库事务中完成。如果涉及跨服务(如计费服务与用户服务分离),使用Saga模式或本地消息表保证最终一致性。
七、Redisson分布式锁与信号量:GPU资源的并发控制
7.1 问题场景
星图AI平台有8张A100 GPU。当100个用户同时提交生成任务时,如果不做并发控制,要么GPU过载崩溃,要么任务堆积导致超时。
我们需要两层控制:
- 分布式锁:同一用户的同一任务不能重复提交(幂等性)
- 信号量:全局GPU并发槽位控制(最多同时跑8个推理任务)
7.2 Redisson实现
@Service
public class GpuResourceService {
private final RedissonClient redisson;
private static final String LOCK_PREFIX = "lock:generation:";
private static final String SEMAPHORE_KEY = "semaphore:gpu_slots";
/**
* 提交生成任务(带分布式锁防重复)
*/
public GenerationResult submitGeneration(String userId, GenerationRequest req) {
String lockKey = LOCK_PREFIX + userId + ":" + req.getTaskId();
RLock lock = redisson.getLock(lockKey);
try {
// 尝试获取锁,等待3秒,持有锁最多60秒(防死锁)
boolean acquired = lock.tryLock(3, 60, TimeUnit.SECONDS);
if (!acquired) {
throw new BusinessException("任务正在处理中,请勿重复提交");
}
// 获取GPU信号量
RSemaphore semaphore = redisson.getSemaphore(SEMAPHORE_KEY);
semaphore.trySetPermits(8); // 初始化8个槽位(仅首次生效)
boolean permit = semaphore.tryAcquire(30, TimeUnit.SECONDS);
if (!permit) {
throw new BusinessException("GPU资源繁忙,请稍后重试");
}
try {
// 执行实际推理
return doInference(req);
} finally {
semaphore.release(); // 释放槽位
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BusinessException("操作被中断");
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
7.3 锁与信号量的协同模型
#mermaid-svg-JSN7GARQeKLGGqPZ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JSN7GARQeKLGGqPZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JSN7GARQeKLGGqPZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JSN7GARQeKLGGqPZ .error-icon{fill:#552222;}#mermaid-svg-JSN7GARQeKLGGqPZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JSN7GARQeKLGGqPZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JSN7GARQeKLGGqPZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JSN7GARQeKLGGqPZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JSN7GARQeKLGGqPZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JSN7GARQeKLGGqPZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JSN7GARQeKLGGqPZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JSN7GARQeKLGGqPZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JSN7GARQeKLGGqPZ .marker.cross{stroke:#333333;}#mermaid-svg-JSN7GARQeKLGGqPZ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JSN7GARQeKLGGqPZ p{margin:0;}#mermaid-svg-JSN7GARQeKLGGqPZ defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-JSN7GARQeKLGGqPZ g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-JSN7GARQeKLGGqPZ g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-JSN7GARQeKLGGqPZ g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-JSN7GARQeKLGGqPZ g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-JSN7GARQeKLGGqPZ g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-JSN7GARQeKLGGqPZ .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-JSN7GARQeKLGGqPZ .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-JSN7GARQeKLGGqPZ .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-JSN7GARQeKLGGqPZ .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-JSN7GARQeKLGGqPZ .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-JSN7GARQeKLGGqPZ .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-JSN7GARQeKLGGqPZ .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-JSN7GARQeKLGGqPZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JSN7GARQeKLGGqPZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JSN7GARQeKLGGqPZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JSN7GARQeKLGGqPZ .edgeLabel .label text{fill:#333;}#mermaid-svg-JSN7GARQeKLGGqPZ .label div .edgeLabel{color:#333;}#mermaid-svg-JSN7GARQeKLGGqPZ .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-JSN7GARQeKLGGqPZ .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-JSN7GARQeKLGGqPZ .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-JSN7GARQeKLGGqPZ .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-JSN7GARQeKLGGqPZ .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-JSN7GARQeKLGGqPZ .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JSN7GARQeKLGGqPZ .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JSN7GARQeKLGGqPZ #statediagram-barbEnd{fill:#333333;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JSN7GARQeKLGGqPZ .cluster-label,#mermaid-svg-JSN7GARQeKLGGqPZ .nodeLabel{color:#131300;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-JSN7GARQeKLGGqPZ .note-edge{stroke-dasharray:5;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-note text{fill:black;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram-note .nodeLabel{color:black;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagram .edgeLabel{color:red;}#mermaid-svg-JSN7GARQeKLGGqPZ #dependencyStart,#mermaid-svg-JSN7GARQeKLGGqPZ #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-JSN7GARQeKLGGqPZ .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JSN7GARQeKLGGqPZ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
tryLock(3s)
超时
返回"重复提交"
锁获取成功
无空闲槽位
等待释放
超时30s,返回"资源繁忙"
获得permit
推理完成/异常
返回结果
请求到达
获取分布式锁
锁获取失败
获取GPU信号量
信号量等待
执行推理
释放信号量
释放分布式锁
⚠️ 注意:Redisson的RLock默认使用看门狗机制(30秒自动续期),但如果你显式指定了leaseTime(如上面的60秒),看门狗会失效。长时间任务务必评估是否需要手动续期或改用RLock的无超时模式。
八、数据流分析法:驯服复杂业务的思维武器
8.1 什么是数据流分析
星图AI的一次"文生图"请求,从前端发起到图片返回,至少经过:参数校验→鉴权→计费预扣→任务入队→GPU调度→模型推理→后处理→存储→推送→计费确认。
如果把这些步骤写成一个2000行的Service方法,没人能维护。数据流分析法的核心思想是:把复杂业务拆解为数据在多个处理阶段之间的流动,每个阶段只关注自己的输入输出。
8.2 用管道模式实现
// 定义处理阶段接口
public interface PipelineStage<I, O> {
O process(I input, PipelineContext ctx);
String name();
}
// 具体阶段实现
@Component
public class AuthStage implements PipelineStage<GenRequest, GenRequest> {
@Override
public GenRequest process(GenRequest input, PipelineContext ctx) {
// 校验用户权限、模型访问权限
if (!authService.canAccess(ctx.getUserId(), input.getModel())) {
throw new ForbiddenException("无权使用该模型");
}
return input;
}
@Override
public String name() { return "鉴权"; }
}
@Component
public class BillingPreDeductStage implements PipelineStage<GenRequest, GenRequest> {
@Override
public GenRequest process(GenRequest input, PipelineContext ctx) {
BigDecimal cost = pricingService.estimate(input);
bookkeepingService.preDeduct(ctx.getUserId(), cost);
ctx.setAttribute("estimated_cost", cost);
return input;
}
@Override
public String name() { return "计费预扣"; }
}
// 管道编排器
@Service
public class GenerationPipeline {
private final List<PipelineStage<?, ?>> stages; // 按顺序注入
public GenResult execute(GenRequest request) {
PipelineContext ctx = new PipelineContext(request);
Object data = request;
for (PipelineStage stage : stages) {
log.debug("进入阶段: {}", stage.name());
data = stage.process(data, ctx);
}
return ctx.getResult();
}
}
8.3 数据流可视化
#mermaid-svg-LDG0SFGnBwzQ868Y{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LDG0SFGnBwzQ868Y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LDG0SFGnBwzQ868Y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LDG0SFGnBwzQ868Y .error-icon{fill:#552222;}#mermaid-svg-LDG0SFGnBwzQ868Y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LDG0SFGnBwzQ868Y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LDG0SFGnBwzQ868Y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LDG0SFGnBwzQ868Y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LDG0SFGnBwzQ868Y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LDG0SFGnBwzQ868Y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LDG0SFGnBwzQ868Y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LDG0SFGnBwzQ868Y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LDG0SFGnBwzQ868Y .marker.cross{stroke:#333333;}#mermaid-svg-LDG0SFGnBwzQ868Y svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LDG0SFGnBwzQ868Y p{margin:0;}#mermaid-svg-LDG0SFGnBwzQ868Y .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-LDG0SFGnBwzQ868Y .cluster-label text{fill:#333;}#mermaid-svg-LDG0SFGnBwzQ868Y .cluster-label span{color:#333;}#mermaid-svg-LDG0SFGnBwzQ868Y .cluster-label span p{background-color:transparent;}#mermaid-svg-LDG0SFGnBwzQ868Y .label text,#mermaid-svg-LDG0SFGnBwzQ868Y span{fill:#333;color:#333;}#mermaid-svg-LDG0SFGnBwzQ868Y .node rect,#mermaid-svg-LDG0SFGnBwzQ868Y .node circle,#mermaid-svg-LDG0SFGnBwzQ868Y .node ellipse,#mermaid-svg-LDG0SFGnBwzQ868Y .node polygon,#mermaid-svg-LDG0SFGnBwzQ868Y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LDG0SFGnBwzQ868Y .rough-node .label text,#mermaid-svg-LDG0SFGnBwzQ868Y .node .label text,#mermaid-svg-LDG0SFGnBwzQ868Y .image-shape .label,#mermaid-svg-LDG0SFGnBwzQ868Y .icon-shape .label{text-anchor:middle;}#mermaid-svg-LDG0SFGnBwzQ868Y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LDG0SFGnBwzQ868Y .rough-node .label,#mermaid-svg-LDG0SFGnBwzQ868Y .node .label,#mermaid-svg-LDG0SFGnBwzQ868Y .image-shape .label,#mermaid-svg-LDG0SFGnBwzQ868Y .icon-shape .label{text-align:center;}#mermaid-svg-LDG0SFGnBwzQ868Y .node.clickable{cursor:pointer;}#mermaid-svg-LDG0SFGnBwzQ868Y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LDG0SFGnBwzQ868Y .arrowheadPath{fill:#333333;}#mermaid-svg-LDG0SFGnBwzQ868Y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LDG0SFGnBwzQ868Y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LDG0SFGnBwzQ868Y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LDG0SFGnBwzQ868Y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LDG0SFGnBwzQ868Y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LDG0SFGnBwzQ868Y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LDG0SFGnBwzQ868Y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LDG0SFGnBwzQ868Y .cluster text{fill:#333;}#mermaid-svg-LDG0SFGnBwzQ868Y .cluster span{color:#333;}#mermaid-svg-LDG0SFGnBwzQ868Y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LDG0SFGnBwzQ868Y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LDG0SFGnBwzQ868Y rect.text{fill:none;stroke-width:0;}#mermaid-svg-LDG0SFGnBwzQ868Y .icon-shape,#mermaid-svg-LDG0SFGnBwzQ868Y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LDG0SFGnBwzQ868Y .icon-shape p,#mermaid-svg-LDG0SFGnBwzQ868Y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LDG0SFGnBwzQ868Y .icon-shape .label rect,#mermaid-svg-LDG0SFGnBwzQ868Y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LDG0SFGnBwzQ868Y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LDG0SFGnBwzQ868Y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LDG0SFGnBwzQ868Y :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
原始请求
参数校验
鉴权
计费预扣
任务入队
GPU调度
模型推理
后处理/超分
对象存储
WebSocket推送
计费确认
💡 小贴士:数据流分析法的精髓在于每个阶段的输入输出类型明确。当你发现某个Stage需要访问前三个Stage的中间结果时,说明职责划分有问题——要么合并,要么通过Context显式传递。
九、Retrofit声明式客户端:优雅地调用下游服务
9.1 为什么选Retrofit而不是RestTemplate
在星图AI平台中,Java后端需要调用ComfyUI、模型管理服务、对象存储等多个下游。Retrofit的优势:
- 声明式:接口即文档,看方法签名就知道调什么
- 注解驱动:@POST、@Body、@Path,告别手动拼URL
- 拦截器机制:统一加签、日志、重试
9.2 实战示例
// 定义ComfyUI服务接口
public interface ComfyUIApi {
@POST("/prompt")
Call<ComfyUIResponse> submitWorkflow(@Body WorkflowRequest request);
@GET("/history/{promptId}")
Call<HistoryResponse> getHistory(@Path("promptId") String promptId);
@GET("/view")
Call<ResponseBody> getImage(@Query("filename") String filename,
@Query("subfolder") String subfolder);
}
// 客户端工厂
@Configuration
public class RetrofitConfig {
@Bean
public ComfyUIApi comfyUIApi() {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor()) // 日志
.addInterceptor(new RetryInterceptor(3)) // 重试3次
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS) // 生成任务可能较慢
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://comfyui-cluster:8188/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit.create(ComfyUIApi.class);
}
}
// 使用
@Service
public class ImageGenerationService {
@Autowired
private ComfyUIApi comfyUIApi;
public String submitTask(WorkflowRequest req) throws IOException {
Response<ComfyUIResponse> resp = comfyUIApi.submitWorkflow(req).execute();
if (!resp.isSuccessful()) {
throw new ServiceException("ComfyUI提交失败: " + resp.code());
}
return resp.body().getPromptId();
}
}
✅ 建议:对于异步场景,使用enqueue()替代execute()避免阻塞线程池。但要注意回调中更新共享状态时的线程安全问题。
十、星图AI平台整体架构
把前面所有技术串起来,看星图AI的全貌:
#mermaid-svg-L4QCEQikc0SGIR63{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-L4QCEQikc0SGIR63 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-L4QCEQikc0SGIR63 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-L4QCEQikc0SGIR63 .error-icon{fill:#552222;}#mermaid-svg-L4QCEQikc0SGIR63 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-L4QCEQikc0SGIR63 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-L4QCEQikc0SGIR63 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-L4QCEQikc0SGIR63 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-L4QCEQikc0SGIR63 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-L4QCEQikc0SGIR63 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-L4QCEQikc0SGIR63 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-L4QCEQikc0SGIR63 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-L4QCEQikc0SGIR63 .marker.cross{stroke:#333333;}#mermaid-svg-L4QCEQikc0SGIR63 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-L4QCEQikc0SGIR63 p{margin:0;}#mermaid-svg-L4QCEQikc0SGIR63 .person{stroke:hsl(240, 60%, 86.2745098039%);fill:#ECECFF;}#mermaid-svg-L4QCEQikc0SGIR63 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
<<person>>
创作者/用户
提交文生图、文生视频等请求
<<person>>
平台管理员
管理模型、监控资源
<<system>>
星图AI平台
多模态AI生成服务
<<external_system>>
对象存储
存储生成的图片/视频
<<external_system>>
GPU推理集群
ComfyUI + 扩散模型
<<external_system>>
LLM服务
Ollama部署的大模型
HTTP/WebSocket
管理后台
任务分发/结果回收
智能对话/提示词优化
存储/读取媒体文件
星图AI平台 – 系统上下文
10.1 技术选型汇总
| 模型推理 | Ollama + vLLM | LLM对话、提示词优化 |
| 图像生成 | Stable Diffusion + ComfyUI | 文生图、图生图、超分 |
| 视频生成 | AnimateDiff + SVD | 文生视频、图生视频 |
| 实时通信 | Spring WebSocket | 进度推送、结果通知 |
| 并发控制 | Redisson | 分布式锁、GPU信号量 |
| 计费审计 | 复式记账(自研) | 资源消耗追踪 |
| 服务调用 | Retrofit | 声明式下游调用 |
| 业务编排 | 数据流管道模式 | 复杂流程解耦 |
| 知识库 | RAG(PGVector) | 智能客服、文档问答 |
10.2 一次完整请求的时序
对象存储
WebSocket服务
ComfyUI节点
任务队列
计费服务
业务服务
API网关
用户客户端
对象存储
WebSocket服务
ComfyUI节点
任务队列
计费服务
业务服务
API网关
用户客户端
#mermaid-svg-jQ8GvMneY8a3k2EA{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jQ8GvMneY8a3k2EA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jQ8GvMneY8a3k2EA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jQ8GvMneY8a3k2EA .error-icon{fill:#552222;}#mermaid-svg-jQ8GvMneY8a3k2EA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jQ8GvMneY8a3k2EA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jQ8GvMneY8a3k2EA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jQ8GvMneY8a3k2EA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jQ8GvMneY8a3k2EA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jQ8GvMneY8a3k2EA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jQ8GvMneY8a3k2EA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jQ8GvMneY8a3k2EA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jQ8GvMneY8a3k2EA .marker.cross{stroke:#333333;}#mermaid-svg-jQ8GvMneY8a3k2EA svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jQ8GvMneY8a3k2EA p{margin:0;}#mermaid-svg-jQ8GvMneY8a3k2EA .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jQ8GvMneY8a3k2EA text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-jQ8GvMneY8a3k2EA .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-jQ8GvMneY8a3k2EA .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-jQ8GvMneY8a3k2EA .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-jQ8GvMneY8a3k2EA .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-jQ8GvMneY8a3k2EA #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-jQ8GvMneY8a3k2EA .sequenceNumber{fill:white;}#mermaid-svg-jQ8GvMneY8a3k2EA #sequencenumber{fill:#333;}#mermaid-svg-jQ8GvMneY8a3k2EA #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-jQ8GvMneY8a3k2EA .messageText{fill:#333;stroke:none;}#mermaid-svg-jQ8GvMneY8a3k2EA .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jQ8GvMneY8a3k2EA .labelText,#mermaid-svg-jQ8GvMneY8a3k2EA .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-jQ8GvMneY8a3k2EA .loopText,#mermaid-svg-jQ8GvMneY8a3k2EA .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-jQ8GvMneY8a3k2EA .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-jQ8GvMneY8a3k2EA .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-jQ8GvMneY8a3k2EA .noteText,#mermaid-svg-jQ8GvMneY8a3k2EA .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-jQ8GvMneY8a3k2EA .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jQ8GvMneY8a3k2EA .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jQ8GvMneY8a3k2EA .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jQ8GvMneY8a3k2EA .actorPopupMenu{position:absolute;}#mermaid-svg-jQ8GvMneY8a3k2EA .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-jQ8GvMneY8a3k2EA .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jQ8GvMneY8a3k2EA .actor-man circle,#mermaid-svg-jQ8GvMneY8a3k2EA line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-jQ8GvMneY8a3k2EA :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
POST /api/generate (文生图)
转发(鉴权通过)
数据流管道: 参数校验→鉴权
预扣费(复式记账)
扣费成功
任务入队
202 Accepted + taskId
获取信号量后分发
执行推理(15s)
上传生成图片
回调(图片URL)
确认扣费
推送完成消息
WebSocket: {type:COMPLETED, url:…}
加载图片
十一、难点分析与常见误区
11.1 难点排行
#mermaid-svg-jwtdTNCz6xwf74nK{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jwtdTNCz6xwf74nK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jwtdTNCz6xwf74nK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jwtdTNCz6xwf74nK .error-icon{fill:#552222;}#mermaid-svg-jwtdTNCz6xwf74nK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jwtdTNCz6xwf74nK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jwtdTNCz6xwf74nK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jwtdTNCz6xwf74nK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jwtdTNCz6xwf74nK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jwtdTNCz6xwf74nK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jwtdTNCz6xwf74nK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jwtdTNCz6xwf74nK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jwtdTNCz6xwf74nK .marker.cross{stroke:#333333;}#mermaid-svg-jwtdTNCz6xwf74nK svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jwtdTNCz6xwf74nK p{margin:0;}#mermaid-svg-jwtdTNCz6xwf74nK :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
重点关注
高频基础问题
可快速掌握
深水区
Retrofit超时配置
数据流管道异常处理
复式记账事务一致性
Ollama模型切换
ComfyUI工作流编排
Redisson锁续期
WebSocket多实例消息路由
低复杂度
高复杂度
少踩坑
频繁踩坑
技术难点:复杂度 vs 踩坑频率
11.2 常见误区清单
| 1 | WebSocket连接后永不主动断开 | 必须实现心跳检测(30s ping/pong),服务端超时未响应则清理 |
| 2 | 分布式锁设置过长超时时间 | 根据业务实际耗时设置,配合看门狗或手动续期 |
| 3 | 信号量只加不减(忘记release) | 用try-finally保证释放,或封装为AutoCloseable |
| 4 | 记账只记一条流水 | 必须同时记录借方和贷方,否则无法审计 |
| 5 | ComfyUI同步等待结果 | 必须异步:提交→轮询/回调,否则线程池耗尽 |
| 6 | Retrofit全局一个超时配置 | 不同下游超时需求不同,应分Client配置 |
十二、常见问题FAQ
以下问题按行业搜索热度 × 读者焦虑权重综合排序。
Q1:Ollama部署后,多人并发访问会不会崩?
Ollama单实例默认串行处理请求(队列机制)。并发量大时,要么用OLLAMA_NUM_PARALLEL开启并行(需显存足够),要么部署多实例+负载均衡。实测A100 80G跑7B模型,num_parallel=4时QPS约12,延迟P99在2秒内。2
Q2:ComfyUI能扛住多少并发?
单GPU一次只能跑一个推理(显存限制)。并发能力取决于GPU数量。8卡A100集群,单张1024×1024图约12秒,理论QPS≈0.67×8≈5.3。要提升吞吐,优先降低分辨率或使用LCM/Turbo类快速模型。
Q3:WebSocket断连后怎么恢复?
前端实现指数退避重连(1s→2s→4s→8s→最大30s)。重连成功后,通过taskId向服务端查询任务状态,补发断连期间的消息。服务端应维护最近5分钟的离线消息缓存。
Q4:复式记账在分布式环境下如何保证一致性?
单服务内用数据库事务。跨服务场景用本地消息表+定时补偿,或引入Seata的AT模式。核心原则:宁可暂时不一致,不可丢账。
Q5:数据流管道中某个阶段失败了怎么办?
每个Stage必须定义补偿逻辑(Compensation)。例如"计费预扣"成功但"GPU调度"失败,需要触发"计费回滚"。实现上,在PipelineContext中记录已执行的Stage栈,异常时逆序执行补偿。
Q6:Retrofit调用下游超时了,任务会不会丢?
不会。任务在入队时已持久化(Redis/DB)。Retrofit超时只影响"提交"这个动作,配合重试拦截器(3次指数退避)基本能覆盖网络抖动。极端情况下,由定时任务扫描"已入队但未分发"的任务做兜底。
十三、总结与行动建议
写到这里,回顾全文覆盖的技术栈:
#mermaid-svg-dJPIyScAKfdxGcOi{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dJPIyScAKfdxGcOi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dJPIyScAKfdxGcOi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dJPIyScAKfdxGcOi .error-icon{fill:#552222;}#mermaid-svg-dJPIyScAKfdxGcOi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dJPIyScAKfdxGcOi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dJPIyScAKfdxGcOi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dJPIyScAKfdxGcOi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dJPIyScAKfdxGcOi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dJPIyScAKfdxGcOi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dJPIyScAKfdxGcOi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dJPIyScAKfdxGcOi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dJPIyScAKfdxGcOi .marker.cross{stroke:#333333;}#mermaid-svg-dJPIyScAKfdxGcOi svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dJPIyScAKfdxGcOi p{margin:0;}#mermaid-svg-dJPIyScAKfdxGcOi .pieCircle{stroke:#000000;stroke-width:2px;opacity:0.7;}#mermaid-svg-dJPIyScAKfdxGcOi .pieOuterCircle{stroke:#000000;stroke-width:1px;fill:none;}#mermaid-svg-dJPIyScAKfdxGcOi .pieTitleText{text-anchor:middle;font-size:25px;fill:#000000;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-dJPIyScAKfdxGcOi .slice{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;fill:#000000;font-size:17px;}#mermaid-svg-dJPIyScAKfdxGcOi .legend text{fill:#000000;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:17px;}#mermaid-svg-dJPIyScAKfdxGcOi :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
20%
20%
20%
15%
15%
10%
本文技术栈占比
私有化部署(Ollama/RAG)
AIGC生成(SD/ComfyUI)
实时通信(WebSocket)
分布式控制(Redisson)
业务建模(复式记账/数据流)
客户端调用(Retrofit)
如果你只有三天时间,我建议的优先级:
如果你有三个月,那就按本文顺序,把星图AI这样的完整平台从头到尾做一遍。没有什么比"从零搭一个能上线的AIGC平台"更能把知识焊死在脑子里了。
📌 核心要点:技术栈会迭代(明年可能Ollama就被新工具替代),但数据流分析的思维、复式记账的严谨性、分布式环境下的并发控制意识——这些底层能力不会过时。
十四、扩展阅读推荐
按阅读优先级排列:
| ⭐⭐⭐ | Ollama官方文档 | 模型管理、API参考、量化方案 | 所有后端开发者 |
| ⭐⭐⭐ | ComfyUI GitHub Wiki | 节点系统、API模式、自定义节点开发 | AIGC工程化开发者 |
| ⭐⭐ | Redisson官方Wiki | 分布式对象、锁、信号量、限流器 | 需要处理并发的Java开发者 |
| ⭐⭐ | 《数据密集型应用系统设计》 | 分布式系统基础理论 | 进阶架构师 |
| ⭐ | Stable Diffusion论文(Rombach et al.) | Latent Diffusion原理 | 想深入理解生成模型的读者 |
| ⭐ | 《复式记账法原理》(会计基础教材) | 借贷记账法的会计学基础 | 需设计计费系统的开发者 |
十五、附录
附录A:项目技术栈全景图
#mermaid-svg-4KCfkmDJF1NvPBqo{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4KCfkmDJF1NvPBqo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4KCfkmDJF1NvPBqo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4KCfkmDJF1NvPBqo .error-icon{fill:#552222;}#mermaid-svg-4KCfkmDJF1NvPBqo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4KCfkmDJF1NvPBqo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4KCfkmDJF1NvPBqo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4KCfkmDJF1NvPBqo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4KCfkmDJF1NvPBqo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4KCfkmDJF1NvPBqo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4KCfkmDJF1NvPBqo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4KCfkmDJF1NvPBqo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4KCfkmDJF1NvPBqo .marker.cross{stroke:#333333;}#mermaid-svg-4KCfkmDJF1NvPBqo svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4KCfkmDJF1NvPBqo p{margin:0;}#mermaid-svg-4KCfkmDJF1NvPBqo .edge{stroke-width:3;stroke:#333333;fill:none;}#mermaid-svg-4KCfkmDJF1NvPBqo .arrow{fill:#333333;}#mermaid-svg-4KCfkmDJF1NvPBqo .node-bkg{fill:none;stroke:hsl(240, 60%, 86.2745098039%);stroke-width:2px;stroke-dasharray:8;}#mermaid-svg-4KCfkmDJF1NvPBqo .node-icon-text{display:flex;align-items:center;}#mermaid-svg-4KCfkmDJF1NvPBqo .node-icon-text>div{color:#fff;margin:1px;height:fit-content;text-align:center;overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;}#mermaid-svg-4KCfkmDJF1NvPBqo :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
API
Gateway
WebSocket
Server
Orchestration
Billing
Generation
Redis
Cache
PostgreSQL
Task
Queue
Full
Stack
Architecture
附录B:WebSocket消息类型枚举
public enum WsMessageType {
TASK_ACCEPTED, // 任务已受理
PROGRESS_UPDATE, // 进度更新
COMPLETED, // 生成完成
FAILED, // 生成失败
SYSTEM_NOTICE, // 系统公告(广播)
HEARTBEAT, // 心跳
BALANCE_ALERT // 余额不足预警
}
附录C:ComfyUI常用节点速查
| CheckpointLoaderSimple | 加载SD模型 | ckpt_name |
| CLIPTextEncode | 文本→条件向量 | text |
| KSampler | 采样(核心) | steps, cfg, sampler_name, seed |
| VAEDecode | 潜空间→像素图 | – |
| ControlNetApply | 注入控制条件 | strength |
| ImageUpscaleWithModel | 超分辨率 | model_name |
附录D:部署Checklist
- Ollama服务健康检查:curl http://localhost:11434/api/tags
- ComfyUI API模式启动:python main.py –listen 0.0.0.0 –port 8188
- Redis集群就绪:redis-cli -h redis-cluster ping
- WebSocket端点可达:wscat -c ws://host:port/ws/generation
- GPU显存监控:watch -n 1 nvidia-smi
- 日志采集:确认所有服务日志接入ELK
附录E:数据流管道阶段一览
#mermaid-svg-UPOVbQ03aEyV03ke{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UPOVbQ03aEyV03ke .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UPOVbQ03aEyV03ke .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UPOVbQ03aEyV03ke .error-icon{fill:#552222;}#mermaid-svg-UPOVbQ03aEyV03ke .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UPOVbQ03aEyV03ke .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UPOVbQ03aEyV03ke .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UPOVbQ03aEyV03ke .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UPOVbQ03aEyV03ke .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UPOVbQ03aEyV03ke .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UPOVbQ03aEyV03ke .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UPOVbQ03aEyV03ke .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UPOVbQ03aEyV03ke .marker.cross{stroke:#333333;}#mermaid-svg-UPOVbQ03aEyV03ke svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UPOVbQ03aEyV03ke p{margin:0;}#mermaid-svg-UPOVbQ03aEyV03ke .commit-id,#mermaid-svg-UPOVbQ03aEyV03ke .commit-msg,#mermaid-svg-UPOVbQ03aEyV03ke .branch-label{fill:lightgrey;color:lightgrey;font-family:\’trebuchet ms\’,verdana,arial,sans-serif;font-family:var(–mermaid-font-family);}#mermaid-svg-UPOVbQ03aEyV03ke .branch-label0{fill:#ffffff;}#mermaid-svg-UPOVbQ03aEyV03ke .commit0{stroke:hsl(240, 100%, 46.2745098039%);fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .commit-highlight0{stroke:hsl(60, 100%, 3.7254901961%);fill:hsl(60, 100%, 3.7254901961%);}#mermaid-svg-UPOVbQ03aEyV03ke .label0{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .arrow0{stroke:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .branch-label1{fill:black;}#mermaid-svg-UPOVbQ03aEyV03ke .commit1{stroke:hsl(60, 100%, 43.5294117647%);fill:hsl(60, 100%, 43.5294117647%);}#mermaid-svg-UPOVbQ03aEyV03ke .commit-highlight1{stroke:rgb(0, 0, 160.5);fill:rgb(0, 0, 160.5);}#mermaid-svg-UPOVbQ03aEyV03ke .label1{fill:hsl(60, 100%, 43.5294117647%);}#mermaid-svg-UPOVbQ03aEyV03ke .arrow1{stroke:hsl(60, 100%, 43.5294117647%);}#mermaid-svg-UPOVbQ03aEyV03ke .branch-label2{fill:black;}#mermaid-svg-UPOVbQ03aEyV03ke .commit2{stroke:hsl(80, 100%, 46.2745098039%);fill:hsl(80, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .commit-highlight2{stroke:rgb(48.8333333334, 0, 146.5000000001);fill:rgb(48.8333333334, 0, 146.5000000001);}#mermaid-svg-UPOVbQ03aEyV03ke .label2{fill:hsl(80, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .arrow2{stroke:hsl(80, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .branch-label3{fill:#ffffff;}#mermaid-svg-UPOVbQ03aEyV03ke .commit3{stroke:hsl(210, 100%, 46.2745098039%);fill:hsl(210, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .commit-highlight3{stroke:rgb(146.5000000001, 73.2500000001, 0);fill:rgb(146.5000000001, 73.2500000001, 0);}#mermaid-svg-UPOVbQ03aEyV03ke .label3{fill:hsl(210, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .arrow3{stroke:hsl(210, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .branch-label4{fill:black;}#mermaid-svg-UPOVbQ03aEyV03ke .commit4{stroke:hsl(180, 100%, 46.2745098039%);fill:hsl(180, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .commit-highlight4{stroke:rgb(146.5000000001, 0, 0);fill:rgb(146.5000000001, 0, 0);}#mermaid-svg-UPOVbQ03aEyV03ke .label4{fill:hsl(180, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .arrow4{stroke:hsl(180, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .branch-label5{fill:black;}#mermaid-svg-UPOVbQ03aEyV03ke .commit5{stroke:hsl(150, 100%, 46.2745098039%);fill:hsl(150, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .commit-highlight5{stroke:rgb(146.5000000001, 0, 73.2500000001);fill:rgb(146.5000000001, 0, 73.2500000001);}#mermaid-svg-UPOVbQ03aEyV03ke .label5{fill:hsl(150, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .arrow5{stroke:hsl(150, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .branch-label6{fill:black;}#mermaid-svg-UPOVbQ03aEyV03ke .commit6{stroke:hsl(300, 100%, 46.2745098039%);fill:hsl(300, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .commit-highlight6{stroke:rgb(0, 146.5000000001, 0);fill:rgb(0, 146.5000000001, 0);}#mermaid-svg-UPOVbQ03aEyV03ke .label6{fill:hsl(300, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .arrow6{stroke:hsl(300, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .branch-label7{fill:black;}#mermaid-svg-UPOVbQ03aEyV03ke .commit7{stroke:hsl(0, 100%, 46.2745098039%);fill:hsl(0, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .commit-highlight7{stroke:rgb(0, 146.5000000001, 146.5000000001);fill:rgb(0, 146.5000000001, 146.5000000001);}#mermaid-svg-UPOVbQ03aEyV03ke .label7{fill:hsl(0, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .arrow7{stroke:hsl(0, 100%, 46.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .branch{stroke-width:1;stroke:#333333;stroke-dasharray:2;}#mermaid-svg-UPOVbQ03aEyV03ke .commit-label{font-size:10px;fill:#000021;}#mermaid-svg-UPOVbQ03aEyV03ke .commit-label-bkg{font-size:10px;fill:#ffffde;opacity:0.5;}#mermaid-svg-UPOVbQ03aEyV03ke .tag-label{font-size:10px;fill:#131300;}#mermaid-svg-UPOVbQ03aEyV03ke .tag-label-bkg{fill:#ECECFF;stroke:hsl(240, 60%, 86.2745098039%);}#mermaid-svg-UPOVbQ03aEyV03ke .tag-hole{fill:#333;}#mermaid-svg-UPOVbQ03aEyV03ke .commit-merge{stroke:#ECECFF;fill:#ECECFF;}#mermaid-svg-UPOVbQ03aEyV03ke .commit-reverse{stroke:#ECECFF;fill:#ECECFF;stroke-width:3;}#mermaid-svg-UPOVbQ03aEyV03ke .commit-highlight-inner{stroke:#ECECFF;fill:#ECECFF;}#mermaid-svg-UPOVbQ03aEyV03ke .arrow{stroke-width:8;stroke-linecap:round;fill:none;}#mermaid-svg-UPOVbQ03aEyV03ke .gitTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UPOVbQ03aEyV03ke :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
main
参数校验
用户鉴权
计费预扣
任务入队
GPU调度
模型推理
后处理
存储上传
消息推送
计费确认
附录F:复式记账科目表(AIGC场景)
| 1001 | 用户余额 | 资产 | 用户充值后的可用额度 |
| 2001 | 平台收入 | 收入 | 用户消费确认后的收入 |
| 5001 | GPU推理费用 | 费用 | 按模型/时长的推理成本 |
| 5002 | 存储费用 | 费用 | 图片/视频存储成本 |
| 3001 | 预扣暂存 | 负债 | 任务未完成时的预扣金额 |
注脚与注释汇总
本文作者:培风图南以星河揽胜,一名在AIGC工程化一线摸爬滚打的全栈开发者。文中所有代码均经过实际项目验证,但环境差异可能导致行为不同,建议先在本地跑通再上生产。
如果这篇文章帮你少踩了一个坑,欢迎点赞收藏。有问题评论区见,我会逐条回复。
标签:#AIGC #Ollama #StableDiffusion #ComfyUI #WebSocket #Redisson #分布式锁 #复式记账 #数据流分析 #Retrofit #智能体开发 #私有化部署 #星图AI #JavaEE
这里所说的"商业闭环"指的是:AIGC平台不仅需要生成能力,还需要完整的计费、审计、资源追踪体系。复式记账法正是解决"每一次GPU推理消耗了多少资源、对应哪个用户账户"这类问题的工程方案。 ↩︎
此数据基于2026年6月Ollama 0.8.2版本,Qwen3-7B Q4量化模型,输入平均512 token、输出平均256 token的工况。 ↩︎




