芥末
发布于 2026-05-20 / 0 阅读
0
0

用 Claude Code Harness 搭建数仓 AI 开发流水线

Claude Code 用在离线数仓开发里,最容易产生价值的地方不是“凭空设计模型”,而是处理大量重复、规则明确、格式稳定的工作:生成 DDL(Data Definition Language,数据定义语言)、编写 Insert SQL(Structured Query Language,结构化查询语言)、补字段注释、生成 DQC(Data Quality Check,数据质量校验)规则、做自测脚本、整理上下游血缘。

问题也很明显。数仓开发对“语义”和“规范”极其敏感,字段单位、业务口径、分区格式、OneData 命名、金额精度、插入模式,任何一个约束丢失,都可能让结果差几个数量级。AI(Artificial Intelligence,人工智能)Coding(人工智能辅助编码)如果只停留在“临时对话里反复提醒模型”,复杂需求做着做着就会失控。

Claude Code Harness 的价值就在这里:不要把所有事情都交给 LLM(Large Language Model,大语言模型)记忆和判断,而是把不同类型的工作拆到不同机制里:

问题不可靠做法Harness 化做法
字段口径容易忘在对话里临时说明写入 CLAUDE.md 或 Memory,compact 后重新注入
SQL 规范靠自觉让 Claude “记得检查”用 hook 在写文件后自动校验
危险 DDL 误执行让 Claude 自己判断风险用 PreToolUse hook 在执行前拦截
血缘、自测结果撑满上下文全塞进主会话交给 subagent 独立执行,主会话只收摘要
SKILL 文件太大每次全文加载subagent 内部读取 SKILL,主会话只接收产物

Harness 工程不是让 Claude 变得更聪明,而是让数仓 AI 开发变得更稳定。


1. 数仓 AI 开发为什么容易失控

Claude Code 在简单任务里表现很顺滑,例如补一个字段注释、改一段 SQL 条件、生成一个建表语句。复杂数仓需求会暴露三类结构性问题。

1.1 上下文压缩后遗忘关键约束

数仓开发经常会在对话中补充临时约束:

  • amount 字段单位是“千元”,不是“元”。
  • 当前迭代用 INSERT OVERWRITE,不能用 INSERT INTO
  • field_a 本次先忽略,不参与计算。
  • dwd_table_b 已上线,只读,不能修改。
  • 分区字段叫 partition_dt,格式是 yyyyMMdd,不是 dt

这些内容如果只存在于聊天历史里,一旦 Claude Code 触发 context compact(上下文压缩),就可能被摘要丢掉。模型后续生成 SQL 时,可能把千元当元、把只读表改掉、把分区字段写错。

典型后果是:对话前半段已经说清楚了口径,后半段模型像没听过一样重新犯错。

1.2 规范执行不稳定

数仓规范通常很多,而且大部分规范不是“建议”,而是必须执行的工程约束:

  • 禁止 SELECT *
  • INSERT 必须带 PARTITION 子句。
  • 金额字段用 DECIMAL(20,4) 或团队指定精度,不使用 DOUBLE
  • UPDATE / DELETE 必须有 WHERE
  • 分区字段统一为 partition_dt string
  • 字段名使用 snake_case
  • 多表 JOIN 必须有明确 ON 条件。
  • 建表注释遵循固定格式。

让 LLM 靠 prompt 记住这些规范,短会话里能工作,长会话里不稳定;开发节奏一快,人工也会漏。规范执行应该交给确定性程序,而不是交给记忆。

1.3 大型需求会快速撑满 context

复杂宽表或长链路需求通常会产生大量上下文:

血缘查询结果:500 ~ 3000 tokens
自测 SQL 执行结果:5000 ~ 15000 tokens
SKILL 规范文件:约 10000 tokens
数据比对样本:大量明细行
DDL、字段枚举、上下游表结构:持续追加

这些内容全部进入主会话后,context 很快接近上限。Claude Code 在接近阈值时会触发 compact,把历史压缩成摘要。压缩可以延长会话,但也会丢掉细节,尤其是临时口径、过程结论、局部决策。

数仓开发的矛盾就出现了:需求越复杂,越需要 AI 辅助;需求越复杂,context 越容易膨胀;context 越膨胀,AI 越容易遗忘关键约束。


2. Harness 到底是什么

在 Claude Code 里,Harness 可以理解为 Claude Code 客户端所在的宿主运行框架。它不是 LLM 本身,而是包住 LLM 的工具链容器,负责管理上下文生命周期、工具调用、hook 执行和 subagent 调度。

可以把 Claude Code 拆成四个层次:

flowchart TB
    U[开发者] --> C[Claude 主会话<br/>负责理解需求、生成方案、编写代码]

    C --> T[工具调用<br/>Read / Write / Edit / Bash / Grep]
    H[Harness 宿主框架] --> C
    H --> HK[Hooks<br/>确定性执行检查与拦截]
    H --> A[Subagents<br/>独立上下文处理高 token 任务]
    H --> M[持久化上下文<br/>CLAUDE.md / Memory / Rules]

    T --> HK
    A --> R[结构化摘要返回主会话]
    M --> C

