POD 趋势感知 Agent:缓存热点模式 + 三图合成 + 热点去重/风格去重 + review 兜底

- 缓存热点批量流程(有采集缓存不触发 Google)
- 简报不足直接从采集缓存生成(轻量补齐)
- 三图合成(模特/印花/底图)+ 底图压缩 <2MB
- 热点去重→风格去重自动切换 + 不适合类目 review 兜底
- 透明背景(background=transparent)+ 提示词清洗(敏感词/背景描述)
- 任务前 basemap 校验 + 模板国家校验 + 模特任务级分配
This commit is contained in:
2026-08-22 14:14:01 +08:00
commit f493bde8a9
98 changed files with 10280 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
"""阿里云 OSS 图床:图片压缩(3:4、≥1340×1785、<2MB+ 上传。
- compress_for_oss(image_path, out_path)
中心裁剪到 3:4 → 缩放/放大到 1340×1785 → JPEG quality 迭代压到 <2MB。
- upload_to_oss(cfg, local_path, object_key)
用 oss2 上传到 config.oss 指定的 bucket,返回可访问 URL。
- build_oss_key(country, timestamp, code, rand4)
key = {国家}/{时间戳}/{货号}_{4位随机}.jpg(货号=前缀+3位计数,000 起最多 999)
- oss 配置(config.yaml oss 段):
oss_bucket / oss_endpoint / oss_key_id / oss_key_secret(或环境变量 OSS_ACCESS_KEY_ID / OSS_ACCESS_KEY_SECRET
"""
import io
from pathlib import Path
from typing import Optional
from PIL import Image
# 目标规格:3:4 宽高比,最小 1340×1785,文件 < 2MB
TARGET_W, TARGET_H = 1340, 1785
MAX_BYTES = 2 * 1024 * 1024
def compress_for_oss(image_path: str, out_path: str, max_bytes: int = MAX_BYTES) -> str:
"""压缩图片到 3:4 / ≥1340×1785 / <2MBJPEG)。返回输出路径。"""
with Image.open(image_path) as im:
im = im.convert("RGB")
# 1) 中心裁剪到 3:4
w, h = im.size
if w / h > 3 / 4: # 太宽 → 裁左右
new_w = int(h * 3 / 4)
x0 = (w - new_w) // 2
im = im.crop((x0, 0, x0 + new_w, h))
elif w / h < 3 / 4: # 太高 → 裁上下
new_h = int(w * 4 / 3)
y0 = (h - new_h) // 2
im = im.crop((0, y0, w, y0 + new_h))
# 2) 缩放到目标尺寸(≥1340×1785,正好 3:4
im = im.resize((TARGET_W, TARGET_H), Image.LANCZOS)
# 3) JPEG quality 迭代,保证 <2MB
out = Path(out_path)
out.parent.mkdir(parents=True, exist_ok=True)
for quality in (92, 85, 78, 70, 62, 55, 48, 40):
buf = io.BytesIO()
im.save(buf, "JPEG", quality=quality, optimize=True, progressive=True)
if buf.tell() <= max_bytes:
out.write_bytes(buf.getvalue())
return str(out)
# 全部超限 → 用最低质量兜底(仍可能 >2MB,打印警告)
out.write_bytes(buf.getvalue())
print(f"[oss] 警告: {Path(image_path).name} 压缩后仍 {buf.tell()/1024/1024:.1f}MB > 2MB(质量 {quality}")
return str(out)
def upload_to_oss(cfg: dict, local_path: str, object_key: str) -> Optional[str]:
"""上传本地文件到 OSS,返回 URL;配置缺失/失败返回 None(不中断)。"""
bucket = (cfg or {}).get("oss_bucket") or ""
endpoint = (cfg or {}).get("oss_endpoint") or ""
key_id = (cfg or {}).get("oss_key_id") or ""
key_secret = (cfg or {}).get("oss_key_secret") or ""
if not (bucket and endpoint and key_id and key_secret):
print("[oss] 配置缺失(config.oss),跳过上传")
return None
try:
import oss2
# 直连 session:忽略环境代理(挂 VPN 时代理会拦截国内 OSS);
# oss2.Bucket 的 session 必须是 oss2.Session(内部封装 requests),设其底层 trust_env=False
_session = oss2.Session()
try:
_session.session.trust_env = False
except AttributeError:
pass
auth = oss2.Auth(key_id, key_secret)
bkt = oss2.Bucket(auth, endpoint, bucket, session=_session)
with open(local_path, "rb") as f:
bkt.put_object(object_key, f)
url = f"https://{bucket}.{endpoint}/{object_key}"
print(f"[oss] 已上传: {url}")
return url
except Exception as e: # noqa: BLE001
print(f"[oss] 上传失败 {local_path}: {e}")
return None
def build_oss_key(country: str, timestamp: str, code: str, rand4: str, ext: str = ".jpg") -> str:
"""对象 key{国家}/{时间戳}/{货号}_{4位随机}.jpg(如 GB/20260820150945/DG000_Ab3x.jpg)。"""
safe = lambda s: "".join(c for c in (s or "") if c.isalnum() or c in "-_").strip()
return f"{safe(country)}/{safe(timestamp)}/{safe(code)}_{safe(rand4)}{ext}"
def random_code4() -> str:
"""4 位随机:大小写英文 + 数字。"""
import random
import string
return "".join(random.choices(string.ascii_letters + string.digits, k=4))