# 数据平台设计与验收(含 v2 十层重构) ## v2:十层现代数据栈重构 + GB10 生产化(2026-07-07,PR #27-#33) **已落地**:L1 U8 接入路径(仿真库全链演练+接入需求清单,真库改 4 个 env 即切)|L2 Flink CDC + U8 批抽增量水位|L3 StarRocks(aarch64)|L4 ELT(dbt 驱动 StarRocks 算力)|L5 dbt ODS→DWD→DWS→ADS(15 模型+tests,anchor_today=2026-06-25)|L6 指标注册表(6 指标口径唯一,query_metric 工具化)|L7 Dagster(43 资产/调度/健康钉钉告警 sensor)|L8 钉钉 ChatBI + Streamlit(18 页)|L9 dbt tests 入健康页+数据目录页+Marking 沿 dbt 血缘传播+logrotate+OpenMetadata(尽力项)|L10 **自研 LangGraph 智能体**(治理内核 dm/tools 8 工具进程内直调,MCP 壳对外;LiteLLM→ollama qwen3.6 本地 GPU 推理,DM_AGENT_IMPL=claude 可回切)。 **v2 验收基线**:smoke.sh 13/13;pytest 28 过;V1-V8 真实入口旅程(钉钉多跳/审批回滚/eval/权限对抗/CDC 顿挫/指标三算一致/提交条件拒绝/外部 MCP)全过。**二期清单**:SQL Server CDC 实时化、多智能体 supervisor、Cube 语义层服务、Langfuse 追踪、MDM(U8↔合成主数据对齐)、应用镜像 CI/CD、Superset 自助 BI、群 webhook 告警(待用户提供)。 --- # v1:最小可见 + 可审计 + 可评测的数据平台闭环 PoC ## Context(为什么做这件事) - **背景**:全新项目(启动时只有一次需求会议的转写 + 一份接口文档,无任何代码)。目标是为一家制造业企业(已匿名化)做一个"轻量化工业软件平台",第一步是把其某个数据源(MES / ERP-U8 等)的数据导入**统一数据平台/数据仓库**,并做一个 **connector** 让**智能体统一访问**。 - **接口文档关键事实**:那份接口文档是 MES 厂商给该企业做的 **MES↔ERP(用友U8)** 对接确认清单。它确认了 **19 类基础数据 + 23 个读写业务流程的范围**,但**每张表的字段映射全是空的**——没有字段级 schema、没有端点地址、没有 DDL。→ 所以今天**用合成数据**(按真实实体结构造),不依赖任何真实库/网络/接口。 - **今天的目标**:做一个**最小但端到端、看得见、留得下痕迹、跑得了评测**的闭环 PoC,验证这套架构跑得通,并能"测—诊断—修",然后再和团队讨论扩展。 ## 今天的"完成"定义(验收标准,可度量) 1. **导入成功 + 仓库可见**:19 张表**全部**成功入库;管理平台能看到每张表的**行数、字段、可预览数据**;合成数据**内部一致**(外键有效、演示/eval 问题都有确定解)。 2. **统一访问 + 全程留痕**:智能体**只能经 MCP 连接器**访问数据(不能绕过);**每一步**(问题 → 计划 → 每步 SQL → 命中哪些表 → 返回行数 → 最终答案)都写审计;管理平台能**完整回放任一会话的任务链**。 3. **Eval 集合可跑可判分**:有一个 **eval 题集**(覆盖 直查 / 多表 / 多步任务链 / 越界问题);**一条命令**跑完整个集合、**自动判分**、输出**通过率 + 逐条红绿**;确定性问题用仓库算出的标准答案自动比对,叙述型用 LLM 评判。**目标:确定性用例全过;越界用例智能体正确说"没有该数据"而不编造。** 4. **钉钉双向可测**:在钉钉网页发消息提问 → 收到答案;该会话同步出现在管理平台。 5. **可调试**:当某条 eval 失败或钉钉答案不对时,能在管理平台**定位到任务链的哪一步出错**(错表 / 错 SQL / 错聚合 / 幻觉),形成"**测—诊断—修**"闭环。 ## 已锁定的设计决策(grilling 结论) | 维度 | 决策 | |---|---| | 数据来源 | **合成数据**,按真实实体结构造;读取层做成**可替换源适配器**(以后换 U8 直连/API,上层不动) | | 仓库 | **DuckDB**(单文件、零配置、列式/OLAP,正合"读多分析为主";以后可迁 Postgres/ClickHouse) | | 实体范围 | 接口文档里的 **19 类基础数据全造** | | 数据保真度 | **代表性字段 + 真实外键关系**,由**一份 schema 定义**同时驱动「建表 + 造数 + MCP 取 schema + eval 标准答案」 | | 连接器 | **MCP 服务器**(只读工具),**每次工具调用自动写审计** | | 智能体大脑 | 复用 **Claude**(`claude -p` 无头 / Claude Agent SDK),多步工具调用白送,复用现有 Claude 登录,**无需额外 API key** | | 智能体入口 | **钉钉 Stream 模式双向**(用户自建内部应用拿 AppKey/Secret);做成**可插拔通道**,key 未就绪时自动回退到 webhook 推送 + 管理平台聊天框 | | 管理平台 | **Streamlit**:仓库视图 + 审计 + 任务链回放 + **Eval 跑批视图**(= 可观测/调试中心) | | 测试 | **Eval 题集自动判分** + 钉钉网页人工探测,二者都把会话留痕汇入管理平台供诊断 | ## 架构 ``` [源适配器] 合成数据(可换U8/API) │ 批量加载(ETL) ▼ [DuckDB 数据仓库] warehouse.duckdb (19业务表 + audit_log + agent_session + eval_run) ▲ 只读 │ [MCP 连接器] list_tables / describe_table / run_sql(只读) ──每次调用写审计──┐ ▲ │ │ MCP ▼ [Claude 智能体] claude -p / Agent SDK,多步任务链 [审计 / 任务链 / eval] ▲ ▲ │ 通道 │ │ 批量评测 ▼ [钉钉Stream双向]◄主入口 [Eval 跑批 run_eval] [Streamlit 管理平台 = 可观测/调试中心] [管理平台聊天框]◄兜底 仓库视图 + 审计 + 任务链回放 + Eval红绿 ``` ## 项目结构(v1 当时的单目录形态;现已演进为 src 布局,见仓库根 README) ``` schema.py # 19实体的单一 schema 定义(列/类型/主键/外键)——驱动建表+造数+MCP+eval generate.py # 按 schema 用 faker 造内部一致的合成数据(源适配器接口,可替换) load.py # 建表 + 灌库到 warehouse.duckdb warehouse.duckdb # 仓库文件(业务表 + audit_log + agent_session + eval_run) mcp_server.py # MCP 连接器:list_tables/describe_table/run_sql(只读) + 审计 agent.py # 接 MCP 的 Claude 智能体(claude -p / Agent SDK),可被 CLI/通道/eval 调用 eval/eval_set.yaml # eval 题集:{id, question, category, expected, grader} eval/run_eval.py # 跑全集 → 判分 → 通过率+逐条结果(每条带 session_id) channels/dingtalk.py # 钉钉通道:先 webhook 单向(必通),再 Stream 双向(需 key) app.py # Streamlit 管理平台(仓库 / 审计 / 任务链回放 / Eval 视图 / 聊天框) requirements.txt ``` ## 数据模型(19 实体,代表性字段 + 关系) 由 `schema.py` 一份定义驱动。关系示意: - 维度/主数据:`物料分类体系` ← `物料信息` → `单位`(+ `单位转换率`);`公司组织信息` ← `部门` ← `职员信息`;`供应商`、`客户`;`字典`(单据分类枚举) - 库存:`仓库` ← `库位`;`即时库存信息` →(`物料`,`仓库`,`库位`) - 单据:`采购单`→(`供应商`,`物料`);`采购到货单`→`采购单`;`销售订单`→(`客户`,`物料`);`发货单`→(`销售订单`,`客户`);`生产订单`→`物料`;`生产订单用料分析表/子件清单`→(`生产订单`,`物料`) - 合成数据**内部一致**:单据/库存引用的物料 id 真实存在且有库存行,保证演示/eval 问题可答、标准答案可由 SQL 算出。 ## Eval 集合(系统化测试) - 文件 `eval/eval_set.yaml`,每条 `{id, question, category, expected, grader}`。 - **类别覆盖**: - `direct` 单表直查 — 例:"物料 M0001 现在总库存多少?分布在哪些仓库?" - `join` 多表 — 例:"供应商 S001 有哪些未完成的采购单?" - `multistep` 多步任务链("能否按时交货"的今日可落地子集)— 例:"销售订单 SO0001 现在库存够不够发货?" → ①查订单要哪些物料及数量 →②查即时库存 →③比对报缺口。 - `out_of_scope` 越界 — 例:"某产线明天的排班/产能够吗?"(数据**不在** 19 表内,期望智能体明确说"无此数据"而非编造)。 - **判分 grader**: - `numeric`/`set`:用 SQL 从仓库**算出标准答案**自动比对(确定性、可靠)。 - `llm_judge`:叙述型/多步结论,用一次 LLM 评判是否命中关键事实。 - `refusal`:越界用例,检查是否正确拒答/说明数据缺失。 - `eval/run_eval.py`:逐条跑 agent → 判分 → 打印**通过率 + 逐条红绿**,每条记录其 `session_id`(可在管理平台回放);失败高亮。 ## 测试与调试闭环 1. **系统化**:跑 `run_eval.py` → 看通过率与失败用例。 2. **探索式**:在**钉钉网页**给机器人发各种消息找 bug。 3. **诊断**:任一失败/错答 → 打开**管理平台**,按 `session_id` 回放任务链,定位错在哪一步(错表/错 SQL/错聚合/幻觉/数据问题)。 4. **修复** → 回到 1,直到验收标准达成。 ## 构建顺序(自底向上,带"保底"检查点) > 原则:先把**看得见的闭环**做出来,钉钉双向放最后;任何一步卡住,前面成果都可独立演示。 - **M1(保底·必达)** `schema.py` + `generate.py` + `load.py` + `app.py` 仓库视图 → **验收 #1**。 - **M2** `mcp_server.py`:MCP 连接器,`list_tables`/`describe_table`/`run_sql`(强制 SELECT-only);每次调用写 `audit_log`。 - **M3** `agent.py` 接 MCP,CLI 跑通演示问题;会话每步写 `agent_session`;`app.py` 增加**审计 + 任务链回放视图** → **验收 #2**。 - **M3.5** `eval/eval_set.yaml` + `eval/run_eval.py` + `app.py` **Eval 视图** → **验收 #3、#5**(可评测 + 可调试)。 - **M4(冲刺)** `channels/dingtalk.py`:先 **webhook 单向**(零审批·必通),再 **Stream 双向**(需用户 key);未就绪自动回退 → **验收 #4**。 ## 技术栈 / 依赖(Python) `duckdb`、`mcp`(Python SDK)、`streamlit`、`dingtalk-stream`、`faker`、`pyyaml`、`anthropic`/`claude-agent-sdk`(或直接 shell 调 `claude -p`)。 > 各 SDK 的确切 API(MCP server、Agent SDK、钉钉 Stream)在构建时按官方文档逐一确认。 ## 仓库内的留痕 / 评测表 - `audit_log`:id, ts, session_id, channel, user, tool_name, tool_args(JSON), sql, tables_touched, row_count, duration_ms, ok, error - `agent_session`:session_id, ts, channel, question, step_no, step_type(plan/tool_call/tool_result/answer), content, final_answer - `eval_run`:run_id, case_id, category, question, expected, got, grader, passed, session_id, ts ## 明确延后(愿景,非今天) - 真实数据源接入(U8 直连 / MES / Co平台 API)——届时换源适配器即可,上层不动。 - 完整"能否按时交货"跨域长链推理(需**人员排班/产能**等数据,**不在 19 张基础表内**)。 - 19 类之外的写接口、PLM / ERP / OA / 金蝶 等其他源、非结构化/数据湖。 - 宋伟主导的**生产级完整架构**(DuckDB 仅为 PoC 起点)。 ## 需要你(用户)配合的事项 1. **钉钉双向(M4)前提**:用自己账户在钉钉开放平台**自建一个内部应用 + 机器人**,把 **AppKey / AppSecret** 给我。(拿不到不影响 M1–M3.5 今天交付,且钉钉 webhook 单向仍可演示。) 2. 确认本机 Python 环境(建议 3.10+)可用。 ## 验证(端到端,对应 5 条验收) 1. 跑 `load.py` → 终端打印 19 表行数;打开 Streamlit 仓库视图核对表/行数/预览 → **#1**。 2. CLI 跑 `agent.py` 问演示问题 → 答案正确;管理平台审计/任务链回放出现该会话、每步可见、命中表/行数/SQL 留痕 → **#2**。 3. 跑 `eval/run_eval.py` → 输出通过率与逐条红绿;确定性用例全过、越界用例正确拒答 → **#3**。 4. 钉钉网页向机器人提问 → 收到正确答案;管理平台同步出现该会话 → **#4**。 5. 故意问一个会出错/越界的问题 → 在管理平台按 `session_id` 回放、定位到出错的具体步骤 → **#5**。