四类能力的边界很重要:

组件适合负责什么不适合负责什么
Claude 主会话需求理解、方案设计、代码生成、解释取舍强制执行规范、长期记忆所有约束
Harness管理工具链生命周期、执行 hooks、调度 subagents替代模型做语义判断
HooksSQL 规范检查、危险命令拦截、任务完整性校验大段业务分析、复杂推理
Subagents血缘探索、自测执行、数据比对、日志分析维护主会话的完整业务上下文
CLAUDE.md / Memory存放跨会话约束、迭代状态、长期踩坑经验存放海量执行结果

关键点是:hook 不是 Claude “想起来才执行”的检查,而是 Harness 在 LLM 推理循环之外按配置确定性执行的动作。


3. context compact 会丢掉什么

Claude Code 的 context window 可以理解成模型当前能看到的工作记忆。当对话历史、文件内容、工具输出不断累积,接近阈值后会触发 compact。compact 会把长历史压缩成较短摘要,token 数量会明显下降,但细节不可避免会损失。

数仓开发里最容易丢的内容如下:

内容类型compact 前compact 后风险数仓后果
临时口径“amount 单位是千元”摘要未保留金额计算放大或缩小 1000 倍
本次迭代约束“只改 V1.0,不碰线上表”约束被省略误改稳定表
中间决策“这版先忽略 field_a”决策链断裂模型重新引入已排除字段
部分读取的 SKILL 内容已读到 Step 3摘要只保留泛化描述重复询问或漏执行步骤
自测明细结果50 行执行结果压缩成模糊结论无法定位失败项
血缘与 DDL多张表结构字段级细节丢失JOIN 键、分区、粒度误判

compact 本身不是坏事,它解决的是“会话继续跑下去”的问题;真正要处理的是:哪些信息不能只放在聊天历史里,哪些信息不应该进入主会话。


4. 五层防御体系

Claude Code Harness 在数仓侧落地,可以按五层构建。越靠前越简单,越靠后越能处理复杂工作流。

flowchart TB
    L1[第 1 层<br/>CLAUDE.md 持久化迭代约束]
    L2[第 2 层<br/>Auto Memory 记录长期经验]
    L3[第 3 层<br/>Hooks 自动检查与拦截]
    L4[第 4 层<br/>Subagents 隔离高 token 操作]
    L5[第 5 层<br/>SKILL 调用方式改造]

    L1 --> L2 --> L3 --> L4 --> L5

4.1 第 1 层:把关键约束写进 CLAUDE.md

项目根目录下的 .claude/CLAUDE.md 是最直接的持久化位置。适合存放当前迭代状态、不可变约束、全局规范。它的优势是简单、稳定,并且可以在会话启动或 compact 后重新注入。

推荐结构:

# 当前迭代状态

## 正在开发
- 表:db_a.dws_table_a
- 版本:V1.0
- node_id:1000000001
- 状态:ETL 开发阶段(Step 3/8)

## 本次迭代约束
- 禁止修改:dwd_table_b(已上线,只读)
- 分区字段:partition_dt(格式 yyyyMMdd,不是 dt)
- amount 字段单位:千元(不是元)
- 本次写入模式:INSERT OVERWRITE
- field_a:本次暂不参与计算

## 当前迭代技术设计决策
- 表名:db_a.dws_table_a
- 主键:order_no + partition_dt
- 粒度:订单明细粒度
- 特殊口径:amount 字段继承上游千元单位,不做单位转换

## 数仓全局规范
- 建表分区字段必须是 partition_dt string
- 禁止 SELECT *
- INSERT 必须带 PARTITION 子句
- UPDATE / DELETE 必须带 WHERE
- 金额字段使用 DECIMAL(20,4),不使用 DOUBLE
- 字段名使用 snake_case
- 多表 JOIN 必须有 ON 条件

维护规则:

时机操作
新需求开始更新“正在开发”和“本次迭代约束”
技术设计完成写入表名、主键、粒度、特殊口径
口径确认后把确认结论写入约束区
上线完成清空本次迭代约束,保留全局规范
规范变化更新全局规范,但控制在 100 行以内

CLAUDE.md 不适合写成一本完整规范手册。它应该像“当前任务的操作台”,只放模型每次都必须看到的信息。

4.2 第 2 层:用 Auto Memory 积累长期经验

Auto Memory 适合存放跨会话复用的知识,例如某张表的长期口径、某个字段的历史坑点、某类需求的默认处理方式。

典型路径:

~/.claude/projects/<project>/memory/MEMORY.md

适合主动要求 Claude 记住的内容:

这张表的 amount 字段单位是千元,请记住。

field_a 在售后场景下可能为空,请记住这个口径。

V1.0 的关键变更是 field_b 逻辑调整,请记住。

