Lantide Data

统一查询层:逻辑表名、Tab 快取与 Source Run

配套③:人类与 Agent 共用同一条 DuckDB 查询管线。Run / Source Run / validate / run_query 的语意契约见 Prompt 与 Context 工程 §七。快取与 Source Run 操作见 使用者指南 §7–8。


目前实作补充(0.3.3)

统一查询入口以 DuckDB SQL 为输入方言,保留反引号相容。来源稳定 ID 与位置版本分离:可恢复的位置变更可同步目前 tab 引用,但不改历史 SQL。GUI 与 Agent snapshot reader 动态附目前来源对应;同来源不代表内容相同,快照衍生的新 tab 仍须重新执行。恢复不扩张原来源范围,也不保证跨磁碟或网路储存追踪。

系列位置

完整导读见 系列导读与产品定位。

顺序 文章 本题
0 系列导读与产品定位 系列导读与产品定位
1–2 Agent 时代的数据分析工作流、可治理的 Agent Memory 工作流、记忆
3–4 Prompt 与 Context 工程、AI Agent 架构 Context、Agent
5 本文 查询层

一、背景:为什么需要统一查询层

使用者与 Agent 在 SQL 里引用的是逻辑表名(档名、快取分页名、MCP 物化表、ATTACH 后的 alias.schema.table),执行时由后端改写为 DuckDB 可读路径或 TEMP TABLE。统一查询层的目标很单纯:人类与 Agent 写同一种 SQL、走同一条管线、受同一套唯读与物化规则约束。具体而言:

  • 编辑器与对话里的 SQL 语法一致;
  • 权限与唯读规则一处实施;
  • Agent 建立的快取与使用者手动 Run 的快取同一套血缘。

SQL-first 的方法论(为何口径与取数以可审阅 SQL 为主契约)见 Agent 时代的数据分析工作流 §三。本篇说明上述原则如何以统一查询层承载(逻辑表名、物化、血缘、Source Run)。


二、逻辑表名 vs 实际来源

使用者 / Agent 撰寫
   SELECT … FROM "monthly_revenue"
              │
              ▼
        sqlglot 解析 + 改寫
              │
     ┌────────┼────────┐
     ▼        ▼        ▼
  本機檔案  TEMP 快取  ATTACH / MCP

表名必须双引号引用(档名含 .csv 等)。Excel 为 "file.xlsx"."SheetName"。外部库为 alias.schema.table 或 alias.table。


三、查询管线总览

典型路径:

  1. 解析 SQL(sqlglot AST)。
  2. 替换表名为路径或保留快取/ATTACH 名称。
  3. 唯读守卫(拒绝 DDL/DML)。
  4. DuckDB 执行。
  5. 可选:物化为 TEMP TABLE 并注册 TabCacheRegistry;或写入 ResultManager 供结果面板分页。

§七展开分析就绪层;§八展开快取与 Source Run;§十一展开 ResultManager 生命周期。


四、sqlglot 改写

字串替换无法安全处理引号、子查询与多表 JOIN。AST 遍历可:

  • 辨识表名是否为已注册快取 → 跳过路径改写;
  • 辨识 ATTACH catalog / db 前缀 → 交给 DuckDB;
  • 将本机档映射为 read_csv_auto / read_parquet 等。

五、唯读安全

使用者 SQL 与 Agent SQL 皆不得透过查询管线修改原始档或外部库内容。CREATE TABLE 等仅能出现在「物化快取」的受控路径,而非使用者自由撰写的 DDL。


六、本机文件映射

工作区 data.json 登记的文件与 Excel 工作表,在解析阶段改写为 DuckDB 可读表达式。侧边栏 schema 与 show_tables 输出与此登记一致。


七、分析就绪层(工程定义)

在 系列导读 §一「分析师的一天」 的 Medallion 隐喻中,Lantide 的 Silver 对应工作区内的分析就绪/中间结果——本篇定义其工程实作,非企业级 ETL。

能力 实作
可引用的中间表 逻辑表名 + TabCacheRegistry
可重跑 Source Run DAG、拓扑排序
可审阅 SQL source_sql、GET /cache/.../source、get_cache_source
人类与 Agent 一致 同一条 DuckDB 管线(§十二)

与 Julius 类「会话内 transform」的差异在于:中间结果是具名、可查、可 Source Run 的 artifact,而非沙箱内隐式变数(对照见 系列导读 §一 与 工作流 §三)。

