从Prompt到产品:一个AI测试用例生成Agent的完整开发实录
做过测试的都知道,写用例是整个流程里最耗体力的环节。一个中等复杂度的功能模块,手工写完 200 条用例,快的半天,慢的一天半。而且写完还不一定靠谱 —— 漏场景、漏边界值、漏异常路径,review 的时候才发现 "这个状态没测"。
AI 当然该干这个活。市面上也确实有工具号称能 "AI 生成测试用例",但用下来发现一个共同问题:写得像,但不靠谱。我想要的不是一个 "看起来像测试用例的文本生成器",而是一个可量化、可校验、覆盖率 100% 的测试用例生产线。
一、为什么做这个 Agent
1.1 痛点:测试用例是体力活
测试用例编写的痛点已经是行业共识:
- 效率低:200 条用例人工编写需要 0.5-1.5 天
- 质量不稳定:依赖测试人员经验,漏测率平均达 35%(来源:《2024 软件测试行业白皮书》)
- 维护成本高:需求变更时,用例更新的工作量相当于重写 60%
- 覆盖率难量化:大多靠 "我觉得测够了",没有客观衡量标准
市面上的 AI 生成工具普遍存在三个问题:输出不可控、格式不稳定、数字不可信。说白了,就是看起来像人写的,但实际不能直接用。
1.2 目标:可量化、可校验、100% 覆盖
我给这个 Agent 定了三个核心指标,缺一不可:
| 要求 | 含义 |
|---|---|
| 可量化 | 每条用例归属哪个维度、覆盖了哪个元素,一目了然 |
| 可校验 | 覆盖率是代码算出来的,不是 AI 编出来的 |
| 100% 覆盖 | 基于 ISO/IEC/IEEE 29119-4 的 9 维度模型,每个元素都有对应用例 |
这意味着不能用 "让 LLM 自由发挥" 的方式 —— 你得控制它、约束它,然后用代码验证它。
二、架构演进:三个版本的踩坑之路
2.1 版本一:纯 Bot Prompt
思路:把 ISO 29119 标准、覆盖方法论、用例编写规范全写进 Bot Prompt,让 LLM 一次搞定。
plaintext
┌─────────────────────────────────────────────┐
│ 纯Bot Prompt 架构 │
│ │
│ 用户需求 ──→ [LLM Bot] ──→ 测试用例+覆盖率 │
│ (全写进Prompt) │
└─────────────────────────────────────────────┘
听起来很美,实际跑起来三个致命问题:
| 问题 | 具体表现 |
|---|---|
| 输出不可控 | 同一需求跑三次,三次字段列表、用例数量、分类方式都不一样 |
| 格式不稳定 | 有时 Markdown 表格、有时纯文本、有时中途截断(token 超限) |
| 数字不可信 | LLM 告诉你 "覆盖率 85%",实际可能是 60% 也可能是 110%—— 它是编的 |
结论:LLM 是生成器,不是校验器。让它写用例可以,让它算覆盖率 —— 算了。
2.2 版本二:Bot + 工作流
既然 LLM 不可信,就把 "可信" 的部分交给代码。思路:LLM 只负责生成,代码做校验。5 节点工作流:
plaintext
┌──────────────────────────────────────────────────────────────┐
│ 版本二:5节点工作流架构 │
│ │
│ 用户需求 │
│ ↓ │
│ [Node1] LLM需求结构化提取 │
│ ↓ JSON │
│ [Node2] 代码隐含补全 (46ms) │
│ ↓ │
│ [Node3] 代码覆盖元素推导 (20ms) │
│ ↓ │
│ [Node4a] LLM主用例生成 ──┐ │
│ [Node4b] LLM补全用例生成 ──┤── 并行 │
│ ↓ │
│ [Node5] 代码校验+合并+统计 (10ms) │
│ ↓ │
│ 12个变量输出 → Bot展示 │
└──────────────────────────────────────────────────────────────┘
解决了 V1 大部分问题:覆盖率代码算、格式代码控、数字代码统计。但新问题来了 —— 数据一致性崩了:
plaintext
┌─────────────────────────────────────────────────┐
│ 版本二的数据流问题 │
│ │
│ Node5输出 ──→ 12个变量 ──→ Bot读取展示 │
│ ↑ ↑ │
│ │ │ │
│ 当次有效 后续轮拿不到! │
│ Bot自己编数据 │
│ │
│ 结果:引导语说75%,详情页说68% → 信任归零 │
└─────────────────────────────────────────────────┘
| 问题 | 原因 |
|---|---|
| 变量后续轮不可访问 | Coze 工作流变量只在当次调用有效 |
| Bot 自行编造数据 | 拿不到变量,LLM 就 "推理" 一套数据出来 |
| 引导语和详情页数字打架 | 两个数据源:Node5 算一套,Bot 编一套 |
结论:生成质量 OK 了,但数据一致性崩了。用户看到两个不一样的数字,信任归零。
2.3 版本三:Bot + 工作流 + 用户变量
数据一致性崩的根因是两个数据源。根治必须统一到一个数据源。思路:用户变量持久化,workflow 结果写入变量,Bot 只从变量读,严禁自造。
plaintext
┌──────────────────────────────────────────────────────────────────┐
│ 版本三:单变量持久化架构(最终版) │
│ │
│ 用户需求 │
│ ↓ │
│ [Node1] LLM需求结构化提取 │
│ ↓ JSON │
│ [Node2] 代码隐含补全 (46ms) │
│ ↓ │
│ [Node3] 代码覆盖元素推导 (20ms) │
│ ↓ │
│ [Node4a] LLM主用例生成 ──┐ │
│ [Node4b] LLM补全用例生成 ──┤── 并行 │
│ ↓ │
│ [Node5] 代码校验+合并+统计 (10ms) │
│ ↓ │
│ ┌─────────────────────────────────┐ │
│ │ all_data (单变量, 用户变量持久化) │ │
│ │ │ │
│ │ @@@COVERAGE@@@ 覆盖率报告 │ │
│ │ @@@D1@@@ 输入字段详情 │ │
│ │ @@@D2@@@ 业务规则详情 │ │
│ │ ... │ │
│ │ @@@D9@@@ 非功能详情 │ │
│ │ @@@GROUP1@@@ [1]功能+接口 │ │
│ │ @@@GROUP2@@@ [2]异常+安全+性能│ │
│ └─────────────────────────────────┘ │
│ ↑ 写入 ↓ 读取 │
│ Node5代码 Bot零计算只抄 │
│ │
│ ┌──────────────────────────────┐ │
│ │ 三层质量保障 │ │
│ │ │ │
│ │ ① LLM生成 → 结构化提取+用例 │ │
│ │ ② 代码校验 → 覆盖率+统计 │ │
│ │ ③ 人工校验 → 未覆盖元素导航 │ │
│ └──────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
踩了最隐蔽的坑 —— 变量写入静默截断,后面详细讲。最终验证通过:登录、注册、支付三个场景,覆盖率报告和详情页数字 100% 一致。
三、8 个关键问题与解决过程
| # | 问题 | 严重度 | 根因 |
|---|---|---|---|
| 1 | LLM 输出截断与超时 | 🔴高 | 单节点输出量 / 执行时间超限 |
| 2 | 列索引偏移 cols [6]→cols [7] | 🔴高 | Markdown 表格 split 后空列偏移 |
| 3 | Bot 自行编造数据 | 🔴高 | Prompt 未禁止 + 变量不可用 |
| 4 | 数据不一致(双数据源打架) | 🔴高 | 架构层面双数据源 |
| 5 | 工作流变量后续轮不可访问 | 🔴高 | Coze 平台限制 |
| 6 | 用户变量写入静默截断 | 🔴极高 | reply_length 影响赋值上限,isSuccess 仍返 true |
| 7 | 变量修改时间误导排查方向 | 🟡中 | 平台元信息与业务操作无关 |
| 8 | 变量赋值 {{}} 模板字符串污染 | 🔴高 | 部分变量未输出导致残留 |
问题 1:LLM 输出截断与超时
现象:Node1 偶发 117 分钟超时(正常 55 秒);Bot 端渲染 5-8 分钟,同会话第二轮 12 分钟 +。定位过程:怀疑 all_data 太大 → 验证:第一轮 all_data 已灌入,为何第二轮更慢?↓真正的膨胀源:Bot 每轮强制携带完整对话历史↓第一轮输出的覆盖率报告 + D 维度表格全留在上下文里第二轮要读全部历史 → token 逐轮膨胀 → 渲染逐轮变慢
解决方案:
| 措施 | 效果 |
|---|---|
| 上下文轮数 3→1 | Bot 不依赖历史,切断膨胀链 |
| Temperature=0 | 减少采样开销 |
| 开场白设预期 | 用户心理预期管理 |
| 引导开新话题 | 避免同会话累积 |
💡 经验:LLM Agent 的性能瓶颈往往不在 LLM 本身,而在平台的上下文管理机制。先搞清楚瓶颈在哪,再决定优化方向。
问题 2:列索引偏移 ——cols [6]→cols [7]
现象:Node5 解析 Node4 输出的 Markdown 表格时,列索引错位。定位过程:
python
# 预期:cols[6] 取第7列
# 实际:split后多了空字符串,索引全偏移+1
# 原始行
"| TC-001 | 提交订单 | 正常 | 成功 | 200 | OK | 无 |"
# split("|") 后
['', ' TC-001 ', ' 提交订单 ', ' 正常 ', ' 成功 ', ' 200 ', ' OK ', ' 无 ', '']
# ↑ 空串!导致所有索引偏移
根因:行首行尾的 | 在 split 后产生空字符串;单元格内容含 | 也会产生额外列。
解决方案:
python
# 修复:split前strip + 过滤空串
cols = [c.strip() for c in line.strip('|').split('|') if c.strip()]
💡 经验:Markdown 表格解析是字符串处理的经典陷阱,永远不要假设 split 后的索引是稳定的。
问题 3:Bot 自行编造数据
现象:用户查询 D1 维度详情,Bot 输出了完整的元素列表和覆盖率数字,但和 workflow 实际结果完全不一致。定位过程:Bot 拿不到 workflow 变量数据(平台限制:变量只当次有效)↓Bot 用 LLM 的 "推理能力" 编了一套看起来合理的数据↓数字看起来对,实际是编的 → 信任崩塌
解决方案:Bot Prompt 铁律(加粗 + 反复强调):
⚠️ 严禁自行编造任何数据⚠️ 所有数据必须从用户变量读取⚠️ 如果变量为空或读取失败,输出异常提示,不得用 LLM 生成的内容替代
💡 经验:LLM 的 "推理能力" 在数据展示场景是 bug 不是 feature。你希望它说 "我拿不到",而不是编一套看起来很对的数据。
问题 4:数据不一致 —— 两个数据源打架
现象:引导语显示 "D1 覆盖率 75%",点进 D1 详情页变成 68%。同一份报告,两个数字。定位过程:引导语数据 ← Node5 代码计算,写入变量详情页数据 ← Bot 从历史对话 "回忆" 并重新格式化↓两个数据源 → 两套计算逻辑 → 两套数字 → 打架
解决方案:统一为单一数据源。
plaintext
Before(双数据源):
Node5计算 → 变量 → Bot展示(引导语)
→ Bot回忆 → 重新展示(详情页) ← 第二个数据源!
After(单一数据源):
Node5计算 → all_data(用户变量) → Bot只抄,零计算零推理
所有数字由Node5代码算,Bot从all_data对应@@@段原样读取
💡 经验:数据一致性不是 "写对了就行" 的问题,是架构问题。只要存在两个数据源,早晚打架。从一开始就设计成单一数据源。
问题 5:工作流变量后续轮不可访问
现象:用户第一次输入需求,workflow 正常,结果展示正常。同一对话里输入 "查看 D1",Bot 拿不到上一次 workflow 的输出。定位过程:Coze 工作流变量只在当次调用生命周期内有效,这是平台限制。
解决方案:
plaintext
Before:工作流变量(当次有效)
用户输入需求 → workflow运行 → 变量有值 → 展示OK
用户查询D1 → 无workflow调用 → 变量为空 → Bot编数据
After:用户变量(持久化)
用户输入需求 → workflow运行 → all_data写入用户变量 → 展示OK
用户查询D1 → 不调workflow → 从用户变量读取all_data → Bot只抄对应段
💡 经验:平台限制不是你能改的,但架构可以绕。关键是在设计之初就想清楚数据生命周期。
问题 6:用户变量写入静默截断 —— 最隐蔽的坑 🔴
现象:变量赋值节点返回 isSuccess: true,变量修改时间也更新了,但实际数据被截断 ——all_data 前半段正常,后半段为空。定位过程:以为是 Node5 代码 bug → 本地运行输出完整 → 排除↓怀疑变量大小限制 → Coze 文档没提上限↓最终定位:Bot 的 reply_length 参数影响变量赋值写入上限默认值 ≈ 12000 字符,而 all_data 经常超过 30KB↓最坑的:截断后 isSuccess 仍返回 true!变量修改时间也正常更新!你完全不知道数据丢了!
解决方案:
| 措施 | 说明 |
|---|---|
| Bot reply_length 调至 50000 | 确保 30KB + 的 all_data 完整写入 |
| 数据完整性校验 | 写入后检查 all_data 是否包含所有 @@@标记 |
| 铁律 | isSuccess 和修改时间都不可靠,只信业务数据本身 |
数据丢失检测逻辑(Node5 代码末尾):
javascript
if (!all_data.includes("@@@GROUP2@@@")) {
return "⚠️ 数据加载异常,请重新输入需求描述";
}
💡 经验:静默失败比显式失败可怕 100 倍。 显式失败你马上知道有问题,静默失败你可能永远不知道数据丢了。任何涉及持久化写入的操作,都必须有独立的完整性校验机制。
问题 7:变量修改时间误导排查方向
现象:查看变量修改时间,时间更新了,确信数据已写入。但实际内容是旧的 —— 变量没被更新,修改时间却被平台刷新了。定位过程:平台变量的元信息更新和数据写入是异步的,某些内部操作会刷新元信息但不改变业务数据。
解决方案:完全不依赖修改时间做判断,用数据内容本身做校验(如检查 all_data 是否包含预期的 @@@标记)。
💡 经验:平台提供的元信息(修改时间、成功标识)是运维层面的,不是业务层面的。业务正确性必须用业务手段验证。
问题 8:变量赋值 {{}} 模板字符串污染
现象:Bot 输出中出现原始的 {{d1_out}} 未解析变量名,或数字位置出现 NaN。定位过程:12 个独立变量 → Bot Prompt 用 {{d1_out}} 等占位↓某个变量 Node5 未输出 → Coze 不解析 → {{d1_out}} 原样留在文本中↓更严重:Bot Prompt 写 {{xxx}} 示例 → Coze 当成变量语法报非法变量错误
解决方案:
plaintext
Before(12变量):
d1_out, d2_out, ..., d9_out, group1_out, group2_out
→ 12条映射,12个可能失败的点
→ 部分缺失 → {{}}残留 → 污染输出
After(1变量):
all_data(单变量,@@@标记分段)
→ 1条映射,0个残留可能
→ Bot按@@@标记读取对应段落,不需要变量名引用
💡 经验:变量越多,出问题的概率越高。N 个变量意味着 N 个可能失败的映射。合并为 1 个变量看似粗暴,但在当前场景下是最稳健的方案。
四、最终架构与验证结果
4.1 最终架构总览
plaintext
┌─────────────────────────────────────────────────────────────────────┐
│ 最终架构:5节点工作流 + 单变量持久化 │
│ │
│ 用户输入需求 │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ [Node1] LLM需求结构化提取(豆包·2.0·mini, ~55s) │ │
│ │ 输入:用户需求文本 │ │
│ │ 输出:9维度结构化JSON │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ JSON │
│ ┌──────────────────────────────────────────────────┐ │
│ │ [Node2] 代码隐含补全 (46ms) │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ [Node3] 代码覆盖元素推导 (20ms) │ │
│ │ 基于JSON推导9维度覆盖元素清单 │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ [Node4a] LLM主用例生成│ │ [Node4b] LLM补全用例 │ ← 并行 │
│ │ (DeepSeek-V3.2,1.24m)│ │ (DeepSeek-V3.2,1.34m)│ │
│ └──────────────────────┘ └──────────────────────┘ │
│ ↓ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ [Node5] 代码校验+合并+统计 (10ms) │ │
│ │ ① 逐条校验用例是否覆盖对应元素 │ │
│ │ ② 计算各维度覆盖率 │ │
│ │ ③ 生成覆盖率报告+维度详情+分组用例 │ │
│ │ ④ 拼装all_data写入用户变量 │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ all_data(用户变量持久化) │ │
│ │ │ │
│ │ @@@COVERAGE@@@ 综合覆盖率+各维度覆盖率+未覆盖元素 │ │
│ │ @@@D1@@@~@@@D9@@@ 9个维度的覆盖元素详情 │ │
│ │ @@@GROUP1@@@ [1]功能测试+接口测试用例 │ │
│ │ @@@GROUP2@@@ [2]异常+安全+性能测试用例 │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Bot(豆包·2.0·lite) │ │
│ │ - 零计算零推理,只从all_data抄对应@@@段 │ │
│ │ - 首轮输出覆盖率报告(@@@COVERAGE@@@) │ │
│ │ - 用户查D1→输出@@@D1@@@段 │ │
│ │ - 用户查1→输出@@@GROUP1@@@段 │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
4.2 9 维度覆盖模型
| 维度 | 提取数据来源 | 用例类型 | 归属分组 |
|---|---|---|---|
| D1 输入字段覆盖 | fields | 功能测试 | [1] |
| D2 业务规则覆盖 | rules | 功能测试 | [1] |
| D3 状态转换覆盖 | states | 功能测试 | [1] |
| D4 异常场景覆盖 | key_operations | 异常测试 | [2] |
| D5 安全缺陷模式覆盖 | security | 安全测试 | [2] |
| D6 输出覆盖 | apis.response_fields | 功能测试 | [1] |
| D7 接口交互覆盖 | apis.error_codes | 接口测试 | [1] |
| D8 组合覆盖 | fields×rules 交叉 | 功能测试 | [1] |
| D9 非功能覆盖 | key_scenarios | 性能测试 | [2] |
4.3 用例类型 5 分类
| 用例类型 | 归属分组 | 说明 |
|---|---|---|
| 功能测试 | [1] | 覆盖 D1/D2/D3/D6/D8 |
| 接口测试 | [1] | 覆盖 D7 |
| 异常测试 | [2] | 覆盖 D4 |
| 安全测试 | [2] | 覆盖 D5 |
| 性能测试 | [2] | 覆盖 D9 |
4.4 四道质量关卡
plaintext
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ ① LLM生成 │───→│ ② 兜底补充 │───→│ ③ 代码校验 │───→│ ④ 人工校验 │
│ │ │ │ │ │ │ │
│ Node1提取 │ │ Node4b对 │ │ Node5逐条 │ │ 覆盖率报告 │
│ Node4生成 │ │ 未覆盖元素 │ │ 校验覆盖率 │ │ 标注未覆盖 │
│ 9维度结构 │ │ 补充生成 │ │ 计算真实值 │ │ 引导人工补 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
4.5 后续规划
| 版本 | 方向 | 说明 |
|---|---|---|
| v6.1 | 链接读取插件 | 支持在线文档链接输入 |
| v6.2 | 性能深度优化 | 按需注入、流式输出 |
| v6.3 | 按需生成 4 场景模式 | 从 "全量生成" 进化到 "按需生成" |
| 长期 | 垂直行业扩展 | 金融、医疗、电商等定制化规则 |
五、写给同路人
5.1 AI Agent 能为测试做什么
| # | 能力 | 说明 |
|---|---|---|
| 1 | 需求结构化提取 | 把自然语言需求拆成 9 维度结构 —— 后续一切的基础 |
| 2 | 覆盖元素推导 | 基于 ISO 29119 标准,自动推导每个维度该测什么 |
| 3 | 批量用例生成 | 200 + 条结构化用例,3 分钟出结果,替代 1 天手工 |
| 4 | 覆盖率量化 | 每个维度覆盖率多少、哪些元素没覆盖 —— 数字说话 |
| 5 | 人工校验导航 | 标注未覆盖元素,引导人工补充 —— 人机协同 |
5.2 适合谁用
・测试工程师:日常写用例太耗时间,想要靠谱的辅助工具
・测试管理者:需要量化团队测试覆盖情况,不想依赖 "我觉得测够了"
・创业者 / 独立开发者:一个人搞定测试,AI 就是你的测试搭档
六、反思与建议
给 Agent 开发者 3 条建议
1️⃣ LLM 是生成器,不是校验器
不要让 LLM 做它不擅长的事。生成内容是强项,校验、统计、格式化 —— 交给代码。你不会让一个诗人去当会计,同理。
2️⃣ 适配平台,而不是对抗平台
每个 Agent 平台都有自己的限制(变量生命周期、赋值上限、上下文管理)。不要试图绕过这些限制,而是把架构适配到平台的能力边界内:・变量不可跨轮 → 用用户变量持久化・赋值可能截断 → 加完整性校验・上下文会膨胀 → 控制轮数
3️⃣ 警惕静默失败
这是最危险的一类 bug。isSuccess: true 不代表真的成功了,修改时间更新不代表数据真的写了。任何涉及持久化写入的操作,都必须有独立的、基于业务数据的完整性校验机制。 否则你可能在错误的基础上越走越远。
给测试行业 1 个思考
AI 不是来替代测试工程师的,它是来改变测试工作重心的。
- Before:80% 时间写用例 + 20% 时间思考和设计
- After :20% 时间审核 AI 生成 + 80% 时间思考和设计
覆盖率报告告诉你 "哪些没覆盖",人工校验决定 "哪些该补充"。AI 是执行者,人是决策者。 这才是人机协同的正确姿势。不是 AI 替代你,是会用 AI 的人替代不会用的人。
试用与合作
- 👉 TestPilot 测试用例生成智能体:立即试用
- 💼 全套 Prompt、工作流代码可商业授权,也欢迎技术交流 —— 私信或评论区留言均可



觉得有用的话,点赞收藏,评论区告诉我你的测试场景,我来看看 Agent 能不能帮到你。遇到问题也欢迎吐槽,这个 Agent 还在持续迭代,你的反馈就是下一个版本的方向。
📢 今日话题:你们团队在 AI 测试用例生成方面遇到过什么坑?欢迎在评论区留言!
更多推荐



所有评论(0)