← 返回 2026-07-30

SpecFirst:将行为规格抽取作为从零程序合成的一等公民步骤 SpecFirst: Behavioral Specification Elicitation as a First-Class Step in Agent-Based Program Synthesis from Scratch

Yihao Chen, Shi Chang, Feng Lin, Khaled Chawa, Boyuan Chen, Shaowei Wang, Ahmed E. Hassan 📅 2026-07-29 👍 20 2026-08-04 18:30
LLM代理 ProgramBench 代码智能 程序合成 规格抽取 需求工程

专门的规格代理先黑盒探针抽取行为规约,再交给代码合成代理实现

前置知识

ProgramBench(从零程序重建基准)

一个 200 实例的命令行程序重建基准,覆盖 FFmpeg、SQLite、PHP 解释器等真实开源工具,源码语言以 Rust(107)、Go(46)、C/C++(45) 为主。每个实例只给一份自然语言文档(README/man page)和一个 execute-only 二进制(行为 oracle),共含 248,853 条隐藏测试,每任务中位数 770 条测试,平均 79.7% 行覆盖。任务是写出一个与二进制行为等价的可执行实现。

这是论文唯一的评测对象,理解其‘只给二进制不给源码’的设定,才能理解为什么‘行为抽取’会成为关键瓶颈。

需求工程中的规格抽取(Requirements Elicitation)

经典软件工程把需求获取视为先于实现的一等公民阶段:需求工程师通过原型演练、场景构造、访谈等交互手段,挖掘书面文档永远无法完整记录的行为细节(边界、错误路径、组合效应),再开始写代码。

本文的整套设计灵感正来自这一范式——把‘先抽取规格再实现’搬到 LLM 代理流水线里,是理解 method 章节的关键。

Spec-Driven Development(OpenSpec / spec-kit)

一类以规格为中心的开发框架:用 Markdown 把需求结构化(如 OpenSpec 的 Purpose + Requirement + Scenario,配合 RFC 2119 关键词 MUST/SHALL/SHOULD/MAY),再驱动 AI 或人实现。它们主要依赖人类输入需求,而非自动从二进制中挖掘行为。

这是 method 章节 VII-B 格式消融的对比对象,也是作者强调自己‘支持自动行为抽取’区别于它们的关键参照。

SWE-agent / mini-SWE-agent

SWE-agent 是为 LLM 定制 agent-computer interface 的代表性代码代理框架;mini-SWE-agent 是其精简脚手架。本文把 mini-SWE-agent 同时用作 Direct-Synthesis 基线和 SPECFIRST 第二阶段的代码合成代理,保证对比时唯一变量是‘是否插入 spec agent’。

理解这一点才能看懂实验:性能差距纯粹来自规格抽取阶段,而非换了更强的代码代理。

ReAct 代理设计

Reasoning + Acting 范式:代理在每次工具调用后强制输出观察、考虑备选动作、推理下一步的 reasoning 文本,形成可解释的推理轨迹,避免盲目的工具调用链。

SPECFIRST 的 spec agent 和 code synthesis agent 都基于 ReAct,作者强调‘强制推理输出’是其可解释性与调试性的来源。

研究动机

LLM 代理在已有代码库的软件工程任务(修 bug、加功能、补全代码)上表现强劲,因为代码库本身就是稳定的上下文锚点;但‘从零构建一个完整程序’要困难得多——代理要自己决定架构、语言、行为边界。ProgramBench 把这个差距量化得非常具体:即便最强的前沿模型(如 GPT-5.5-high),在 200 个真实 CLI 程序上完全解决的实例不到 1%。现有代理框架(SWE-agent、OpenHands、mini-SWE-agent)把‘读文档 + 探索二进制 + 写代码’揉在同一个循环里,造成三类系统性问题:第一,探索性探针不充分——agent 在还没探索完时就转向写代码,例如 gomplate 实例下基线只探了 README 提到的 env/data 命名空间,coll/math/net/random 几乎没验证;第二,规格在长程中丢失——GPT-5.5-high 在第 4 轮就拿到了 aws/gcp/sockaddr/semver/sprig/cidr 等全部命名空间签名,但此后 25 轮里这 6 个名字再没出现在推理或工具调用中,原因是上下文压缩(reasoning token 剥离、对话摘要)会选择性丢弃编码原始规格的 token,最终 126 个测试用例因此失败;第三,无锚点的错误传播——Qwen3.6-35B-A3B 在第 4–5 轮正确读到了 `Seq(start, end, step int64) []int64`,但到第 57 轮实现时退化成 `func Seq(args ...interface) []interface`,丢掉了类型参数和返回值,此后 140 轮里 math 命名空间被重构 5 次,每次都重新发出错误签名却从不回看原始文档,最终 43 个 Seq 相关测试中有 25 个因类型错误失败。

