写点什么

AI 改变的是数据接口,而不是查询引擎

作者:Peihao Yuan
  • 2026-11-03
    北京
  • 本文字数:6478 字

    阅读完需:约 21 分钟

AI摘要

AI智能体在运维分析中嵌入轻量查询引擎(如DuckDB),实现端到端结构化报告生成,无需依赖传统湖仓架构。核心在于将确定性计算(指标聚合)与概率性推理(故障归因)解耦,按实际需求选择嵌入式引擎或托管湖仓。

关键观点:嵌入式引擎适用于低延迟、确定性聚合+小规模推理场景;湖仓仍必要于跨团队共享、高并发SQL、长期治理与合规审计;判断依据包括数据时效性、协作广度、计算确定性及治理要求。

适合SRE工程师、数据平台架构师、AI工程化负责人阅读。

AI 智能体如今可以生成分析代码、从对象存储读取文件,并将运维数据转为结构化报告。这是否意味着数据湖、湖仓一体和 SQL 引擎已经过时?我们在对一场大型容灾演练做自动化分析时遇到了这个问题。我们最初的可行方案是:将 30 秒粒度的服务指标以 Parquet 格式存入兼容 S3 的对象存储,使用 DuckDB 进行确定性聚合,再由 AI 智能体对故障转移结果进行分类并给出证据说明。这个原型在没有共享湖仓的情况下也能工作,但并不是因为查询引擎消失了。恰恰相反,引擎变成了嵌入在智能体工作流内部的组件。本文将讲解这个架构,介绍确定性计算与概率性推理之间的实用划分,并给出四个判断依据,用来决定什么时候用嵌入式引擎就足够,什么时候需要托管湖仓。

原型跑通之后才出现的问题

这个问题并不是头脑风暴空想出来的,而是在一个真实工作流成功产出第一份报告之后出现的。当时我们正在分析一次容灾隔离演练。演练中,一个区域被有意隔离,流量预期会转移到幸存区域。对每一个服务,我们都需要回答三个问题:

  • 幸存区域是否吸收了来自被隔离区域的流量?

  • 吸收流量之后,服务是否仍然可用?

  • 延迟是否保持在可接受范围内,还是额外负载造成了持续性劣化?

我们以 30 秒粒度导出服务指标,存为 Parquet 文件放到兼容 S3 的对象存储中,然后让 AI 智能体生成结构化分析报告。当这个工作流端到端跑通后,一个诱人的想法就出现了:

既然智能体能从对象存储读取文件并给出答案,那为什么我们还需要表、目录、湖仓或 SQL?

这个问题听起来合乎情理,因为面向用户的交互方式已经变了。工程师不再需要打开仪表盘或者手写查询,而是直接让智能体分析演练数据。智能体会识别相关数据,按需生成 DuckDB SQL 或者 Python 代码,通过确定性工具执行分析,再解释发生了什么。举个例子,为了验证流量是否在故障转移时从 A 区转移到了 B 区,智能体会针对预设的表集合与时间范围生成一条只读的 DuckDB 查询。执行前,这条查询会经过白名单校验,仅允许访问许可的数据集与操作,随后在沙箱分析运行时中执行。运行时返回结构化查询结果,再由确定性应用代码规整成事实数据包,里面包含分区域流量、时间戳、变化百分比等字段。一条简化的示例查询如下:

generated_sql = """SELECT    region,    SUM(request_count) AS requestsFROM traffic_metricsWHERE ts BETWEEN :start_time AND :end_timeGROUP BY region"""validate_sql(    generated_sql,    allowed_tables={"traffic_metrics"},    allowed_operations={"SELECT"},)rows = sandbox.execute(    generated_sql,    params={        "start_time": exercise_start,        "end_time": exercise_end,    },)fact_packet = {    "metric": "request_count",    "window": [exercise_start, exercise_end],    "by_region": {        row["region"]: row["requests"]        for row in rows    },}
复制代码

在这个简化示例里,智能体只负责生成分析查询。确定性校验层会在执行前拦截对白名单外的表或非只读操作的访问。查询在沙箱环境运行后,返回的数据行会由应用代码转换成结构化事实数据包。大模型拿到的是这个数据包(例如 A 区、B 区的请求量),而不是由模型自己编造或直接估算这些数值。

为什么容灾分析不只是简单扫描指标

容灾分析的难点不在于检索时序数据,而在于判断这个时间序列意味着什么。假设 A 区被隔离,B 区保持正常,一次健康的故障转移应该产生一个可识别的序列:

  • A 区域的 QPS 下降。

  • B 区域的 QPS 相应增加。

  • B 区域的成功率保持在服务目标之上。

  • 尾部延迟可能短暂上升,但不应持续居高不下。