但分析就绪层不是每次分析的必经路径。一次性、口径清楚的多表 JOIN 可以直接执行;只有当中间结果需要重用、审阅或重跑时,persist tab / Source Run 才是更好的选择。


八、快取表与 Source Run

8.1 单次 Run

持久分页执行查询(POST /query 或工具 run_sql_tab)时,结果物化为 DuckDB TEMP TABLE,并在 TabCacheRegistry 注册,名称通常与分页标题一致。其他 SQL 可直接:

SELECT category, SUM(amount)
FROM "clean_orders"
GROUP BY category;

快取存在目前工作区的 DuckDB 连线记忆体中;切换工作区或重启应用会清除 — 符合「分析中间结果」定位,非长期仓储。

Agent 快取(run_query 条件物化)亦注册为可查表,但语意上为代理分析用,不参与 Source Run DAG(见 Prompt 与 Context 工程(Query Execution Model))。

条件物化策略(run_query)

条件 物化 reason_code
显式 materialize=true 是 explicit_true
单表探索(单依赖、LIMIT、无 JOIN/GROUP)且显式 false 否 exploratory_single_table
对话 ledger 已有 ≥1 成功步 强制 multi_step_conversation
SQL 引用既有快取表名 强制 references_cached_table
JOIN 表 ≥2 或 CTE ≥3 强制(含首步) complex_query
预设 否 default_ephemeral

推断与 LLM 传入 materialize=false 冲突时采 auto_warn:仍物化、status=ok,并附 warnings。

这里的「条件物化」是 run_query 的数据重用策略,不等同于阻挡查询。前景 run_sql_tab 的阻挡型 complexity gate 目前只保留给过度复杂、难以审阅的形态(例如 3+ CTE);纯多实体表 JOIN 允许执行,最多回传非阻挡的 optimization_hint,提醒可视情况改用 persist tab。

source_sql 与查看 API

TabCacheRegistry 的 CachedTableState 可存 source_sql、result_id、source_kind(persist | agent)。agent 物化时写入 SQL;persist 可由分页内容懒载入。

GET /api_v1/cache/{tab_id}/source

解析顺序:registry → persist 分页 content → ResultManager → workspace ledger 扫描。回传 sql_source(registry | tab_content | result_manager | ledger | unavailable)。UI 右键 查看 SQL 不依赖 conversation_id。

8.2 依赖解析与 DAG

当 SQL 引用其他持久分页的快取名,系统解析依赖边,组成 DAG,并做循环侦测与拓扑排序。实作集中于 tab cache 的 dependency 模组:从 sqlglot 撷取 FROM 中引号包裹的持久表名,与已注册快取比对。

规则摘要:

  • 依赖边 = 被引用的持久快取表名;
  • 禁止自引用自己的快取名;
  • 临时分页不进 Source Run。

8.3 Source Run 与 SSE

Source Run 对目标持久分页:先依 DAG 依序重算所有上游,再执行目标 SQL。透过 POST /source_run/stream 以 SSE 推送 plan、node_start、node_done、complete 等事件;前端显示流程图进度。失败时清理相关快取状态。

Agent 对应工具 source_run_sql_tab(UICommand + 后端适配),与人类按工具列 Source Run 同源。操作见 §8。

8.4 Deep lineage

Check Lineage 可不执行查询,仅建深度血缘图:Physical(文件)、Cached(上游分页快取)、Target(目前分页)、Unknown。用于理解数据从哪来,再决定是否 Source Run。

8.5 run_id 与 abort

进行中的互动查询与 Source Run 可带 run_id 登录;使用者 Abort 时对 DuckDB 连线 interrupt(),并以统一方式辨识取消错误,使 Tab/结果状态收敛。互动查询与 Source Run 中止语意对齐。


九、外部数据源:ATTACH、Cloud folder 与 ODBC staged

9.1 ATTACH 原生 DB(PostgreSQL / MySQL / SQLite)

PostgreSQL / MySQL / SQLite 透过 DuckDB ATTACH 挂载,跨源 JOIN 与本机档在同一 SQL 中完成。解析器对已 ATTACH 的 alias 不做「拉回本机路径」改写。连线生命周期由 Connection Manager 管理(试连、Connect/Disconnect、schema 列举)。

9.2 Cloud folder