本文的目标是把‘行为规格抽取’从一个被隐含在主循环里的副产物,提升为代码合成之前一个显式、独立、可复用的一等公民阶段。具体目标是:(1) 用一个专职的 spec agent 在不与写代码竞争预算的前提下,系统性地黑盒探针二进制 B,挖出文档没写或写错的行为细节(边界、错误路径、flag 组合效应、输出格式),形成结构化规格 SPEC.md;(2) 让代码合成代理基于文档、二进制和 SPEC.md 进行实现,使规格成为稳定的行为契约,锚定后续每一步实现决策;(3) 把规格外存成文件而非隐含在上下文里,使其在上下文膨胀或被压缩时仍然完好。可量化的目标是:在 ProgramBench 全 200 实例、跨两个模型族(Qwen、GPT)和能力相差一个数量级的 4 个模型上,稳定提升平均测试通过率和二进制探索覆盖率,且统计显著。

与已有工作不同的是,已有工作各有缺口:spec-driven 框架(OpenSpec、spec-kit)只靠向人类利益相关方提问来获取规格,不支持自动通过二进制探针做行为抽取;经典需求工程虽然把抽取当一等公民,但用的是人类工程师演练原型,本文则把 LLM 实例化为主动抽取器去探针运行中的系统,最接近 BDD(行为驱动开发)的精神但产物是需求规格而非测试套件;黑盒程序分析(协议逆向 Prospex/Polyglot、规格挖掘、Daikon 动态不变量检测)虽然共享黑盒观测模型,但产物是机器可读的协议文法或形式化不变量,服务于安全分析,且往往需要源码插桩或被动 trace,而非 LLM 可直接消费的自然语言行为规格;LLM 反编译(LLM4Decompile)需要反汇编访问,而本设定明确禁止。ProgramBench 暴露了这个空白但没给方法。SPECFIRST 正是首个把‘发现步骤’显式设计为可复用流水线组件的工作。

核心方法

直觉上,SPECFIRST 就像把传统软件工程师‘在写第一行代码前先演练原型、构造场景、挖掘行为细节’的做法,搬进 LLM 代理流水线。整体是两阶段串联(见图 1 绿框接在蓝框之前):第一阶段是专门的 spec agent,只拿到文档 D 和 execute-only 二进制 B,唯一任务是理解目标程序的行为,通过对 B 做系统性黑盒探针,产出结构化行为规格 SPEC.md;第二阶段是任意一个现成的代码合成代理(论文用 mini-SWE-agent,也可换成 OpenHands 或 SWE-agent),同时拿到 D、B 和 SPEC.md,唯一任务是写实现。两个代理都遵循 ReAct 设计,强制在每次工具调用后输出推理。两阶段之间的上下文做压缩:把 SPEC.md 作为结构化产物喂给代码代理,但丢弃 spec agent 冗长的推理和工具调用输出,避免注意力稀释。这样设计的好处是探索与编码互不争抢预算、文档歧义在编码前就被探针消解、行为知识外存后不会再随上下文压缩而丢失。

核心创新是把‘行为规格抽取’从单循环里的一项交错活动,提升为一个与代码合成解耦的前置一等公民阶段。和已有方法的本质区别有三点:(1) 专职的 spec agent 在不与编码竞争预算的前提下自由探索二进制,直接对冲‘探索不充分’——基线其实并不缺预算,RQ4 显示所有基线运行都主动提前终止,中位只用 22–177 轮(仅占 1000 轮上限的 2–18%),瓶颈是‘行为理解的构建质量’而非算力;(2) 抽取出的规格作为外部产物 SPEC.md 持久化,而非隐含在模型上下文里,因此无论上下文如何膨胀或被压缩都完好无损,并作为稳定的行为契约锚定所有后续实现决策,对冲‘规格丢失’和‘无锚点错误传播’;(3) 解耦让文档歧义在写代码前就通过探针被消解,而不是在编码中途反复试错。代码合成代理在实现过程中若遇到 SPEC.md 的歧义或缺口,仍可回头探针 B 解析,因此灵活性没有损失。

