Week05|实验|把客服运营口径写进工程:用 dbt 产出 KPI 包,并封装安全指标查询工具 v1
跑出 Week05 的最小 analytics engineering 闭环
这次实验不是在项目外做一个独立 dbt demo。
你要围绕当前 OmniSupport Copilot 工程,把口径从 source 一路推进到 mart、registry、tool contract、runtime guard 和 audit evidence:
让 BI 和 Agent 都能消费同一套受控 KPI。
这次实验要完成什么
本次实验以 omnisupport-copilot/runbooks/week05/README.md 为真实执行顺序,覆盖:
- 启动 PostgreSQL / MinIO / devbox 依赖;
- 如需要,生成并导入 Week05 工单数据;
dbt debug验证 profiles 和连接;dbt build --select tag:week05构建 sources / staging / intermediate / marts;dbt docs generate生成 docs / manifest / catalog / lineage artifacts;- 校验
analytics/metric_registry_v1.yml; - 运行
query_support_kpis_v1正例和负例; - 验证 Tool API endpoint;
- 跑 Week01–Week05 回归测试;
- 生成
reports/week05/week05_lab_execution_summary.md。
这不是“跑命令打卡”。真正目标是把 source 到 mart 的 transform 跑通,用 tests / docs / lineage 证明口径可信,用 registry 固定可查询指标,用 tool contract 验证 Agent 不能乱查,最后用 evidence 文件把过程交付出来。
参考实验时间
建议按一次完整实验安排:先跑通命令,再整理 evidence、排障记录和实验总结。
本次实验产出
| 产出 | 路径 | 合格标准 |
|---|---|---|
| dbt project | analytics/ |
source、staging、intermediate、marts、tests、docs 结构清楚 |
| KPI mart | analytics/models/marts/support_kpi_mart.sql |
能产出 metric rows |
| safe view | analytics/models/marts/agent_tool_input_view.sql |
不暴露 PII、正文和 raw SQL 入口 |
| metric registry | analytics/metric_registry_v1.yml |
validator 通过,至少 6 个指标 |
| tool contract | contracts/tools/tools/query_support_kpis_v1.json |
schema、拒绝码、审计字段完整 |
| evidence | reports/week05/dbt_build_evidence.md |
记录 build、docs、registry、tool、pytest 结果 |
| examples | reports/week05/query_tool_examples.md |
正例和负例可复核 |
| summary | reports/week05/week05_lab_execution_summary.md |
写清结果、失败处理、下一步 |
实验总结、工具正负例和 delivery summary 可以参考附录的 Week05 模板库,不要把临时 logs 或 analytics/target/ 当作正式模板。
先看一张闭环图
先看证据文件怎么互相支撑
实验页验证“能跑通”,作业页整理“能交付”。不要把 analytics/target/ 或临时日志当作主要提交物;它们是本地证据来源,正式交付应落到 reports/week05/*.md。1
0. 环境前提
本实验默认在 omnisupport-copilot 项目仓库根目录执行。
开始前先确认:
- 你没有在课程站点仓库里运行项目命令;
- Docker Desktop / Docker Compose 可用;
infra/env/.env.example存在;- 你能看到
analytics/、contracts/tools/tools/query_support_kpis_v1.json、services/tool_api/、runbooks/week05/README.md; - Week03 / Week04 的本地临时输出不需要提交到 main。
| 前置检查 | 合格标准 | 不满足时先做什么 |
|---|---|---|
| 当前目录 | 是 omnisupport-copilot 项目根目录 |
不要在课程站点仓库跑项目命令 |
.env.local |
infra/env/.env.local 存在且和 .env.example 对齐 |
先复制示例并补缺项 |
| Docker Compose | docker compose 可用 |
先启动 Docker Desktop |
| Week05 工程文件 | analytics/、tool contract、runbook 都存在 |
先拉取项目最新分支 |
| runbook | runbooks/week05/README.md 存在 |
命令以项目 runbook 为准 |
| 仓库边界 | 课程站点和项目仓库不混用 | 回到正确目录再执行 |
课程站点只负责讲义,实验命令在 omnisupport-copilot 项目仓库里执行。两个仓库不要混着跑。2
实验步骤导航
| 步骤 | 为什么做 | 期望看到 | 常见失败先看 |
|---|---|---|---|
| 1. 启动依赖 | 只把 Week05 需要的 postgres / minio / init 起起来 | 服务容器 healthy | Docker、端口、.env.local |
| 2. 准备工单数据 | 确保 source 有数据可 transform | source 表有记录 | Week03 ingest state / report |
3. dbt debug |
验证 profile 和数据库连接 | connection passed | DBT_PROFILES_DIR=.、服务名 |
4. dbt build |
build + tests 一起验收 | tag:week05 通过 | source、staging、mart grain |
| 5. docs / lineage | 生成可解释证据 | manifest / catalog / run_results | docs 描述是否缺失 |
| 6. registry 校验 | 确认指标白名单可读 | valid=true |
YAML 字段、safe view 字段 |
| 7. 工具正负例 | 验证允许和拒绝都可控 | 正例 allowed,负例 denial code | registry、role、window |
| 8. API endpoint | 验证服务层边界 | health 和 POST 正常 | 端口、service build |
| 9. 回归检查 | 防止 Week1-4 退化 | contract / integration tests 通过 | 先看失败周次 |
| 10. 实验总结 | 把过程变成交付证据 | summary 写清结果和失败处理 | 缺 evidence 文件 |
1. 启动依赖
cp infra/env/.env.example infra/env/.env.local
docker compose --env-file infra/env/.env.local -f infra/docker-compose.yml \
up -d --build postgres minio minio_init如果你已经有 .env.local,不要直接覆盖。先对照 .env.example 补缺项。
2. 准备工单数据
如果本地已有 Week03 / Week04 数据,可以直接进入 dbt。否则先生成并导入一批工单数据:
docker compose --profile tools --env-file infra/env/.env.local \
-f infra/docker-compose.yml run --rm devbox \
python data/synthetic_generators/ticket_simulator.py --count 500 \
--output data/canonization/tickets/tickets-seed-week05.jsonl
docker compose --profile tools --env-file infra/env/.env.local \
-f infra/docker-compose.yml run --rm devbox \
python -m pipelines.ingestion.ticket_ingest \
--input data/canonization/tickets/tickets-seed-week05.jsonl \
--batch-id week05-demo期望输出:
- ticket ingest 完成;
- PostgreSQL 中有
ticket_fact、customer_dim、ticket_comment_fact等 source; - 失败时优先回到 Week03 ingest state / report 排查。
3. dbt debug
docker compose --profile tools --env-file infra/env/.env.local \
-f infra/docker-compose.yml run --rm devbox \
bash -lc 'cd analytics && DBT_PROFILES_DIR=. dbt debug'期望输出:
- profiles 能加载;
- adapter 能连接 Docker 网络内的
postgres; analytics/models/sources.yml中的omni_postgressource 不需要下游猜表。
常见错误:
| 错误 | 优先检查 |
|---|---|
| profile not found | 是否在 analytics/ 内设置 DBT_PROFILES_DIR=. |
| could not translate host name | 是否在 Docker devbox 内执行,服务名是否为 postgres |
| password authentication failed | .env.local 和 profiles.yml 是否一致 |
4. dbt build
docker compose --profile tools --env-file infra/env/.env.local \
-f infra/docker-compose.yml run --rm devbox \
bash -lc 'cd analytics && DBT_PROFILES_DIR=. dbt build --select tag:week05'期望输出:
stg_*、int_*、support_case_mart、support_kpi_mart、agent_tool_input_view构建成功;- dbt tests 全部通过;
no_pii_columns_in_agent_tool_input_view通过;- 当前项目证据为 37/37 通过,
support_case_mart=50,support_kpi_mart=300。
失败后不要先改 SQL。先判断:
- source 表是否存在;
- source 字段是否和
sources.yml对齐; - staging 是否把枚举和时间字段规范化;
- mart 粒度是否导致重复;
- safe view 是否暴露了不该暴露的字段。
5. 生成 docs / lineage evidence
docker compose --profile tools --env-file infra/env/.env.local \
-f infra/docker-compose.yml run --rm devbox \
bash -lc 'cd analytics && DBT_PROFILES_DIR=. dbt docs generate'你要检查的不是页面好不好看,而是:
target/manifest.json是否生成;target/catalog.json是否生成;- 关键 model 是否有描述;
- lineage 是否能从 source 追到 marts;
agent_tool_input_view是否只暴露 safe columns。
analytics/target/ 是本地 runtime 输出,通常不提交到 main。
6. 校验 metric registry
docker compose --profile tools --env-file infra/env/.env.local \
-f infra/docker-compose.yml run --rm devbox \
python analytics/scripts/validate_metric_registry.py --json期望输出:
valid: truemetric_count >= 6- safe view 字段包含
metric_date、metric_name、metric_value和允许维度; max_window_days没有被无意放大。
7. 运行受控 KPI 工具正负例
正向调用:
docker compose --profile tools --env-file infra/env/.env.local \
-f infra/docker-compose.yml run --rm devbox \
bash -lc 'PYTHONPATH=services/tool_api python -m app.kpi_query --example valid'未知指标被拒绝:
docker compose --profile tools --env-file infra/env/.env.local \
-f infra/docker-compose.yml run --rm devbox \
bash -lc 'PYTHONPATH=services/tool_api python -m app.kpi_query --example bad_metric || true'角色无权限被拒绝:
docker compose --profile tools --env-file infra/env/.env.local \
-f infra/docker-compose.yml run --rm devbox \
bash -lc 'PYTHONPATH=services/tool_api python -m app.kpi_query --example bad_role || true'验收点:
| 用例 | 期望 |
|---|---|
valid |
allowed=true,有 rows 和 audit |
bad_metric |
allowed=false,denial_code=METRIC_DENIED |
bad_role |
allowed=false,denial_code=ROLE_DENIED |
8. 验证 Tool API 端点
docker compose --env-file infra/env/.env.local \
-f infra/docker-compose.yml up -d --build tool_api
curl -s http://localhost:8001/health
curl -s -X POST http://localhost:8001/api/v1/tools/query_support_kpis \
-H 'Content-Type: application/json' \
-H 'X-Actor-ID: instructor-local' \
-d '{
"actor_role": "instructor",
"metrics": ["ticket_count"],
"date_from": "2026-04-01",
"date_to": "2026-04-30",
"dimensions": ["product_line", "priority"],
"limit": 20
}'期望输出:
/health正常;- POST 返回
allowed=true; audit.actor_id能带上X-Actor-ID;- row count 有上限,不返回无限结果。
9. 回归检查
docker compose --profile tools --env-file infra/env/.env.local \
-f infra/docker-compose.yml run --rm devbox \
pytest tests/contract/test_json_schemas.py \
tests/contract/test_week02_gate.py \
tests/contract/test_week05_metric_contracts.py -q
docker compose --profile tools --env-file infra/env/.env.local \
-f infra/docker-compose.yml run --rm devbox \
pytest tests/integration/test_ingest_state.py \
tests/integration/test_replay_backfill_dry_run.py \
tests/integration/test_week4_lakehouse_smoke.py \
tests/integration/test_week05_metric_registry.py \
tests/integration/test_week05_kpi_query_tool.py -q通过标准:
- Week01 / Week02 contract gate 不退化;
- Week03 ingest state / replay / backfill dry-run 不退化;
- Week04 lakehouse smoke 不退化;
- Week05 registry 和 KPI 工具测试通过。3
10. 写实验总结
最后补齐 reports/week05/week05_lab_execution_summary.md,至少回答:
- 哪些 source 被 dbt 接住;
- 哪些 models 被 build;
- 哪些 tests 保护了核心口径;
- docs / lineage artifacts 是否生成;
- registry 中开放了哪些指标;
- KPI 工具正例和负例结果是什么;
- 哪些本地 target / logs 不提交;
- 下一步作业要整理哪些正式交付物。
验收不变量
| 检查点 | 必须满足 | 失败时先看 |
|---|---|---|
dbt debug |
profile 和 Postgres 连接通过 | DBT_PROFILES_DIR=.、devbox、.env.local |
dbt build --select tag:week05 |
models 和 tests 通过 | source 表、字段、mart grain |
| docs artifacts | manifest.json、catalog.json、run_results.json 生成 |
dbt docs generate 输出 |
| registry | valid=true 且 metric_count >= 6 |
analytics/metric_registry_v1.yml |
| 正例 | allowed=true,有 rows 和 audit |
日期窗口、seed 数据、role |
| 负例 | 返回对应 denial_code |
metric / role / dimension / window guard |
| 回归 | Week01–Week05 合同和集成测试不退化 | 先定位失败所属周次 |
| summary | week05_lab_execution_summary.md 存在 |
是否记录 build、docs、registry、tool、pytest |
如果要做 6–8 分钟演示,录 dbt debug → dbt build → registry validator → KPI 工具正例 → KPI 工具负例。重点讲“为什么负例通过才说明工具边界可信”。4
排障提示
| 问题 | 优先检查 |
|---|---|
profile not found |
是否在 analytics/ 内运行,是否设置 DBT_PROFILES_DIR=. |
| Postgres connection refused | postgres 容器是否启动,devbox 是否在 Docker 网络内 |
| source table not found | Week03/Week04 数据是否导入,sources.yml 表名是否真实 |
| dbt test failed | 先看失败字段和 grain,不要直接放宽测试 |
| registry invalid | metric name、allowed dimensions、safe view 字段是否对齐 |
PYTHONPATH=services/tool_api 模块找不到 |
是否在项目根目录执行,路径是否打错 |
| API 端口不可访问 | tool_api 是否 build / up,端口 8001 是否被占用 |
| 时间窗口被拒绝 | 先确认是否超过 max_window_days: 31,这通常不是 bug |
| 正例没数据 | 先检查日期窗口和 seed 数据,而不是放大工具权限 |
延伸阅读
| 主题 | 推荐资料 | 为什么读 |
|---|---|---|
| dbt build | dbt Docs: build command | 理解 build 为什么适合实验验收 |
| dbt data tests | dbt Docs: Data tests | 理解核心口径的自动检查 |
| dbt docs generate | dbt Docs: docs generate | 理解 docs / catalog 证据来自哪里 |
| dbt artifacts | dbt Docs: Artifacts | 理解 manifest / catalog / run_results 的作用 |
| OpenAI Function Calling | OpenAI Docs: Function Calling | 理解工具调用为什么需要契约 |
| OpenAI Structured Outputs | OpenAI Docs: Structured Outputs | 理解结构化工具输出和 schema 的关系 |