S3/OSS/ADLS Gen2 等云端 prefix 以独立 alias 注册;引用格式与 Additional folder 相同("alias"."file.parquet")。传输层由 DuckDB httpfs/azure extension 承载;Connect 会 list prefix 并建索引(有硬上限)。Sidebar Select 100 rows 走 Probe 路径 POST /probe/describe,不经主库 /query。状态 degraded 表示可用但索引可能不完整。

9.3 SQL Server / Azure SQL(ODBC staged)

不 ATTACH。每条 upstream SQL 须单源(仅引用该 ODBC alias);系统将可下推的筛选/聚合转 T-SQL 在远端执行,结果物化为本机 cache,再与本地档、agent/persist 快取或已 ATTACH DB 做第二步 JOIN。单一 statement 透明跨源 JOIN 会被拒绝(SQLSERVER_CROSS_SOURCE_REQUIRES_MATERIALIZATION)。

大结果 preflight: 远端估计超过 500,000 列 时,validate_query/run_query 回传 warning_required + warning_id;须 ask_user 后以 large_remote_warning_id 重试。

9.4 Probe vs 主库执行

Tier 用途 典型入口
Probe(次要 DuckDB) metadata、validate_query、describe、Sidebar 预览 /probe/describe
主 connector 正式 /query、物化、Source Run run_query / run_sql_tab

分离可避免 metadata 扫描与 heavy execute 互抢同一 in-process 连线。

9.5 Connections lifecycle

每笔绑定可 Connect/Disconnect/Remove/Edit。Disconnect 保留设定但 Sidebar 隐藏 alias。Remove、Edit Save 或变更 folder 路径若影响 SQL tab 引用,会先显示 impact 确认。状态:connected(DB ATTACH)、available(folder/cloud 已挂载)、degraded(cloud 索引不完整)、disconnected、error 等。


十、MCP 物化 Parquet

MCP 工具回传结果物化为工作区内 Parquet,以 mcp_alias__table 形式注册进查询层。Stale 时由 MCP 工具刷新,而非在 SQL 内隐式连外网。这是把 Bronze(远端/API 原始源)拉入工作区 Silver(本机可查表)的典型路径。


十一、ResultManager 生命周期

互动查询的结果面板绑定 query_result_id:支援分页浏览、排序、汇出。与 TEMP 快取表分工不同 — 快取服务下游 SQL 引用;ResultManager 服务人类阅读与汇出。Agent sql_tab_result 可读持久分页对应的快取结果。Run 路径可能物化进 Registry(供下游 FROM)或写入 ResultManager(供结果面板),依执行入口而定;对照 §三管线图的物化分支。


十二、人类与 Agent 共用管线

动作 人类 Agent(典型工具)
单分页执行 Run run_sql_tab
上游链重算 Source Run source_run_sql_tab
validate_query 编辑器执行前 validate_query(SQL Server 大结果可回 warning_required)
预览列 结果面板 run_query(≤200 行;大结果须 large_remote_warning_id)
读快取 SQL FROM "tab" 同上 + sql_tab_result
查看 SQL 右键 查看 SQL / GET /cache/{tab_id}/source get_cache_source(与 API 同源,见 AI Agent 架构 §6.2)

Prompt 层的 Query Execution Model(Prompt 与 Context 工程 §七)与工具矩阵(AI Agent 架构)必须与本层语意一致,否则模型会写出无法 Source Run 的 SQL 或混淆 agent 快取。


十三、错误设计

错误讯息指向可修正项(表名未加引号、依赖分页未执行、循环依赖、唯读违规)。查询失败时,Context 可把错误 + 触发 SQL 一并送给 Agent(见 §12.11)。


十四、工程决策总览

决策 取舍
DuckDB in-process 低延迟;快取随程序
sqlglot 安全改写 vs 正则
TEMP TABLE 快取 速度 vs 持久化
Source Run DAG 正确顺序 vs 单次 Run 简单
SSE 进度 可观测长链执行
多表 JOIN 不硬挡 实务分析可完成;cache 以 hint 引导

十五、结语

统一查询层让「分析师写的 SQL」与「Agent 写的 SQL」落在同一套物化与血缘规则上——是 SQL-first 与 分析就绪层 的工程落点。

方法论见 工作流 §三;Execute 与持久分页的产品故事见同篇 §四、§七;模型何时可呼叫 source_run_sql_tab,见 AI Agent 架构。