每一个结论都需要上下文支撑。QPS 上升不等于故障转移成功,涨幅可能不足以承接隔离区域流失的流量。成功率下降也不代表容灾失败,指标里可能包含预期内的业务拒绝请求。切换瞬间的延迟尖峰也可能无害,只要系统能快速回到稳态。因此我们设置了两组基线和一个稳态窗口:

  • 历史基线:前一天同一时间段,用来捕捉正常时段的流量特征。

  • 即时基线:演练开始前一小时,反映服务最近的运行状态。

  • 稳态窗口:切换完成后的一段时间,排除短暂过渡期,重点观察持续性表现

分析一共收集四大类核心指标:

  • qps(每秒请求数)

  • err_qps(错误请求数)

  • success_rate(成功率)

  • latency_pNN,包含 p99 这类尾部延迟指标

针对每个服务,确定性代码会计算出流量吸收率、SLO 违规情况、延迟变化、持续劣化时长等事实。智能体再结合服务、演练配置和已有证据对这些事实进行解读。这个区分至关重要:难点往往在于判断,但判断只有在底层事实是确定性的时候才可信。

架构:对象存储、嵌入式引擎与智能体

该工作流分为三个逻辑层:数据准备、确定性计算和智能体驱动的解释,并将可复现性视为硬性需求。数据准备层使用可复用的 DuckDB 构建逻辑——生成一次即可复用——把原始指标转换为对象存储中的自描述资产。对于每个分析请求,智能体选择相关输入,生成受约束的 SQL 或 Python 代码,并编排执行确定性计算。智能体解释由此产生的结构化事实包,并生成最终报告。在整个工作流中,输入数据、配置、可复用构建逻辑、确定性事实和智能体执行元数据都被保留下来,以便后续能够重建、重放和评估该分析。

图 1. AI 智能体发起并调度分析流程,确定性工具计算事实,智能体解读事实数据包。来源:作者自制示意图,由 ChatGPT 辅助绘制。

存储与自描述元数据

物理数据存放在 S3 兼容对象存储中。每次演练都有一个自描述目录:

s3://<bucket>/<root-prefix>/  index.json  <exercise-id>/    manifest.json    exercise_config.json    metrics-*.parquet    service_dimension.parquet
复制代码

这些文件用途各异:

  • index.json 列出可用的演练任务。

  • manifest.json 记录 Schema、行数、文件大小和校验和。

  • exercise_config.json 记录时间窗口、区域、阈值和演练标识。

  • metrics-*.parquet 存放 30 秒粒度的指标事实数据。

  • service_dimension.parquet 存放服务元数据,如归属团队和服务等级。

清单文件(manifest)很重要,一堆零散文件不等于可信的数据资产。使用者需要知道里面有什么、使用的是什么 Schema、写入是否完成、后续能不能校验文件完整性。

使用 DuckDB 进行确定性处理

存储边界的两侧都使用了 DuckDB。在摄入阶段,它读取原始指标记录、排序、写入压缩 Parquet 文件,并为 manifest 生成校验统计信息。在分析阶段,它直接读取 Parquet 文件,过滤目标时间窗口,关联服务元数据,并计算供智能体使用的事实包。该架构受益于 DuckDB 的若干特性:

  • 运行在进程内,因此可以随智能体工作流一起移动,不需要单独的数据库服务。

  • 直接读取 Parquet,并能把过滤和列选择下推到 Parquet 扫描中。

  • 为聚合和连接查询提供 SQL 语义。

  • 可以把较大操作溢出到本地存储,不需要整个工作集都留在内存中。

这并不是说 DuckDB 在所有场景下都优于 Spark、Trino 或者托管湖仓。核心原因只是:当前负载还不需要一套分布式共享查询引擎。

使用 AI 智能体进行概率性解释

智能体并不会收到数十万条原始指标,然后自己去统计错误或计算延迟均值。相反,它收到的是每个服务精简的、确定性的证据数据包。一个简化的证据包如下例所示:

{"service_id": "service-a","exercise_window": "post-cutover-steady-state","expected_failover_qps": 1200,"observed_qps_increase": 910,"traffic_absorption_ratio": 0.758,"success_rate_baseline": 0.9992,"success_rate_during_exercise": 0.9941,"service_slo": 0.9990,"p99_latency_baseline_ms": 84,"p99_latency_during_exercise_ms": 191,"sustained_degradation_minutes": 18,"data_quality_flags": []}
复制代码

