存量积累:生图素人感约束+评图分+幂等防重跑+审核回路
- 015-017迁移:image_candidate 文案复审/AI视觉分/重生标记 - constants C7素人感约束(反电商摆拍对齐真实笔记)+C3叠字口子 - celery visibility_timeout=2h 防长任务被误判重投重复烧钱(task75教训) - image_scorer 评图分(只筛选+展示,真实信号才进飞轮权重) - storyboard/image_gen/pipeline_io 生图存量 - task_actions/tasks/task_service/flywheel 审核回路+飞轮存量 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import os
|
||||
from typing import Any, Protocol
|
||||
|
||||
from .constants import IMAGE_RETRY_ATTEMPTS, IMAGE_RETRY_BACKOFF_BASE, IMAGE_SIZE_DEFAULT
|
||||
from .image_scorer import score_image
|
||||
from .storyboard import plan_image_set, sanitize_text
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -126,15 +127,24 @@ async def generate_storyboard_images(
|
||||
reference_images: list[bytes] | None = None,
|
||||
analysis: dict | None = None,
|
||||
strategy: str | None = None,
|
||||
target_role: str | None = None,
|
||||
custom_prompt: str | None = None,
|
||||
) -> list[dict]:
|
||||
"""
|
||||
按 storyboard 逐张生图(asyncio.gather 并发),返回每张结果列表。
|
||||
strategy: None=默认叙事,'A'/'B'/'C'=三套正交叙事策略
|
||||
target_role: 非空时只生成该 role 那一张(R2 单张重生)
|
||||
custom_prompt: 非空时追加到每张 per_prompt 末尾(R2 人工提示词)
|
||||
每项:{role, name, image_bytes, error}
|
||||
"""
|
||||
plan = plan_image_set(note, product, image_count, analysis, strategy=strategy)
|
||||
storyboard = plan["storyboard"]
|
||||
base_prompt = plan["base_prompt"]
|
||||
# R2 单张重生:只保留目标 role 的分镜(匹配不到则原样全跑,避免空结果)
|
||||
if target_role:
|
||||
_filtered = [it for it in storyboard if it.get("role") == target_role]
|
||||
if _filtered:
|
||||
storyboard = _filtered
|
||||
|
||||
async def _gen_one(item: dict) -> dict:
|
||||
# 逐图 prompt 9 字段(扒 promptFromStoryboard:323-334),每张差异化
|
||||
@@ -156,12 +166,31 @@ async def generate_storyboard_images(
|
||||
"中文文字少而清晰,主标题+最多3个短点位;可自然用✅✨🌿💧🪞🧴📦🔍种草符号但不堆砌;"
|
||||
"不要生成App截图或笔记详情页界面。"
|
||||
)
|
||||
# R2 人工提示词:追加到末尾权重最高,但不覆盖前面合规/真实约束
|
||||
if custom_prompt:
|
||||
per_prompt += f"\n运营补充要求(在不违反上述合规与真实约束前提下尽量满足):{sanitize_text(custom_prompt, 200)}。"
|
||||
try:
|
||||
img_bytes = await generate_one_image(client, per_prompt, reference_images)
|
||||
return {"role": item["role"], "name": item["name"], "image_bytes": img_bytes, "error": None}
|
||||
# 注:gpt-image-2 渲染中文偶发错别字(约1/6)。vision/OCR 文字校验闸门实测
|
||||
# 不可靠(漏报形近字+幻觉误伤品牌词),倩倩姐2026-06-16拍板先撤,纯生图,
|
||||
# 错别字作已知问题记录,后续迭代再处理。详见记忆 clover-image-text-check-shelved。
|
||||
#
|
||||
# C3 canvas 叠字口子(倩倩姐2026-06-12拍板"只留口子不实现"):
|
||||
# 当 OVERLAY_TEXT_RENDER_ENABLED=True 时,此处由 PIL 在模型出的干净底图上
|
||||
# 叠 item['overlay_text']/brand_keyword(字体资源+排版坐标后续补),彻底解决中文乱码。
|
||||
# TODO(C3-overlay): from .constants import OVERLAY_TEXT_RENDER_ENABLED
|
||||
# if OVERLAY_TEXT_RENDER_ENABLED: img_bytes = overlay_text_on_image(img_bytes, item)
|
||||
# E12 AI评图分:只做展示+排序,绝不进飞轮权重,失败返 None 不阻断(倩倩姐2026-06-16)。
|
||||
visual = await score_image(client, img_bytes)
|
||||
return {"role": item["role"], "name": item["name"], "image_bytes": img_bytes,
|
||||
"error": None, "text_review": None,
|
||||
"ai_visual_score": (visual or {}).get("score"),
|
||||
"ai_visual_note": (visual or {}).get("note")}
|
||||
except Exception as exc:
|
||||
logger.error("分镜 %s 生图失败: %s", item["role"], exc)
|
||||
return {"role": item["role"], "name": item["name"], "image_bytes": None, "error": str(exc)}
|
||||
return {"role": item["role"], "name": item["name"], "image_bytes": None,
|
||||
"error": str(exc), "text_review": None,
|
||||
"ai_visual_score": None, "ai_visual_note": None}
|
||||
|
||||
# 限并发:apiports 图片接口有 QPS 限制,6 张全并发会撞 429/503
|
||||
concurrency = int(os.environ.get("IMAGE_CONCURRENCY", "2"))
|
||||
|
||||
Reference in New Issue
Block a user