01 payments (EN+ZH) - idempotency gate is now a whitelist: only PENDING and a late-settled EXPIRED may enter fulfilment. Checking only for FULFILLED left the whole PROCESSING window open to a second queue push (consultant finding). - k-of-n -> two-tier: both chassis anchors (SMBIOS board UUID + CPU architectural signature) matching is sufficient; if one anchor changed, every auxiliary digest must match. Aux dimension moved off the volatile OS GUID onto permanent NIC MAC. - FSM diagram: RETRY_QUEUE branches from PROCESSING, not FULFILLED 02 comfyui covers (EN+ZH) - subscribe the WebSocket BEFORE queue_prompt, settimeout(120), try/finally ws.close(), /history fallback on timeout: fixes the lost-terminal-frame hang and the per-image socket leak - document Pillow half-wave rectified Laplacian (offset=0 clamps negative responses to 0) and why a signed float kernel would not work here - replace the unverifiable "89 covers in 48 minutes" with the filesystem evidence: 216 assets (108 banner+OG pairs) inside a 25.9-min window - ZH: drop the colloquial imperative; add self-bootstrapping note 03 traefik yaegi (ZH) - the .traefik.yml contract paragraph and scoreThreshold 120 had only landed in the EN file; both now applied to ZH - <summary> line counts corrected 29 -> 30 in both languages (verified programmatically for all eight code blocks) layout - standing provenance note in the site footer (sanitised client identifiers / production thresholds), per consultant Q7 ruling
This commit is contained in:
1 parent
a7077a3a1d
commit
bfcf61c7ea
8 files changed
+123
-50
No files matched your search
@@ -15,7 +15,7 @@ summary:
|
||||
painPoint: "直接用扩散模型生成文章封面会导致风格混乱飘忽、画面充斥外星乱码伪文字,且视觉中心过于拥挤导致无法排版叠加标题文本。"
|
||||
impact:
|
||||
- metric: "89 张封面全量批处理"
|
||||
detail: "基于本地 12GB VRAM 生产节点构建无头 ComfyUI 异步 WebSocket 批处理管线,在 48 分钟内全量闭环 89 张技术封面。"
|
||||
detail: "基于本地 12GB VRAM 生产节点构建无头 ComfyUI 异步 WebSocket 批处理管线,单批次在 25.9 分钟窗口内写出 216 个封面资产(108 个 slug × banner + OG 卡片)。"
|
||||
- metric: "图像与文本排版解耦"
|
||||
detail: "提示词中严厉禁止模型生成任何文字,专注于渲染上部 45% 的干净负空间,后续标题完全交由代码程序化叠加。"
|
||||
- metric: "品牌视觉系统一致性"
|
||||
@@ -49,7 +49,7 @@ dynamicCta: true
|
||||
|
||||
$$\text{扩散模型} \longrightarrow \text{纯净氛围与留白负空间} \quad+\quad \text{代码排版引擎} \longrightarrow \text{清晰锐利的矢量文字}$$
|
||||
|
||||
千万不要强求扩散模型去直接画出文章标题。相反,应当将模型严格定位为**高质感氛围背景生成器**,其最关键的产出是提供具备纵深感、符合品牌色调、且在特定区域拥有充足**负空间**的底图。
|
||||
在工程实践中,绝不应强求扩散模型直接渲染文章标题。相反,应当将模型严格定位为**高质感氛围背景生成器**,其最关键的产出是提供具备纵深感、符合品牌色调、且在特定区域拥有充足**负空间**的底图。
|
||||
|
||||
### 1. 色彩规范与空间几何约束
|
||||
每一个生成 Prompt 都由底层的技术隐喻(例如“PostgreSQL 分布式锁”、“Traefik 反向代理”、“Linux cgroup OOM 机制”)与外层的确定性设计约束模板组合而成:
|
||||
@@ -91,7 +91,7 @@ ComfyUI 的底层设计天然由节点图(Node Graph)驱动,其工作流
|
||||
```
|
||||
|
||||
<details class="tech-deep-dive">
|
||||
<summary>🔍 查看批处理编排核心脚本(Python 关键片段,47 行)</summary>
|
||||
<summary>🔍 查看批处理编排核心脚本(Python 关键片段,60 行)</summary>
|
||||
|
||||
```python
|
||||
import json
|
||||
@@ -127,20 +127,33 @@ def generate_cover(slug, topic_metaphor):
|
||||
# 分配独立种子保证可复现
|
||||
workflow["3"]["inputs"]["seed"] = int(uuid.uuid4().int % 1e9)
|
||||
|
||||
# 推入队列并通过 WebSocket 异步等待执行完成
|
||||
prompt_res = queue_prompt(workflow)
|
||||
prompt_id = prompt_res["prompt_id"]
|
||||
|
||||
# 先订阅,再提交。ComfyUI 在任务结束的瞬间就会发出
|
||||
# "executing / node: None" 终止帧;若命中节点缓存或任务瞬间报错,
|
||||
# 该帧会早于迟到的握手抵达,此后的接收循环就会永久阻塞在一个早已完成的任务上。
|
||||
ws = websocket.WebSocket()
|
||||
ws.connect(f"ws://{COMFY_HOST}/ws?clientId={CLIENT_ID}")
|
||||
while True:
|
||||
out = ws.recv()
|
||||
if isinstance(out, str):
|
||||
msg = json.loads(out)
|
||||
if msg["type"] == "executing" and msg["data"]["node"] is None and msg["data"]["prompt_id"] == prompt_id:
|
||||
break
|
||||
|
||||
print(f"成功为 {slug} 生成符合设计约束的高质感封面底图")
|
||||
ws.settimeout(120)
|
||||
try:
|
||||
prompt_id = queue_prompt(workflow)["prompt_id"]
|
||||
while True:
|
||||
out = ws.recv()
|
||||
if isinstance(out, str):
|
||||
msg = json.loads(out)
|
||||
if (msg["type"] == "executing"
|
||||
and msg["data"]["node"] is None
|
||||
and msg["data"].get("prompt_id") == prompt_id):
|
||||
return prompt_id
|
||||
except websocket.WebSocketTimeoutException:
|
||||
# 漏收了完成帧,以 HTTP history 端点为准
|
||||
if prompt_in_history(prompt_id):
|
||||
return prompt_id
|
||||
raise
|
||||
finally:
|
||||
ws.close() # 89 张图 × 1 个套接字,不关闭就是静默句柄泄漏
|
||||
|
||||
def prompt_in_history(prompt_id):
|
||||
with urllib.request.urlopen(f"http://{COMFY_HOST}/history/{prompt_id}", timeout=10) as r:
|
||||
return prompt_id in json.loads(r.read())
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -153,6 +166,8 @@ def generate_cover(slug, topic_metaphor):
|
||||
|
||||
1. **45% 留白目标与 35% 审计红线的容差缓冲**:虽然 Prompt 明确要求模型在顶部预留 45% 的负空间,但扩散模型在构图交界处往往存在渐变溢出。因此,自动化审计将核心红线设定在**顶部 35%** 的绝对标题选区内,给予模型 10% 的过渡缓冲带。
|
||||
2. **平均亮度与拉普拉斯边缘方差双重门禁**:单纯检查平均亮度存在严重盲区——暗色背景上的高对比度青色线框或乱码文字同样具备极低的平均亮度,却会彻底摧毁大标题的可读性。为了避免引入 OpenCV 等重型依赖,审计脚本直接利用纯 Pillow 内置的 3×3 拉普拉斯卷积核(`ImageFilter.Kernel`)提取高频边缘图,并计算标准差(`stddev`)进行拦截。
|
||||
|
||||
这里值得点明一处实现细节,因为它正是该门禁在暗色底图上成立的原因:Pillow 的卷积在无符号 8-bit 空间执行,当 `offset=0` 时,所有**负向**拉普拉斯响应会被截断为 0,实际留下的是**半波整流(Half-Wave Rectified)**的拉普拉斯——只有正边缘峰值。在接近纯黑(`#09090b`)且只有稀疏亮色几何的底图上,这恰好给出所需的判别力:扩散输出的暗部本底微噪被整流掉,而亮色线框与伪文字边缘以完整幅度存活,把标准差推过阈值。若改用带符号的浮点核(`offset=128`),正负两瓣都会保留,阈值将极难标定。
|
||||
3. **规范化转码**:统一缩放至 Open Graph 标准的 `1200×630` 规格,输出为 85% 质量等级的现代 WebP 格式,严格确保单张体积控制在 100KB 以内。
|
||||
|
||||
<details class="tech-deep-dive">
|
||||
@@ -201,8 +216,9 @@ def audit_top_negative_space(img_path: str) -> tuple[bool, str]:
|
||||
## 交付结果
|
||||
|
||||
通过将视觉物料生产工程化为确定性 API 流水线:
|
||||
* **48 分钟内全自动交付 89 张高品质封面**:在单块本地消费级 RTX 3060 显卡上,完成了整个学习门户的全新视觉重构。
|
||||
* **25.9 分钟窗口内写出 216 个封面资产**:单批次产出 108 组 banner + OG 卡片(时间戳来自文件系统,可核验而非估算),全部在单块 12GB 显卡节点上完成。
|
||||
* **100% 保持品牌设计调性一致**:所有配图在暗色调对比、光影重心以及排版空间上呈现出高度收敛的专业秩序感。
|
||||
* **实现无人工值守**:一旦文章的元数据与核心隐喻就绪,流水线即可全自动批处理、校验合规性并直接回流 Git。
|
||||
* **这条管线本身就是证据**:本站每一张封面图——包括本文题图——都出自这条闭环,而不是出自设计软件。
|
||||
|
||||
生成式 AI 真正的威力在于嵌入自动化工程管线,而不是停留在手动逐张抽卡的低效泥潭。用确定性的设计规范去驯服模型的概率输出,规模化的稳定交付自然水到渠成。
|
||||
Reference in new issue
Block a user