在去中心化重构之上落地 benchmark 对比管线:5 个系统(single/strong/chain/sub_agent/swarm) 跑同一任务集、同一执行后端,产出统一 BenchmarkRunRecord → 评估器算 G_E/G_E,c → 报告 + 回放。 - benchmark/runners/:backend(Offline 确定性 / OpenAI 真实)+ base + 5 个 runner。各 runner 用 held-out fixture 测试在 Group B 沙箱里评分得 TestPassRate(权威,非自评)。 - benchmark/tasksets/:统一任务集 + 加载器(coding-set-1,1 个 fixture)。 - benchmark/reports/、benchmark/replay/:G_E/G_E,c/coverage/confidence + 归档。 - benchmark/baselines/comparison.py:BenchmarkRunRecord 的 CodeReview/UserAcceptance 改为 Optional(掩码归一,未采集即 None,规则 #9)。 - scripts/run-benchmark-suite.py harness + scripts/test-benchmark-runners.py。 与去中心化重构对齐:swarm runner 拓扑已**重指向去中心化流程**(种子→自选→自主分解→竞争→ 同伴交叉评审→收敛,calls=6/review=1),非旧 Master「分解→派发→单评审」。仍用同一离线后端 建模以保证公平对比(驱动活体编排器会换后端→记录不可比;活体全流程由 test-workflow-e2e 验证)。 沙箱适配:runner 评分走 fail-closed 沙箱(#24),故 test + CI 步骤设 HEICODE_SANDBOX_ISOLATED=1 (仅 CI/隔离 Pod)。 影响范围:agent_swarm(benchmark 层 + 测试 + docs + CI)。不碰 orchestrator 编排逻辑、 不改 Manager↔Swarm 契约、不影响 Client/计费/密钥/审计/发布链路。 诚实边界: - **离线后端只验证管线**:所有系统拿同一参考解 → quality 相同 → G_E=0、swarm_valid=False, 刻意不显示蜂群优势(反造假)。真实 G_E>0 需 --backend openai + 足量冻结任务集 + 多次运行。 - 故 Closes #21(运行器 + 统一记录已落地并产出合规非 NaN 记录);Refs #20(仅 1/5 场景)、 Refs #22(评估器/报告/回放已建,但 Quality 仅 TestPassRate,CodeReview/UserAcceptance 缺)、 Refs #13(验收 EPIC,需真实 run 证明 Swarm>baselines,未满足)。 Closes #21 Refs #20 Refs #22 Refs #13 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
104 lines
3.7 KiB
Python
104 lines
3.7 KiB
Python
"""Benchmark task fixtures with HELD-OUT acceptance tests.
|
|
|
|
A fixture pairs a task objective with authoritative tests that the swarm never sees. These tests
|
|
— not the swarm's own testing-agent output — are what produce TestPassRate (Owner ruling: avoid
|
|
self-grading; the swarm's own tests are kept as a separate signal in quality.py).
|
|
|
|
Layout per fixture: benchmark/fixtures/<id>/fixture.json + <id>/tests/test_*.py
|
|
|
|
`fixture.json` schema:
|
|
{
|
|
"id": "add_function",
|
|
"objective": "...", # what the swarm is asked to build
|
|
"required_capabilities": [...], # used to decide expects_code()
|
|
"entrypoint": "calc.py", # the module the tests import (informational)
|
|
"target_time_seconds": 60, # V_speed target for reward()
|
|
"test_files": ["tests/test_add.py"] # held-out acceptance tests (relative to <id>/)
|
|
}
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import dataclass, field
|
|
from pathlib import Path
|
|
from typing import List, Optional
|
|
|
|
_FIXTURE_ROOT = Path(__file__).resolve().parent
|
|
|
|
# Capabilities that imply the deliverable is runnable/testable code → TestPassRate applies.
|
|
CODE_CAPABILITIES = {"python", "code_generation", "coding", "testing", "pytest", "implementation"}
|
|
|
|
|
|
@dataclass
|
|
class FixtureTest:
|
|
path: str
|
|
content: str
|
|
|
|
|
|
@dataclass
|
|
class Fixture:
|
|
id: str
|
|
objective: str
|
|
required_capabilities: List[str] = field(default_factory=list)
|
|
entrypoint: Optional[str] = None
|
|
target_time_seconds: Optional[float] = None
|
|
test_files: List[FixtureTest] = field(default_factory=list)
|
|
|
|
@property
|
|
def expects_code(self) -> bool:
|
|
return expects_code(self.required_capabilities)
|
|
|
|
|
|
def expects_code(required_capabilities) -> bool:
|
|
"""A task is code-shaped (TestPassRate applies) if it requires any coding capability."""
|
|
return bool(CODE_CAPABILITIES & {str(c).lower() for c in (required_capabilities or [])})
|
|
|
|
|
|
def fixture_dir(fixture_id: str) -> Path:
|
|
return _FIXTURE_ROOT / fixture_id
|
|
|
|
|
|
def available_fixtures() -> List[str]:
|
|
return sorted(
|
|
p.name for p in _FIXTURE_ROOT.iterdir()
|
|
if p.is_dir() and (p / "fixture.json").exists()
|
|
)
|
|
|
|
|
|
def load_fixture(fixture_id: str) -> Fixture:
|
|
base = fixture_dir(fixture_id)
|
|
meta_path = base / "fixture.json"
|
|
if not meta_path.exists():
|
|
raise FileNotFoundError(f"unknown fixture: {fixture_id}")
|
|
meta = json.loads(meta_path.read_text(encoding="utf-8"))
|
|
tests = []
|
|
for rel in meta.get("test_files", []):
|
|
tpath = base / rel
|
|
if not tpath.exists():
|
|
raise FileNotFoundError(f"fixture {fixture_id} missing test file: {rel}")
|
|
# Flatten to a basename so the sandbox runner (which scans CWD for test_*.py) finds it.
|
|
tests.append(FixtureTest(path=Path(rel).name, content=tpath.read_text(encoding="utf-8")))
|
|
return Fixture(
|
|
id=meta.get("id", fixture_id),
|
|
objective=meta.get("objective", ""),
|
|
required_capabilities=meta.get("required_capabilities", []),
|
|
entrypoint=meta.get("entrypoint"),
|
|
target_time_seconds=meta.get("target_time_seconds"),
|
|
test_files=tests,
|
|
)
|
|
|
|
|
|
def load_reference_solution(fixture_id: str) -> List[FixtureTest]:
|
|
"""Offline reference solution files (path/content), used ONLY by the offline backend.
|
|
|
|
Returns [] if the fixture has no reference_solution/ dir. These files are NOT the swarm's
|
|
work — they exist so hermetic, key-free runs can validate the benchmark pipeline.
|
|
"""
|
|
ref_dir = fixture_dir(fixture_id) / "reference_solution"
|
|
if not ref_dir.is_dir():
|
|
return []
|
|
return [
|
|
FixtureTest(path=p.name, content=p.read_text(encoding="utf-8"))
|
|
for p in sorted(ref_dir.glob("*.py"))
|
|
]
|