3. 生成式AI数据接入与摄取

生成式AI应用的效果上限,往往在数据接入阶段就已经被决定。无论是构建RAG知识库、微调多模态模型,还是驱动实时智能体,第一步都是把云存储中的文件、Kafka中的事件流、外部API返回的JSON或二进制内容,稳定、可追溯、可增量地送进湖仓。本节围绕Databricks上的两条主线展开:Auto Loader负责云存储中海量文件的增量发现与解析,Delta Live Tables(DLT,现称Lakeflow Declarative Pipelines)负责把流式与批式数据编排成有质量约束的管道。二者结合,可以覆盖从对象存储、消息队列到HTTP API的多模态摄取场景。

一、三条摄取通道的定位与差异

云存储、Kafka、API在数据形态和接入语义上差别很大,不能套用同一种写法。云存储适合“文件到达即处理”,典型对象是PDF、图片、音频、视频、JSON日志;Kafka适合“事件持续追加”,典型对象是用户行为、设备遥测、变更日志;API适合“按需拉取或轮询”,典型对象是第三方SaaS返回的JSON、分页列表、带鉴权的二进制资源。三者的关键差异在于:云存储靠目录扫描与文件通知发现新数据,Kafka靠offset维护消费位置,API靠游标、时间戳或分页token维护拉取进度。理解这一点,才能正确选择Auto Loader、Kafka连接器或自定义轮询逻辑。

多源流式摄取:云存储 / Kafka / API → 铜层 → 银层 云存储(S3 / ADLS / GCS) PDF · 图片 · 音频 · JSON 日志 目录:/raw/docs/ /raw/media/ Auto Loader cloudFiles · 增量文件发现 Kafka 事件流 topic: user_events / telemetry key · value · offset · 时间戳 read_kafka offset 维护 · 恰好一次 外部 REST API JSON 分页 · 鉴权 Token 游标 / 时间戳增量拉取 轮询 + 自动加载 落地文件后交给 Auto Loader 铜层 Bronze(原始落地) 保留原始二进制 / JSON · 追加 schema 元数据 _rescued_data · 文件路径 · 摄取时间 银层 Silver(清洗与解析) 文本抽取 · 图像/音频元数据 · 结构化字段 DLT 期望约束:非空、格式、去重 DLT 管道控制点 • schema 演进与漂移 • checkpoint 位置 • 期望约束与告警 • 流式/批式触发模式 • 多模态解析 UDF • 血缘与重跑

二、Auto Loader:云存储增量摄取的核心机制

Auto Loader 通过 cloudFiles 数据源工作,它有两种文件发现模式:目录列表模式和文件通知模式。目录列表模式无需额外云资源,适合中小规模;文件通知模式依赖云厂商的事件通知(如 S3 EventBridge、ADLS Event Grid),适合海量文件、低延迟场景。无论哪种模式,Auto Loader 都会维护一个 RocksDB 状态存储,记录已处理文件,从而保证“新文件只处理一次”。

一个典型的文本摄取示例:

raw_text = (spark.readStream
  .format("cloudFiles")
  .option("cloudFiles.format", "text")
  .option("cloudFiles.schemaLocation", "s3://lake/_schemas/text/")
  .option("cloudFiles.inferColumnTypes", "true")
  .load("s3://lake/raw/docs/"))

(raw_text.writeStream
  .option("checkpointLocation", "s3://lake/_chk/text/")
  .trigger(availableNow=True)
  .toTable("bronze.docs_text"))

这里 schemaLocation 用于存放推断出的 schema 及演进历史,checkpointLocation 用于记录流进度。trigger(availableNow=True) 表示“把当前积压一次性处理完再停止”,非常适合按小时或按天调度的准实时场景;若需要持续低延迟,则改用默认的微批触发。对于图片、音频等二进制文件,Auto Loader 可以先用 binaryFile 格式读入,得到 path、content(二进制)、length 等字段,再在后续步骤中调用解析函数或模型服务提取特征。

当上游文件格式可能变化时,建议开启 cloudFiles.schemaEvolutionMode(默认 addNewColumns),并配合 _rescued_data 列捕获无法解析的字段,避免因个别脏文件导致整条流失败。