CLAUDE.md 和 Memory 的分工可以这样理解:

存储位置生命周期内容
CLAUDE.md当前项目、当前迭代正在开发什么、不能改什么、本次口径是什么
Auto Memory跨会话长期存在表级口径、字段坑点、团队习惯、历史经验

4.3 第 3 层:用 hooks 做自动验证

Hooks 是 Harness 工程里最关键的一层。它把“记得检查 SQL 规范”变成“每次写 SQL 文件后自动检查”。

项目结构建议:

数仓项目根目录/
└── .claude/
    ├── settings.json
    ├── CLAUDE.md
    ├── context/
    │   └── dw_conventions.md
    └── hooks/
        ├── validate_sql.sh
        ├── block_dangerous_ddl.sh
        └── inject_context.sh

settings.json 示例

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/validate_sql.sh",
            "timeout": 60,
            "statusMessage": "检查 SQL 规范..."
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block_dangerous_ddl.sh"
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "cat \"$CLAUDE_PROJECT_DIR\"/.claude/context/dw_conventions.md",
            "statusMessage": "重注入数仓规范..."
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "检查用户要求的所有任务是否都已完成。如果还有未完成项,返回提示但不要重新开始。检查 stop_hook_active 是否为 true,如是则直接 exit。",
            "model": "claude-haiku-4-5-20251001"
          }
        ]
      }
    ]
  }
}

这里有三个关键动作:

Hook触发时机作用
PostToolUseWrite / EditSQL 文件保存后自动检查规范
PreToolUseBash拦截生产表 DROP / TRUNCATE 等危险命令
SessionStart会话或 compact 后重新注入数仓规范
StopClaude 准备停止时检查任务是否遗漏

SQL 规范检查脚本

.claude/hooks/validate_sql.sh

#!/usr/bin/env bash
set -euo pipefail

INPUT="$(cat)"

FILE_PATH="$(
  printf '%s' "$INPUT" | python3 -c '
import sys, json
try:
    d = json.load(sys.stdin)
    print(d.get("tool_input", {}).get("file_path", ""))
except Exception:
    print("")
'
)"

# 只处理 .sql 文件
[[ -z "$FILE_PATH" ]] && exit 0
[[ "$FILE_PATH" != *.sql ]] && exit 0
[[ ! -f "$FILE_PATH" ]] && exit 0

SQL="$(cat "$FILE_PATH" 2>/dev/null || true)"
[[ -z "$SQL" ]] && exit 0

ERRORS=()

# 规范 1:禁止 SELECT *
if echo "$SQL" | grep -iqE 'SELECT\s+\*'; then
  ERRORS+=("CRITICAL: 发现 SELECT *,必须明确列名")
fi

# 规范 2:INSERT 必须带 PARTITION
if echo "$SQL" | grep -iqE 'INSERT\s+(INTO|OVERWRITE)'; then
  if ! echo "$SQL" | grep -iqE 'PARTITION\s*\('; then
    ERRORS+=("CRITICAL: INSERT 缺少 PARTITION 子句")
  fi
fi

# 规范 3:金额字段不建议使用 DOUBLE
if echo "$SQL" | grep -iqE '\bDOUBLE\b'; then
  ERRORS+=("WARNING: 金额字段建议用 DECIMAL,不用 DOUBLE")
fi

# 规范 4:UPDATE / DELETE 必须有 WHERE
if echo "$SQL" | grep -iqE '\b(UPDATE|DELETE)\b'; then
  if ! echo "$SQL" | grep -iqE '\bWHERE\b'; then
    ERRORS+=("CRITICAL: UPDATE/DELETE 缺少 WHERE 条件")
  fi
fi

if [ "${#ERRORS[@]}" -gt 0 ]; then
  echo "=== SQL 规范检查失败:$FILE_PATH ===" >&2
  for err in "${ERRORS[@]}"; do
    echo "  $err" >&2
  done

  # Claude Code 中需要使用 exit 2 才能阻断或反馈给模型修正
  exit 2
fi

echo "SQL 规范检查通过: $(basename "$FILE_PATH")" >&2
exit 0

这个脚本适合做第一道防线。它不是 SQL 解析器,无法覆盖所有语法边界,但足够拦住高频低级错误。复杂规则可以逐步演进,例如引入 SQLFluff、自研解析器,或者针对 ODPS(Open Data Processing Service,开放数据处理服务)/ MaxCompute 的语法做专门检查。

危险 DDL 拦截脚本

.claude/hooks/block_dangerous_ddl.sh

#!/usr/bin/env bash
set -euo pipefail

INPUT="$(cat)"

CMD="$(
  printf '%s' "$INPUT" | python3 -c '
import sys, json
try:
    d = json.load(sys.stdin)
    print(d.get("tool_input", {}).get("command", ""))
except Exception:
    print("")
'
)"

