智能体软件工厂黑客松(GOSIM 2026)决赛作品:一个低成本的"软件工厂"智能体,读入 YAML 需求树,在 React + Express 应用上增量实现需求,交付可构建、可启动的 Web 应用。
| 结果 | |
|---|---|
| 决赛 | 第 5 名,91.42 分:Spreadsheet 28/30 + GitHub 18/30(共 46/60),总开销 ¥3.16 |
| 初赛 | 48.83 分,晋级决赛 |
📄 项目复盘与技术交流简册(PDF,9 页):docs/retrospective.pdf:架构、成本控制、工程问题与弯路、比赛经验与人机协作。
本仓库 agent/ 目录 与决赛最终提交逐字节一致(见下文"来源与校验"),没有为开源做任何改动。
决赛的题目是"在初赛交付的应用上,按变更文档增量实现新需求"。平台先把上一阶段的应用放进输出目录,再运行智能体。
需求 YAML ──► 解析需求树(spec.py)
│
├─ 输出目录里已有应用?──是──► 演进模式(evolve.py) ← 决赛走这条路
│ 1. 算出变更范围:带 "Modified" 标记的需求 + 界面名称在现有代码里找不到的需求
│ 2. 先修基线:后端模块加载失败的先修好
│ 3. 单写手,逐个需求一次会话:
│ · 确定性地挑出相关文件放进上下文(不花轮数去探索)
│ · 按该需求的场景写一个 Playwright 自测并跑通
│ · 轮数用完还没做完 → 带当前代码再续一次
│ 4. 守卫:改动让页面抛 JS 错误 → 修一次,仍坏就回滚这个需求
│ 5. 确定性检查(构建、后端加载、页面崩溃、重名控件、种子数据覆盖)→ 修复会话
│
└─ 否 ──► 全新构建(pipeline.py):规划 → 各单元并行编写 → 集成 → 冒烟 → 修复
│
▼
交付:总能构建、能启动,进程退出码恒为 0
几个关键设计:
- 单写手 + 确定性上下文:每个需求一次会话,相关代码由程序挑好再交给模型。决赛单任务开销约 ¥1.5,是初赛的约 1/9。
- 预置内核(
agent/factory/kernel/):后端路由加载、JSON 存储、会话、页面就绪门,前端 api/session。模型只写业务代码,不重复造基础设施。 - 确定性检查优先:能用程序判断的(构建失败、页面报错、同名控件、种子数据缺记录)不交给模型猜,查出问题再开修复会话。
- 交付永不丢:
main.py在子进程里跑工厂;子进程被杀(如内存超限)时,回到最后一个"能构建、能启动、已提交"的 git 状态,再补一次前端构建。
更详细的模块说明见 docs/architecture.md。
agent/ 决赛最终提交(= 上传的 zip 内容)
main.py 入口 + 进程守护
effort.env 高投入设置档(每个需求 60 轮、续做 36 轮、修复 5 轮)
requirements.txt Python 依赖
factory/
pipeline.py 全新构建流水线;判断是否进入演进模式
evolve.py 演进模式(决赛主路径)
spec.py 需求树解析、变更范围、场景提取
prompts.py 全部提示词
agent.py 带轮数上限、时钟和历史压缩的工具调用循环
tools.py 给模型的文件/检查工具(并发安全的编辑)
workspace.py 模板准备、npm 安装、构建、启动、浏览器爬取、Playwright
llm.py OpenAI 兼容的 chat-completions 客户端(重试、用量统计)
vision.py 参考截图 → 结构说明(可选)
kernel/ 预置内核,复制进生成的应用
agent.sha256 agent/ 下每个文件的 SHA-256
tools/package.sh 把 agent/ 打成可上传的 zip
docs/architecture.md 架构说明
docs/retrospective.pdf 项目复盘与技术交流简册
比赛平台的调用方式:
python3 agent/main.py <需求目录或 YAML 文件> --output-dir <输出目录>环境要求:
- Python 3.10+(建议),
pip install -r agent/requirements.txt - Node.js 与 npm、git;Playwright 浏览器(
npx playwright install chromium) - 一个 OpenAI 兼容的模型接口:
OPENAI_BASE_URL、OPENAI_API_KEY、MODEL(比赛中用的是平台提供的 DeepSeek 模型) - 一个 React + Express 应用模板:目录下有
backend/package.json(Express,带 Playwright)和frontend/package.json(Vite + React)。比赛用的是官方模板web-react-express,它不属于我们,本仓库没有附带。用ARC_AGENT_TEMPLATES_ROOT=<模板根目录>指定,智能体会找<模板根目录>/web-react-express。如果输出目录里已经有应用(演进模式),就不需要模板。
需求文件是一棵 YAML 树。每个节点有 id、name、type、description、dependencies、children;叶子节点带 scenarios(每个场景有 name 和由 keyword(GIVEN/WHEN/THEN)、content 组成的 steps)。一个最小的例子:
id: APP
name: Notes
type: FOLDER
children:
- id: REQ-1
name: Create a note
type: FEATURE
description: A signed-in user can create a note with a title and a body.
scenarios:
- name: Create
steps:
- {keyword: GIVEN, content: "user `alice` is signed in"}
- {keyword: WHEN, content: "she creates a note titled `Groceries`"}
- {keyword: THEN, content: "`Groceries` appears in her note list"}常用设置(全部是环境变量,effort.env 里的值在没有显式设置时生效):
| 变量 | 作用 | 默认 |
|---|---|---|
ARC_EVOLVE_TURNS / ARC_EVOLVE_CONT_TURNS |
演进模式每个需求的轮数 / 续做轮数 | 40 / 24(effort.env:60 / 36) |
ARC_EVOLVE_FIX_ROUNDS |
最后的确定性检查 + 修复轮数 | 3(effort.env:5) |
ARC_EVOLUTION_MIN / ARC_TIME_BUDGET_MIN |
演进模式 / 全新构建的时钟(分钟) | 120 / 150 |
ARC_EVOLVE_TOKEN_CAP / ARC_TOKEN_BUDGET |
输入 token 软上限 | 40e6 / 45e6 |
ARC_THINKING |
是否让模型开启思考 | disabled |
ARC_SUPERVISE |
是否用子进程守护 | 1 |
代码里还留着比赛平台的痕迹(ARCBENCH_* 环境变量、/opt/arcbench/... 默认路径),因为这是原样提交的版本。
| 项目 | 值 |
|---|---|
| 上传的 zip | arc-finals-r14.zip,SHA-256 dd1c66924c272225919fd5068c8521b089043904cb3336f93efef943429378be |
| 平台提交 ID | efc459974f30(2026-10-08) |
| 内容 | agent/ 与该 zip 解压后的内容逐字节一致;每个文件的哈希在 agent.sha256 |
验证:
shasum -a 256 -c agent.sha256zip 里带有文件时间戳,重新打包得到的 zip 哈希会不同,所以按文件校验。