
1. 项目概述1.1 为什么我会动手写这个平台做测试开发这几年我一直被一个问题反复折磨测试用例怎么才能写得又快又全业务迭代频繁新接口每两周就要上线一批测试同学只能加班补用例。手写用例有几个绕不开的痛点回归用例数量少、边界值覆盖不全、异常场景靠经验、接口变更后存量用例直接失效。这些问题不是靠增加人力就能解决的。2023年下半年GTP类大语言模型的能力逐渐成熟之后我开始琢磨一个思路如果用大模型来辅助生成测试用例能不能把覆盖面拉上去同时把人力成本降下来当时团队里正好没有现成工具支撑这件事我决定自己动手搭一个——这就是AI-TestHub的由来。这个平台的目标非常具体输入一份接口文档或者需求描述自动输出结构化的测试用例包括正常流程、边界值、异常输入、鉴权校验等维度并且能直接导出成项目里通用测试管理平台的格式。开发过程中我对LLM的调用策略、用例模板的抽象方式、prompt的组织逻辑做了大量实验和重构这里记录的是一套经过真实业务场景验证的可行方案。1.2 整体亮点和适合谁参考做这个平台有几个让我自己比较满意的点不直接依赖大模型的输出结果而是用规则层做兜底校验不搞复杂的微调全部基于提示词工程实现生成的用例通过模板引擎渲染成多种格式输出。整个架构不算复杂但每个模块都有明确的职责边界。如果你是测试开发工程师、QA效能团队的一员或者正在做类似的LLM落地项目这篇文章里关于设计取舍、踩坑记录、参数调优的部分应该能帮你少走不少弯路。代码量不大核心逻辑全部跑通在几百行以内属于一个中小团队完全可以内部复现的项目。2. 整体设计思路与方案选型2.1 先解决三个根子问题动手写第一行代码之前我把需求拆成了三个层面。第一个层面是生成质量。LLM在自然语言理解上是强项但在工程严谨性上天生有短板——同一个接口你让它生成用例它可能给你写翻车字段名猜错、枚举值编造、断言逻辑不自洽这些问题在纯靠模型输出的场景下几乎无法避免。所以平台架构上不能把LLM摆在唯一权威的位置必须有一个规则引擎在它背后做校准。第二个层面是集成体验。测试用例生成出来不是给人看的是要跑起来的。那就要先想清楚用例最终会流向哪些工具可能是接口测试平台也可能是自动化测试代码库还有可能是手工测试用例管理平台。如果只输出纯文本每个接入方都要二次加工那使用成本高到没人愿意用。第三个层面是可解释性。测试用例是要被人Review的如果LLM给出一堆用例测试同学不知道怎么来的、覆盖了哪些规则、为什么这么设计那信任感就很难建立。我的做法是让生成过程带完整的标签体系和来源追踪每条用例都能回溯到对应的需求点或者接口字段。这三点确认之后技术方案的大方向基本就定了LLM生成核心内容规则层做约束和校验模板层做格式适配标签体系做解释和统计。2.2 技术选型的具体思考聊几个关键选型的决策过程。大模型这块我没有做微调直接用现成的通用大模型API。原因很简单——微调需要一批高质量的领域数据而我们当时最大的痛点是连高质量用例都缺这就成了先有鸡还是先有蛋的问题。通用大模型配合精心设计的Prompt和少量示例在测试用例生成场景已经能拿到可用结果。等积累了一定量的优质生成样本后再考虑用这部分数据做微调这是更稳妥的路径。模板渲染引擎选了Jinja2而不是直接写死字符串拼接。测试用例的形态非常多接口测试用例、UI操作用例、数据校验用例结构差异巨大。Jinja2支持复杂的条件判断、循环嵌套和过滤器链可以把用例模板做成类似接口请求模板、数据库校验模板、页面操作模板等多种形态切换起来成本很低。任务调度这块用了Celery加Redis。因为用例生成的耗时不固定简单接口一两秒就能生成完复杂系统级场景可能要跑十几分钟。这种耗时任务必须做异步化不能阻塞Web请求链路。前端提交任务后轮询结果任务队列里按优先级调度这套模式在内部团队使用场景下非常稳定。Embedding模型选择了通用的开源向量模型配合语义检索来召回相似的存量用例。这个功能解决的问题非常实际——新需求进来时先看看历史上有没有被测过的类似接口把旧用例作为参考样例喂给LLM比让LLM凭空臆测要靠谱得多。2.3 架构为什么不走微服务对比了微服务和单体应用之后我选了单体架构。平台的核心用户量就是团队内部几十人单机部署完全够用微服务带来的部署运维复杂度反而会拖慢开发进度。但单体不是大泥球我在代码层面做了严格的分层生成服务、模板服务、检索服务、执行服务通过内部接口调用保留未来拆分的可能性。架构上有一个非常重要的设计——LLM调用层是独立隔离的。所有对外部大模型API的调用都经过统一网关封装支持多模型源切换、超时控制、重试机制、成本统计。这么做的好处是后续如果换一个更强的模型只需要改网关配置业务层完全不用动。实际运行中某家大模型的服务不稳定过几次因为网关做了自动降级到备用模型整个生成流程没有受到明显影响。这个教训很重要凡是接外部AI能力的系统务必要把供应商锁死的风险在设计初期就规避掉。模型会迭代、会变更服务策略、会有故障网关层是最后一道防线。3. 核心模块设计与实现细节3.1 用例模板体系怎么设计用例模板是整个平台的心脏。我花了很多时间打磨模板的组织方式最终确定了一套三层结构根模板定义用例骨架组件模板定义请求或操作细节字段模板定义参数规则。先看根模板。一份完整的接口测试用例骨架基本固定用例名称、前置条件、请求动作、预期结果、标签。用Jinja2表达出来大概长这样{% set scenario case.scenario %} 用例名称: [{{ scenario.module }}] {{ scenario.operation }}_异常场景_{{ scenario.error_type }} 前置条件: {% for pre in case.preconditions %} - {{ pre }} {% endfor %} 请求动作: method: {{ case.request.method }} url: {{ case.request.url }} headers: {{ case.request.headers | tojson }} body: {{ case.request.body | tojson }} 预期结果: 状态码: {{ case.expected.status_code }} 业务码: {{ case.expected.business_code }} 校验规则: {% for rule in case.expected.rules %} - {{ rule }} {% endfor %} 标签: {% for tag in case.tags %} - {{ tag }} {% endfor %}这样设计的好处是即使没有前端界面测试同学直接维护一份YAML格式的用例定义也能通过模板引擎渲染出格式统一的用例文档。模板和数据结构完全解耦数据的增删不会污染模板逻辑。字段模板的抽象比较关键。一个接口的参数至少包括参数名、类型、是否必填、约束条件、边界值。我把这些抽成一个标准的字段描述结构LLM生成时只输出结构化字段描述渲染时才决定它最终长什么样。看一个实际的字段描述例子name: page_size type: integer required: true constraints: - 最小值: 1 - 最大值: 100 boundary_values: - 0 - 1 - 100 - 101这套结构化的字段描述是后面所有边界值用例生成的基础。3.2 生成器的核心流程从拿到一份接口文档到输出完整用例集中间经过四个阶段解析、规划、生成、校验。解析阶段做的事情是把接口文档里的关键信息抽取出来。一个POST接口的请求体往往包含几十个字段嵌套结构复杂LLM直接读全文很容易遗漏字段。我先用规则把接口的路径、方法、请求参数、响应结构拆出来再按字段结构生成一份字段清单LLM只基于清单做推理注意力被聚焦生成质量明显上升。规划阶段的核心是设计用例策略。输入一份数据字典系统先确定需要生成哪几类用例基础正向流程、必填项校验、字段边界值、类型异常、枚举非法值、鉴权缺失、关联依赖校验。每一类用例对应一套Prompt模板和输出约束。这里我没有让LLM自己决定要覆盖什么而是用规则预先规划因为测试用例的覆盖维度是有明确工程标准的不是靠模型自由发挥。规划阶段有一个数据结构很关键——用例覆盖矩阵。每个字段的每种测试维度都对应一个覆盖点模型生成时按矩阵逐项完成最后统计覆盖率一目了然。| 字段名 | 正向流程 | 必填校验 | 边界值 | 类型异常 | 枚举非法 | |--------|---------|---------|--------|---------|---------| | user_id | ✅ | ✅ | ✅ | ✅ | - | | amount | ✅ | ✅ | ✅ | ✅ | - | | pay_type | ✅ | ✅ | - | - | ✅ |生成阶段是LLM集中工作的环节。我会在Prompt里同时给出接口定义摘要、字段清单、覆盖矩阵、3到5条历史参考用例以及一份严格定义输出格式的JSON规范。参考用例的加入非常重要它相当于给LLM做了一个少样本示范模型输出的格式符合率和语义贴切度都会有明显提升。校验阶段是落到工程可用的关键一步。LLM输出的是JSON格式用例描述包含一系列字段名和期望值。校验器先做格式检查再看枚举值是否存在再看字段名是否都来自字段清单最后做逻辑自洽性检查——比如一个删除接口的用例预期结果不应该是创建成功。校验不通过的就打回重试最多三次三次都失败就把问题标记出来走人工Review流程。整个流程从解析到输出用例集平均耗时在十秒级别其中LLM推理占了大部分。校验和解析消耗的资源极少瓶颈完全在模型调用端。3.3 LLM Prompt的组织策略Prompt设计是AI-TestHub里迭代次数最多的地方。我总结了一套对测试用例生成场景特别有效的组织方式。系统消息里讲清楚身份和任务边界你是测试用例设计专家你只负责根据给定的接口信息生成测试用例不回答任何其他问题。所有输出必须是合法JSON。这条消息的作用是锚定模型行为防止跑题。用户消息里按固定顺序组织核心内容首先是接口背景介绍包含业务场景描述和调用链关系其次是字段清单用表格形式呈现字段类型、是否必填、约束条件一目了然然后是覆盖矩阵告诉模型需要输出哪些维度的用例最后是参考用例用真实历史用例作为格式示范。Prompt里我刻意避免了一个错误不让LLM一次性生成所有用例。分维度生成的效果远好于一刀切。我把覆盖矩阵拆成多轮调用每轮生成一个测试维度的用例比如本轮只做字段边界值类型下轮再做异常流程。每轮任务聚焦模型输出的准确率高很多也不容易出现前面用例和后面用例内部逻辑冲突的问题。关于温度参数的设置初始版本我用了0.7生成结果虽然多样性强但也带来不少随机性错误比如字段名漏写、枚举值写错。后来经过一轮参数实验发现温度在0.2时生成质量和稳定性最均衡。不同维度用例的温度设置其实应该不同——正向流程用例更看重规范温度压低异常探索类用例更看重发散性温度可以适当拉高。我在平台里做成按维度可配置默认值是0.2。3.4 种子用例的语义检索前面提到参考用例对生成质量影响很大那参考用例从哪里来两种来源一是历史已验证的正确用例库二是嵌入向量库里的相似用例。实现上很直接。每一条用例入库时把它的标题、请求路径、字段名列表、标签拼成一段摘要文本送入Embedding模型生成向量存入向量数据库。新任务进来时同样的方式生成当前任务的查询向量用余弦相似度召回最相近的TOP K条记录。这个模块的价值在两个地方体现。一是一致性相似接口生成的用例风格高度统一避免同一个团队里产生风格迥异的测试资产。二是冷启动加速一个同类型的新接口接入时即使测试同学没有提供任何参考系统也能自动找到类似的存量用例来引导生成。踩过一个具体的坑把整个用例全文做Embedding效果反而不如用摘要文本。因为用例全文含大量操作步骤语义噪声很大相似度检索出来的结果往往只是看起来长的像实际业务场景完全不相关。改成摘要文本之后召回准确率提升明显。这件事的经验是——检索用的向量内容需要针对下游任务设计不是原文塞进去就万事大吉。3.5 执行验证闭环生成用例只是第一步用例靠不靠谱最终要看跑不跑得通。我在平台里做了一个轻量级的执行验证模块可以直接把生成的接口用例发到被测环境。这个模块的设计原则是尽量轻薄。底层直接用HTTP客户端包一层根据用例描述发起真实请求然后把实际响应和预期断言做比对。不需要集成复杂的自动化测试框架因为这些用例的定位是辅助设计不是替代全量回归。执行一次验证成功率能到多少如果某个用例接口返回500那说明生成的预期值可能有问题也可能被测环境本身异常需要人工判断。执行结果会回写到用例库作为新用例的标签数据。跑通的打上已验证标签跑不通的打上待复核这些状态会被语义检索模块引用让参考用例优先选择已验证的样本。这样就形成了一个正向循环生成越多、执行验证越多、参考样本质量越高、后续生成越准。4. 实操过程与关键代码实现4.1 项目结构和环境准备平台后端是Python写的前端用了一个非常轻的管理界面。核心依赖有FastAPI、Celery、Redis、Jinja2、OpenAI SDK、向量数据库客户端。虚拟环境装依赖配置Redis和向量库再配好模型API的Key整个平台十几分钟内能跑起来。项目目录结构概览如下ai_testhub/ ├── api_gateway/ # LLM调用网关统一管理模型路由 ├── parser/ # 接口文档解析模块 ├── planner/ # 用例规划和覆盖矩阵构建 ├── generator/ # 用例生成与Prompt组织 ├── validator/ # 用例校验器 ├── template_render/ # Jinja2模板引擎适配层 ├── retriever/ # 向量检索模块 ├── executor/ # 轻量级用例执行器 ├── web/ # FastAPI路由与管理界面 └── config.py # 全局配置每个模块保持接口简单模块间通过数据类交互。比如解析模块输出的是一份标准的接口描述对象规划模块基于它产出覆盖矩阵对象生成模块消费它们。数据结构用Pydantic定义既能在编辑器里智能提示也能在校验阶段拿到类型保障。4.2 用例规划器的实现规划器的主要职责是构建覆盖矩阵。来看核心实现逻辑def build_coverage_matrix(api_schema: ApiSchema) - CoverageMatrix: matrix CoverageMatrix(api_idapi_schema.id, items[]) for field in api_schema.fields: row CoverageItem(field_namefield.name) # 正向流程所有字段都参与 row.positive True # 必填字段需要做缺失校验 if field.required: row.required_validation True # 根据字段类型决定边界值策略 if field.type integer: row.boundary_values derive_integer_boundaries( field.min, field.max, field.step ) elif field.type string: row.boundary_values derive_string_boundaries( field.max_length, field.min_length ) # 枚举字段需要检查非法枚举值 if field.enum_values: row.invalid_enum_values field.enum_values [__invalid__] matrix.items.append(row) return matrix这里的derive_integer_boundaries是一个确定性函数根据数据库字段的最小最大值和步长推导出一组边界测试数据。比如最小值是1、最大值是100那就生成1、2、99、100、0、101六个边界值。去掉1和100这种合法边界剩下0和101这种非法边界就成了异常用例的素材。这个函数没有任何机器学习成分但它提供了LLM最不擅长的精确计算能力。把确定性规则和概率生成结合起来是这类平台设计里最重要的认知。4.3 LLM网关的容错设计LLM调用网关做了重试和降级机制因为外部API的超时和限流是家常便饭不能因为一次调用失败让整个任务挂掉。async def call_llm_with_fallback(prompt_messages, config): last_error None for attempt in range(config.max_retries): try: response await async_client.chat.completions.create( modelconfig.primary_model, messagesprompt_messages, temperatureconfig.temperature, response_format{type: json_object}, timeoutconfig.request_timeout, ) return response.choices[0].message.content except TimeoutError as e: last_error e await asyncio.sleep(2 ** attempt) # 指数退避 except RateLimitError as e: last_error e await asyncio.sleep(config.rate_limit_backoff) # 主模型彻底失败降级到备用模型 try: response await async_client.chat.completions.create( modelconfig.fallback_model, messagesprompt_messages, temperatureconfig.temperature, response_format{type: json_object}, timeoutconfig.request_timeout * 1.5, ) return response.choices[0].message.content except Exception as e: raise LLMServiceUnavailable(f主备模型均不可用: {e})重试策略用了指数退避避免在服务抖动时产生请求风暴。备用模型超时时间放宽因为备用模型通常推理速度更慢。这个网关上线后因为上游服务不稳定导致的生成失败率从最初的15%降到了1%以内。4.4 一个完整的生成调用链看一条完整的用例是怎么从任务创建变成最终结果的。任务创建后Celery Worker从队列里获取任务先读取接口文档原文调用解析模块。解析模块返回结构化的接口描述然后规划器构建覆盖矩阵。生成器拿到矩阵后按维度逐项调用LLM网关。比如当前维度是字段边界值Prompt里会列出所有边界值候选要求模型为每个候选值生成一条用例。LLM返回之后进入校验器。校验器拿着接口描述和覆盖矩阵逐条检查。看一个具体例子接口的amount字段定义为number要求大于0且小于10000LLM生成了这样一条请求{ name: 金额字段超上限校验, request: { method: POST, url: /api/v1/payment, body: {user_id: 10086, amount: 10001, pay_type: balance} }, expected: {status_code: 400, business_code: AMOUNT_EXCEED_LIMIT} }校验器先检查字段名user_id、amount、pay_type都在字段清单里。再检查amount10001确实超过了10000的上限。pay_type是枚举值balance合法。断言也符合业务规则。这条用例就通过校验进入渲染阶段。但如果LLM把字段名写成amount_money字段清单里不存在校验器直接拒绝重跑一次。第二次生成的概率性错误率会下降不少这是少样本修正起作用了。4.5 前端交互和用户反馈前端虽然简单但交互设计上有一个原则不能让用户面对一个黑盒。任务提交后页面会展示生成进度——当前在解析接口文档、正在规划用例矩阵、正在生成第几维度的用例、校验通过了多少条。每个环节的状态都看得见用户知道系统在想什么而不只是傻等一个生成中。生成完成后页面会展示一份用例总览列表按测试维度分组。每条用例右侧有一个确认/修改/驳回三态按钮。用户确认的用例进入正式用例库修改的会记录下来驳回的带着原因回到生成器作为负样本参考。这套反馈机制对后续Prompt的迭代优化特别有价值。一个人机协作系统的成功不在于AI能完全替代人而在于AI能显著减少人的重复劳动同时人在关键节点上保留决策权。这是AI-TestHub产品设计上的核心理念。5. 常见问题与排查技巧5.1 大模型生成的用例格式不稳定怎么办这是上线初期遇到最多的一个问题。用例描述偶尔会出现字段缺失、JSON解析失败、枚举值不在定义范围内。排查的思路是分三层先确认Prompt是否明确定义了输出JSON结构再确认response_format参数是否设置最后看校验器的容错策略。实践中发现一个规律Prompt里给一份完整的JSON示例比只描述输出JSON格式的成功率高得多。输出格式方面在系统消息里强制指定JSON Object模式并设置校验失败自动重试能覆盖掉九成左右的格式问题。剩余一成的格式问题需要靠校验器里的宽容解析兜底——尝试直接JSON解析失败后尝试提取大括号包裹的片段再解析再不行就标记为异常用例给人工处理。5.2 生成结果同质化严重怎么破在很多测试维度上LLM生成的用例读起来差不多比如多个异常场景之间只是字段值变了测试逻辑几乎没有差异。这个问题根因在Prompt里示例单一。当Few-shot示例里只给了一种风格的用例模型自然会模仿那个风格输出。破解方法是丰富参考用例的异质性。我在参考样本选择阶段做了一个多样性排序——召回相似用例时不只按相似度排序还要求覆盖不同的断言风格和应用场景。比如一个接口既要有返回业务码的校验用例也要有直接比对数据库状态的用例。这样模型能学到更加多样化的用例设计思路。5.3 检索召回结果不准确前面提到用摘要文本替代全文向量的经验这里再补充一个排查方向Embedding模型的选择。通用场景的模型对技术术语的理解差异很大有些模型对鉴权幂等字段越界这类词没有足够的语义区分度导致明明不同领域的用例被召回在一起。建议做法是内置模型评测脚本抽样一批接口描述用人工标注相关性跑一个简单的RecallK指标选择在当前数据分布下表现最好的Embedding模型。评测一次成本不高但能长期保障检索质量。5.4 生成慢和成本失控LLM调用是平台日常运行的最大开支。上线初期没有做成本管控一个复杂的系统接口生成全套用例可能触发几十次模型调用成本高耗时长。优化策略有几个一是设计用例维度缓存相同或相似接口生成过的维度直接从缓存取二是并行度控制多个维度同时并发请求把整体耗时压下来三是模型路由简单字段校验类用例走轻量模型复杂系统场景分析走重模型费用差距非常可观。经过这轮优化单个任务的生成成本下降了约六成耗时缩短了一半。6. 数据评估与效果调优6.1 衡量平台效果的关键指标平台要持续迭代没有衡量标准等于闭着眼睛开车。我先后跑了多轮评估最核心的几个指标如下指标名称指标含义优化手段需求覆盖率生成的用例覆盖了多少需求点和字段维度强化覆盖矩阵规划逻辑JSON格式合法率模型输出能直接解析的比例Prompt示例迭代、格式约束无效用例率断言错误、字段不存在的用例占比校验器与重试机制用例唯一率去重后用例占比反映同质化程度多样性召回、温度参数人工修改率用户确认时发生修改的比例参考样本质量提升在内部数据集上稳定运行三个月后的数据是JSON格式合法率从最初的78%提升到了95%以上无效用例率从20%下降到5%以下人工修改率稳定在25%左右。最后这个数字让我比较满意——100条用例里75条可以直接用剩下的15条微调即可真正推倒重来的只有个位数。6.2 基于反馈的Prompt迭代方法AI-TestHub的Prompt不是写完就固定的它有一套迭代机制。用户驳回用例时填写的驳回原因会进入反馈池。我每周做一次反馈归类找出高频错误类型针对性修改Prompt内容。一个实际的迭代案例最开始生成的用例普遍缺少前置条件比如创建订单用例没有说明用户已登录。反馈池里缺少前置条件占了很大比例。我在Prompt的示例中增加了一组带详细前置条件的用例下一次迭代后该类型错误率显著下降。这种基于真实反馈的迭代比拍脑袋优化Prompt有效得多。给所有正在做类似项目的同行一个建议把用户的每次修正和驳回当成最重要的标注数据这类数据的积累价值会随时间快速增长甚至可能超过模型本身带来的提升。7. 核心经验总结AI-TestHub从立项到内部稳定使用核心沉淀下来的经验可以压缩成三条。第一条LLM应用的工程化重点永远在约束而非生成。模型的能力边界就在那里真正决定平台质量上限的是约束层的设计水平。规则引擎、校验器、模板系统、反馈回路这些工程组件是否健壮决定了生成结果能不能进入生产链条。第二条处理好确定性和概率性的分工。字段边界推导、枚举校验、格式解析这些工作应该由确定性代码完成不要交给模型。模型负责的是理解业务语义、设计测试思路、组织用例表达这恰好是它擅长的事情。各司其职系统才会整体最优。第三条人在闭环中的位置不能省。AI生成用例不是终点人工Review确认是必须的环节。工具存在的意义是让人的精力花在最需要判断力的地方而不是替代人的判断。最后聊一点执行层面的体会。做这类平台最大的风险不是技术实现而是一开始就想做得太大。我见过不少团队立项时规划了超级完整的功能矩阵结果开发半年还在搭基础框架。AI-TestHub在最初只做了单接口的用例生成随着使用反馈逐步加入检索、执行、反馈迭代等能力。小步快跑、真实场景驱动是这类工具能够真正落地的关键。如果你也想做类似的尝试建议先拿一个高频场景跑通最小闭环再思考怎么扩展这条路会顺畅很多。