# 拦截生产表 DROP / TRUNCATE,放行 _dev / _test / _stg 后缀
if echo "$CMD" | grep -iqE '\b(DROP\s+TABLE|TRUNCATE\s+TABLE)\b'; then
  if ! echo "$CMD" | grep -qiE '(_dev|_test|_stg)\b'; then
    echo "BLOCKED: 检测到生产表 DROP/TRUNCATE 操作,请确认表名是否正确" >&2
    exit 2
  fi
fi

exit 0

Hook 通信规则要特别注意:

规则说明
输入Harness 会把工具调用信息以 JSON 形式写入标准输入
输出stderr 中的信息可用于提示 Claude 修正
exit 0放行
exit 2阻断或把错误反馈给 Claude
exit 1通常只表示脚本失败,不应作为业务阻断信号

需要阻断时用 exit 2,不要用 exit 1

4.4 第 4 层:用 subagents 隔离高 token 操作

Subagent 的价值是独立 context。主会话不需要看到全部过程数据,只需要看到结构化结论。

适合下放给 subagent 的数仓任务:

任务为什么要隔离主会话需要什么
表结构探索DDL、字段、注释很多表粒度、核心字段、特殊口径
血缘查询上下游链路容易展开过多一层血缘摘要
23 项自测SQL 结果行数多PASS / FAIL 和失败项
数据比对样本数据量大超阈值字段和差值
SQL 规范复核日志细节多问题列表和行号
性能分析执行计划、DDL、血缘混合瓶颈排序和优化建议

sql-validator agent

.claude/agents/sql-validator.md

---
name: sql-validator
description: ODPS/MaxCompute SQL 语法验证与规范检查专用 agent。生成或修改 SQL 文件后调用,在独立 context 中运行,避免验证日志污染主会话。
tools: Read, Bash, Grep, Glob
model: haiku
permissionMode: dontAsk
---

你是数仓 SQL 规范专家,只做验证,不修改文件。

验证项按优先级执行:
1. 禁止 SELECT *
2. INSERT 必须带 PARTITION
3. 字段使用 snake_case 命名
4. 金额字段使用 DECIMAL,不使用 DOUBLE
5. 多表 JOIN 必须有 ON 条件
6. 检测笛卡尔积风险
7. 检测 UPDATE / DELETE 是否缺少 WHERE

输出格式:
- 状态:PASS / FAIL
- 问题列表:CRITICAL / WARNING / INFO
- 修改建议:具体到文件和行号

限制:
- 不输出大段 SQL 原文
- 不超过 50 行
- 只返回结构化报告

dw-explorer agent

.claude/agents/dw-explorer.md

---
name: dw-explorer
description: 数仓结构探索 agent。需要大量读取表结构、DDL、字段信息、血缘关系时调用,避免大量文件内容进入主 context。只读,不修改任何文件。
tools: Read, Glob, Grep, Bash
model: haiku
permissionMode: dontAsk
---

你是数仓探索专家,只执行只读操作。

任务:
1. 读取指定表的 DDL、字段信息、分区策略
2. 分析一层上下游血缘
3. 识别关键字段口径,重点关注金额、日期、状态类字段
4. 标记可能影响计算结果的特殊口径或历史坑点

输出不超过 80 行,包含:
- 表基本信息:层级、粒度、分区策略
- 核心字段定义:含口径说明
- 上下游血缘:只列出表名,不展开完整内容
- 特殊口径或风险点

data-quality-checker agent

.claude/agents/data-quality-checker.md

---
name: data-quality-checker
description: 数仓自测与数据质量检查 agent。适合执行多条检查 SQL,并只返回汇总结果,避免执行明细撑满主 context。
tools: Bash, Read, Grep
model: haiku
permissionMode: dontAsk
---

你是数仓数据质量检查专家。

执行要求:
1. 根据输入表名、分区和口径约束执行标准自测
2. 检查主键非空、分区数据量、金额准确性、枚举值合法性、上下游一致性
3. 只输出 PASS / FAIL 汇总和失败项详情
4. 不输出原始大结果集
5. 不超过 50 行

输出格式:
- 状态:PASS / FAIL
- 检查项总数
- 失败项列表:检查项、失败原因、关键差值、建议动作

主会话调用方式:

用 dw-explorer 分析 db_a.dwd_table_a 的结构,只返回摘要。

对刚生成的 insert_dws_table_a.sql 用 sql-validator 验证。

用 data-quality-checker 对 db_a.dws_table_a 的 partition_dt=20260520 执行标准自测,只返回 PASS/FAIL 汇总。

如果希望避免 Claude 自行判断是否调用 agent,可以显式指定:

@"sql-validator (agent)" 验证 path/to/insert_dws_table_a.sql

4.5 第 5 层:改造 SKILL 调用方式

SKILL 可以理解为 Claude Code 中封装任务说明、规范和工具使用方式的技能文件。数仓研发常见的 8 步流程可以写成 8 个 SKILL:需求分析、技术设计、ETL 开发、自测、数据比对、SR 导入、性能优化、SLA/DQC。

