N.13 · 产品 · 2026.07.24 · 2026.10.24 更新 · 约 3 分钟

两张一模一样的照片,帮我守住了Retake Coach 的边界

一次 PicSpeak Retake Coach 的开发复盘:为什么重拍进步必须来自同一次成对评估,以及我怎么用边界测试守住这条线。

Retake Coach 最早来自一个问题:用户按 AI 建议重拍了一次,产品该怎么告诉他“这一张有没有变好”?

单张点评很好做。给照片一个总分,拆成构图、光线、色彩、感染力、技术五个维度,再给几条建议。问题出在第二张照片出现之后。如果原图的分数来自旧 Qwen 路径,重拍图再交给 GPT-5.6 Terra 评分,页面上能算出一个 delta。那个数字看起来干净,实际混进了模型差异和时间差异。

所以 Retake Coach 的核心约束变得很明确:原图和重拍图必须进入同一次成对评估。后端用 Responses API 发出两个有顺序的 input_image,让 GPT-5.6 Terra 在同一把尺子下重新看 before 和 after。模型负责给出五个维度的前后分数、可见证据、下一轮拍摄动作和成功检查;真正的 delta、overall mean、overall delta 全部由 Python 计算。UI 只展示服务端算出来的结果,不接收模型顺手写出的算术。

这个决定也影响了数据结构。Build Week 开始前,PicSpeak 已经有单图点评、异步 review task、历史记录、source_review_id 和 GPT Image 2 reference flow。时间很紧,我没有给 P0 新增比较表。Retake Coach 复用 Review.source_review_id,把完整结果写进 Review.result_json.comparison。Workspace 提交 analysis_type=retake_compare,任务处理器加载源 Review、原图和重拍图,比较完成后仍然通过 Review 详情页读回。

我最喜欢的验收点是同图边界测试。2026-07-17 的 live routing 里,普通点评走 gpt-5.5,单图输入,严格 JSON Schema,最终分数 5.4;Retake comparison 走 gpt-5.6-terra,两张图输入,同样是严格结构化输出。我故意把同一张授权样例图放进 original 和 retake 两个位置。结果整体 delta 是 0.0,五个维度齐全,还返回了 4 条 next actions。这个测试很朴素,但它让我放心:当画面没有变化时,系统不会为了显得聪明硬讲进步。

另一个边界是不可比较。两张图如果场景、主体或拍摄目标对不上,结果可以保留 caveat,用户仍然能读到分析,但它不能进入进步曲线。进步曲线只接纳同一练习链里的 comparable attempts。摄影练习已经足够主观,产品层面至少要守住这条线。

最后跑完的验证比功能本身更能说明这次开发的重心:208 个 backend tests、11 个 subtests、97 个 frontend tests、typecheck、lint、121 页 production build、桌面和移动端浏览器检查都通过。后面前端工具台重构时,又补到 108 个前端测试,把核心页面串成更清楚的练习路径。

我后来回看这次实现,真正有价值的地方在那条被守住的边界:进步必须来自同一次成对观察,数字必须由确定性代码计算,低可信比较不能混进成长记录。两张一模一样的照片没有让 demo 更好看,却帮我确认了系统有没有说实话。

后来

模型后来换过一轮:现在 Flash 模式用 GPT-6 Sol,Pro 模式用更强的 GPT-6.1 Sol。成对评分和「差值由服务端计算」的约定没有改,已经保存的结果会保留当时实际用的模型。换模型的时候,这条边界反而让我省心——比较方式不变,换掉的只是看照片的那双眼睛。