灵感卡加到 68 张,导航跟着重做了一遍
68 张灵感卡重新分类
这篇讲一个 12 小时的 session。起因是修 bug,攒了几天的五个小毛病,都不难,想着修完就去干别的。修到一半来了个用户需求,问能不能加旅行目的地的灵感卡,香港、日本、东南亚那种。这一天下来,ÉLAN 从 31 张灵感卡加到了 68 张,我用并行 agent 跑出了 37 个卡定义、37 张封面、444 张样本图,顺带把浏览分类也推倒重来了一遍。
做卡本身不难,麻烦的是卡一多,用户在里面找不到自己想要的那张了。这篇主要讲这个。
先把五个 bug 修了
碰新功能之前,我一般会先把手上欠的债还掉。这次是五个问题:
-
web 端的文案生成跑了两遍。服务端已经用 sse 并行把文案生成好了,移动端也老老实实用了这个流,web 端却根本不看服务端返回了什么,自己又调了一遍。同样的活干了两次,API 的钱白白烧了好几天。
-
gemini 拦内容有两种拦法,这个坑我踩了两次才搞明白。
promptFeedback.blockReason是 prompt 本身就被拒了,还没开始生成;candidate.finishReason === 'SAFETY'是输出撞上了安全过滤,这时候已经生成了一部分。两个都得查。我之前只查了第一个,所以有些被拦下的输出就悄悄返回了空结果,连个报错都没有。 -
微信内置浏览器。很多现代 API 它都不支持,blob url 直接崩,js 引擎还有自己的脾气。我最后决定不兼容了,检测到微信的 user-agent,就弹个框说明情况,再给一个「复制链接到浏览器打开」的按钮。有的东西真不值得修,绕开就行。
-
图片太大。gemini 只出 png,一张 3-5 MB,用户在移动网络上要等很久。我在服务端用 sharp 转成 jpeg(quality 85),一下压到 600KB-1MB,肉眼基本看不出差别。
-
zustand 的闭包陷阱,react 的老坑了。useEffect 抓住的是 zustand store 的旧引用,effect 跑起来读到的是上一次渲染的值。改成在 effect 里面用 getState() 读最新值就好了。这种 bug 烦人的地方在于 95% 的时候都正常,剩下那 5% 出问题的时候,你完全想不到是它。
五个 bug 花了半天。然后开始搞卡。
需求很宽泛,先派 agent 去调研
用户的需求就一句话,想要旅行目的地,港澳台、东南亚、日本、云南、西藏、新疆、欧洲。七个地区,每个地区好几个场景,每个场景都要一整套灵感卡定义,包括场景配置、服装、pose、调色和文案。手写 40 多张卡不现实,所以我先派了四个调研 agent 并行去跑,每个负责一块地区:
- 日本 — 神社、庭园、街景、时令景点
- 东南亚 + 港澳台 — 热带海滩、殖民建筑、夜市、天际线
- 国内 — 云南梯田、西藏高原、新疆沙漠、张家界
- 欧洲 — 地中海小镇、巴黎咖啡馆、瑞士雪山、冰岛
每个 agent 交回一份按优先级排好的场景清单,每个场景带一个可行性评分(1-10 分),打的是现在的模型能不能把这个场景生成好。
调研结果挺有意思。自然风光生成得很好,梯田、薰衣草田、高山湖泊这些,线条干净,光线一致,也没有容易出错的文字和精细细节。标志性建筑也行,鸟居、圣托里尼的蓝顶、吴哥窟的剪影,形状一眼就认得出来。但密集的文字和招牌不行。涩谷十字路口、道顿堀、香港霓虹街,只要画面里字多,AI 生成出来的就是乱码,一眼穿帮,这些直接砍了。人群也不靠谱。夜市、庙会、街拍这种背景里得有一堆逼真路人的场景,很容易看着怪,所以只留了人物相对独立的构图。
40 个候选场景,筛完留下 37 个(可行性 7 分以上)。加上原来的 31 张,ÉLAN 一共有 68 张灵感卡了。然后更大的麻烦来了。
68 张卡塞进去,导航崩了
31 张卡的时候,原来那四个分类标签用着没问题。远方的光、城市漫游、日常诗意、时令之美,每个标签 7-10 张卡,逛起来、挑起来都很顺。68 张卡一塞进去就乱了。
我第一反应是按地区分标签:日本 | 东南亚 | 欧洲 | 国内 | 港澳台,按地理来组织。看着挺合理,新卡本来就是按目的地做的。但正准备动手的时候我停下来了,因为有三个问题。
第一,桶的大小差太多。国内 12 张卡,港澳台只有 3 张,有的标签点进去满满当当,有的空荡荡。
第二,氛围一样的卡被拆散了。巴厘岛海滩、三亚海滩、普吉海滩,这三张卡情绪几乎一样,却分在三个地区标签里,用户想找海滩照,得翻三个标签才看得全。
第三个最要紧,用户根本不是按地理去想的。打开 ÉLAN 的时候,他脑子里想的不是「我要一张日本照片」,想的是「我要一张度假感的照片」「一张都市夜景」「一张有文化感的」。用户先想到的是氛围,不是地图。旅行预订 app 按地区分导航很合适,因为你已经定好去哪了;逛灵感不一样,你是在找一种感觉。
用户逛灵感卡时想的是氛围而不是地理,按地区分的导航和用户心智对不上
双轴方案
最后定下来的方案分两层。主轴是氛围标签,问用户「你现在想要什么情绪」:
| 标签 | 里面是什么 |
|---|---|
| 度假放松 | 海滩、泳池、度假村晨光——一切说"我在放松"的场景 |
| 自然秘境 | 雪山、梯田、森林、高原——原始自然之美 |
| 都市光影 | 天际线、天台、霓虹、街景——城市能量 |
| 文化体验 | 寺庙、茶室、传统建筑——厚重与底蕴 |
| 精致生活 | 咖啡馆、画廊、高级餐厅、SPA——日常的高级感 |
| 时令限定 | 樱花、红叶、初雪——限时场景 |
副轴是地区芯片,想用的时候再叠上去筛:
🇯🇵日本 | 🌴东南亚 | 🇭🇰港澳台 | 🇪🇺欧洲 | 🇨🇳国内
芯片排在氛围标签下面。点一下,就在当前氛围里只看这个地区,再点一下取消,默认显示全部地区。这样用户逛「度假放松」的时候,能同时看到巴厘岛、三亚、普吉、圣托里尼,因为它们氛围一样;也可以点 🇯🇵 只看日本的度假场景;或者干脆不管地区,就按心情逛。
主轴是氛围标签、副轴是可叠加的地区芯片,同一种氛围的卡聚在一起再按地区收窄
原来的 31 张卡也很自然地归进了新分类。「远方的光」按每张卡的实际氛围,分到了「度假放松」和「自然秘境」,「城市漫游」整个进了「都市光影」,没有哪张是硬塞进去的。
按氛围分以后,前面三个问题都解决了。每个氛围标签 8-14 张卡,桶的大小差不多了;海滩卡不管是哪个国家的,都在一个标签里;逛的顺序也跟用户实际怎么想对上了,打开 app,先选心情,想缩小地区再缩小。
讨论这个分类大概花了 30 分钟,实现花了一个小时。我觉得这半小时比写代码那一小时值钱多了,因为分类不想清楚,68 张卡用户根本逛不动。
37 张卡具体怎么生出来的
分类定了,接下来得把 37 张新卡真做出来。每张卡要三样东西:
- 卡定义 — 场景配置、服装、pose、调色、文案,大概 200 行结构化数据
- 封面图 — 用户浏览时看到的缩略图
- 样本图 — 每张卡 12 张(4 张棚拍 + 4 张自拍 + 4 张拍立得),给用户看效果
加起来是 37 个定义 + 37 张封面 + 444 张样本图,一个 session 里全部出完。
卡定义。37 张卡分给 4 个 agent 并行写,日本 8 张、东南亚 + 港澳台 10 张、国内 11 张、欧洲 8 张。每个 agent 拿到三样东西,MuseCard 的 TypeScript 接口定义,三张现有的卡当风格参考,还有分给它的场景清单,然后写出完整的卡定义文件,四个加起来大概 7000 行结构化数据。有一个 agent 写到一半撞了 token 上限,因为 11 个卡定义一次输出太长了,拆成两个子 agent、每个写 5-6 张就好了。编排上我记住了一点,就算 context 窗口很大,一次生成几千行结构化数据也是有天花板的,得提前规划。
封面图。37 张封面用 gemini API 生成,风格跟现有的封面保持一致,干净,让人想去,不加文字。原始输出是 png(gemini 只有这一个选项),平均一张 2 MB,批量转成 webp 以后降到 150 KB 左右,压了 13 倍,手机上的浏览网格一下就加载出来了。
样本图。37 张新卡,每张 12 张,一共 444 张。按卡分批生成,每一批都拿这张卡的整套 prompt 去跑参考的人脸图。
QC。量一上来,质量就成了真问题。最大的毛病是脸会漂,生成的脸跟参考照片对不上,出来的图不像用户本人。我用 gemini flash 搭了一个 QC 管线,把每张样本和参考的人脸图比一比,让 flash 打一个 1-10 的相似度分,低于 7 分的标出来重新生成。第一轮通过率 95.2%,444 张里标出来 21 张;重新生成以后到了 97% 以上。剩下那几张都是角度特别刁或者阴影特别重的边缘情况,这种漂移躲不掉。
反正这一天就是我一个人,加上 claude code 的并行 agent 和 gemini 的图像生成,把 37 个卡定义、37 张封面、444 张样本图全弄出来了。这也是我在超级个体里讲的那个状态。生成这块 AI 完全搞得定,质控也能交给自动化,得人来拍板的是选哪些场景、怎么分类、往什么审美方向推。
用户怎么逛,得重新想
卡片一天之内涨到了原来的 2.2 倍。加卡本身很容易,费心思的是用户怎么在 68 张卡里逛。
按氛围分类这件事,我事先没规划过,也不在任何路线图上,就是卡一多,原来的导航撑不住了,硬被逼出来的。要是把 37 张新卡直接塞进老的四个分类,每个标签 15-20 张卡,用户点进去会直接选择恐惧。内容一多,导航就得跟着重做,结果原来的 31 张卡也跟着沾了光。
这种事我最近碰到不止一次。一下午能生成 37 张卡,分类却没法一下午想清楚,一般都是内容堆到那儿了,才被逼着去想怎么组织。回头看这一天,最有用的反而是中间那 30 分钟的分类讨论。修 bug 和生成都是体力活,agent 干得比我快;用户怎么逛,还是得我自己想。
第一篇:不经意的优越感 | 第二篇:架构 | 第三篇:灵感卡设计 | 第四篇:从 Web 到移动端 | 第五篇:衍生品 | 第六篇:发现体验重设计
This post is also available in English.


