综合小项目:用 Python 构建可复核的 AI 数据异常说明器
数据质量问题不适合完全交给模型发现。缺失值、数值范围、邮箱格式和重复记录,都可以用确定性规则准确判断;模型更适合把已经确认的异常解释给人,并给出修复建议。本篇做一个小项目:程序先发现异常,模型再返回结构化说明,最后由 Python 检查说明是否覆盖了全部异常。
先把“发现异常”和“解释异常”分开
一个可靠的处理流程可以拆成三步:
- Python 读取数据,使用固定规则生成异常列表。
- 模型只接收异常列表,解释每个异常的影响和建议。
- Python 校验模型是否逐条回应,校验通过后再交给人工查看。
这样做的好处是边界清晰。模型不会因为“看起来合理”就凭空增加一条异常,程序也不会把一段自然语言直接当成可执行的修复命令。模型负责语言表达,程序负责事实和完整性。
本文使用一个包含姓名、年龄和邮箱的内存数据集。生产环境可以把 ROWS 换成 CSV、数据库查询或上游接口的结果,但仍应保留“规则发现、模型说明、程序校验”的分层。
用确定性规则生成异常列表
示例中的规则很简单:姓名不能为空,年龄必须是 0 到 120 之间的整数,邮箱必须包含基本格式;如果一个合法邮箱已经出现过,后续记录标记为重复。每条异常都有稳定的 id,这是后续把模型说明对应回原始问题的关键。
模型不需要看到完整原始数据,只需要看到这样的中间结果:
1 | { |
如果异常来自真实用户数据,还应在发送前移除姓名、邮箱等不必要的个人信息。只把完成任务所需的最少字段传给模型,既降低隐私风险,也让提示词更容易测试。
用 Pydantic 描述模型输出
模型需要返回两部分:整体摘要,以及与异常 id 一一对应的说明列表。ConfigDict(extra="forbid") 用来拒绝未声明的字段;validate_report 则补充检查模型无法从 JSON Schema 中决定的业务约束,例如是否漏掉了某个异常、说明文字是否为空。
示例使用 OpenAI Python SDK 的 Responses API 和 responses.parse。官方的Structured Outputs 指南说明了如何用 Pydantic 定义结构,并通过 text_format 让 SDK 解析为对象。结构合法不代表结论一定正确,所以仍需要程序校验和人工复核。
编写最小可运行程序
先在虚拟环境中安装依赖,并通过环境变量提供密钥和模型名称:
1 | python -m pip install openai pydantic |
把下面代码保存为 anomaly_explainer.py。--self-test 分支只验证本地规则和输出校验,不会调用模型;不带参数时才会发送真实请求。
1 | import json |
这段程序中,detect_issues 完全不依赖模型,因此同样的输入会得到同样的异常列表。Issue.id 是程序生成的稳定编号,模型只能引用它。responses.parse 负责把结构化结果解析成 Report,validate_report 再检查异常数量、编号集合和非空字段。即使模型返回了合法结构,也不能跳过最后一步。
先运行不消耗 API 额度的本地自测:
1 | python anomaly_explainer.py --self-test |
确认本地校验通过后,再运行真实调用:
1 | python anomaly_explainer.py |
真实回答会随模型和输入变化,不能把某次输出写死在程序中。如果 API 返回拒答、截断或解析结果为空,应停止并保留错误信息,不要用一份空报告覆盖已有结果。生产代码还应记录模型名、异常数量、耗时和请求结果状态,但不要记录完整密钥或不必要的原始个人信息。
常见问题
为什么不直接让模型从原始表格中找异常? 让模型同时负责发现和解释,会把格式错误、业务规则和语言判断混在一起。规则明确的检查交给 Python,更容易测试和回归;模型只处理规则难以表达的说明文字。
结构化输出通过了,是否代表报告可信? 不代表。结构化输出主要保证字段形状,不能证明模型对业务影响的判断正确。关键数据仍应由负责人抽查,严重问题可以增加固定规则或人工审批。
为什么只发送异常列表,不发送整张表? 这是最小权限原则的体现。解释异常通常不需要姓名和完整邮箱,减少输入内容可以降低隐私暴露、上下文长度和成本。若必须保留定位信息,也应使用内部行号或脱敏标识。
如何接入真实 CSV 或数据库? 保持 detect_issues 的输入输出边界不变,把 ROWS 替换成读取函数即可。读取阶段负责类型转换和字段映射,规则阶段负责生成异常,模型阶段只接收已经脱敏的异常列表。不要把数据库连接或写操作交给模型。
模型漏掉一条异常怎么办? validate_report 会因为编号集合不一致而失败。程序应把失败记录下来并进入人工复核或有限次数的重试,而不是静默接受部分结果。重试时要控制次数和费用,不能无限请求。
小结
这个综合项目的核心不是让模型替代数据校验,而是把它放在适合的位置:Python 用确定性规则发现异常,模型用自然语言解释异常,Python 再检查输出是否完整。稳定的异常编号、最小化输入、结构化输出和业务层校验,组合起来才能让一次模型调用变成可复核的开发流程。