第 15 讲 · 实战:写代码与数据分析
写代码适合与 AI 协作,因为输入、输出和测试可以落成可执行契约;数据分析却多了一层风险:代码可能成功运行,同时悄悄改变了记录集合、join 粒度、缺失处理或分母。这里的重点不是让模型代做真实分析,而是学会把每一个结论还原成一条可检查的溯源链。
学习层:先审计“数据变成了什么”,再相信指标
谜题:同一批订单,为什么“north 合格率”会变?
下面的 toy 账本有 6 条订单和一张客户维表。目标指标暂定为:
请先预测,再打开实验:如果客户维表出现重复键、少一个键,或在计算前先筛掉不合格记录,naive 结果会怎样?这些数字是为了暴露机制而构造的确定性练习,不是任何真实业务的结论。
最小溯源账本:原始记录 → 变换 → 指标
| order_id | customer_id | region | qualified | amount |
|---|---|---|---|---|
| o1 | c1 | north | 是 | 10 |
| o2 | c2 | south | 是 | 20 |
| o3 | c3 | north | 否 | 30 |
| o4 | c4 | south | 是 | 40 |
| o5 | c5 | north | 是 | 50 |
| o6 | c6 | south | 是 | 60 |
正确基线中,north 的分子是 o1、o5 共 2 条,分母是 o1、o3、o5 共 3 条,因此是 2/3。注意:join 不是指标;它是会改变行集合的变换,必须把输入行数、输出行数和被丢弃/复制的键写出来。
四个护栏:让静默错误变成可见状态
- validate join:如果业务语义是一条订单对应一条客户记录,先断言维表
customer_id唯一。重复键不是“多一点数据”,而是 join 粒度改变。 - 行数检查:若这是保留全部订单的 enrichment,join 后应有 6 行;少于 6 行提示缺失/inner join,超过 6 行提示一对多膨胀。
- 缺失报告:列出未匹配的键与受影响的源行。inner join 的默认行为可能只是返回一个看起来很整齐的子集。
- 分母声明:报告“2/3”时同时写分子、分母和筛选顺序;“先筛 qualified 再算合格率”会把分母改成 2,变成 2/2,这回答的是另一个问题。
交互实验:预测 → 逐步护栏 → 对比结果
无 JavaScript 时的静态读法:默认基线为 2/3。切换四个预设,记录 join 前后行数,再手算 north 的分子/分母;点击“启用下一项护栏”逐项打开 4 个护栏,重复键会被阻断,缺失键会被报告,分母口径差异会显式显示。点击“去重后重跑”只是在教学上修复重复维表键,真实项目仍应回到来源修复并保留修改记录。
| 预设 | 故障点 | naive 观察 | 审计动作 |
|---|---|---|---|
| 正确基线 | 无 | 6 → 6;2/3 | 四项护栏通过 |
| 重复键 | c5 出现两次 | 6 → 7;3/4 | validate join 阻断,再修复键 |
| 缺失 | c6 不在维表 | 6 → 5;south 行被丢 | 报告 c6 与 dropped row |
| 分母变化 | 先筛 qualified | 2/2,不是全量问题的 2/3 | 声明全量或筛选后口径 |
如何读 naive 与 audited result
- naive result 是“代码顺利跑完后直接报出的数”,不等于它错误,也不等于它有业务含义;先问它使用了哪一组行、哪一个分母。
- audited result 不是“更漂亮的数字”,而是带有 join 约束、行数、缺失清单与分母声明的结果。若护栏失败,最诚实的 audited result 可能是“阻断,暂不下结论”。
- 重复行、缺失值和分母变化会改变结论的算术对象;它们不需要复杂模型,也不需要真实数据才能被发现。小型确定性表适合做第一道验收测试。
迁移到真实分析:让 AI 交付证据链
让 AI 写分析代码时,不只要求“给我一个百分比”,而是要求它交付:源表行数与键约束、每一步变换的行数、缺失报告、分子/分母和筛选顺序、关键中间表的抽样行,以及可独立运行的断言。你仍要抽查原始记录和中间结果;本实验只训练审计习惯,不替代领域定义、数据治理或因果设计。
推荐的最小提示词是:“先列出数据契约和指标分母;每次 join 后报告行数、重复键、未匹配键;再给 naive 与 audited 两条结果;任何护栏失败就停在警告,不要静默修复。”
1. 三个层次,三种用法
| 层次 | 工具形态 | 你的角色 |
|---|---|---|
| L1 片段级 | 聊天框 / IDE 补全 | 写代码的人,AI 递工具 |
| L2 任务级 | 编程智能体 | 提需求、做验收的人 |
| L3 项目级 | 智能体 + 规划 + 迭代 | 产品经理 + 架构评审 |
2. L1 日常辅助:问得好,答得准
- 写小函数:给足输入输出契约,并要求边界测试、失败模式和不变量;例如指定保留索引、空列行为和测试用例。
- 看不懂的代码:逐块解释数据流,要求指出隐式类型转换、对齐规则、异常处理和可能改变行集合的操作。
- 报错求医:贴完整报错栈、相关代码和已尝试步骤;修复后要求复现原错误并跑回归测试。
- 翻译:跨语言迁移是常见的辅助场景,但仍需核对库版本、数值行为和边界条件。
3. L2/L3 智能体开发:把验收写进循环
用编程智能体从零做一个工具时,先让它给出需求拆解、数据格式和验证计划,再逐步实现。每一步都运行主流程和边界测试;尤其对数据任务,先固定样本、键约束、筛选口径和期望计数,再扩大数据规模。若一口气改动多个模块,出了问题会更难定位,因此要把变更切成可回看的小步。
一个可复用的请求模板:
先给我数据契约、变换图和验收断言,不要直接下结论。
每次 join 报告:左/右行数、键唯一性、匹配/未匹配计数、输出行数。
指标必须同时输出 numerator、denominator、筛选顺序与审计备注。
先用固定小样本跑通,再扩展;任何断言失败就停下来说明原因。
代码经验少时,要求 AI 解释“这一步为什么不会复制/丢失记录”,并自己改一个小边界案例。能复述数据流与断言,才算理解了代码。
4. 自动数据分析:从 CSV 到可复跑报告
数据分析可以使用代码解释器或编程智能体,但“自动”只意味着减少机械操作,不意味着跳过定义与验收。推荐顺序是:
- 给数据与背景:说明字段含义、来源、时间范围、单位、主键和目标问题;
- 先探索后假设:先输出形状、缺失、重复键、分布和异常值,再提出可检验的问题;
- 逐步钻取:每次分组、过滤或 join 都保留行数与分母,避免只看最终图;
- 交付脚本与账本:报告图表、原始计数、指标公式、版本和可复跑脚本;
- 独立抽查:手算或用第二种工具核对关键数字,并检查静默丢行、重复膨胀和口径变化。
统计方法要说明问题与前提,关键数字要能回到中间结果,相关关系不能自动升级为因果结论。真实数据还需要处理测量误差、抽样机制、隐私与访问权限;本讲的玩具实验只覆盖数据管线层面的第一道防线。
动手:先运行本讲的“数据分析溯源账本”,再把相同的断言格式移植到 labs/lab10_auto_analysis.py 的小样本流程中。它可以帮助你检查分析智能体的代码,但不应替你决定指标定义或研究结论。
5. 常见问题速答
- 代码风格老旧或用了废弃 API? 给出项目实际版本和官方文档片段,再让它补一个最小回归测试。
- 改 A 坏了 B? 限定变更范围,先读取相关模块和测试,再逐步提交;重要项目保留可回滚版本。
- 生成的代码越改越乱? 新开会话,只带当前代码、契约、失败测试和明确的验收标准。
- 什么时候不该用 AI 写? 在安全敏感、性能关键、数据权限或你无法独立验证的地方,先缩小问题、找人工复核或暂停自动化。
本讲小结
- 写代码的闭环来自契约、断言和可复跑测试;
- 数据分析的关键不是最终百分比,而是原始记录、变换步骤、分子/分母和缺失/重复状态;
- 让 AI 同时交付 naive 与 audited result,护栏失败时允许阻断;
- 理解权不外包:你必须能解释每个数字来自哪些行、经过哪些操作、回答哪个问题。
下一讲从代码切到图像:文生图的原理(扩散模型,给你的数学背景加一点推导彩蛋)与实操(提示词结构、工具选择、版权边界)。