直接在主会话中触发大型 SKILL,会带来一个问题:SKILL 文件全文被加载进主 context,文件越规范、越详细,越容易加速 compact。

更稳的方式是:

flowchart LR
    U[主会话发出任务] --> A[Subagent 读取完整 SKILL]
    A --> E[执行读文件、查血缘、自测、比对]
    E --> O[生成文件或结构化报告]
    O --> U[主会话只接收摘要和产物路径]

改造前后对比:

调用方式主 context 承担内容风险
主会话直接读 SKILL完整规范、过程数据、执行结果compact 提前触发
subagent 内部读 SKILL主会话只收摘要主会话干净,细节隔离
path-scoped rules按文件路径加载相关规则避免无关规范常驻

路径规则示例:

---
# .claude/rules/etl-rules.md
paths:
  - "**/*insert*.sql"
  - "**/*_di.sql"
  - "**/*_df.sql"
---

# ETL 开发规范

- 必须使用 partition_dt 分区
- INSERT OVERWRITE 前检查目标分区是否已存在
- INSERT 必须带 PARTITION 子句
- 不允许无血缘关系的跨库 JOIN
- 禁止 SELECT *
- 金额字段使用 DECIMAL,不使用 DOUBLE

SKILL 文件可以继续保留完整规范,但主会话不要反复加载全文。让 subagent 在独立上下文里读取和执行,主会话只接收结果。


5. 数仓 Harness 总体架构

完整方案可以分成三层:持久化层、Harness hook 层、subagent 层。

flowchart TB
    Dev[开发者] --> Main[Claude 主会话]

    subgraph Persist[持久化层:防遗忘]
        ClaudeMd[.claude/CLAUDE.md<br/>当前迭代状态与约束]
        Memory[Auto Memory<br/>长期字段口径与踩坑经验]
        Rules[Path-scoped Rules<br/>按文件路径加载规范]
    end

    subgraph Harness[Harness 层:确定性执行]
        Post[PostToolUse<br/>写 SQL 后检查]
        Pre[PreToolUse<br/>执行 Bash 前拦截]
        Session[SessionStart<br/>compact 后重注入]
        StopHook[Stop<br/>任务完整性检查]
    end

    subgraph Agents[Subagent 层:隔离上下文]
        Validator[sql-validator<br/>SQL 验证]
        Explorer[dw-explorer<br/>血缘与 DDL 探索]
        Dqc[data-quality-checker<br/>自测]
        Comparator[data-comparator<br/>数据比对]
    end

    Persist --> Main
    Main --> Harness
    Main --> Agents

    Post --> Check[validate_sql.sh]
    Pre --> Block[block_dangerous_ddl.sh]
    Session --> Inject[注入 dw_conventions.md]

    Agents --> Summary[结构化摘要]
    Summary --> Main

这三个层次分别解决不同问题:

层次解决的问题典型内容
持久化层compact 后遗忘约束CLAUDE.md、Memory、rules
Harness hook 层规范执行不稳定SQL 检查、危险 DDL 拦截
Subagent 层高 token 操作污染主会话血缘、自测、比对、性能分析

分工越清楚,主会话越稳定。Claude 主会话应该集中处理语义和决策,不应该背负所有日志、规范全文和执行明细。


6. 8 步数仓研发工作流

数仓研发可以拆成 8 个标准步骤。每一步都要决定:留在主会话处理,还是交给 Harness 机制处理。

flowchart LR
    S1[需求分析] --> S2[技术设计]
    S2 --> S3[ETL 开发]
    S3 --> S4[自测]
    S4 --> S5[数据比对]
    S5 --> S6[SR 导入]
    S6 --> S7[性能优化]
    S7 --> S8[SLA / DQC]
步骤适合机制原因
需求分析主会话 + dw-explorer需要语义理解,也需要少量表结构摘要
技术设计主会话 + CLAUDE.md设计决策要沉淀为迭代约束
ETL 开发主会话 + PostToolUse hook + sql-validator写 SQL 后必须自动检查
自测data-quality-checker23 项检查结果不应进入主会话
数据比对data-comparator明细样本和差异列表 token 很高
SR 导入SKILL + dw-explorer需要 DDL、血缘、字段类型风险摘要
性能优化dw-explorer + 主会话血缘和 DDL 隔离,优化建议留给主会话整合
SLA/DQC主会话 + 规则模板输出配置,过程数据较少

Step 1:需求分析

推荐提示词:

用 dw-explorer subagent 先读取上游表结构,只返回摘要。

基于需求文档生成:
1. 需求摘要,不超过 5 行
2. 表字段口径草稿
3. 待确认问题清单,按优先级排序
4. 可能影响计算结果的字段单位、时间口径、状态口径

需求文档 URL:[粘贴 PRD 链接]

PRD(Product Requirement Document,产品需求文档)里经常写的是业务语言,例如“用户视角 GMV”。模型需要把它转换成可落地的数仓口径,例如交易视角、订单视角、履约视角是否一致。

