目标:把历史微信头像(都是我家猫的照片)做成一套微信表情包,并在微信表情开放平台提交上架。

整体流程分四步:素材挖掘(微信本地缓存 + agy 识图)→ 文案创作(Gemini 生成 + 人工把关)→ 表情制作(PIL 生成 240×240 透明圆角图)→ 平台提交(Playwright 自动化)。最终「玳瑁烟大王」17 张静态表情提交成功,进入审核。

[!timeline] 整体流程

  • 10:00 · 定位微信头像缓存,140+7839 张待筛选
  • 10:20 · 发现 agy 可识图,prompt 写图片路径即可
  • 10:40 · 蒙太奇批量识别,140 张 → 70 张猫
  • 11:00 · 正样本匹配过滤,确认 63 张是我的猫
  • 11:10 · 感知哈希去重,仅 27 张不同照片
  • 11:30 · 文案 v1 直译,无网感
  • 12:00 · 文案 v2 网红梗,太油
  • 12:30 · 文案 v3 简短口语,定稿 17 个
  • 13:30 · 文字样式迭代 6 版,透明圆角定稿
  • 15:00 · 横幅重做(高清原图裁剪)
  • 15:30 · Playwright 登录,创建专辑
  • 16:00 · 名称被占用 → 玳瑁烟大王
  • 17:00 · 素材/含义词/弹窗全部处理
  • 17:30 · 提交成功,等待审核

1. 素材挖掘

1.1 微信头像缓存在哪

微信 Mac 版把所有出现过的头像缓存在本地。消息数据库是 SQLCipher 加密的,头像缓存是明文 jpg:

~/Library/Containers/com.tencent.xinWeChat/.../2.0b4.0.9/
├── Avatar/              # 应用级:140 张,时间跨度 2021-2026
└── cc7e82cd.../Avatar/  # 账户级:7839 张 + 572 张 HD

1.2 批量识图:蒙太奇 + agy

逐张识别太慢。把 25 张缩略图拼成 5×5 蒙太奇,让 Gemini 一次识别哪些格子是猫。140 张只用了 6 次调用,识别出 70 张猫图。

识图工具是 Antigravity CLI(agy)。关键用法:图片绝对路径必须写在 prompt 文本里,agy 才会把它作为多模态输入附加:

agy -p "识别这张图: /path/to/img.jpg" --dangerously-skip-permissions

1.3 从”猫”到”我的猫”

70 张猫图里混着好友的猫头像。做法:

  1. 人工确认 15 张正样本
  2. 颜色直方图 + 感知哈希粗筛(区分度不足,背景色干扰大)
  3. 让 Gemini 看”样本拼图 + 待确认拼图”,按毛色/花纹判断是否同一只个体

最终确认 63 张。

1.4 去重:63 张其实只有 27 张不同

感知哈希去重后发现:微信把同一张图以不同文件名缓存了多次。63 张去重后只剩 27 张不同的照片。这正是”历史头像”的本质——每次换头像,同一张图可能被存成多个 hash 文件名。

27 张选 24 张做一套,够用。后因我对其中 7 张不满意且无更多素材,改为 17 张(微信最少 16 张)。

2. 文案创作

2.1 “网感”的迭代

版本 做法 结果
v1 按姿态直译(“震惊!”“求摸摸”) 太平,无网感
v2 网红梗堆砌(“泰裤辣”“栓Q”“我真的会谢”) 更差,太油
v3 简短口语有画面感,参考”仰望大佬”“彻底摆烂” 通过

结论:网感 ≠ 网红梗。简短(2-6 字)、日常口语、有画面感、略带幽默,不要梗堆砌。

我附加的约束:禁脏话、禁”喵”字、”仰望大佬”必须保留。

2.2 选择交互

光给文案我对不上图。生成带图选择卡:每张猫图 + 编号 + 3~5 个备选文案,我报”编号+数字”即可。

最终 17 个文案:

编号 文案 编号 文案
01 什么情况? 09 彻底摆烂
02 被拿捏了 10 拒绝打工
03 你在教我做事? 11 有事?
04 吃瓜中 12 思考复杂的人生
05 听不懂你说啥 13 别问我
06 让我来拉扯一下 14 优雅
07 审判你的罪行 15 仰望大佬
08 悄悄探头 16 电量仅剩1%
    17 原地隐身

