Skip to content

OpenFoodData 案例:营养数据平台的产品决策复盘

一句话定位:面向健康饮食研究者的结构化营养数据平台,追求数据质量的深度控制而非数据量的无限增长。

一、项目背景与缘起

1.1 为什么做这个产品

市面上不缺营养数据工具——MyFitnessPal、FatSecret、OpenFoodFacts 都有海量数据。但它们普遍面向"条码扫描 + 热量记录"场景,存在几个共性问题:

  • 数据质量不可控:众包(UGC)模式导致数据完整性和准确性参差不齐
  • 搜索范式单一:以品牌商品搜索为主,缺乏"标准品优先"的搜索体验
  • 评分体系缺失:没有统一的营养质量评分,用户无法横向比较
  • 加工关系模糊:苹果和苹果汁的关系、鸡胸肉和水煮鸡胸肉的关系没有被建模
  • 数据来源不透明:不知道数据到底来自 USDA 还是用户手动输入

产品核心假设:存在一个未被充分服务的用户群体——他们不是想记录"今天吃了多少卡路里",而是想理解"哪些食物在某个营养维度上表现更好"。

1.2 竞品定位差异

维度OpenFoodDataOpenFoodFacts / MyFitnessPal
数据来源政府权威数据库(USDA、中国食物成分表)用户众包 + 品牌提交
搜索范式标准品优先(苹果 → 富士苹果)条码扫描、品牌名称
评分体系四层权威分级 + 多维度评分无统一评分
加工关系模型化(苹果 → 苹果汁、苹果酱)无独立建模
数据质量来源权威分、冲突检测、置信度依赖用户投票

二、产品定位决策

2.1 核心价值主张

不做"热量记录工具",而是做"食物营养知识库"。

OpenFoodData 追求的是:当你想知道"低糖水果有哪些"或"哪种食物蛋白质密度最高"时,给你一个可靠、透明、结构化的答案。

2.2 目标用户

  • 健康饮食研究者:想了解食物营养构成的系统性知识
  • 减脂/控糖人群:需要比较不同食物的营养指标
  • 营养师/健身教练:需要权威、可引用的营养数据
  • 食品科技从业者:需要结构化的食物营养数据库

2.3 不做的事情

  • 不做条码扫描——不追品牌级数据覆盖,聚焦食材级知识
  • 不做饮食记录——不是 MyFitnessPal 的替代品
  • 不做医学建议——所有评分标注 disclaimer

三、核心产品决策复盘

决策 1:搜索范式 —— 标准品优先

决策内容:搜索以 Canonical Food(标准品)为第一优先级,再展示 Variant(品种变体)和 Source Food(来源食品行)。

搜索优先级

  1. Canonical Food 的名称匹配
  2. Variant 的关联匹配
  3. 原始 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

加权聚合策略

  • 每个 DataSourceauthority_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 + 能量奖励

为什么这样选

  1. 可解释性:用户能理解"我为什么得了这个分数",而不是面对一个魔法数字
  2. 可调性:产品经理可以直接调整公式参数,无需模型训练
  3. 免责安全:公式给出的是"基于公开营养数据的参考评分",而非"医学建议"

效果:用户可以在详情页看到评分的具体构成,增强了信任。

决策 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 看起来像"食物维基百科"而非"食物电商"。搜索体验是用户对产品的第一印象——设计搜索时,想清楚你希望用户觉得"这个产品是什么"。

MIT License