做后端开发的朋友,十有八九都有过这种经历:需求评审说“测试环境要造一批真实一点的数据”,然后你打开数据库,手写INSERT、写Python脚本、翻Excel复制粘贴,忙活两小时,数据量上去了,业务逻辑一跑,全是漏洞。关联外键对不上、枚举值乱窜、日期格式混搭、内容千篇一律到分不清谁是谁。这种感觉就像拿一堆残缺积木搭房子,看起来能站住,实际一碰就倒。
后来我开始把AI模型引入到这个流程里,把“造数据”变成了让模型根据我定义好的业务模式去“拟真生成”,才发现这件事可以很轻松。核心不是让模型瞎编,而是给它一套带约束的“剧本”,它会输出既真实又合规的数据。这招适合测试开发、后端工程师、数据工程师,对经常需要模拟业务数据的运营同学也有用。今天就把我攒下来的经验、踩过的坑,一次性说清楚。
1. 造数据为什么这么烦:先看清问题再谈AI
1.1 传统造数据的三种方式,各有各的坑
以前在项目里造测试数据,基本就是三条路。第一条是手写SQL,适合小表,两三个字段还能应付,字段一旦超过十个,光编手机号、身份证、地址就觉得脑壳疼,写出来的数据还特别“正经”,姓氏永远是“张伟”,地址永远是“某市某区某路”。第二条是写脚本批量随机生成,这招看似专业,实际很依赖你提前写好全套规则,从字符集到长度、到日期范围、到枚举值权重,任何一条规则漏了,生产出来的数据就会在联调时给你惊喜。第三条是从生产库脱敏复制,数据质量最高,但权限、合规审查流程长,而且只能在项目前期导一次,想按不同场景反复变化基本不可能。
这三条路其实是同一个问题的三种表现:你是在“手写规则”,而不是在“描述业务”。业务场景往往是模糊的,比如“一批订单,包含已支付、已发货、已完成、已取消几种状态,金额有大有小,下单时间集中在最近三个月”,这种表述你很难用代码去精确表达每一个字段的值。AI生成的方式不一样,你可以把这句话直接扔给模型,它会把状态值、金额分布、日期范围、甚至用户昵称的风格一并考虑进去,输出一整套符合直觉的数据。这里的关键变化不是“工具先进了”,而是从“编程式造数”变成了“语义式造数”,你只需要描述业务,剩下的事交给模型推断。
1.2 AI生成数据的核心思路:让模型理解业务,而不是堆规则
我最初用AI造数据的时候,也走偏过。我还在按老思路写正则、写Faker规则,然后让模型临时生成几个字段,效果很差。后来想明白一个事情:AI生成数据的能力不在“随机”,而在“理解”。模型读过海量文本,知道订单、客户、商品、物流这些概念通常长什么样。你做的工作不是教它做算术,而是要把你的业务边界告诉它,比如枚举值有哪些、金额上限是多少、日期范围是什么,剩下的填充工作,它做得比人快得多。
所以正确思路是:把AI生成过程拆成“模式定义 + 提示词 + 校验后处理”三层。模式定义管结构和合法范围,提示词管风格和业务语义,校验后处理保证输出结果能落到数据库里。这就像一个导演定了剧本大纲和演员要求,AI是执行导演,具体的台词和表演它自己发挥,但大纲不可逾越。
我把这套流程标准化以后,造一份1000条电商订单数据的时间,从之前的两小时缩短到十五分钟,包括检查和修数据的时间。质量上,我拿同一套数据去跑报表查询和联调测试,几乎没有因为数据本身的问题中断过。
2. 关键设计:字段、关系和分布,一个都不能少
2.1 字段约束:从“看起来像”到“合法可用”
AI模型默认生成的是“像数据”的值,但不一定“可用”。比如它可能会给你生成一个手机号“138 0013 8000”,看起来很顺眼,但校验的时候发现第二位是3,不符合运营商号段规则;它也可能生成一个订单金额为0元,这通常不符合业务逻辑。所以在提示词里,必须把所有硬性约束写清楚。
我的习惯是给每个字段列一个描述表,不仅仅写字段名,还要写取值范围、格式、示例值、边界要求。举个例子:
- 用户ID:101~999之间的整数,不要带前导零;
- 手机号:必须是11位数字,以1开头,第二位为3-9;
- 订单金额:单位元,2位小数,范围50~20000;
- 订单状态:只能从“已创建、已支付、已发货、已完成、已取消”中选择;
- 订单日期:2025-01-01到2025-12-31之间。
这些约束放到提示词里,模型会明显更听话。但别指望它100%遵守,总会有漏网之鱼。所以要加一道程序化校验,我在后面实操部分会给出代码方案。这里有一个经验:一条约束如果被两个以上的字段共享,一定要明确说明“这两个字段的取值必须来自同一个集合”,否则模型容易各编各的。比如商品ID和商品名称,如果提示词只说了“商品ID为1-100之间的整数,商品名称为中文名称”,模型可能给ID=5配一个“洗衣液”,给ID=6配一个“洗衣液”,看起来合理,但如果你还有一张商品明细表需要关联,就乱套了。正确的做法是预先设计一个“商品词典”,让模型从词典里选。
2.2 表间关联:外键、状态流转和业务一致性
造数据最烦的不是单个字段,而是表间关系。你生成了一张订单表,里面每笔订单有“用户ID”,那你要么先造用户表,要么就必须保证订单表里的用户ID都能在用户表里找到。同样的,订单明细里每个商品ID都要能关联到商品表,订单状态和支付流水之间也要满足时序关系。AI模型本身不会自动维护这种关系,它看着一个订单号的字段,不会自己去翻你的用户表。
所以我在实践里的惯例是:先做主表和基础字典表,再做关联表。基础表包括用户表、商品表、类目表,这些数据很少变化,可以先由模型生成,然后固定下来。订单表、订单明细表这类行为型数据,再基于基础表的实际ID范围去生成。这样在提示词里就不需要写抽象的“用户ID是1-999”,而是直接告诉模型“用户ID从以下列表中选取”,把用户ID列表塞进提示词。如果用户数量有1万个,塞不下提示词怎么办?那就用程序先随机抽一批ID,拼到提示词里,让模型从中选。
状态流转一致性也很容易翻车。比如订单状态有个正常顺序:已创建 -> 已支付 -> 已发货 -> 已完成。模型生成的订单可能直接是“已发货”,但支付时间字段却是空的,或者取消时间早于下单时间。针对这种情况,我通常把生成拆成两步:第一步生成订单主记录,第二步根据主记录里的状态生成对应的时间字段。打个比方,如果状态是“已取消”,就生成“取消时间≥下单时间”,如果状态是“已支付”,就确保有“支付时间>下单时间”。用AI生成时,只需要在提示词里给出一句“请保证各时间字段满足业务顺序”还不够,我会再加一句“如果某条记录不满足,我会扣除你的正确性评分”,这类激励性语言对模型有强约束效果。
2.3 数据分布与样本量:别让测试数据“带节奏”
测试数据最隐秘的坑,就是分布偏离真实。很多时候我们造数据会下意识地均匀生成:10%的已创建、20%的已支付、30%的已发货、40%的已完成,看起来很规整。但真实业务里往往是已完成的订单占80%以上,已取消的占5%,退款订单占2%。如果你拿均匀分布的数据去测促销报表,很可能得到一堆不符合实际的分组结果,最终导致SQL优化方向做错。
AI生成能处理这个问题,但前提是你在提示词里说清楚。比如“订单状态按以下比例分布:已创建10%,已支付15%,已发货20%,已完成45%,已取消10%”。模型会大致遵循这个比例,因为它在训练中学习过“按比例生成”这种指令。不过别指望精确到小数点,我的做法是生成完再在SQL里跑一次count统计,偏差超过两个百分点就重新生成,或者在程序里做配额控制,根据每个状态的目标数量来计算实际需要生成多少条,再逐批生成、补齐差额。
同时要注意样本量的设计。如果你是做功能测试,每个状态各10条可能就够;但如果要压测,就需要几万条。AI生成大量数据的效率比传统脚本低,因为它一句一句话地生成,比较慢。所以我的建议是:AI生成用来产“多样性样本”,再用程序复制放大。模型生成200条具有代表性的种子数据,然后写一个小脚本对字段值做细微扰动,比如随机偏移日期、加个小数值、修改昵称后缀,轻松扩到两万条。这既保留了AI理解业务的优势,又解决了速度问题。
3. 实操案例:一条提示词生成一套可入库的电商订单测试数据
3.1 定义目标数据模式
为了让这套方法便于理解,我把一个模拟项目的完整过程放出来。场景就是某电商系统的后端测试,需要生成一个“订单管理”模块的测试数据集,包含用户表、商品表、订单表、订单明细表。表的字段如下示例(已隐去非关键信息):
用户表字段:用户ID、昵称、手机号、注册日期、用户等级
商品表字段:商品ID、商品名称、类目、价格、库存
订单表字段:订单ID、用户ID、订单金额、订单状态、下单时间、支付时间、发货时间、收货地址
订单明细表字段:明细ID、订单ID、商品ID、商品数量、商品单价、小计金额
关系很明确:用户表是主表,商品表是基础表,订单表引用用户ID,订单明细表引用订单ID和商品ID。我们要造的数据量是:用户表80条,商品表120条,订单表1000条,订单明细表大约1800条。
需求一句话总结:订单状态按比例分布,金额和商品价格对应,时间线合理,地址多数在国内主要城市。接下来所有生成工作,都要围绕这个目标。
3.2 编写生成提示词模板
如果你用的是本地部署的大模型,或者任意一种兼容OpenAI接口的大模型服务,都可以按这个思路构造提示词。我的模板通常包含四个部分:
- 角色与任务设定
- 字段定义与约束
- 数据规范与样本示例
- 输出格式要求(必须是JSON数组)
核心部分看起来像这样:
你是一名数据工程师,请根据以下要求生成用户表测试数据。 要求: 1. 字段:用户ID(整数,范围101-180)、昵称(中文昵称,风格如"清风拂柳")、手机号(11位,1开头,第二位3-9)、注册日期(2024-01-01到2025-06-30,格式YYYY-MM-DD)、用户等级(黄金会员、白银会员、普通用户,其中普通用户占比约50%)。 2. 用户ID必须从101开始连续递增,不要重复。 3. 输出为JSON数组,每个元素是一个对象,键名按照字段名输出。 4. 只输出JSON,不要输出任何解释文字。如果是生成订单表,提示词里要加入关联条件和状态比例:
用户ID从[101,102,103,104,105,...]中随机选择(列表由程序拼接) 订单状态比例:已创建10%,已支付15%,已发货20%,已完成45%,已取消10% 已完成和已取消的订单需要分别有完成时间和取消时间,取消时间必须大于下单时间 收货地址从"北京市朝阳区xxx、上海市浦东新区xxx、深圳市南山区xxx..."中随机选择把这样的提示词发送给模型,返回结果通常是比较规范的JSON数组。但模型偶尔会把订单金额写成“100.0”,小数点后只有一位,或者把枚举值“已发货”写成“已发运”,这都需要在下一步处理。
3.3 程序化调用模型并做结构校验
我不会直接把模型生成的结果写入数据库,而是会让程序先做一次校验。这一步非常关键,它能拦截掉90%的脏数据。我用Python写了一段通用校验代码,使用了pydantic库。先定义模型结构:
from pydantic import BaseModel, Field from typing import Optional, Literal class OrderItem(BaseModel): item_id: int order_id: int product_id: int quantity: int = Field(ge=1, le=10) unit_price: float = Field(ge=5, le=1000) subtotal: float class Order(BaseModel): order_id: int user_id: int amount: float = Field(ge=50, le=20000) status: Literal["已创建", "已支付", "已发货", "已完成", "已取消"] created_at: str paid_at: Optional[str] = None shipped_at: Optional[str] = None address: str然后写一个函数把模型返回的JSON字符串解析成Order对象,解析失败或校验失败的记录直接丢弃并重新生成:
from pydantic import ValidationError def parse_orders(raw_json): data = json.loads(raw_json) orders = [] errors = [] for item in data: try: order = Order(**item) orders.append(order) except ValidationError as e: errors.append((item, e.errors())) return orders, errors这里的核心思想是:正向约束交给模型,反向约束交给代码。正向约束就是提示词里说的话,让模型尽量生成符合要求的候选;反向约束就是代码里的强校验,保证任何不符合格式的数据都会被拦在门外。两类约束双保险,生成成功率能到95%以上,剩下的5%通过一次重试基本就能补齐。
3.4 批量生成、插入与验证
有了校验函数,整个批量流程就很简单了。我用一个循环控制生成批次,每批生成20条,达到目标数量后停止。原因是模型输出长度有限,如果一次生成太多,结果会被截断,最后一行总是少个括号,还得手工补,所以小批次更稳。
批次生成后,把所有通过校验的记录累加进列表。如果某批次失败率太高,比如一条都没通过,我会把失败的样本和错误信息发回模型,在下一轮提示词里加一句“上一次生成的结果里,订单金额出现了负数,请避免”,模型下一轮大概率就会修正。这是一种简单的“生成-校验-反馈”闭环,实际体验下来比一次生成千条再全部清洗要快得多。
数据全部生成后,还没有结束,需要做一次数据库层面的验证。我把数据分别插入用户表、商品表、订单表、订单明细表,然后执行几个追查SQL:
- 订单表里的user_id是否都能关联到用户表?
- 订单明细表里的order_id是否都在订单表里?product_id是否都在商品表里?
- 每个订单的订单金额是否等于订单明细小计金额之和?
第三项是一致性最强的检查,也是AI最容易出错的地方。模型在生成订单表和明细表时,往往不会精确到让订单金额等于明细小计之和。这时候就需要后处理。我的做法是:先让模型生成订单表和商品表,再让模型或者简单程序随机生成明细行数、数量、单价,计算出小计总金额,然后回填到订单表的订单金额字段。这样从业务逻辑上就完全一致了。这里要记住一个教训:AI生成数据时,别把它当成计算器,涉及需要精确相等的数值,让人工用程序算完再填进去,比让模型硬算靠谱得多。
4. 常见问题与排查技巧实录
4.1 生成结果总是缺字段、多字段怎么办
模型输出不一定是结构化的,偶尔会在JSON里多出一个“remark”字段,或者漏掉“paid_at”。解决方法是两层的。第一层,在提示词最后反复强调“只根据我提供的字段输出,不要增加任何新字段,也不要省略任何字段。”对大模型来说,明确列出字段清单比只说“按格式输出”有效得多。第二层,用pydantic校验拦截,缺字段就报错,多字段可以在解析时额外设置,也可以在提示词里给一个“字段枚举表”让它严格参考。
如果发现模型总是缺某个字段,往往是这个字段的业务含义不明确。比如“支付时间”对状态为“已创建”的订单本来就不应该有,模型认为是可选字段就经常省略。我的经验是,把可空字段在提示词里定义为“如果没有则输出null而不是省略”,这样程序转换起来更干净。
4.2 数据看起来真实,但业务规则一跑就崩
这种情况最常见的原因就是,我没在提示词里写清楚“业务规则”。比如订单都有优惠券字段,但优惠券金额不能超过订单金额;再比如退款单的退款金额不能大于订单实付金额。模型对隐性业务规则一无所知,你不写它就大概率违反。
后来我养成一个习惯:在写提示词之前,先把要造数据的规则一条条列出来,尤其是那些“在业务代码里有断言”的规则。列完之后挑出三条最核心的规则写进提示词,然后在程序校验里实现完整规则。举例来说:如果一个订单状态是“已完成”,那它必然有支付时间、发货时间、完成时间,且时间顺序是下单<支付<发货<完成。针对这种规则,我写了专门的校验函数,挨个判断,不满足自动重生成该条记录。这样做以后,联调时因为测试数据质量问题被打回来的次数几乎为零。
4.3 生成速度太慢,怎么提速
AI生成速度受限于模型推理速度和网络传输。我试过一次性生成500条,模型等了五分钟,然后输出被截断了,白白浪费时间。改成每次20条的批次后,每批大约10到20秒,整体速度上反而更快。提速技巧有以下几点:
- 模型输出token上限设高一些,但也不要一次索要太多,避免截断;
- 使用并发请求,开3到5个线程同时请求不同的批次,再用队列汇总结果;
- 把“生成+校验”流程做成流水线,第一个批次在校验时,第二个批次已经在请求了;
- 如果只是需要多样本,用小批量的种子数据配合程序扰动扩样,速度提升非常明显。
我最常用的方案就是前面说的种子数据+扰动放大。先用AI生成150条业务逻辑完全正确的订单,然后用一个脚本随机改用户ID、日期偏移、金额加一点小数、地址换几个城市,快速扩到5000条。扰动幅度控制住,比如日期最多偏移15天、金额最多上下浮动5%,业务规则就不会被破坏。
4.4 敏感数据混淆与脱敏处理
AI生成测试数据最大的优势之一,就是天然不会直接暴露真实用户信息。但如果你把真实业务场景描述或真实字典喂给模型,模型可能会“记住”并输出接近真实的值。所以在生成数据时,我坚持所有基础字段使用假数据:手机号用不可15倍数、不连续重复的虚拟号段;身份证如果不需要真实校验就直接不放,或者生成“18位满足基本规则但前6位是虚拟地区码”的数据;地址只生成到“市/区”一级,不写门牌号。昵称不要跟真实员工或客户的用户名相似,稍微改几个字或者随机组合。
脱敏这个环节不能完全依赖AI。我会在生成完成后做一轮扫描,用正则和关键词清单查掉可能的敏感值,比如银行卡号、手机号、身份证前几位,凡是匹配到就替换成假的。扫描之后再过一遍程序化校验,才落库。整个过程下来,测试环境的数据既真实可用,又不带敏感风险,这也是为什么很多团队愿意把造数从生产环境抽离,完全交给AI管线的原因。
最后再分享一个小技巧:给AI生成数据时,尽量在同一批数据里包含一些“边缘情况”。比如在用户表中故意生成几条注册日期为空或密码强度弱的数据,在订单表中生成几条金额刚好等于50元的下限值和20000元的上限值,这些东西写进提示词后,测试人员能直接用它们覆盖边界测试用例,比自己手工造这种极端值快多了。我做了几次之后,测试同事会主动来问我“这轮数据有没有带边界样本”,现在这已经成了我们测试环境里的标配操作。