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 | 替代模型做语义判断 |
| Hooks | SQL 规范检查、危险命令拦截、任务完整性校验 | 大段业务分析、复杂推理 |
| 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 | 触发时机 | 作用 |
|---|---|---|
PostToolUse | Write / Edit 后 | SQL 文件保存后自动检查规范 |
PreToolUse | Bash 前 | 拦截生产表 DROP / TRUNCATE 等危险命令 |
SessionStart | 会话或 compact 后 | 重新注入数仓规范 |
Stop | Claude 准备停止时 | 检查任务是否遗漏 |
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-checker | 23 项检查结果不应进入主会话 |
| 数据比对 | 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-etl | ETL 开发 | DDL、Insert SQL、SQL 规范 | 主会话 + hook + sql-validator |
/dw-test | 自测 | 23 项标准检查 | data-quality-checker |
/dw-compare | 数据比对 | 新旧表核心指标对账 | data-comparator |
/dw-sr | SR 导入 | SR 建表参数、同步风险 | subagent 内读 SKILL |
/dw-optimize | 性能优化 | 血缘、DDL、SQL 瓶颈分析 | dw-explorer |
/dw-dqc | SLA/DQC | DQC 规则、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 中绑定 PostToolUse 和 PreToolUse。
最小可用版本只需要两件事:
| 文件 | 必须能力 |
|---|---|
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 返回摘要 |
| 把自测明细全部发给 Claude | 用 data-quality-checker 返回失败项 |
| 主会话直接加载完整 SKILL | subagent 内读 SKILL,主会话收结果 |
9. Harness 能解决哪些数仓问题
数仓 AI 开发的准确率可以用一个简化公式理解:
准确率 = 语义理解深度 × 数据规范覆盖度
LLM 擅长生成代码,但它不天然理解业务语义。amount 是元还是千元、GMV 是用户视角还是交易视角、is_perform=1 是否代表履约订单,这些都不是 SQL 语法问题,而是业务语义问题。
Harness 的作用是把语义和规范放到更稳定的位置:
| 变量 | 不稳定位置 | 稳定位置 |
|---|---|---|
| 字段口径 | 聊天历史 | CLAUDE.md / Memory |
| SQL 规范 | prompt 记忆 | hook 脚本 |
| 自测过程 | 主会话 context | subagent context |
| 血缘明细 | 主会话 context | dw-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”的工具,而是能嵌入数仓研发流程的工程化执行单元。