从零构建特征存储:最小可用实现
本文从零开始用Python、DuckDB、Parquet、Redis和FastAPI构建最小特征存储,涵盖注册表、离线存储、在线存储、物化管道和检索API五个组件,并探讨AI时代特征存储的设计变化。
特征存储是解决机器学习生产中数据不一致问题的关键基础设施。许多团队在实际部署中才意识到其必要性:一个欺诈检测模型在笔记本中表现完美,但在生产环境却频繁出错;一个支持代理由于缺乏用户上下文而给出泛泛的回复;一个推荐流水线在三个作业中重复计算相同的“30天消费额”,其中两个结果不一致。这些问题的根源在于训练与服务之间的数据偏差(skew),而特征存储正是为此设计的。它允许你一次性定义特征,将其存储为两种形态——一种用于训练,一种用于服务——并保持两者同步。
本文将带领你使用Python、DuckDB、Parquet、Redis和FastAPI,从零构建一个最小可用的特征存储。我们还将探讨AI应用如何改变我们对特征存储的实际使用方式。完整的代码仅有约200行,足以覆盖每个核心组件。
特征存储解决的现代问题
传统上,特征存储主要解决训练-服务偏差,即构建训练集的SQL代码与推理时运行的代码不同,导致特征值逐渐漂移。离线/在线分离是标准修复方案。然而,如今的挑战更为广泛:大语言模型(LLM)代理和检索增强生成(RAG)流水线需要在每次请求的10毫秒内获取结构化用户上下文。LLM没有用户记忆,若要实现个性化输出,我们必须将用户的计划层级、近期活动、账户状态等信息注入提示词,且需要一个能够快速、一致地返回这些值的系统。这正是特征存储的在线存储和检索API提供的功能。因此,我们构建的特征存储同时服务于传统预测机器学习和LLM上下文两大用例,五个组件兼容两者。
五个核心组件
- 特征注册表:以代码形式定义特征。使用Python数据类(dataclass)声明每个特征的名称、实体、数据类型和数据源(Parquet文件路径)。注册表是所有其他组件的单一事实来源,修改特征只需在这一处进行。在生产系统中,注册表通常作为YAML文件或Python模块存储在Git仓库中,每次更改都需代码审查。
- 离线存储:基于Parquet文件,使用DuckDB作为查询引擎。离线存储保存每个特征值的完整历史记录。DuckDB可以直接读取Parquet文件,无需额外数据库。通过ASOF LEFT JOIN实现点对点连接,确保每个训练行只使用到该时间点之前已存在的特征值,从而防止数据泄露。这对于任何需要训练或微调的模型都是必须的。即使是纯推理的LLM用例,离线存储也是回填、评估数据集和审计的来源。
- 在线存储:基于Redis,仅存储每个实体的最新特征值。Redis的哈希查找在亚毫秒级完成。键的格式为entity:entity_id,值是一个包含所有特征的哈希。单次HMGET即可在一个往返中返回所有请求的特征。例如,在本地Redis实例上查询三个特征,耗时远低于1毫秒。
- 物化管道:定期将离线存储中的最新值推送到在线存储。通过QUALIFY子句选取每个实体的最新记录,然后将同一实体的所有特征合并为一次Redis写入,减少往返次数。调度频率根据特征更新需求设定:watch_count_30d每小时更新,last_genre近乎实时,user_segment每天更新。在真实系统中,此管道可由Airflow、cron或流处理作业运行。
- 检索API:基于FastAPI,提供类型化的HTTP端点(如POST /get-online-features),供模型或LLM应用调用。以个性化LLM推荐服务为例:当用户打开流媒体应用时,端点为用户ID返回用户段、30天观看次数和最后观看类型,这些上下文被注入LLM提示词,促使其生成个性化的“接下来看什么”消息。特征存储将“用户8a2f”转化为LLM可用的结构化上下文。
特征存储与向量数据库的边界
向量数据库(如Pinecone、Weaviate、pgvector)常被混淆为特征存储,但二者解决不同的检索问题。向量数据库返回最相似的过往观看会话,而特征存储返回用户段和近期统计。一个完整的LLM栈二者皆需:向量数据库提供相似性检索,特征存储提供结构化键值查找,两者互补而非替代。
常见反模式
- 在模型服务中计算特征:导致训练笔记本和API中的逻辑定义漂移。
- 将在线存储视为数据源:Redis可能在重启后丢失数据,离线存储才是权威,在线存储应视为缓存。
- 跳过注册表:不同团队独立定义同一特征(如active_user),导致仪表盘与模型不匹配。
- 将向量数据库称为特征存储:它无法进行实体键控的结构化查找。
- 未使用点对点连接进行回填:训练集看似良好,生产模型却崩溃,其差距即为数据泄露。
与Feast、Tecton和Databricks的对比
我们的约200行代码以微缩形式实现了相同的功能。若期望继续使用自托管方案,Feast是最接近的开源选择。Tecton和Databricks提供托管路径,并具备明确的LLM特性(如Tecton的LLM特征检索API、Databricks的复合生成AI特征服务)。选择主要取决于希望自运营的程度以及现有技术栈是否已基于Databricks。
结论
一个可工作的特征存储仅需五个组件:注册表、离线存储、在线存储、物化步骤和检索API。亲手构建一次,就能理解生产级系统为何如此设计。它也揭示了AI对设计的改变:在线检索路径是LLM的接入点,点对点连接在训练和评估时至关重要,向量数据库则与特征存储并肩工作。一旦掌握这些组件,将简化版替换为Feast、Tecton或Databricks基本上只是注册表的迁移,系统架构保持不变。
——作者Nate Rosidi,数据科学家及产品策略专家,StrataScratch创始人。