Step 2:技术设计

推荐提示词:

基于已确认需求,按 OneData 规范完成技术设计。

输入:
- 目标表名:[按 层级_域_主题_粒度_周期 命名]
- 粒度:[描述]
- 分区:partition_dt string,格式 yyyyMMdd
- 主键:[字段列表]
- 禁止:任何与上游口径不一致的字段命名

输出:
- OneData 建模说明
- 字段清单
- 主键与分区说明
- 特殊业务口径
- 不超过 60 行

设计完成后,把稳定决策写入 CLAUDE.md

## 当前迭代技术设计决策
- 表名:db_a.dws_table_a
- 主键:order_no + partition_dt
- 粒度:订单明细粒度
- amount 字段继承上游千元单位,不做转换
- GMV 口径:用户视角,不使用交易视角

Step 3:ETL 开发

ETL 开发是 Harness 工程最能发挥作用的步骤。因为它会写 SQL 文件,而写文件可以触发 PostToolUse hook。

推荐提示词:

按 ETL 开发规范生成建表 DDL 和 Insert SQL。

输出文件:
- ddl_[表名].sql
- insert_[表名].sql

要求:
- INSERT 使用 OVERWRITE 模式
- INSERT 必须带 PARTITION(partition_dt='${bizdate}')
- 金额字段使用 DECIMAL(20,4)
- amount 单位继承上游千元,不做单位转换
- 禁止 SELECT *
- 多表 JOIN 必须有 ON 条件

生成完毕后,用 sql-validator subagent 验证两个文件。

执行链路如下:

sequenceDiagram
    participant User as 开发者
    participant Claude as Claude 主会话
    participant Hook as PostToolUse Hook
    participant Validator as sql-validator

    User->>Claude: 生成 DDL 和 Insert SQL
    Claude->>Claude: Write/Edit .sql 文件
    Claude->>Hook: Harness 触发 validate_sql.sh
    Hook-->>Claude: PASS 或 exit 2 错误信息
    Claude->>Claude: 如失败则修正 SQL
    Claude->>Validator: 独立验证 SQL
    Validator-->>Claude: PASS/FAIL + 问题列表

这条链路的关键是:SQL 规范不再依赖 Claude 是否记得,文件一写就检查。

Step 4:自测

推荐提示词:

用 data-quality-checker subagent 对 [表名] 执行 23 项标准自测。

输入:
- bizdate = [日期]
- 口径约束:[例如 is_perform=1 只取履约订单]

输出限制:
- 只返回 PASS/FAIL 汇总
- FAIL 项返回检查项、失败原因、关键差值
- 不返回原始 SQL 执行结果
- 不超过 50 行

自测结果通常很长,尤其是数据量、主键重复、金额对账、枚举分布等检查。如果全部塞进主会话,会显著增加 compact 风险。

Step 5:数据比对

推荐提示词:

用 data-comparator subagent 对比新旧表。

新表:[新表名],partition_dt = [日期]
参考表:[旧表名或线上表]
比对字段:[核心金额字段列表]
金额类容差:≤ 0.01%

只返回:
- 差异超过容差的字段列表
- 新旧值、差值、差异比例
- 可能原因
不返回全量对比数据。

数据比对的目标不是让主会话看到所有样本,而是让主会话知道“哪些字段不一致、差多少、可能由什么口径造成”。

Step 6:SR 导入

StarRocks(下文简称 SR)导入需要关注字段类型、Key 选择、分区、桶数和时间字段存储类型。

推荐提示词:

用 dw-sr SKILL 生成 SR 建表和同步任务建议。

先用 dw-explorer 查询以下内容,只返回摘要:
- 源表:[ODPS 表名]
- 目标表:[SR 表名]
- 一层上下游血缘

基于 DDL 摘要分析同步任务风险:
1. DECIMAL / DOUBLE 是否存在精度丢失风险
2. Key 字段选择是否合理,重复率是否过高
3. partition_live_number 是否匹配下游查询窗口
4. DISTRIBUTED BY HASH 的 bucket 数是否匹配数据量
5. DATETIME 字段是否被存成 VARCHAR,导致时间过滤无法高效执行

输出:
- 同步任务配置建议
- 按风险高 / 中 / 低排序
- 每条格式:[风险等级] 问题描述 → 建议修改方式
- 不超过 20 行

Step 7:性能优化

推荐提示词:

用 dw-explorer subagent 查询 [表名] 的一层上下游血缘和 DDL,只返回摘要。

然后分析当前 Insert SQL 的性能瓶颈:
1. 是否有全表扫描
2. 是否有笛卡尔积风险
3. 是否可以用 MAP JOIN 替代 HASH JOIN
4. 分区裁剪是否生效
5. 大表 JOIN 顺序是否合理

输出:
- 优化建议
- 按收益排序
- 不超过 30 行

