Files
beige/plans/修改处理方案-第二批.md
yangqianqian 6a2632da70 baseline: Clover 独立仓库首次基线提交
将 Clover 从上层产品包旧仓库中独立出来,建立专属版本控制。
当前状态=纵切片端到端已打通(登录→选品→出文出图→审核→下载包),
M1文案质量去套路化已验收。此提交作为后续按核销清单逐条修复的基线。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 11:30:22 +08:00

149 lines
6.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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文案日志
```python
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前
```python
# 生图调用留痕(与文案对称)
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
```python
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 方法(现只有图)。
```python
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
```python
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后加真实字节头校验。
```python
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无则置灰"开始"+引导。
```tsx
// 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→开任务页"开始"置灰+引导文案+跳配置链接。
---
> 第二批小结修8magic number无歧义可直接做修6生图日志建议做、token降级修7/修9需你拍板多半可降级/保持现状修10纯前端体验补强。