智能体使用这个证据包来对结果进行分类、解释支持性证据,并提出下一步排查方向。相应的输出可能是:

{"classification": "partial_failover","confidence": "high","evidence": ["The surviving region absorbed approximately 76% of the expected traffic.","Availability remained below the service SLO during the steady-state window.","The p99 latency increase was sustained for 18 minutes."],"recommended_action": "Review routing capacity and downstream dependency limits."}
复制代码

精确数值由引擎算出。模型的职责是判断这些数字该如何解读、如何表述。

简单的基于阈值的规则并不总是够用。例如,在故障转移后,某个服务的 p99 延迟阈值可能暂时超标,但流量已经正常转移、可用性仍在服务目标之内。一个把任何阈值违规都视为失败的规则会把该服务判定为已劣化。而智能体可以比较过渡期窗口与稳态窗口,识别出延迟在几分钟内回到了基线,判定这是一次伴随短暂抖动的成功切换,而不是持续性容灾故障。

更重要的是,有些情况包含相互冲突的证据,无法通过单一阈值或时间规则来解决。一个服务可能吸收了预期流量并恢复了自身延迟,但由于下游依赖已接近饱和,依然存在风险。反过来,一个 Tier-2 异步服务可能出现临时积压增加,但未违反可用性目标;同样的模式若出现在 Tier-0 同步依赖中,就必须升级处理。在这些情况下,确定性层仍然产生底层事实(即流量转移、延迟、成功率、积压、依赖饱和度、服务等级和 SLO 状态),智能体将这些事实与服务上下文结合,判断哪些证据优先级更高、事件该如何归类。

因此,智能体的价值不在于取代确定性计算,而在于解决依赖上下文、相互冲突的证据——否则我们就要维护越来越多、每个服务单独定制的规则。

确定性切分

这个项目最值得复用的经验,我称之为确定性拆分原则:把精确计算和上下文推理分开。

表 1. 精确计算与上下文推理的实用划分

这种划分可以避免一种常见反模式——把 LLM 当作查询引擎。语言模型不应该读取数十万行数据并统计错误数、百分位数或平均值。这种做法速度慢、Token 消耗高,且难以复现。更重要的是,它把确定性工作交给了概率性系统。更安全的模式是:

  • 用确定性引擎计算出少量权威事实。

  • 智能体生成受限的 SQL / Python 代码,用来推导这些事实。

  • 代码交由确定性工具执行,把结果数据包连同必要上下文交给大模型。

  • 要求大模型基于数据包内字段给出结论,同时保留输入、生成代码、执行元数据、输出,用于重放和审计。

智能体可以决定要调查哪个问题、动态生成受约束的 DuckDB SQL 或 Python,并调度执行。代码的生成过程可以带有概率性,但最终数值事实必须来自确定性执行,而不是大模型凭空算出。

从工作负载看架构设计

这次演练数据集包含:

  • 30 秒指标粒度

  • 1127748 行事实数据

  • 1523 个服务

  • 约 3.8 MB 压缩 Parquet 事实数据

跨服务上游采集任务由并发 worker 执行,耗时大约一小时。由此产生的分析数据集足够紧凑,嵌入式引擎可以在靠近智能体的位置完成计算,不需要引入共享数据平台。这点很重要:架构决策要贴合真实负载,而不是看到“大数据”标签就上重型组件。一百多万行数据听起来很多,但仅凭行数并不能作为部署分布式湖仓的理由。列数、压缩方式、查询形态、并发量、访问模式、数据迁移成本都需要纳入考量。

一个典型分析示例

下面是一个基于该演练工作流的典型示例,其中服务标识和数值已脱敏。

对于某个服务,确定性执行层产出的事实包显示:幸存区域吸收了约 76% 的预期故障转移流量,可用性在稳态窗口内低于服务 SLO,p99 延迟持续偏高 18 分钟。与此同时,该服务仍然在处理请求,没有完全中断。

智能体将这些事实解释为部分故障转移,而非简单的成功或失败二元判定。它的解释引用了流量吸收率、持续的 SLO 违规和延迟劣化这几点证据,并建议检查路由容量和下游依赖限制。

随后需要结合底层确定性事实和演练时间线来验证该结论。评审者可以把智能体解释中的每一句话追溯到事实包中的字段,并使用保留的输入数据和配置重跑同样的 DuckDB/Python 分析。确认结论后,下一步动作就是排查:调查路由容量或下游瓶颈是否阻碍了幸存区域吸收全部的预期负载。

这一验证步骤很重要:智能体提出解释和下一步行动,但数值证据保持可复现、可独立验证。

让工作流可复现

