将 Clover 从上层产品包旧仓库中独立出来,建立专属版本控制。 当前状态=纵切片端到端已打通(登录→选品→出文出图→审核→下载包), M1文案质量去套路化已验收。此提交作为后续按核销清单逐条修复的基线。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.7 KiB
Clover 修改处理方案 · 第二批(🟡 做了一半)
现状代码均已逐行读核。每项含现状/改后/文件/风险/验收。
修6 · ai_call_logs 记录不全
根因:
pipeline_io.py:78-91只在文案后写1条,且prompt_tokens/completion_tokens从不填(httpx响应没解析usage)。run_image_generation(L122-216) 整个函数无任何 AiCallLog 写入——生图调用零留痕。
现状代码(pipeline_io.py:78-91,文案日志):
log = AiCallLog(
workspace_id=workspace_id, user_id=task.operator_id, key_id=key_id,
task_id=task.id, provider="apiports", model=clients._model,
call_type="text", success=llm_success, latency_ms=latency_ms,
) # 缺 prompt_tokens / completion_tokens
改后:分两处。 ① 文案日志补token(依赖_call_llm返回usage,需text_variants.py一并改透传usage,工作量上升)。一期可降级:token填None保持现状,但至少把生图日志补上。
② 生图日志新增(run_image_generation 末尾,db.commit前):
# 生图调用留痕(与文案对称)
try:
img_log = AiCallLog(
workspace_id=workspace_id, user_id=task.operator_id, key_id=None,
task_id=task.id, provider=getattr(clients, "_image_provider", "apiports"),
model="gpt-image-2", call_type="image",
success=any(not r.get("error") for r in image_results),
latency_ms=image_latency_ms, # 需在gather前后记时间
)
db.add(img_log)
except Exception as e:
logger.warning("生图 ai_call_logs 写入失败(非阻断): %s", e)
gather 前加 t0 = time.monotonic(),后算 image_latency_ms。
改动文件:pipeline_io.py(生图日志,约12行);token数透传需 text_variants.py + 调整_call_llm返回(约20行,建议二期)。
风险:飞轮一期是否强依赖token数?若不依赖,token降级、只补生图成败/耗时/provider即可。
验收:跑一次任务,ai_call_logs 表里文案+生图各一条,生图条 provider/model/success/latency 齐。
待拍板:token数这期补不补?建议先补生图留痕,token二期。
修7 · 文案 Gemini 兜底层未接
根因:现链路 LLM(Claude)→本地模板 两层(text_variants.py:89-91)。gemini_factory.gemini_generate 只服务生图,文案无Gemini通道。
现状代码(text_variants.py:89-91):
copies: list[dict] = await _generate_in_batches(llm_client, product, count, extra)
if not copies:
copies = list(build_local_drafts(product, count)) # 直接掉本地兜底
改后:Claude失败后、本地兜底前插Gemini文案。需 gemini_factory 加 chat 方法(现只有图)。
copies = await _generate_in_batches(llm_client, product, count, extra)
if not copies:
logger.warning("Claude文案失败,尝试Gemini兜底")
try:
copies = await _generate_via_gemini(product, count, extra) # 新函数
except Exception as e:
logger.error("Gemini文案兜底也失败: %s", e)
if not copies:
copies = list(build_local_drafts(product, count)) # 最终本地兜底
新增 _generate_via_gemini:调 gemini chat → 复用 parse_json_array。
改动文件:text_variants.py(约15行)、gemini_factory.py(加chat_complete方法,约15行)。
风险:①Gemini文案质量/JSON格式遵守度要验,可能不如Claude ②Gemini key 是否各operator都配了?没配则这层形同虚设。
验收:停Claude(模拟503),确认走Gemini出文案而非直接本地模板。
待拍板:一期是否需要三层?我建议——若operator普遍没配Gemini key,先不接,留两层(Claude+本地)即可。
修8 · 上传只查 content-type 无 magic number
根因:products.py:174 只查 file.content_type(HTTP header,可伪造)。无字节头校验。
现状代码(api/v1/products.py:174-179):
if file.content_type not in _ALLOWED_CONTENT_TYPES:
raise_business(f"不支持的文件类型 {file.content_type}...")
data = await file.read()
if len(data) > _MAX_SIZE_BYTES:
raise_business("文件超过 10 MB 限制")
改后:读完data后加真实字节头校验。
data = await file.read()
if len(data) > _MAX_SIZE_BYTES:
raise_business("文件超过 10 MB 限制")
# magic number 校验真实文件类型(防伪造content-type)
if not (data[:3] == b"\xff\xd8\xff" # JPEG
or data[:8] == b"\x89PNG\r\n\x1a\n" # PNG
or (data[:4] == b"RIFF" and data[8:12] == b"WEBP")): # WebP
raise_business("文件内容不是有效的 JPEG/PNG/WebP 图片")
改动文件:backend/app/api/v1/products.py(约6行)。
风险:无。
验收:把 .exe 改名 .jpg + 伪造 content-type 上传 → 被拒。
修9 · 文案优化轮默认关(确认题,非bug)
现状(text_variants.py:107-110):TEXT_OPTIMIZE_ENABLED=false 默认关。
注释写明:apiports 60s网关下,优化轮的失败要白等60s才503,拖慢出文案+100s+。
这是有意为之的工程权衡,不是bug。 两个选择:
- A(建议)保持关:等北哥prompt方案到位再开,架构已留位(改环境变量即开)。
- B 改造绕60s:优化轮也走分批小请求(≤4条/批),降低单请求触发60s概率。工作量M。
改动文件:A=0改动;B=text_variants.py优化轮分批(约20行)。 待拍板:A还是B?我建议A。
修10 · 无 key 不置灰引导
根因:配置页有"暂无Key请录入"提示(ApiKeysTab.tsx:92),但 /tasks/new 开任务页不检测key、不置灰入口。用户没录key也能点开始→任务必然失败。
现状:NewTaskForm.tsx:38,112-115 只处理"产品入镜但无参考图"的置灰(blockedByNoImage),无key检测。
改后:开任务页加载时查当前用户是否有key,无则置灰"开始"+引导。
// NewTaskForm 加载时
const { data: keys } = useApiKeys(); // 复用配置页的key查询
const noKey = !keys || keys.length === 0;
const blocked = blockedByNoImage || noKey;
// 开始按钮 disabled={blocked}
{noKey && <Banner>请先到「配置中心」录入 API Key 才能开任务 <Link href="/config">去录入</Link></Banner>}
改动文件:frontend/src/components/tasks/NewTaskForm.tsx(约10行)、可能复用key查询hook。
风险:无。
验收:清空key→开任务页"开始"置灰+引导文案+跳配置链接。
第二批小结:修8(magic number)无歧义可直接做;修6(生图日志)建议做、token降级;修7/修9需你拍板(多半可降级/保持现状);修10纯前端体验补强。