主题: 第21部分. DataLakeHouse 的 Qwen Code 流程
难度等级: 中等
预计学习时间: 90-120 分钟(理论:45 分钟,实践:45-75 分钟)
前置要求: 对 Data Lakehouse 架构有基本理解
熟悉 dbt(data build tool)及其模型
理解数据契约(data contracts)的概念(ODCS、ODPS)
具有使用版本控制系统(Git)的经验
对 AI 代理和 LLM 工具有一般性的了解
理解银行环境中职责分离(separation of duties)的原则
学习目标: 解释为什么 SDD 流程应被拆分为带有人员确认点的单独命令,而不是由单一命令执行
描述五个命令各自的目的和职责范围:/sdd:profile、/sdd:contract、/sdd:build、/sdd:verify、/sdd:release
区分技能(skills)与子代理(sub-agents),并解释为什么生成器和审查者必须是不同的角色
确定应在哪些场景下引入钩子(hooks),以及钩子应阻止哪些类型的变更
为 DataLakeHouse 团队制定包含工件(artifacts)的最小流程图
概述: 第21章将全卷内容整合为一个可运行的与 Qwen Code 协作处理 DataLakeHouse 的循环。核心是五步流程 /sdd:profile → /sdd:contract → /sdd:build → /sdd:verify → /sdd:release,其中每个命令都有明确的职责范围和一个人工确认点。本章的核心观点是:单个命令绝不应独自执行整个流程,尤其是在银行数据场景下,因为契约漂移(contract drift)可能被发现得太晚。本章讨论了技能(data-profiler)、子代理(data-contract-review)、作为纪律层的钩子(hooks)概念,以及一个典型错误——创建一个统一的 /sdd:do-everything 命令,这会消除人工确认点并使 SDD 再次退化为自动驾驶。本章为本卷收尾,使读者能够有意识地选择每个命令的职责范围,以及哪个工件应记录该决策。
关键概念: Sdd (specification-driven development):一种从数据的正式规范(契约)入手、而非直接修改模型的数据处理方法。在 Qwen Code 的语境下,这意味着代理的行为由源与消费者之间的明确契约定义,而非 LLM 的即兴发挥。
五步流程 /sdd:profile → /sdd:contract → /sdd:build → /sdd:verify → /sdd:release:将整个数据处理循环拆分为五个独立的命令,每个命令都具有明确的职责范围和一个人工确认点。这种分解防止代理对数据拥有无限权力。
/sdd:profile:用于观察数据源(source)的命令。它读取元数据、schema、数据量、统计信息,并生成 Schema Manifest——一份关于数据事实的工件,但不会写入任何模型。它无权更改 marts 或 contracts 的结构。
/sdd:contract:用于起草或审核承诺(promise)的命令。它处理 ODCS/ODPS 文档,这些文档描述了数据源向消费者保证的内容。它不会触碰模型——只处理规范。
/sdd:build:用于对 dbt 模型进行变更的命令。它在契约获得人工批准后触发。它生成 SQL,但不应自行验证自己。
/sdd:verify:用于执行检查并查找漂移(drift)的命令。它执行 dbt build、运行测试,并将实际数据与契约进行比较。它寻找承诺与现实之间的差异。
/sdd:release:用于组装证据包(evidence package)的命令。它为利益相关方生成工件:变更内容、通过了哪些测试、由谁确认。
Data-profiler(技能):一个用于读取数据源并生成清单(manifest)的狭义技能。它仅返回事实,无权编写模型。这是限制代理角色的技能示例。
Data-contract-review(子代理):一种只读模式的子代理,它对提议的变更应用审查清单(checklist)。它无法编辑 SQL——只能进行评审。
生成器与审查者的角色分离:变更作者与审查者必须是不同实体的原则。否则代理会验证自己的猜测,这在银行数据中是危险的。
人工确认点:流程中代理必须停下来并将决策交由真人处理的位置。没有这些点,SDD 会退化为自动驾驶。
钩子(hooks):一种自动阻止危险变更类别的机制:models/marts/ 中的 PII 字段、未带确认标记的 YAML 契约变更、未带影响说明的 grain 变更。钩子不能替代审查,但能防止明显的错误在审查前就溜过去。
工件(artifact):命令工作的物化成果(Schema Manifest、更新后的 ODCS、更改后的 dbt 模型、审查者报告)。规则:如果某个命令不留下工件,则它在第一轮中是不必要的。
流程图:本章最精简的输出——命令与工件对应关系的表格。它能让你快速向团队解释谁负责什么。
契约漂移(contract drift):数据源所承诺的内容(契约)与数据实际发生情况之间的差异。在银行环境中尤为关键,因为漂移往往被发现得太晚。
Pii(personally identifiable information):客户的个人数据。在 marts 模型中添加带 pii_ 前缀的字段是典型的危险变更类别,应被钩子阻止。
Grain(模型的粒度):dbt 模型中记录的详细程度(例如"一个客户对应一行")。在没有关于下游影响的明确说明的情况下更改 grain 是错误的常见来源。
重要日期: 在 datalakehouse 中引入 sdd:流程成熟度的阶段,在此阶段重复出现的违规行为变得明显——这是编写钩子的信号
第一次实践考核:在此之后团队已理解典型违规行为,并能为钩子制定规则
在 /clear 之后:只读模式的审查者尤为重要的时刻:如果它无法仅凭文件理解变更,则意味着作者将语义留在了聊天中——这是纪律性差的信号
练习题: 名称: 练习 1:为你的项目绘制流程图
问题: 你所在的银行 DataLakeHouse 团队正在使用 Qwen Code 过渡到 SDD。请描述流程图:你将使用哪五个命令、每个命令留下什么工件、以及人工确认点应设在哪里。使用本章中的表格格式。
解决方案: 步骤 1。按正确顺序列出五个命令:/sdd:profile、/sdd:contract、/sdd:build、/sdd:verify、/sdd:release。步骤 2。为每个命令定义工件:profile → Schema Manifest;contract → 更新 ODCS/ODPS;build → dbt 模型变更;verify → dbt build + 验证事实;release → 证据包。步骤 3。为 data-contract-review 添加第六行 → 审查者报告。步骤 4。用箭头标注人工确认点的位置(在 contract 和 build 之间,在 verify 和 release 之间)。步骤 5。自我检查:每个命令都有工件吗?如果没有——该命令在第一轮中是多余的。
难度: beginner
名称: 练习 2:分离作者与审查者的角色
问题: 用两种模式对同一 dbt 模型变更请求(例如在 marts 表中添加 customer_segment 字段)进行处理:(a)单个代理独自完成——分析数据源、提出变更、编写 SQL 并自行审查;(b)角色分离:作者代理提出变更,只读模式的审查者代理进行检查。比较哪种模式能发现仅在角色分离时出现的意见。
解决方案: 步骤 1。准备上下文:数据源描述、当前契约、现有的 dbt 模型。步骤 2。模式(a):让单个代理执行整个循环。记录它自行放行了哪些风险。步骤 3。模式(b):启动作者——它写出变更。在只读模式下启动审查者(仅查看文件,不看聊天)——它应用审查清单。步骤 4。比较结果。仅在角色分离时出现的典型意见包括:"未说明对 grain 的影响"、"未更新 ODCS"、"新字段缺少 NULL 测试"、"未检查该列的使用者"、"未注明该字段可能包含 PII"。步骤 5。得出结论:角色分离能够揭示作者未注意到的风险,因为这些风险处于其"乐观视野之外"。
难度: intermediate
名称: 练习 3:设计第一个钩子
问题: 团队已经进行了三个 SDD sprint。审查分析显示,最常重复的三类违规行为是:(1) models/marts/ 中出现了带 pii_ 前缀的字段;(2) 未经人工确认标记的 YAML 契约变更;(3) 在 PR 描述中未说明的模型 grain 变更。请设计一个能阻止这三类变更的钩子。以概念方式描述(不写代码)。
解决方案: 步骤 1。阐明阻止条件:当尝试向 models/marts/ 写入文件时触发钩子。步骤 2。规则 1:如果新增或修改的文件包含以 pii_ 开头的字段——阻止提交并提示"PII 进入 marts 需要单独审批"。步骤 3。规则 2:如果 PR 中修改了 contracts/ 目录或 .odcs.yaml 文件中的任何文件,并且提交/PR 消息中缺少 contract-approved 标记——阻止提交。步骤 4。规则 3:如果修改了 models/marts/ 中的文件,并且其中修改了 {{ config(materialized=..., grain=...) }} 行或等效行——要求 PR 描述中必须包含 grain-impact: 字符串。步骤 5。决定钩子抛出错误的顺序以及哪些仅作为警告。步骤 6。明确:钩子不能替代审查,但能防止明显的错误在审查前就溜过去。
难度: intermediate
名称: 练习 4:诊断 "/sdd:do-everything" 错误
问题: 团队创建了一个统一的 /sdd:do-everything 命令,它能分析数据源、更新契约、生成 dbt 模型、运行测试并发布报告。一个月后,一个不在契约中的 PII 字段进入了生产环境。请进行分析:哪些人工确认点被消除了?哪些工件缺失?
解决方案: 步骤 1。列出该命令本应具有的确认点:(a) profile 之后——确认清单正确;(b) contract 之后——确认承诺已与数据源利益相关方达成一致;(c) build 之后——确认变更正确;(d) verify 之后——确认证据包完整。步骤 2。在 /sdd:do-everything 命令中,所有这些点被合并为一个自动执行流程。人只能看到最终报告,而非中间工件。步骤 3。缺失的工件包括:Schema Manifest 未经过数据源专家审核;ODCS 更新未经数据所有者签署;契约审查者未收到单独的工单;日志中缺少人工确认标记。步骤 4。PII 在哪里"漏过":代理在源中看到该字段就将其添加到 marts 中,而该数据源的契约要么未更新,要么是自动生成的。步骤 5。结论:统一命令消除了人工确认点,并将 SDD 变为自动驾驶。解决方案是将其拆分为五个命令,并明确要求在它们之间进行人工确认。
难度: advanced
案例研究: 名称: 案例 1:在银行 DataLakeHouse 中引入五步 SDD
场景: 一家中型零售银行(500 万客户)正在引入 Qwen Code,以加速企业 DataLakeHouse 中 dbt 模型的开发。在引入之前,分析师花费多达 40% 的时间来描述数据源和协调契约,而变更经常绕过正式程序。一个由 6 名数据工程师组成的试点团队从创建五个独立命令开始:/sdd:profile、/sdd:contract、/sdd:build、/sdd:verify、/sdd:release。
挑战: 主要挑战包括:(1) 如何说服团队不要因为切换"不方便"而将流程合并为一个命令;(2) 如何形式化人工确认点,使其不会沦为形式上的勾选;(3) 如何为生成器和审查者设置不同角色,使后者不依赖于聊天上下文;(4) 如何选择第一个由钩子阻止的变更类别,同时不过度复杂化流程。
解决方案: 团队分几个阶段推进。第一阶段:创建文件 .qwen/commands/sdd/profile.md、contract.md、build.md、verify.md、release.md——每个文件包含其狭义指令。第二阶段:引入 data-profiler 技能,它仅返回 Schema Manifest,无权编写模型。第三阶段:分离出 data-contract-review 子代理,它仅与仓库文件交互(只读模式)并应用审查清单。第四阶段:第一个月后,团队分析了重复出现的违规行为——发现在 11 个案例中有 4 个案例中 PII 字段在未在 ODCS 中标注的情况下进入了 marts。这成为第一个钩子的基础,该钩子阻止在 models/marts/ 中写入带 pii_ 前缀的字段。第五阶段:固化流程图,并将其作为新分析师入职的必备内容。
结果: 三个月后:从变更请求到发布的平均时间缩短了 28%(从 9 天减少到 6.5 天);由非契约字段引起的回归数量降至零;描述数据源的时间减少了 35%。主要的质性效果是:每个工件都有了明确的所有者,并且有了一个明确的由人而非 LLM 做出决策的节点。
经验教训: 拆分为五个命令不是官僚作风,而是使责任可见化的方式:profile 不写模型,审查者不编辑 SQL,生成器不自我证明安全性
只有在团队亲眼看到重复违规行为后,钩子才有价值——否则钩子要么太弱,要么太烦人
只读模式的审查者在 /clear 之后尤其有价值:如果它无法仅凭文件理解变更,则意味着作者将语义留在了聊天中——这是一个可以通过形式化检查轻松捕获的纪律性差的信号
流程图是最精简但必备的工件:没有它,新成员无法理解自己的确认点在哪里
相关概念: 五步流程 /sdd:profile → /sdd:contract → /sdd:build → /sdd:verify → /sdd:release
技能与子代理
生成器与审查者的角色分离
人工确认点
钩子
名称: 案例 2:核心银行 DataLake 中统一命令 /sdd:do-everything 的错误
场景: 一家大型银行(排名前十)为"加速 DataOps"创建了一个统一的 /sdd:do-everything 命令,该命令自行分析数据源、更新契约、生成 dbt 模型、运行测试并发布报告。其思路是使分析师的单个请求在一个周期内转化为完整的变更。最初两个月一切顺利。
挑战: 第三个月,一个带 pii_ 前缀的 passport_series 字段进入了 marts 表 mart_customer_360,而该字段不在源数据的 ODCS 契约中。该字段进入了三个下游报告,包括央行的监管仪表板。难点在于:(1) 该命令未留下中间工件——只有最终报告;(2) Schema Manifest 未经数据源领域专家审核;(3) ODCS 更新是自动生成的,未经数据所有者签署;(4) 日志中缺少人工确认标记。原因排查耗时超过一周。
解决方案: 该银行放弃了 /sdd:do-everything,转向五个命令。profile 和 contract 被转移到一个单独的角色——Data Steward。dbt 模型生成器的作用域受到限制(仅限于 build),并被禁止更新 contracts/。data-contract-review 审查者被作为一个单独的子代理启动,采用只读模式,仅接收 Git 中的 diff,而不使用聊天上下文。钩子被立即编写:它阻止在 models/marts/ 中写入任何以 pii_ 开头的字段,并要求任何 ODCS 文件的变更必须带有 contract-approved 标记。人工确认点被固化在安全策略中:任何 dbt 模型在进入 main 之前都必须有 Data Steward 在 PR 描述中的签字。
结果: 在接下来的一个季度中,此类事件未再发生。央行审计人员特别指出,拥有带工件的明确流程图是 DataOps 成熟度的积极信号。开发速度在最初下降后的 6 周内恢复——分析师承认,五个带有明确确认点的短步骤比一个带有事故排查的长命令更快。
经验教训: 统一的 /sdd:do-everything 命令在演示中很方便,但在银行数据中很危险:人工确认消失了,而契约漂移被发现得太晚
工件不是报告,而是调查的支点:没有 Schema Manifest、没有签署的 ODCS,就没有可以问责的对象,也没有可以证明承诺内容的证据
角色分离不是形式,而是了解谁本应注意到问题的方式:发生错误后,正是角色揭示了哪里漏掉了步骤
银行环境中的钩子应阻止的不是"所有可疑内容",而是已知的狭义危险类别——否则人们会开始绕过它们
相关概念: 典型错误:/sdd:do-everything
契约漂移
marts 中的 PII
作为支点的工件
作为纪律层的钩子
名称: 案例 3:风险建模团队中从技能到子代理的演进
场景: 一家投资银行的风险建模团队使用 Qwen Code 来加速为信用评分模型准备数据集市。在第一阶段,他们将 data-profiler 描述为一项技能,将 data-contract-review 描述为聊天中的一条简单指令。两个月后,很明显"聊天中的审查"很容易被忽视:作者只需回答"一切正常"然后继续。
挑战: 问题在于:审查者必须是一个结构上独立的角色,而不是同一代理的一种行为模式。当审查是聊天指令时,代理仍处于同一会话中,并继承了作者的上下文,包括其对决策正确性的信心。此外,审查者应仅查看文件,而非对话记录,否则它将评估的是意图而非代码。
解决方案: 团队将审查抽离到一个独立的子代理 data-contract-review,它具有自己的系统提示并对仓库采用只读模式。唯一输入是 Git 中的 diff,不包含任何聊天上下文。审查清单被形式化,现在包括以下要点:"带 pii_ 前缀的字段——在 marts 中被禁止"、"grain 变更——需要在描述中包含 grain-impact"、"ODCS 更新——需要在提交中包含 contract-approved"、"对源的新依赖——需要重新执行 profile"。在 /clear 会话之外,审查者保持完全功能,这正是所需要的。
结果: 在一个季度内,审查者发现的意见数量从每个 PR 的 0.4 条增加到 2.7 条。尤其有价值的是"仅凭文件无法理解变更做了什么"这一意见——它出现在那些习惯于在聊天中而非在提交中解释逻辑的作者身上。这迫使团队严格化提交消息和 PR 描述的实践,从而在 AI 工具之外提高了整体代码质量。
经验教训: 技能(skill)和子代理(sub-agent)是不同层级的隔离:技能在同一会话中缩小角色,子代理提供独立的上下文和模式
只读模式的审查者在 /clear 之后效果最佳:它不能被说服,因为它没有访问聊天的权限
审查清单必须是明确的,否则审查会退化为"看起来一切正常"
"仅凭文件无法理解"这一意见不是审查者的缺陷,而是一个特性:它捕获了糟糕的变更文档
相关概念: 技能与子代理
data-contract-review
data-profiler
只读模式
审查清单
学习建议: 在创建命令文件之前,先在纸上绘制流程图:五个命令、五个工件、明确的人工确认点。这会花费 20 分钟,但能节省数周与 /sdd:do-everything 的斗争
用两种模式运行同一项任务——一个命令和五个命令——并记录发现的风险数量差异。这比任何理论都更直观
不要在看到重复违规行为之前编写钩子:最初的两到三个 sprint 收集统计数据,然后再制定规则
始终保持审查者为只读模式且无法访问聊天:这是迫使作者编写清晰提交和 PR 描述的唯一方法
在 /clear 之后,检查审查者是否能够仅凭文件理解变更。如果不能——这是一个信号,意味着作者将语义留在了聊天中,纪律要求进行修正
"如果命令不留下工件,则它在第一轮中是不必要的"原则是一个很好的过滤器:五个命令产出五个工件;没有工件的命令要么是多余的步骤,要么是定义不清的角色
将每条规则与变更类别关联:marts 中的 PII、grain 变更、未带标记的 ODCS 更新。诸如"代码应该具有高质量"之类的抽象规则在审查和钩子中都不起作用
使用本章的术语:"人工确认点"、"工件"、"契约漂移"、"粒度"。这是通用语言,使团队能够快速协调变更
附加资源: Open data contract standard (odcs):本章中使用的数据契约描述标准。它有助于形式化源对消费者的承诺。https://bitol-io.github.io/open-data-contract-standard/
Dbt (data build tool):数据转换工具,在 /sdd:build 和 /sdd:verify 命令的上下文中被提及。文档:https://docs.getdbt.com/
数据湖仓中的 Pii:数据湖仓中处理个人数据的实践概述。有助于理解为什么 marts 中的 PII 是典型的危险类别
Data contracts(作者:马尔特·洛佩斯-雷斯特雷波等):关于数据契约的概念基础,作为数据生产者与消费者之间承诺的固化方式
Qwen code 和 qwen3-coder:AI 工具 Qwen Code 的文档,包括命令、技能和子代理的使用。https://qwenlm.github.io/
规范驱动开发 (sdd):本章所基于的方法论。与 BDD 和 TDD 相关,但侧重于数据规范和契约
银行环境中的职责分离 (sod):银行内部控制领域的概念,解释了为什么生成器和审查者必须是不同的角色
摘要: 第21章将整卷内容整合为 DataLakeHouse 的 Qwen Code 可运行循环:五个命令(/sdd:profile、/sdd:contract、/sdd:build、/sdd:verify、/sdd:release),每个命令具有明确的角色、明确的工件以及它们之间的人工确认点。核心观点是:单个命令不应完成整个流程——否则人工确认点会消失,契约漂移被发现得太晚,这在银行数据中尤其关键。当技能(data-profiler)和子代理(data-contract-review)缩小角色范围时很有用:分析器不编写模型,审查者不编辑 SQL,生成器不自我证明安全性。钩子是下一个纪律层级,但它们不能在流程之前编写:团队必须首先看到重复的违规行为(marts 中的 PII、未带标记的 ODCS 变更、未带说明的 grain 变更),然后才能将它们形式化为规则。本章最精简的输出是包含五个命令、五个工件和审查者报告一行的流程图。阅读后,读者应能自信地回答三个核心问题:为什么流程不能由单个命令完成,技能与子代理的区别是什么,以及人工确认点应放在哪里。