方法步骤详情

第一阶段 spec agent 的探针循环:每个轮次,spec agent 选定一个探针(用目前已推断出的信息来选择参数、flag、stdin 去调用 B),并从三通道(stdout、stderr、exit code)观察运行结果。交互用自由 bash 而非结构化探针 API,原因是它最贴近人类工程师面对陌生工具时的操作方式,并允许链式命令(一步内构造输入文件、跑二进制、检查输出);对交互式/TUI 程序,预装的 tmux/libtmux 提供虚拟终端发送按键并捕获屏幕状态。探针是迭代的:每次观察都指导下一次探针,从 D 提供的行为骨架出发,逐步深挖 D 欠描写的四类行为——边界探针(空输入、最长字符串、特殊字符定边界语义)、错误路径抽取(畸形输入、缺参、冲突 flag 触发错误并记录 stderr 和退出码)、组合 flag 测试(暴露 per-flag 文档里没有的交互效应)、输出格式精修(比较相邻输入的输出以确定字段顺序、分隔符、空白处理)。防捷径:提示词明确禁止源码恢复(克隆仓库、装包、下源码 tarball)和二进制内省(反汇编、tracer、反编译),自动裁判扫描命令历史里的违规模式,一旦命中就记 0 分;包装/shim 二进制同样禁止。交付物格式为 6 个命名小节的 Markdown(Overview、Flags、Input & stdin、Output format、Error patterns、Edge cases),只规定‘写什么’不规定‘怎么发现’。终止条件按优先级:自我宣告完成(主条件)、步数上限 1000(安全网,实践中多数远早于此自终止)、墙钟 6 小时兜底;交付门控:提交时无 SPEC.md 则拒绝并要求重写(最多 8 次拒绝后记失败)。第二阶段:代码合成代理(mini-SWE-agent)拿 D、B、SPEC.md 做实现,可在遇到 SPEC.md 歧义时回头探针 B。两阶段间用 LiteLLM 路由、每阶段独立上下文防止信息泄漏。

技术新颖性

技术新颖性体现在与五类已有工作的明确切割:(1) 与 SWE-agent/OpenHands/mini-SWE-agent 等 LLM 代理框架不同——它们把抽取和合成揉在同一个无差别循环里,SPECFIRST 显式解耦;(2) 与 OpenSpec/spec-kit 等 spec-driven 框架不同——它们依赖向人类提问,不支持自动二进制探针式行为抽取;(3) 与经典需求工程不同——它用人类工程师演练原型,SPECFIRST 把 LLM 实例化为主动抽取器去探针运行中的系统;(4) 与协议逆向(Prospex、Polyglot)、规格挖掘、动态不变量检测(Daikon)等黑盒分析不同——它们产出机器可读文法或形式化属性、用于安全分析,且需要源码插桩或被动 trace,而非 LLM 代码代理可直接消费的自然语言行为规格;(5) 与 LLM4Decompile 等反编译不同——它需要反汇编访问,而本设定明确禁止。SPECFIRST 是首个把‘发现步骤’显式设计为可复用流水线组件、用于从零程序构建的工作。

Framework of SPECFIRST
Figure 1: Framework of SPECFIRST

实验结果