只有把可复现性当成一等需求,原型才能成为正式的工程系统。每一次演练都会保存一个重放包,其中包含:

  • Parquet 输入

  • manifest 与校验和

  • 演练配置

  • 生成的 SQL 或 Python 代码

  • 执行元数据

  • 推导得到的确定性事实

  • 模型输出

  • 智能体版本与提示词

  • 模型标识符

重放包可用于离线重跑分析、复核有争议的结论或者用同一套证据对比两个智能体版本的表现。我们还显式定义了数据语义。一个容易忽略的点是空值含义:某些场景下,QPS 或者错误计数为空可能代表没有流量;但成功率或延迟为空往往代表没有可供统计的流量,而不是成功率为零、延迟为零。

把所有缺失值填零会造成假失败和假恢复。Schema 和数据质量标志需要保留这种语义差异,保证引擎和智能体对数据集的解释保持一致。

轻量架构什么时候不够用

智能体加 DuckDB 并不是所有分析系统的最终架构。当工作负载具有与之相匹配的形态时它是有效的。有四个信号表明轻量架构开始遇到压力,但即使出现这些信号也并不意味着要立刻采用完整的湖仓方案。每个信号都指向一种可以逐步引入的能力,包括用于共享发现与治理的目录、用于安全并发写入和演进的开放表格式,或当计算必须靠近大型共享数据集时引入的分布式查询引擎。

独立的消费者

如果仪表盘、定时任务、笔记本或其他团队需要直接访问数据,就需要一个稳定的契约。这些消费者不应该去调用原始智能体,或者自己反向解析存储桶目录。共享目录让他们能够按表名查找、理解 Schema、配置权限,并用标准工具查询。

数据大到无法移动到智能体环境

数据达到 TB 级别之后,把文件拉到本地或者开发环境进行分析,效率非常低。查询应该在数据存储侧执行(也就是近数据处理),利用分区裁剪、谓词下推、分布式计算,只把结果传回给智能体。

工作流必须与大型共享数据集连接

当两边数据都不大时,DuckDB 可以高效连接本地文件。当另一侧是组织级权威数据集、已注册在共享目录中、太大或更新太频繁而无法安全导出时,边界就出现了。这时更稳妥的做法是把新数据注册到同一环境中,用共享引擎完成连接查询。

需要并发写入或表级演进

单写入者约定简单有效,适合早期工作流。当多个生产者更新数据集、Schema 独立演进或读取方需要一致快照时,这种方式就会失效。Apache Iceberg 等开放表格式在 Parquet 文件之上增加了表元数据、快照、Schema 演进和事务性提交语义。托管湖仓随后可以结合三项能力:

  • 用于发现、归属、权限、Schema 和分区的目录;

  • 用于快照、演进和安全写入的开放表格式;

  • 用于分布式扫描和连接的共享查询引擎。

这些能力很有价值,但会引入集成、治理、计算和运维成本。只有在真实存在数据共享、规模、事务需求时才引入,不要默认所有 AI 项目上线第一天就需要湖仓。

实用的决策表

表 2. 根据访问模式、数据规模、写入语义选择平台

这不是 AI 与 SQL 的二元选择,也不是文件和表的二元选择。真正要判断的是:

嵌入式、由智能体驱动的分析引擎是否足够,负载是否需要托管的共享数据平台?

结论:AI 改变的是接口,而不是引擎

AI 智能体降低了数据分析的使用门槛。它们能够把业务问题转化为分析步骤,筛选相关依据、对行为进行归类,并输出工程师可直接落地的报告。但它们并不能取代精准执行的环节。

在我们的容灾分析工作流中,Parquet 和 DuckDB 完成了机械性工作:存储、过滤、聚合、校验和连接。智能体负责上下文判断:综合解释流量吸收、可用性和延迟,并输出结论。架构刻意保持轻量,因为真实负载暂时不需要共享湖仓。

跑通这套工作流之后,我们对分析交互入口的看法发生了变化。工程师不再每一次排查都直接和查询引擎交互,但引擎依然是产出可信事实的关键。智能体变成负责规划分析、生成 SQL / Python、解读证据的层。

因此,主要的架构权衡不是 AI 对 SQL,也不是文件对表,而是对于手头的工作负载,智能体到底需要多强的分析基础设施。对于聚焦的工作流,小型嵌入式引擎可能已经足够;只有当共享、规模、写入并发或治理产生具体需求时,才需要引入共享目录、开放表格式和分布式引擎。引擎一直都在,变的只是接口。

查看英文原文:https://www.infoq.com/articles/ai-changes-data-interface/