OpenFoodData 案例:营养数据平台的产品决策复盘
一句话定位:面向健康饮食研究者的结构化营养数据平台,追求数据质量的深度控制而非数据量的无限增长。
一、项目背景与缘起
1.1 为什么做这个产品
市面上不缺营养数据工具——MyFitnessPal、FatSecret、OpenFoodFacts 都有海量数据。但它们普遍面向"条码扫描 + 热量记录"场景,存在几个共性问题:
- 数据质量不可控:众包(UGC)模式导致数据完整性和准确性参差不齐
- 搜索范式单一:以品牌商品搜索为主,缺乏"标准品优先"的搜索体验
- 评分体系缺失:没有统一的营养质量评分,用户无法横向比较
- 加工关系模糊:苹果和苹果汁的关系、鸡胸肉和水煮鸡胸肉的关系没有被建模
- 数据来源不透明:不知道数据到底来自 USDA 还是用户手动输入
产品核心假设:存在一个未被充分服务的用户群体——他们不是想记录"今天吃了多少卡路里",而是想理解"哪些食物在某个营养维度上表现更好"。
1.2 竞品定位差异
| 维度 | OpenFoodData | OpenFoodFacts / MyFitnessPal |
|---|---|---|
| 数据来源 | 政府权威数据库(USDA、中国食物成分表) | 用户众包 + 品牌提交 |
| 搜索范式 | 标准品优先(苹果 → 富士苹果) | 条码扫描、品牌名称 |
| 评分体系 | 四层权威分级 + 多维度评分 | 无统一评分 |
| 加工关系 | 模型化(苹果 → 苹果汁、苹果酱) | 无独立建模 |
| 数据质量 | 来源权威分、冲突检测、置信度 | 依赖用户投票 |
二、产品定位决策
2.1 核心价值主张
不做"热量记录工具",而是做"食物营养知识库"。
OpenFoodData 追求的是:当你想知道"低糖水果有哪些"或"哪种食物蛋白质密度最高"时,给你一个可靠、透明、结构化的答案。
2.2 目标用户
- 健康饮食研究者:想了解食物营养构成的系统性知识
- 减脂/控糖人群:需要比较不同食物的营养指标
- 营养师/健身教练:需要权威、可引用的营养数据
- 食品科技从业者:需要结构化的食物营养数据库
2.3 不做的事情
- 不做条码扫描——不追品牌级数据覆盖,聚焦食材级知识
- 不做饮食记录——不是 MyFitnessPal 的替代品
- 不做医学建议——所有评分标注 disclaimer
三、核心产品决策复盘
决策 1:搜索范式 —— 标准品优先
决策内容:搜索以 Canonical Food(标准品)为第一优先级,再展示 Variant(品种变体)和 Source Food(来源食品行)。
搜索优先级:
- Canonical Food 的名称匹配
- Variant 的关联匹配
- 原始 Source Food 的模糊匹配
为什么这样选:
用户搜索"苹果"时,想要的是关于"苹果"这种食物的完整营养信息,而不是"某个超市品牌的某款苹果"。标准品优先让搜索结果符合用户意图。
替代方案 vs 选择理由:
| 方案 | 问题 |
|---|---|
| 品牌商品优先 | 搜索"牛奶"出来 100 个品牌,信息噪音大 |
| 混合无排序 | 标准品和品牌混在一起,无法快速定位 |
| 标准品优先(选择) | 先给"牛奶"的完整画像,品牌作为补充 |
效果:搜索体验更接近"维基百科"而非"电商搜索"。
决策 2:数据质量体系 —— 权威等级 + 加权聚合
决策内容:建立 A/B/C/D 四层权威等级体系,采用加权平均聚合多源数据。
A: 原始营养数据 energy_kcal, protein, total_fat...
(政府数据库级别)
B: 标准衍生指标 salt_equivalent, protein_density, GL
C: 规则分级指标 sugar_level, sodium_level
D: 产品评分指标 diabetes_friendly_score, weight_loss_friendly_score...
⚠️ 强制标注 disclaimer加权聚合策略:
- 每个
DataSource有authority_score(默认 0.8) - 代表值 = Σ(值 × 权重) / Σ(权重)
- 置信度 = min(0.95, 0.7 + 0.05 × 来源数)
- 争议标记:当 max-min > |均值| × 30% 时,标记
is_contested = true
为什么这样选:
多源数据必然存在冲突——USDA 和中国食物成分表对同一食物的营养值可能有差异。简单的取平均或取最新都不够好。加权聚合让更权威的来源有更大话语权,同时置信度和争议标记让用户了解数据的可靠性。
决策 3:评分方法论 —— 可解释的公式,而非黑盒
决策内容:所有评分(D 级)使用明确的数学公式,而非 ML 模型。
示例评分公式:
控糖友好分 = 5 + 纤维×0.5 - 糖/10 + GI奖励 + GL奖励 + 热量密度奖励
减脂友好分 = 5 + 能量奖励 + 纤维×0.5 - 糖/10 + 蛋白质×0.3 + 脂肪奖励
饱腹感分 = 3 + 蛋白质×0.3 + 纤维×0.4 + 水/40 + 能量奖励为什么这样选:
- 可解释性:用户能理解"我为什么得了这个分数",而不是面对一个魔法数字
- 可调性:产品经理可以直接调整公式参数,无需模型训练
- 免责安全:公式给出的是"基于公开营养数据的参考评分",而非"医学建议"
效果:用户可以在详情页看到评分的具体构成,增强了信任。
决策 4:加工关系建模 —— 独立 canonical 而非属性标签
决策内容:加工食品(苹果汁)作为独立的 Canonical Food,通过 FoodProcessingRelation 表与原材关联。
为什么这样选:
错误方案:给"苹果"加一个"加工形态:果汁"的属性。
问题是:苹果汁的营养构成和苹果完全不同(糖浓度、纤维、维生素 C 等),把它当作"苹果"的一个属性会污染数据。
正确选择:苹果汁是独立的食物实体,通过 processed_from 关系与苹果关联。这保持了两个实体的营养数据独立性,同时保留了"它们有关系"的信息。
决策 5:品种(Variant) vs 独立 Canonical
决策内容:富士苹果和嘎啦苹果是"苹果"的 Variant,而非独立 Canonical Food。
为什么这样选:
富士和嘎啦的营养差异很小(主要是糖度微差),如果各自作为独立 canonical,会让搜索结果充斥着"苹果 × N"的噪音。Variant 模式在"展示品种差异"和"保持搜索简洁"之间取得平衡。
决策 6:缺失值显示 null 而非 0
决策内容:营养数据缺失时显示 null,而非 0。
为什么这样选:
显示 0 会让用户误以为"这种食物不含该营养素"。显示 null 表示"我们没有这个数据",避免了误导。这是一个看似微小但影响信任的设计决策。
决策 7:API 设计 —— 胖详情 API
决策内容:食物详情 API 一次请求返回全部数据(基本信息、摘要指标、全量营养表、评分、排行榜、变体、来源)。
为什么这样选:
- 减少前端多次请求(适合移动端)
- 简化前端状态管理
- 单请求即可渲染完整页面
权衡:对后端性能要求更高,每次请求都需要聚合多张表的数据。当前数据量下可行,未来可能需要缓存策略。
决策 8:基础设施预留而非预埋
决策内容:Elasticsearch 和 Redis 已在 docker-compose 中定义但未实际接入代码。
为什么这样选:
当前数据量下,PostgreSQL ILIKE 搜索和直接查询即可满足性能需求。过早引入 Elasticsearch 和 Redis 增加了运维复杂度(索引管理、缓存失效等),而收益不明显。
预留策略的好处:
- docker-compose 已定义 → 未来接入只需改几行代码
- 没有引入不必要的复杂度
- 开发环境无需启动额外服务
四、踩坑与教训
坑 1:多源数据冲突比预期严重
不同政府的食物数据库对同一食物的营养值可能有 20-30% 的差异。例如 USDA 和日本食物成分表对"鸡蛋"的蛋白质含量定义不同(是否包含蛋壳重量?计算基准是否一致?)。
教训:需要建立更精细的 NutrientMapping 机制,不只是映射营养素编码,还要理解不同数据库的测量基准差异。
坑 2:聚合层的滞后效应
RepresentativeNutrientValue 基于多源加权平均,但导入新数据源后需要重新计算所有代表值。当前没有自动触发重新计算的机制。
待解决问题:需要建立代表值更新的事件驱动机制,或者定期批量重算。
坑 3:DisplayProfile 已设计未实现
模型中定义了 DisplayProfile(用户偏好配置),但没有任何 API 使用它。这意味着"每个用户可以自定义显示哪些营养指标"的功能只完成了一半。
教训:设计了但未实现的功能会在代码库中造成困惑——其他开发者会疑惑"为什么有这个表但没 API"。
- 要么做完
- 要么删掉设计文档中的提及
- 要么标注
TODO: not yet implemented
坑 4:对比功能的数据路径不统一
POST /api/v1/calculate(对比功能)使用的是 legacy food_nutrients 表,而详情页使用的是新的代表值体系。两条数据路径的结果可能不一致。
教训:新旧数据迁移期间要确保所有消费者 API 的数据路径一致,或者明确标注差异。
坑 5:排名/评分的好友分享和社会化缺失
排行榜目前是纯工具的视角——列出食物和分数。缺少"你可以把这个排名分享给朋友"或"查看社区热议"的社交维度。
这不是 bug,是有意取舍:当前阶段聚焦数据质量和用户体验,社交功能放在后续路线图中。
五、对其他 AI PM 的启发
1. "数据质量 > 数据数量" 是数据型产品的核心选择
OpenFoodData 用权威等级、加权聚合、冲突检测、置信度系统建立了一套透明的数据质量体系。用户可以看到数据的"信源"和"可信度",而不只是看到一串数字。
这提示我们:数据产品不只是提供数据,还要提供对数据的理解框架。
2. 评分体系的可解释性是信任的基础
如果用户看到"控糖友好分:7.2"却不知道这个分数怎么来的,信任感会降低。OpenFoodData 用公开的公式让用户理解评分的构成——这个设计原则适用于任何涉及评分/评级的产品。
3. "不做"清单比"要做"清单更有价值
明确不做条码扫描、不做饮食记录、不做医学建议——这些边界决定了产品的差异化定位。在 AI 产品领域,同质化竞争严重,敢于定义"我不做什么"往往比"我做什么"更能形成护城河。
4. 搜索范式决定产品气质
标准品优先的搜索范式让 OpenFoodData 看起来像"食物维基百科"而非"食物电商"。搜索体验是用户对产品的第一印象——设计搜索时,想清楚你希望用户觉得"这个产品是什么"。