三、Kafka 流式摄取:offset 与恰好一次

Kafka 接入使用 read_kafka,核心是把 broker、topic、消费组和起始位置配置清楚。示例:

events = (spark.readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "broker1:9092,broker2:9092")
  .option("subscribe", "user_events")
  .option("startingOffsets", "latest")
  .option("kafka.security.protocol", "SASL_SSL")
  .load())

parsed = (events
  .selectExpr("CAST(value AS STRING) AS json_str", "timestamp")
  .select(from_json("json_str", schema).alias("d"), "timestamp")
  .select("d.*", "timestamp"))

Kafka 消息的 value 通常是字节流,需要先转成字符串再解析 JSON。offset 由 checkpoint 维护,配合 Delta 的幂等写入,可以实现端到端的恰好一次语义。startingOffsets 只在首次启动时生效,之后由 checkpoint 接管;如果误删 checkpoint,流会按 startingOffsets 重新开始,可能造成重复,因此 checkpoint 目录必须与表一起纳入备份和权限管理。

四、API 摄取:轮询、落地、再增量

API 没有原生流式连接器,常见做法是“定时轮询 + 落地文件 + Auto Loader 增量读取”。轮询任务用 Databricks Job 或 Lakeflow 调度,每次携带上次的游标或时间戳,把返回的 JSON 写入云存储的 /raw/api/ 目录,文件名带时间戳保证唯一。随后 Auto Loader 自动发现这些新文件并写入铜层。这样做的价值在于:API 的鉴权、分页、限流逻辑与湖仓摄取解耦,失败重试只影响轮询任务,不会污染流状态。

五、用 DLT 把三条通道编排成可治理管道

Delta Live Tables 用声明式方式定义表和流,并自动管理依赖、重试、血缘和质量约束。下面把云存储、Kafka 两条通道汇入同一套铜银层:

import dlt
from pyspark.sql.functions import col, from_json

@dlt.table(name="bronze_docs")
def bronze_docs():
    return (spark.readStream.format("cloudFiles")
      .option("cloudFiles.format", "text")
      .option("cloudFiles.schemaLocation", "s3://lake/_schemas/docs/")
      .load("s3://lake/raw/docs/"))

@dlt.table(name="bronze_events")
def bronze_events():
    return (spark.readStream.format("kafka")
      .option("kafka.bootstrap.servers", "broker1:9092")
      .option("subscribe", "user_events")
      .load())

@dlt.table(name="silver_docs")
@dlt.expect_or_drop("non_empty", "length(value) > 0")
def silver_docs():
    return dlt.read_stream("bronze_docs").select(
        col("value").alias("content"), col("_metadata.file_path").alias("src"))

@dlt.expect_or_drop 表示不满足约束的行直接丢弃并计入指标;若希望保留但标记,可用 expect;若希望整条管道失败,用 expect_or_fail。DLT 会在事件日志中记录每条期望的通过/失败数量,这是判断数据质量是否达标的第一手依据。多模态场景下,可以在银层调用 ai_query 或自定义 UDF,对图片做OCR、对音频做转写、对PDF做版面解析,把非结构化内容转成可检索的文本与向量。

六、结果判断与常见排障

摄取是否健康,可以从四个信号判断:一是铜层表行数是否随新文件/新消息持续增长;二是 _rescued_data 是否出现非空比例异常升高,说明上游格式漂移;三是 DLT 事件日志中期望约束的失败率是否超过阈值;四是 checkpoint 目录是否持续更新。若流停止推进,优先检查 checkpoint 路径权限、schema 冲突和 Kafka 消费组是否被其他作业占用。对于 API 轮询,重点看游标是否被正确持久化,避免每次全量拉取造成重复。

不要把 checkpoint 和 schema 目录放在会被生命周期策略自动删除的路径下;一旦被清理,流会从头重放或重新推断 schema,可能产生重复数据和类型错乱。

把三条通道统一到铜银层之后,生成式AI应用就可以在银层之上构建向量索引、特征表或检索服务。摄取阶段留下的文件路径、摄取时间、质量指标,正是后续做数据溯源、增量刷新和问题定位的关键依据。