RQ1(有效性):4 个模型在 200 实例上,SPECFIRST 一致提升平均测试通过率 6.9%–21.3%(全部 p<0.01)。具体到 GPT-5.5-high 为 59.02%→65.14%(+10.4%),赢/输/平 150/38/12;Qwen3.5-397B 为 33.66%→40.84%(+21.3%),130/58/12;GPT-5.4-mini 为 39.09%→41.78%(+6.9%)。更重要的是上尾扩张:在 GPT-5.5-high 上,通过率 ≥90% 的近完美解从 5.5% 翻到 16.5%(约 3 倍),≥95% 从 1.5% 翻到 6.5%(超 4 倍)。RQ2(难度分层):所有难度所有模型都涨,Hard 实例绝对涨幅最大——GPT-5.5-high 在 Hard(n=29) 从 30.8%→40.0%(+29.9%),Qwen3.5-397B 在 Hard 从 20.0%→23.0%(+15.0%),表明规格阶段对复杂逻辑是认知脚手架。RQ3(探针覆盖率):提升 9.4%–18.5%,SPECFIRST 总覆盖 58.3%–60.3% vs 基线 49.2%–55.1%。拆分看 spec agent 单独(54.9%–58.3%) 一致超过 code synthesis agent(31.2%–51.3%),并集仅边际增加,证明增益来自专职抽取阶段而非顺带探索。RQ4(行为重塑):代码代理从‘探针-再-构建’转向更早、更持久的构建——GPT-5.5-high 上基线平均花 11–20 轮探针,SPECFIRST 只花 2–9 轮就转向实现;最终代码库大 7%–29%,意味着更完整的实现而非提前终止。RQ4 还排除了‘基线缺预算’的解释:零实例触到 1000 轮上限,全部主动提前终止,中位 22–177 轮(2–18% 预算)。成本(Table VII):每实例总成本增加 48%–130%,GPT-5.5-high 增 130%($2.54→$5.85),主要由 spec agent 本身($3.16)驱动;但 GPT-5.4-mini 的合成成本反而降 17%,说明清晰的前置规格减少了编码期的探索开销。失败分析(50 例):F1 规格遗漏 10%、F2 规格错误 4%、F3 规格不够精确 26%、F4 执行故障 52%(主导)、F5 环境相关 8%。

Median reconstructed codebase size (LOC) vs normalized run progress
Figure 2: Median reconstructed codebase size (LOC) vs normalized run progress
Comparison of the code synthesis phase between SPECFIRST and baseline from four GPT-5.5 cases
Figure 3: Comparison of the code synthesis phase between SPECFIRST and baseline from four GPT-5.5 cases
查看结构化数据
任务指标本文基线提升
ProgramBench 全 200 实例 平均测试通过率 65.14%(GPT-5.5-high + SPECFIRST) 59.02%(GPT-5.5-high Direct-Synthesis) +10.4% 相对,赢 150/200 实例,p<0.01
ProgramBench Hard 难度(n=29) 平均测试通过率 40.0%(GPT-5.5-high) 30.8%(GPT-5.5-high Direct-Synthesis) +29.9% 相对,Hard 档绝对涨幅最大
ProgramBench(Qwen3.5-397B-A17B) 平均测试通过率 40.84% 33.66% +21.3% 相对,四模型中涨幅最大
二进制行为探索 探针覆盖率(并集) 60.3%(GPT-5.5-high) 55.1% +9.4%,spec agent 单独即达 58.3%
近完美解占比 通过率 ≥90% 的程序比例 16.5%(GPT-5.5-high) 5.5% 约 3 倍(上尾显著扩张)
规格格式消融(GPT-5.4-mini, 50 实例) 平均测试通过率 62.6%(Sections 格式) 55.9%(无 spec)/ Freeform 60.7% / OpenSpec 61.7% Sections 格式最优,所有 spec 格式都优于无 spec

局限与改进

作者承认的局限:(1) 即便最强模型基线也只在 200 实例里完全解决 1 个(0.5%),SPECFIRST 把平均通过率拉到 65.14%,但论文未报告 SPECFIRST 的完全解决数,绝对分辨率仍然很低;(2) 外部有效性——只评了 ProgramBench 的确定性 CLI 程序,未必能推广到 GUI、非确定性行为、复杂进程间通信;(3) 只在 4 个模型、单一脚手架(SWE-agent)上验证;(4) 构造有效性——平均测试通过率把‘能不能构建’和‘行为对不对’混在一起(不过作者指出纯技术性编译失败极罕见,最多 3/200);(5) 失败模式 F4 占 52%,说明即便 SPEC.md 正确也不保证实现正确,纯抽取侧改进有天花板。我自己观察到的:(a) spec agent 的‘自我宣告完成’是研究变量,没有外部 oracle 衡量 SPEC.md 是否真的完整,completeness 实际上无度量;(b) 探针覆盖率用可执行行覆盖做代理,并不直接等价于行为等价性;(c) GPT-5.5-high 上 +130% 成本对大规模部署可能不可接受;(d) 论文未给出 SPECFIRST 自身在不同 spec agent 模型/规模下的消融。