3. 表情制作

3.1 文字样式迭代 6 版

版本 样式 问题
v1 白底 + 下方文字条 整体不满意
v2 文字叠图上 + 黑描边 字太大、融合差
v3 白字柔和阴影,固定底部 位置太固定
v4 智能找空白区放文字 长文案被圆角截掉
v5 动态字号 + 边界约束 圆角变黑色
v6 RGBA 透明圆角 定稿

智能定位:分 4×4 网格找局部方差最小的块(最平坦区域)放文字,避开猫脸,每张位置自然不同。

黑角 bug 的根源:canvas.convert('RGB') 会把透明像素变成纯黑(0,0,0),圆角区域因此变黑角。修复:直接保存 RGBA PNG(微信支持透明背景)。

3.2 横幅:三次被否

微信要求横幅 750×400、图片中避免出现任何文字。前 3 版:带文字(违规)→ 贴纸拼图(“几个图片拼的”)→ 渐变装饰(“太丑”)。

问题本质:把 240×240 的贴纸拉大到 320px,又糊又变形。正确做法是用高清原图按比例裁剪(1080×1080 → 750:400),清晰自然。

4. 平台提交

4.1 登录

Playwright 无法复用 Safari 登录态,启动自动化浏览器重新扫码,再用 storage_state 保存登录态供 headless 复用。playwright install 版本与本地 Chromium 不匹配,直接指定已有浏览器可执行路径解决。

4.2 表单卡点

按踩坑顺序记录:

名称被占用。”猫猫日常”提交时提示已存在。通用名都易重名,改用猫的毛色特征起名——我家猫是”玳瑁烟色”,定名「玳瑁烟大王」,一次通过。

fill() 不触发 Vue 事件。Playwright fill() 填入的值保存后丢失——表单用 Vue 双向绑定,需要模拟真实键盘输入并 dispatch 事件:

await el.click()
await el.fill('')
await page.keyboard.type(text, delay=60)
await el.dispatch_event('input')
await el.dispatch_event('change')

级联下拉选角色。”角色/内容”是两级级联:先选”宠物/动物角色”,再出现二级菜单选”猫”。第一级选项被覆盖层拦截 pointer events,需 force=True 或 JS click()。

含义词必填 + 4 字静默截断。每个表情必须填”含义词”(聊天页显示名),输入框是点击表情卡片后才出现的(placeholder="输入含义词"),需先激活全部 17 个再填。6 字的”你在教我做事”被静默截成 4 字”你在教我”——填完必须回头核对。

裁剪弹窗。上传横幅/封面/图标后有”裁剪 XXX”对话框,不点”确定”素材等于没传(提交时报”横幅不能为空”)。

类型与主题。默认”动态表情”,选”静态”后有确认弹窗,漏点会变回动态;主题 radio 必选(选了”怼人/斗图”)。

agy 免费额度。批量识别 HD 头像时连续超时,根因是 Individual quota reached(免费额度用完,3.5 小时重置)。文本 prompt 还能过,图片请求(耗 token 高)直接卡死。

4.3 提交

全部字段验证通过后提交,页面返回”表情提交成功,请耐心等待审核”。作品 ID stiker_178619483496f62efcde2da135,审核通常 1-5 个工作日。

项目 内容
名称 玳瑁烟大王
类型 静态表情专辑(17 张)
角色 宠物/动物角色 → 猫
风格 日常、软萌可爱、搞笑

5. 顺带产出

把 agy 的识图/搜索能力封装成了可复用工具:

  • agy_bridge.py:MCP server,暴露 agy_vision(批量识图)/ agy_websearch / agy_ask
  • agy-vision-websearch 插件:agy plugin validate 通过,已安装
  • 能力研究报告:docs/agy_capability_report.md

总结

  • 微信不保留自己的头像历史。本地缓存是”出现过的人”的头像,自己的旧头像只能靠好友端缓存间接找回,且同图多缓存导致大量重复,去重后素材远少于预期。
  • AI 配文案不等于网感。网红梗堆砌反而最不讨喜;简短口语、有画面感的文案才耐看。审美仍需人工把关。
  • 自动化提交的坑集中在”表单事件”。Vue 双向绑定、级联下拉、裁剪弹窗、静默截断,每个都是”看起来成功实际失败”的陷阱,必须保存后重新打开页面验证。