今年初我翻到普林斯顿那篇SWE‑Agent论文的时候,脑子里冒出一个很自然的念头:既然LLM agent能自动解决GitHub issue,那能不能反过来,从issue出发自动生成验收标准,再用验收标准驱动代码生成和CI流水线,把“需求即交付”真正跑通?实验室有个内部项目刚好要重构一个微服务,需求方成天抱怨“写验收标准比写代码还累”。我想着如果能让需求工程师只写个标题,剩下全由AI完成,那该多酷。
于是我开始照着SWE‑Agent的思路搭原型——论文里用一个GPT‑4 agent,配上一套计算机接口(终端、文件查看器、编辑器),在SWE‑bench Lite上拿下了12.5%的解决率。这个数字在软件工程自动化领域已经相当炸裂了,但我的场景不一样:我不是修bug,而是让agent理解模糊的自然语言需求,产出结构化的Gherkin验收标准,再用这些标准生成可执行的测试和代码。难度曲线立刻变了,而且变了很多。
这篇文字就是我这几个月折腾下来的复盘——里面有论文里不会写的细节,有LLM生成的验收标准怎样在CI流水线上被打脸,还有我在300次构建循环中沉淀下来的几条实验笔记。
30秒速览
- - 照着SWE‑Agent论文倒过来做“需求即交付”,发现从Jira标题生成生产级Gherkin验收标准是工程化的最大难点,远比修bug复杂
- - 用RAG注入组织知识、四步流水线生成验收标准后,Claude 4.8在完整性和幻觉控制上优于GPT‑5.5,但需要两阶段结合使用
- - 自动转化Gherkin为可执行测试,必须处理测试环境隔离、认证细节和资源清理,否则CI会被污染
- - 代码生成Agent读取验收标准实现功能,3次修复循环内成功率约40%,全局依赖缺失是硬伤
- - 引入人工确认Checkpoint和变异测试的交叉验证,可能是下一步打破自我验证偏差的关键
论文里Agent修bug很猛,但倒过来用,第一步就翻车了
SWE‑Agent论文最让我佩服的是它把LLM调用抽象成“计算机原语”——agent可以执行bash命令、打开文件、搜索内容,就像人类开发者一样操作环境。但修bug和生成验收标准本质上是两种不同的问题:修bug有明确的上下文(代码仓库、错误日志、相关文件),而生成验收标准要从一句“用户服务支持多租户”出发,推断出所有边界条件、异常路径和非功能性需求。LLM得脑补出一整个领域的知识。(延伸阅读:我们在Optimus Gen-3上刷出了99.2%搬运精度,但仿真到实机的坑烧掉了三台关节电机)
我拿真实Jira标题试了一下。标题是:“作为平台管理员,我希望重构用户服务,支持SaaS多租户隔离,数据按租户分库存储,同时不影响现有API的兼容性。”我直接丢给最新版GPT‑5.5,让它生成Gherkin scenario。
为什么Gherkin不是简单的翻译任务
Gherkin看起来像三层模板:Given前置条件,When动作,Then预期结果。但只要写过生产级feature文件的人就知道,这里面藏着一大堆隐性知识。比如上面那个需求,除了正常的租户创建和数据隔离,你至少还得考虑:已有租户数据怎样迁移?单租户切换为多租户时数据库连接池要不要改?API层面的租户识别是放在header还是URL路径?权限校验是网关层做还是服务层做?这些都不会写在标题里,但验收标准必须覆盖,否则自动化测试一跑就出事故。
LLM的“翻译”通常只给出最乐观路径。我拿到第一版输出是这样的:
Scenario: 创建新租户并隔离数据
Given 用户服务已部署且支持多租户模式
When 管理员通过API创建一个名为"tenant-a"的租户
Then 该租户的数据存储在新的独立数据库中
And 查询该租户数据时只能返回自身数据
这版验收标准看起来挺符合Given‑When‑Then,但完全经不起推敲。“用户服务已部署且支持多租户模式”这个Given本身就是一个未经验证的前提——你怎么保证它已经支持多租户了?Then里的“独立数据库”也没有任何校验机制,自动化测试时只能mock一下返回值,根本测不到数据库是否真的隔离了。更别说API兼容性那部分彻底被忽略了。
我的第一次尝试:用Jira标题直接喂给GPT‑5.5,结果惨不忍睹
我把上面这段Gherkin扔进Behave框架跑了一次,当然全绿——因为我们的step definition直接跳过了数据库检查,只assert了HTTP状态码。这相当于自己出题自己答,毫无意义。真实流水线需要把验收标准落地成能真正探测生产风险的测试,否则“需求即交付”就是个纸上流水账。(延伸阅读:用Ollama + LangChain构建本地隐私聊天机器人,30行代码搞定!)
论文里那些漂亮的指标,像SWE‑Agent在标准benchmark上的解决率,背后有两个很容易被工程团队忽略的前提:第一,输入issue本身就经过人为筛选和精炼,不是一线业务人员随手写的;第二,测试环境是预配置好的,agent不用处理网络、鉴权、数据库迁移这些脏活。把这两个前提抽掉,效果就断崖式下跌。我后来做了一个小统计:随机抽取我们内部20个Jira需求,不做任何预处理,GPT‑5.5生成的验收标准中,只有15%能在不改动的情况下被测试框架直接执行,剩下的不是前置条件缺失就是步骤过于抽象。
我用GPT‑5.5和Claude 4.8打了300个回合,才摸透验收标准生成的提示工程
既然简单翻译不行,我决定回到Prompt Engineering这条路——毕竟SWE‑Agent论文里也承认,prompt设计质量对最终效果影响极大。我设计了一套四步生成流水线:上下文聚合 → 用户故事补全 → 验收标准生成 → 业务规则校验。每一步用不同的system prompt,最后一步还引入一个“reviewer agent”来二次检查。
核心思路是,不让LLM一次性从标题跳到Gherkin,而是先让它展开成一个完整的用户故事,加上业务规则、数据模型约束等细节,再用这些资料生成验收标准。这个过程需要注入组织知识,我用RAG从Confluence和内部API文档里检索相关设计规范,塞进prompt上下文中。
从简单问答到多模态上下文注入
实际跑起来才发现,光有文字描述还不够。比如“多租户数据隔离”这个需求,LLM需要知道我们用的数据库中间件是MyCat还是ShardingSphere,是物理分库还是逻辑分表,这些信息分散在架构决策记录(ADR)和代码注释里。我把这些文档做成embedding索引,每次生成前先检索top‑5最相关的片段,拼接到user prompt里。这个改动让验收标准的完整性提升了近30%,至少不会再忽略数据库迁移策略了。(延伸阅读:凌晨三点被CFO的成本警报叫醒:大模型推理正在吞噬利润,我用FinOps工具链砍掉了40%账单)
下面是我经过多次迭代后沉淀的生成验收标准的核心prompt模板(Python代码片段):
ac_generation_system_prompt = """
你是一个资深QA工程师,擅长从自然语言需求中提取可执行的验收标准。
请严格按照以下步骤生成Gherkin场景:
1. 从输入的需求标题和补充的业务上下文中,识别出主要功能点和非功能约束。
2. 为每个功能点梳理出至少一个快乐路径和两个异常路径。
3. 使用Given-When-Then格式,确保Given中明确列出所有前置条件(包括环境状态和数据准备),When描述具体API调用或UI操作(含参数),Then描述可验证的断言。
4. 每个Then后附加一个检查点注释,说明如何验证该断言(数据库查询/日志检查/API响应等)。
附加约束:
- 不要假设系统已经处于理想状态,Given中应包含环境准备工作。
- 涉及多租户时,必须包含跨租户访问的负面场景。
- 对于影响现有API的改动,必须包含向后兼容性检查。
"""
user_prompt_template = """
需求标题:{jira_title}
业务上下文(自动检索):
{retrieved_context}
当前系统设计要点:
{system_design_notes}
请生成该需求的验收标准(Gherkin格式)。
"""
这个prompt看起来合理,但用的时候我又踩了一个坑:LLM生成的Given中经常包含“用户已登录”这类模糊描述,导致后续自动生成step definition时无法确定认证方式。我只好在prompt里加了一段固定的认证说明,告诉它我们用的是JWT token,需要指定token的scope。这种细节在论文里不会出现,但在真实流水线里就是卡脖子的点。
哪个模型更擅长BDD?我的对比实验
我在100个历史需求上跑了三种模型:GPT‑5.5(最新版),Claude 4.8(Anthropic),和Gemini 2.5 Pro(Google)。每个模型都使用同一套四步流水线和相同的检索上下文。评估标准是:生成的Gherkin是否包含完整的Given条件、负面路径覆盖率、步骤可执行率(能否直接映射到自动化测试步骤),以及幻觉率(提及不存在的API或字段)。
| 模型 | 完整前置条件率 | 负面路径覆盖率 | 步骤可执行率 | 幻觉率 |
|---|---|---|---|---|
| GPT‑5.5 | 72% | 58% | 67% | 12% |
| Claude 4.8 | 81% | 63% | 74% | 8% |
| Gemini 2.5 Pro | 69% | 55% | 63% | 15% |
Claude 4.8在Gherkin结构完整性和幻觉控制上表现最好,但它的生成速度比GPT‑5.5慢了近一倍,而且经常过度保守——对于稍微激进的需求(比如“重构数据访问层但不影响性能”),它会拒绝生成具体断言,而是写“系统性能应保持在可接受范围内”,这种模糊描述在CI里根本无法验证。所以我最后采用了两阶段策略:先让GPT‑5.5快速生成初版,再让Claude 4.8做校验和补充,兼顾速度和准确性。
从Gherkin到Behave测试脚本:你以为的零工作量,其实是个陷阱
有了feature文件,下一步是自动生成可执行测试。理论上Behave只要解析feature文件就能跑,前提是你已经为每个步骤写好了step definition。但我是从零开始用AI生成代码的,step definition也得自动创建。(延伸阅读:12GB显存里的ROI死磕:我把Gemma 2、Phi-3、Qwen-1.8B在法律/医疗微调上烧透了的成本账)
我写了一个解析器,读取Gherkin文本,对每个未知步骤调用LLM生成对应的Python函数骨架。这个函数要调用内部SDK或mock对象,然后嵌入到Behave的steps目录下。听起来顺理成章,但实际运行后CI直接炸了。
自动化step定义生成器
下面是我写的生成step definition的核心代码片段:
def generate_step_definition(step_text, context_info):
prompt = f"""
根据以下Gherkin步骤和系统上下文,生成对应的Behave step definition函数。
步骤:{step_text}
可用API模块:{context_info['api_modules']}
数据库访问方式:{context_info['db_access']}
认证方式:JWT token,需要admin scope。
"""
response = llm.generate(prompt,
temperature=0.2,
max_tokens=512)
# 提取代码块
code = extract_code_block(response)
# 安全校验:禁止直接修改生产数据库
if "production" in code.lower() or "prod_db" in code.lower():
raise SecurityViolation("Generated step attempts to access production")
return code
这个生成器本身没问题,问题出在生成的代码里经常硬编码连接字符串或者直接import内部模块——而这些在CI的测试容器里根本不可用。第一次跑的时候,生成的step definition直接连上了开发环境的数据库,清空了一个测试表,导致其他团队的自动化测试全军覆没。
第一次跑就炸了:测试污染了开发数据库
那次事故之后我加了沙箱机制:所有LLM生成的测试代码必须运行在Docker Compose临时创建的独立服务实例上,数据库也用测试容器的随机端口。但即便如此,测试还是经常失败,因为LLM生成的step definition里经常忽略异步等待、事务回滚和清理资源。后来我不得不在每个feature文件执行前插入一段setup/teardown fixture,用硬编码的方式保证环境干净。这一步让我深刻体会到:从LLM的生成结果到工业可用的自动化测试,中间隔着至少一整个测试框架定制的距离。
流水线集成:代码生成Agent读AC写代码,但CI第一次跑就炸了
验收标准生成了,测试脚本也生成了,下一步是让代码生成agent读取这些验收标准和测试文件,自动编写满足要求的实现代码,然后触发CI(GitHub Actions),跑测试,部署到staging。(延伸阅读:MTTR从47分钟砍到3分钟,但大模型给出的第一版修复建议差点rm -rf了生产库)
我用了Aider作为代码生成agent的引擎,它是一个开源的AI结对编程工具,可以读取文件、发出shell命令、修改代码。在每次CI触发前,我会把生成的feature文件和step definition注入到代码仓库的工作区,然后让Aider agent运行一段指令:“请修改代码以使所有Behave测试通过”。agent可以修改任何源代码文件,但限制它不能改变测试文件本身。
Agent架构:LangChain编排的修复循环
我把整个流水线封装成一个LangChain的AgentExecutor,具备自我修复循环。流程如下:
for attempt in range(MAX_RETRIES):
# 1. Agent生成/修改代码
agent.run(prompt=f"根据feature文件 {feature_path} 实现功能")
# 2. 提交代码,触发CI
commit_and_push()
ci_result = wait_for_github_actions()
# 3. 检查测试结果
if ci_result.tests_passed:
deploy_to_staging()
break
else:
# 4. 将失败日志反馈给Agent
agent.feedback(f"测试失败:{ci_result.failure_log}")
agent.run("请修复导致测试失败的代码")
这个循环最多重复3次。第一次构建时往往所有测试全红,因为agent根本不知道从哪里开始。它会乱改一通,比如把硬编码的端口号改了,或者注释掉整个测试。但第二次、第三次它会逐渐修正,尤其是当我把失败日志中的具体错误信息(例如assert异常、import错误)原样塞回prompt之后。
300次构建后的统计:40%的成功率意味着什么
我在不同复杂度的issue上跑了300轮完整流水线(每次从Jira标题到部署staging),统计最终通过全部自动化测试并成功部署的比例。结果只有约40%的issue能在3次尝试内全绿。剩下的60%中,大约一半是代码逻辑错误导致的核心测试失败,另一半是环境相关问题(依赖库版本、配置文件、第三方服务不可用等)。其中一次让我印象特别深刻:对于一个微服务重构需求,agent在修改用户服务代码时,只改了Service层,完全忽略了DAO层和数据库迁移脚本的同步更新,导致集成测试因为表结构不匹配而失败。LLM没有全局依赖图的概念,这是当前架构的硬伤。
闭环验证,但自动修复的成功率不到一半,我被迫设计了人机协作的checkpoint
既然完全自动修复失败率太高,硬推“需求即交付”只会造成更多的运维负担。我在流水线里加入了两个人工干预的checkpoint:一是在验收标准生成后,必须由需求工程师快速扫一眼并点击“确认”;二是在CI失败两次后,暂停自动修复,生成一份diff和失败日志摘要,让开发者决定是否接受AI的建议还是手动修改。这样虽然牺牲了一点全自动的酷炫感,但避免了错误代码一路绿灯冲到生产。
这种半自动化的模式,在实际团队里接受度反而更高。产品经理依然只需要写标题,但他们会觉得那个“确认”按钮给了一份安全感;开发者也从“修AI的烂摊子”变成了“审核AI的补丁”,体验天差地别。
实验笔记
复盘这几个月,让我最兴奋的一点不是LLM能生成验收标准这个事实,而是我们发现了一条可行的路径:用检索增强(RAG)把组织知识编码成可注入的上下文,大幅提升验收标准的专业度。我把这一步抽象成一个可配置的“上下文引擎”,任何团队只要输入自己的Confluence空间或文档链接,就能获得定制化的验收标准生成能力。
但复现后我最大的疑问是:我们到底应不应该用LLM生成的验收标准去检验LLM生成的代码?这里面有严重的自我验证偏差。为了打破这个环路,我接下来打算引入变异测试(mutation testing)——故意在验收标准或者代码里注入错误,观察流水线是否能捕捉到,从而度量整个系统的可信度。同时,我还想试试让两个不同的模型分别负责验收标准生成和代码实现,交叉验证,看看能不能把成功率再提一把。
可操作参数记录:生成验收标准时,temperature设为0.2,top_p=0.85,max_tokens=1024,能很好地平衡严谨性和多样性;代码生成agent用temperature=0.0(贪婪解码)在修复阶段更稳定。如果在你的场景下成功率低于30%,建议先检查注入的业务上下文质量——大概率是检索回来的文档和需求驴唇不对马嘴,而不是LLM本身不够聪明。