独立分析的弱点

(1) 终止完全靠 agent 自我判断,缺乏客观 oracle——可改为覆盖率驱动终止,例如连续 k 个探针不再使行覆盖增长超过阈值 ε 就停,或引入差分测试/属性测试发现未覆盖分支;(2) 主导失败 F4(52%)是‘规格对但实现错’,说明 spec→code 的链路偏弱——改进方向是把规格与实现紧耦合:从 SPEC.md 的 Scenario 自动生成可执行 harness 测试,加入 test-driven repair 循环,或在提交前对实现做与 SPEC.md 的自一致性检查;(3) spec agent 与 code synthesis agent 用同一个模型且无角色特化,而抽取任务更程序化——可用更小的轻量模型跑 spec agent 以压成本(GPT-5.5-high 上 +130% 主要来自 spec agent 本身的 $3.16),或对简单实例跳过深度抽取;(4) 固定 6 小节格式对程序特异性维度可能不够,比如 FFmpeg 的编解码参数、SQL 方言语法没有专门小节——可做自适应 section schema(按程序类型选择)或学习式格式选择;(5) ‘纯黑盒、禁反汇编、禁查源码’的人工限制在真实工程里不现实,工程师会用一切可得信息——可探索有界内省的混合设定,可能更有效也更贴近现实;(6) 当前是单轮 spec→单轮 synth,没有闭环——可让代码合成阶段的失败反馈回 spec agent 做迭代精修。

未来方向

作者明确提出:为压制主导失败 F4(52%),需要更强的执行阶段推理——更好的指令遵循、与 SPEC.md 的自一致性检查、test-driven repair 循环;为压制 F1/F2/F3(合计 40%),需要更有针对性的探针策略,系统性挖掘文档欠描写的表面(错误路径、flag 交互、输出路由)。基于成果可延伸的方向:(1) 论文自己指出二进制只是‘任意可探针-可观测 oracle’的代理,可推广到 REST API、容器化服务、编译型 SDK、远程 CLI,覆盖大多数维护、迁移、集成场景;(2) 多模态规格抽取,目前用 tmux 处理 TUI,但 GUI 程序未试;(3) 把范式迁移到代码之外的长程代理任务,如科学复现(探针参考模拟器)、系统迁移;(4) 闭环迭代规格精修,让实现失败反哺抽取;(5) 成本感知调度,按实例难度动态决定抽取深度;(6) 把规格抽取作为通用‘上下文锚点’思路,迁移到其它易发生上下文漂移的长 horizon 任务。

复现评估

开源情况:论文明确说对 mini-SWE-agent 和 ProgramBench 的本地修改‘以补丁形式发布到对应 commit’,使用 LiteLLM 作 OpenAI-API 兼容路由;但 spec agent 提示词、shortcut 裁判、覆盖率插桩脚手架是否完整开源表述不充分,复现者可能需要自己补齐提示工程部分。数据:ProgramBench 是公开基准(arXiv:2605.03546),200 实例来自真实仓库(Rust 107/Go 46/C/C++ 45/Java 1/Haskell 1),248,853 条隐藏测试,每任务中位 770 条,平均 79.7% 行覆盖,测试对代理隐藏。算力与成本:每次运行在隔离的无网 Docker 容器里,墙钟 6 小时、步数 1000 兜底;每实例成本 $0.35–$5.85;全量评测 = 4 模型 × 200 实例 × 2 配置 ≈ 1600 次运行,仅 GPT-5.5-high 一档粗估就要 ~$2340,完整复现成本不低。复现难度:中偏高——除模型 API 费用外,还需搭建 ProgramBench、各语言覆盖率工具链(go build -cover、gcc --coverage、cargo llvm-cov)、Docker 隔离、shortcut 检测裁判;好消息是格式消融(Table VI)只在 50 个 GPT-5.4-mini 实例上做,说明小规模部分复现(如单模型、子集)是可行的入门路径。