为什么要自己造轮子
数据中台每晚跑 391 个 ETL 任务,分布在同步、计算、质量三个模块。平台自带的调度管理界面能看单个任务状态,但回答不了运维最关心的几个问题:
- 凌晨 2:00-3:00 到底有多少任务在并发?超限了吗?
- 一个计算任务的上游同步有没有跑完?它们之间有没有时间重叠?
- 某个 ETL 的 SQL 改了,下游字典文档有没有跟着更新?
平台不提供这些视角,手动排查要打开十几个页面交叉比对。作为数据团队唯一的开发,我决定自己造一个。
但我不是前端工程师,不是全栈开发者——我是写 SQL 的数仓工程师。能在三周内从零做出一个前后端分离的运维平台,核心原因是 AI 协作。
三代进化:从能用到好用
第一代:Streamlit 单文件(v1,2025-12)
动机: 想看每晚任务并发情况,判断哪些时段过载。
实现: 一个 400 行的 app_v3.py,通过 HTTP API 爬取平台数据,Plotly 画折线图。
痛点:
- Token 手动从浏览器复制,经常过期
- 数据靠爬虫,平台接口一改就废
- 只能看并发折线,没有任务间依赖关系
AI 协作方式: 把需求描述给 AI,它直接生成整个 Streamlit 应用。Cron 表达式解析、时间轴坐标转换这些逻辑,说一遍需求就能拿到完整实现。
第二代:数据库直连(v2,2026-03)
动机: Token 过期太烦了,而且发现平台底层就是 MySQL,我有 root 权限。
改动: 去掉 HTTP 爬虫,换成 SQLAlchemy 直连。同时加了 SeaTunnel 任务的 UNION 查询,覆盖面从单一同步模块扩展到全量。
收益:
- 再也不用复制 Token
- 数据实时,不受 API 缓存影响
- 组织列表从硬编码 15 个变成从 sys_org 动态加载
AI 协作方式: 描述"我想把 API 数据源换成数据库直连",AI 给出 SQLAlchemy 连接池方案 + 参数化查询写法。一轮对话完成迁移。
第三代:前后端分离全栈应用(v3,2026-05)
动机: Streamlit 天花板到了——想做 DAG 拓扑图、想做可交互的节点点击、想做知识图谱联动跳转。Streamlit 做不了这些。
决策: 拆成 FastAPI 后端 + React 前端,用 AntV X6 画 DAG。
AI 协作方式转变: 不再是"给我生成一个页面"的单轮交互,而是持续对话式开发——
- 我描述业务需求("我想看一个调度内所有节点的上下游依赖关系")
- AI 提出技术方案("可以解析 ETL SQL 提取 FROM/JOIN 表名,再反查血缘表找到上游同步任务")
- 我补充业务约束("血缘表的 to_table_name 有空格,而且 WITH 子句里的临时别名不能算输入表")
- AI 迭代代码,我验证数据正确性
这不是"AI 写代码我抄",是双方各自发挥优势:我知道业务逻辑和数据里的坑,AI 擅长把想法快速变成可运行代码。
六个功能模块
v3 最终形成 6 个页面,每个解决一类运维问题:
| 模块 | 解决什么问题 | 技术要点 |
|---|---|---|
| Dashboard 看板 | 任务总览、状态分布、异常计数 | 聚合查询 + 卡片式布局 |
| 全量 DAG | 跨模块依赖可视化 | SQL 解析 + 血缘反查 + dagre 自动布局 |
| 调度排布分析 | 并发超限检测、时段优化建议 | cron 解析 + 分钟级并发计算 + 甘特图 |
| 统一任务列表 | 全量任务检索、状态筛选 | 多表 UNION + 动态过滤 |
| 知识图谱 | 表级血缘追溯 | 图数据库思路的关系查询 |
| ETL 同步 | SQL 变更自动同步字典文档 | 快照 diff + Markdown patch + Git 推送 |
下面展开讲两个我觉得最有价值的模块。
全量 DAG:让隐藏的依赖关系浮出水面
问题
平台的调度编辑器只展示"计算任务"之间的先后顺序(手动连线)。但真实的数据流是:
同步任务 → 写入 ODS 表 → 计算任务读 ODS 表 → 写入 DWS 表 → 质量任务检测 DWS 表
这条链路跨了三个模块,平台里看不到全貌。如果一个同步任务凌晨跑失败了,下游计算任务照跑——产出的就是脏数据。
方案
自己建立跨模块依赖关系。核心逻辑:
- SQL 解析器:用正则从计算任务的 SQL 中提取输入表(FROM/JOIN)和输出表(INSERT INTO/CREATE TABLE)
- 血缘反查:输入表名 → 查平台血缘表
data_blood_tb→ 找到对应的同步任务 - 质量匹配:输出表名 → 匹配
data_governance_quality_config的 metatable → 挂载质量节点
最终每个调度生成一张完整的三层 DAG:上游同步(橙色)→ 计算任务(绿色)→ 下游质量(紫色)。
SQL 解析器的取舍
用正则而不是语法解析库(如 sqlglot),原因:
- 391 个 ETL 的 SQL 方言混杂(Doris SQL、MySQL、标准 SQL),完整解析器兼容性反而差
- 我只需要 FROM/JOIN 后面的表名,不需要完整语法树
- 正则 + 几条过滤规则(排除 WITH 别名、排除 tmp_ 前缀、排除 SQL 关键字误匹配)覆盖了 95% 的场景
# 输入表提取:FROM/JOIN 后的表名
INPUT_PATTERN = re.compile(
r'\b(?:FROM|JOIN)\s+(?:`?(\w+)`?\.)?`?([a-zA-Z_][\w]*)`?',
re.IGNORECASE,
)
剩下 5% 的 edge case(动态 SQL、多层子查询嵌套),手动在血缘表里补录就行。工具不需要完美,够用比完美重要。
前端渲染
用 AntV X6 + dagre 自动布局。节点按模块分色,边支持点击高亮上下游。选中一个计算节点时,自动高亮它的所有上游同步(橙色连线)和下游质量(蓝色连线),其余边降为浅灰。
比起盯着平台里十几个页面来回翻,一张图看清全貌。
ETL 同步:字典文档自动跟 SQL 走
问题
数仓项目有 53 个字典 Markdown 文件,记录每张表的字段含义和编码对照。每次改 ETL SQL 里的 CASE WHEN 映射(比如状态码从 3 种变 4 种),都要手动找到对应字典文件去改编码表。
改 SQL 不改文档是常态,改文档不改 SQL 也有——口径漂移就是这么来的。
方案
做一个自动同步服务,周期性轮询 ETL 变更并 patch 字典:
运维库 ETL SQL → hash 对比快照 → 提取 CASE 映射 → diff 新旧映射
→ 定位字典文件 → patch Markdown 编码表 → Git commit + push
核心链路:
- 快照对比:每个 ETL 任务的 SQL 存一份 hash,变了才处理
- 映射提取:正则提取 CASE WHEN 中的编码→中文对照关系
- Markdown Patch:找到字典文件中对应字段的编码表,增量更新行
- Git 推送:自动 commit 带语义化消息,推到远程仓库
这套机制跑起来后,ETL SQL 改了映射 → 5 分钟内字典文档自动更新 → 下游消费者看到的永远是最新口径。
AI 协作的真实体感
三代开发下来,AI 在不同阶段扮演的角色不同:
| 阶段 | AI 角色 | 我的角色 |
|---|---|---|
| v1 原型 | 代码生成器(说需求→出代码) | 需求描述者 |
| v2 迁移 | 方案顾问(给技术选型建议) | 决策者 |
| v3 全栈 | 结对程序员(持续对话迭代) | 架构师 + 业务专家 |
几个具体的协作场景:
场景 1:dagre 布局不收敛
我发现节点超过 30 个时,dagre 布局出来的图挤成一团。告诉 AI 这个现象,它建议调整 nodesep 和 ranksep 参数,并加了一个 fallback:如果 dagre 对半数以上节点没生成坐标,就回退到按模块分层的手动排列逻辑。
场景 2:血缘表脏数据
血缘表 data_blood_tb 的 to_table_name 字段有前导空格和大小写混乱。我发现这个问题后告诉 AI,它在查询层统一加了 LOWER(TRIM(...)),而不是在应用层逐条处理——这是正确的做法,但如果我不告诉它数据里有坑,它生成的代码不会主动加。
场景 3:前端我不会,但我能描述交互
我不懂 React,但我能精确描述"点击节点高亮上下游、点击空白恢复、模块按颜色区分"。AI 把这些交互需求翻译成 X6 的事件绑定代码。我验证效果,提修改意见,再迭代。
核心体会:AI 不能替代你对业务数据的理解。 它不知道血缘表有脏数据、不知道 WITH 子句的别名不能算输入表、不知道 SeaTunnel 和传统同步是两套表要 UNION。这些业务知识必须你喂给它,它才能写出正确的代码。
技术栈选型回顾
| 层 | 选型 | 为什么选 |
|---|---|---|
| 后端框架 | FastAPI | 异步、自带 OpenAPI 文档、Python 生态熟悉 |
| ORM | SQLAlchemy(raw text) | 查询都是复杂 JOIN,ORM 反而碍事 |
| 前端框架 | React + TypeScript | AI 生成 React 代码质量最稳定 |
| UI 库 | Ant Design | 中后台组件齐全,Table/Select/Tabs 开箱即用 |
| DAG 渲染 | AntV X6 + dagre | X6 交互能力强,dagre 自动布局省手动排 |
| 定时任务 | APScheduler | 轻量,不需要引入 Celery 那套 |
| 本地存储 | SQLite | ETL 快照存储,不依赖外部服务 |
没有用 Next.js、没有用 GraphQL、没有上 K8s——工具是给 5 个人的数据团队用的,overengineering 是浪费。
部署:一个 bat 文件的事
start_prod.bat:
1. npm run build(前端打包到 dist/)
2. uvicorn backend.main:app --host 0.0.0.0 --port 8000
3. FastAPI 托管前端静态文件,单端口 8000 对外
局域网内任何人浏览器打开 http://我的IP:8000 即可使用,不需要装任何东西。
目前跑在我的开发机上,数据团队 5 个人日常使用。如果将来需要稳定运行,扔到 Linux 服务器 nohup 跑就行。
总结
| 维度 | v1 → v3 变化 |
|---|---|
| 代码量 | 400 行 → 3000+ 行(后端)+ 2000+ 行(前端) |
| 功能 | 1 个并发图 → 6 个模块 |
| 架构 | 单文件脚本 → 前后端分离 + 定时服务 |
| 数据来源 | API 爬虫 → 数据库直连 |
| 用户 | 只有自己 → 团队 5 人 |
| 开发周期 | 3 周(业余时间) |
| AI 参与度 | ~70% 代码由 AI 生成,我负责架构、业务逻辑、数据验证 |
一个数仓工程师,不会前端,不会 React,用 AI 协作三周做出一个能用的全栈运维工具。这不是炫技——是数据团队资源有限时的现实选择。平台不提供的视角,自己造。