性能优化需要大量上下文,但主会话真正需要的是“瓶颈排序”和“修改建议”。DDL、血缘、执行计划细节应该留在 subagent 里。

Step 8:SLA / DQC

SLA(Service Level Agreement,服务等级协议)和 DQC 通常可以模板化生成。

推荐提示词:

按 SLA/DQC 规范为 [表名] 生成 9 类 DQC 规则。

要求:
- 完整性:主键非空、分区数据量
- 准确性:核心金额字段与上游比对,容差 0.01%
- 一致性:is_perform 与 perform_flag 联动逻辑
- 合法性:枚举值范围检查
- 唯一性:主键重复检查
- 波动性:核心指标环比波动检查
- 时效性:产出时间 SLA ≤ 次日 8:00
- 血缘一致性:上游分区是否齐备
- 空值率:核心字段空值率阈值

输出 DQC 配置 JSON,可直接用于配置。

JSON(JavaScript Object Notation,轻量级数据交换格式)输出要控制结构稳定,便于复制到平台配置里。


7. SKILL 命令体系设计

为了减少每次重复描述,可以把 8 步流程封装成命令。命令名可以按团队习惯调整,重点是每个命令都要固定输入、固定规范、固定产出。

命令对应步骤封装内容推荐执行位置
/dw-requirement需求分析需求摘要、口径草稿、待确认问题主会话 + dw-explorer
/dw-design技术设计OneData 建模、字段设计、粒度定义主会话
/dw-etlETL 开发DDL、Insert SQL、SQL 规范主会话 + hook + sql-validator
/dw-test自测23 项标准检查data-quality-checker
/dw-compare数据比对新旧表核心指标对账data-comparator
/dw-srSR 导入SR 建表参数、同步风险subagent 内读 SKILL
/dw-optimize性能优化血缘、DDL、SQL 瓶颈分析dw-explorer
/dw-dqcSLA/DQCDQC 规则、SLA 配置主会话

/dw-etl 为例,一个成熟命令应该封装四类内容。

规范内容

- 分区字段必须是 partition_dt string,格式 YYYYMMDD
- 金额字段使用 DECIMAL(26,4) 或团队指定精度
- INSERT 必须使用 INSERT OVERWRITE
- INSERT 必须带 PARTITION 子句
- 禁止 SELECT *
- 禁止无血缘依据的跨库 JOIN
- 建表语句必须包含字段注释

产出格式

输出文件:
├── ddl_[表名].sql       # ODPS 建表语句,含字段注释、生命周期配置
├── insert_[表名].sql    # ODPS Insert SQL,含分区裁剪、JOIN 规范
└── ddl_sr_[表名].sql    # SR 建表语句,含 Key、分区、桶数建议

自动护栏

每次 .sql 文件写入后:
- PostToolUse hook 自动执行 validate_sql.sh
- 发现 SELECT *:exit 2 阻断
- 发现 INSERT 缺少 PARTITION:exit 2 阻断
- 发现 DOUBLE:返回 WARNING
- Claude 根据错误信息修正后继续

subagent 卸载

生成完成后:
- sql-validator 验证 DDL 和 Insert SQL
- 主会话只接收 PASS/FAIL 和问题列表
- 验证日志、grep 结果、执行细节不进入主 context

验收指标可以按这几项观察:

指标未 Harness 化Harness 化目标
SQL 规范遵守依赖 prompt 记忆,容易波动hook 强制检查,目标 95%+
compact 触发频率血缘、自测、比对都进入主会话高 token 任务隔离,降低 50%~70%
字段口径遗忘临时口头约束容易丢CLAUDE.md + Memory 持久化
自测可读性大量结果刷屏只返回 PASS/FAIL 和失败项
返工来源口径确认不足需求分析阶段输出待确认问题清单

8. 落地步骤

8.1 建立项目级上下文

在数仓项目目录创建:

mkdir -p .claude/context
touch .claude/CLAUDE.md
touch .claude/context/dw_conventions.md

CLAUDE.md 放当前迭代信息,dw_conventions.md 放可重注入的数仓规范。两者不要写成超大文档,重点是让 Claude 每次都能看到最关键的信息。

建议初始内容:

# 数仓项目上下文

## 正在开发
- 表:
- 版本:
- 当前阶段:

## 本次迭代约束
- 

## 字段口径
- 

## 禁止事项
- 禁止 SELECT *
- 禁止生产表 DROP/TRUNCATE
- INSERT 必须带 PARTITION

8.2 配置 hooks

创建 hooks 目录:

mkdir -p .claude/hooks
touch .claude/settings.json
touch .claude/hooks/validate_sql.sh
touch .claude/hooks/block_dangerous_ddl.sh
chmod +x .claude/hooks/*.sh

把 SQL 检查、危险 DDL 拦截写入脚本,再在 settings.json 中绑定 PostToolUsePreToolUse

最小可用版本只需要两件事:

文件必须能力
validate_sql.sh拦截 SELECT *INSERT 缺少 PARTITION
block_dangerous_ddl.sh拦截生产表 DROP TABLE / TRUNCATE TABLE

8.3 创建核心 subagents

创建 agents 目录:

mkdir -p .claude/agents
touch .claude/agents/sql-validator.md
touch .claude/agents/dw-explorer.md
touch .claude/agents/data-quality-checker.md

最先落地三个 agent:

Agent优先级原因
sql-validator每次 ETL 开发都需要
dw-explorer血缘、DDL 是 context 膨胀大户
data-quality-checker中高自测结果量大,隔离收益明显

8.4 调整研发习惯

Harness 化之后,日常工作方式也要变化:

旧习惯新习惯
在对话里临时说口径口径确认后写入 CLAUDE.md
写完 SQL 手动提醒检查hook 自动检查
把血缘结果贴进主会话dw-explorer 返回摘要
把自测明细全部发给 Claudedata-quality-checker 返回失败项
主会话直接加载完整 SKILLsubagent 内读 SKILL,主会话收结果

9. Harness 能解决哪些数仓问题

数仓 AI 开发的准确率可以用一个简化公式理解:

准确率 = 语义理解深度 × 数据规范覆盖度

LLM 擅长生成代码,但它不天然理解业务语义。amount 是元还是千元、GMV 是用户视角还是交易视角、is_perform=1 是否代表履约订单,这些都不是 SQL 语法问题,而是业务语义问题。

Harness 的作用是把语义和规范放到更稳定的位置:

变量不稳定位置稳定位置
字段口径聊天历史CLAUDE.md / Memory
SQL 规范prompt 记忆hook 脚本
自测过程主会话 contextsubagent context
血缘明细主会话 contextdw-explorer 摘要
任务完整性人工检查Stop hook

问题一:字段口径遗忘

现象:

对话开始时说明 amount 单位是千元。
compact 后 Claude 只记得有 amount 字段,不记得单位。
生成 SQL 时按元处理,结果差 1000 倍。

处理方式:

- 当前迭代口径写入 CLAUDE.md
- 长期字段口径写入 Auto Memory
- SQL 生成提示词中要求引用 CLAUDE.md 中的口径

问题二:需求理解偏差

现象:

业务要“用户视角 GMV”,模型按“交易视角 GMV”生成逻辑。
自测时数据对不上,需要返工。

处理方式:

- 需求分析阶段强制输出口径草稿
- 待确认问题清单写入 CLAUDE.md
- 关键问题未确认前,不进入 ETL 开发
- Stop hook 检查未完成事项

问题三:SQL 规范执行不一致

现象:

规范要求 INSERT 必须带 PARTITION。
短会话里 Claude 能遵守,长会话或 compact 后开始遗漏。

处理方式:

- PostToolUse hook 监听 Write / Edit
- 每次 .sql 文件保存后执行 validate_sql.sh
- 严重违规使用 exit 2 阻断
- Claude 根据错误信息修正 SQL

问题四:大型需求 context 耗尽

现象:

血缘查询、DDL、自测结果、数据比对样本、SKILL 文件全部进入主会话。
compact 触发后,关键约束和中间决策丢失。

处理方式:

- 血缘查询交给 dw-explorer
- 自测交给 data-quality-checker
- 数据比对交给 data-comparator
- SKILL 文件由 subagent 内部读取
- 主会话只接收摘要、结论和产物路径

10. 从对话辅助到流水线自动化

传统数仓 AI 开发更像一次性对话:开发者不断提醒 Claude 记住规范、记住口径、记住检查项。Harness 化之后,研发流程会变成规则嵌入式流水线:

flowchart LR
    Req[需求] --> Design[技术设计]
    Design --> Context[写入 CLAUDE.md]
    Context --> ETL[生成 ETL SQL]
    ETL --> Hook[Hook 自动检查]
    Hook -->|失败| Fix[Claude 修正]
    Fix --> Hook
    Hook -->|通过| Test[Subagent 自测]
    Test --> Compare[Subagent 数据比对]
    Compare --> Optimize[Subagent 血缘与性能分析]
    Optimize --> DQC[生成 SLA / DQC 配置]

各层职责保持固定:

负责内容
Claude 主会话理解需求、设计模型、生成代码、整合建议
Hooks规范检查、危险操作拦截、任务完整性检查
Subagents血缘探索、自测执行、数据比对、日志分析
CLAUDE.md / Memory字段口径、迭代约束、长期经验
SKILL标准流程、产出格式、团队规范

数仓 AI 开发真正需要的不是“让模型记住一切”,而是把该持久化的持久化、该自动检查的自动检查、该隔离的隔离。Claude 负责语义和生成,Harness 负责确定性工程约束,subagent 负责消化高 token 任务,CLAUDE.md 和 Memory 负责保存不会随着 compact 消失的关键信息。

这套分工建立起来后,AI 不再只是“帮忙写一段 SQL”的工具,而是能嵌入数仓研发流程的工程化执行单元。


评论