565 lines
31 KiB
Markdown
565 lines
31 KiB
Markdown
# Agent Comparison Benchmark Dev Guide
|
|
|
|
이 문서는 IOP one-shot agent/model comparison benchmark의 현재 구현, dev 환경 구성, caller 연결 방식, managed credential 경계, 실행 절차와 장애 대응 기준을 한곳에 모은 운영 가이드다.
|
|
|
|
raw token, provider API key, private key, slot alias, lease id와 개인 endpoint는 이 문서에 기록하지 않는다. 실제 host, checkout, Node/provider endpoint와 최신 process 상태는 아래 source of truth에서 확인한다.
|
|
|
|
- benchmark manifest: `scripts/fixtures/agent-comparison-benchmark-iop-one-shot.json`
|
|
- public CLI: `scripts/agent_comparison_benchmark.py`
|
|
- dev environment: `agent-test/dev/rules.md`
|
|
- machine-readable dev inventory: `agent-test/inventory-dev.yaml`
|
|
- Edge/Node verification: `agent-test/dev/edge-smoke.md`, `agent-test/dev/node-smoke.md`
|
|
- API contracts: `agent-contract/outer/anthropic-compatible-api.md`, `agent-contract/outer/openai-compatible-api.md`, `agent-contract/outer/gemini-compatible-api.md`
|
|
- benchmark SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-one-shot-agent-model-comparison/SDD.md`
|
|
|
|
이 문서는 위 계약과 manifest를 설명할 뿐 덮어쓰지 않는다. 값이 다르면 manifest, contract, inventory, environment rule 순서로 최신 상태를 확인한다.
|
|
|
|
## 1. 현재 상태
|
|
|
|
2026-08-12 기준 상태는 다음과 같다.
|
|
|
|
| 영역 | 상태 |
|
|
|---|---|
|
|
| Gemini-native Edge ingress | 구현 완료. route-qualified `streamGenerateContent`, `x-goog-api-key` IOP principal auth, request/tool/SSE 변환을 지원한다. |
|
|
| official agy adapter | 구현 완료. `agy 1.1.12`, Gemini API-key provider, real `init/step_update/result` JSONL을 사용한다. |
|
|
| managed dev credential runtime | 구성 완료. Control Plane projection, credential slot/route, sealed lease, Edge HTTPS, CP↔Edge/Edge↔Node mTLS를 사용한다. |
|
|
| caller readiness | 최근 완료된 readiness evidence에서 C01-C09 `ready=9`를 확인했다. 실제 실행일에는 fresh preflight가 다시 필요하다. |
|
|
| controller recovery | control socket symlink와 caller launch 전 interruption 회귀 수정 및 테스트가 완료됐다. |
|
|
| Codex production JSONL | `cache_write_input_tokens` 수용과 config-owned effective binding 정합화가 완료됐다. |
|
|
| deterministic tests | focused 41 tests, 전체 benchmark 429 tests가 통과했다. 숫자는 당시 snapshot이며 현재 suite 결과는 fresh 실행으로 판단한다. |
|
|
| C01-C09 scored execution | 아직 완료되지 않았다. 과거 incomplete/failed run은 append-only evidence로 보존하며 정상 결과로 간주하지 않는다. |
|
|
| blind scoring/report | 유효한 9-cell execution이 생긴 뒤 수행하는 후속 단계다. |
|
|
|
|
과거 실행 실패는 새 run을 정당화하는 완료 evidence가 아니다. 이전 run tree를 수정하거나 old run을 암묵적으로 `resume`/retry하지 않는다. 새 scored execution은 그 실행을 명시적으로 소유하는 현재 plan과 사용자 권한이 있을 때만 한 번 수행한다. 이 문서 자체는 지속적 실행 승인을 부여하지 않는다.
|
|
|
|
## 2. 시스템 구성
|
|
|
|
```text
|
|
Benchmark runner
|
|
├─ Claude Code ── Anthropic Messages ───────────────┐
|
|
├─ agy ───────── Gemini streamGenerateContent ─────┤
|
|
└─ Codex ─────── OpenAI Responses ─────────────────┤
|
|
v
|
|
IOP Edge HTTPS
|
|
│
|
|
┌───────────────────────────┴───────────────────────────┐
|
|
│ │
|
|
direct model route execution preset
|
|
│ selector/plan/work/review/repair
|
|
└───────────────────────────┬───────────────────────────┘
|
|
v
|
|
managed credential lease
|
|
Control Plane projection + Node sealed lease
|
|
│
|
|
v
|
|
Node-owned provider
|
|
```
|
|
|
|
핵심 보안 경계는 다음과 같다.
|
|
|
|
- benchmark caller는 하나의 IOP principal token으로 Edge만 인증한다.
|
|
- caller token은 upstream provider credential이 아니다.
|
|
- provider credential은 Control Plane에 등록된 slot과 Node 대상 sealed lease에서만 온다.
|
|
- Edge는 provider credential을 복호화하지 않고 caller가 보낸 token을 upstream auth로 재사용하지 않는다.
|
|
- managed mode는 legacy static principal/provider credential과 혼용하지 않는다.
|
|
- config observation은 secret이 아닌 route/model/stage binding의 독립 증거다.
|
|
|
|
## 3. 환경 프로필
|
|
|
|
### 3.1 Benchmark runner
|
|
|
|
현재 검증된 runner class는 Linux/AArch64다. 명령은 repository root에서 실행한다.
|
|
|
|
필수 command:
|
|
|
|
```bash
|
|
command -v python3
|
|
command -v git
|
|
command -v claude
|
|
command -v agy
|
|
command -v codex
|
|
```
|
|
|
|
2026-08-12 확인 snapshot:
|
|
|
|
| Tool | 확인된 버전 | 정책 |
|
|
|---|---|---|
|
|
| Claude Code | `2.1.228` | 고정 버전으로 추정하지 않고 매 execution preflight에서 `--version`/`--help`를 확인한다. |
|
|
| agy | `1.1.12` | adapter가 이 버전을 명시적으로 gate한다. 다른 버전은 재검증 전 fail closed한다. |
|
|
| Codex CLI | `0.147.0` | 고정 버전으로 추정하지 않고 매 execution preflight에서 `--version`과 `exec --help`를 확인한다. |
|
|
|
|
### 3.2 Testbed
|
|
|
|
- path: `../iop-s2`
|
|
- 현재 확인 branch: `dev`
|
|
- 현재 확인 상태: clean
|
|
- benchmark는 testbed를 read-only provenance로 취급한다.
|
|
- caller별 workspace와 session은 run tree 아래에 새로 만들며 서로 공유하지 않는다.
|
|
- testbed를 benchmark 결과로 수정하거나 결과 파일을 다시 복사하지 않는다.
|
|
|
|
fixture checksum과 source file 목록은 manifest가 고정한다. testbed HEAD와 clean 상태는 실행일에 다시 확인한다.
|
|
|
|
### 3.3 Dev runtime
|
|
|
|
현재 검증된 runtime class는 macOS/ARM64 remote dev runner다. exact SSH target과 checkout은 `agent-test/dev/rules.md` 및 `agent-test/inventory-dev.yaml`을 따른다.
|
|
|
|
benchmark 관련 runtime 역할:
|
|
|
|
| Port | 역할 |
|
|
|---:|---|
|
|
| `18082` | Edge artifact/bootstrap HTTP |
|
|
| `18083` | managed Edge public HTTPS; Anthropic/OpenAI/Gemini caller ingress |
|
|
| `18084` | native dev-runtime Edge↔Node TCP |
|
|
| `19093` | Edge admin/config refresh |
|
|
| `19101` | Edge metrics |
|
|
|
|
2026-08-12 read-only 확인에서 위 listener와 managed Edge process는 모두 active였고 remote checkout은 clean release 상태였다. exact commit, binary checksum, process id와 endpoint는 실행 evidence에만 기록하고 이 가이드에 고정하지 않는다.
|
|
|
|
최근 완료된 live readiness evidence는 4 connected Nodes와 8 healthy/available providers를 확인했다. 최신 Node/provider 세부와 접속 위치는 반드시 `agent-test/inventory-dev.yaml`에서 다시 확인한다.
|
|
|
|
이 benchmark는 compose dev stack의 Edge-Node TCP `19003`이 아니라 native dev-runtime provider pool의 `18084`를 사용한다. compose와 native profile은 포트, process와 판정 evidence가 서로 다르므로 한 실행에서 섞지 않는다. 현재 inventory가 가리키는 배포 산출물은 다음과 같다.
|
|
|
|
| Artifact | Path |
|
|
|---|---|
|
|
| native Edge config | `build/dev-runtime/edge.yaml` |
|
|
| Edge binary | `build/dev-runtime/bin/edge` |
|
|
| macOS Node binary | `build/dev-runtime/bin/iop-node` |
|
|
| Linux ARM64 Node binary | `build/dev-runtime/bin/iop-node-linux-arm64` |
|
|
| Windows AMD64 Node binary | `build/dev-runtime/bin/iop-node-windows-amd64.exe` |
|
|
|
|
모든 Edge/Node binary는 scored execution 전에 동일 source ref로 rebuild·redeploy·restart한다. `build/dev-runtime/**`의 runtime config와 untracked credential material은 원격 runner가 소유하며 tracked 문서나 testbed로 복사하지 않는다.
|
|
|
|
## 4. 보호 파일과 credential 역할
|
|
|
|
benchmark runner의 `token/` 아래에는 다음 파일이 준비돼 있다. 파일 존재와 mode만 확인하며 내용을 출력하지 않는다.
|
|
|
|
| Path | 역할 | 실행 시 사용 |
|
|
|---|---|---|
|
|
| `token/.iop-bench` | benchmark용 IOP principal token | preflight/run/score caller가 Edge를 인증할 때 사용 |
|
|
| `token/iop-dev-ca.pem` | managed dev Edge HTTPS CA certificate | `SSL_CERT_FILE`, `NODE_EXTRA_CA_CERTS`로 전달 |
|
|
| `token/.claude` | Claude provider credential의 초기 provisioning source | provider slot 등록 시에만 사용; benchmark caller에 전달하지 않음 |
|
|
| `token/.gemini` | Gemini provider credential의 초기 provisioning source | provider slot 등록 시에만 사용; benchmark caller에 전달하지 않음 |
|
|
| `token/.gpt` | GPT provider credential의 초기 provisioning source | provider slot 등록 시에만 사용; benchmark caller에 전달하지 않음 |
|
|
|
|
현재 secret source 파일은 `0600`, CA certificate는 `0644`로 확인됐다. CA certificate는 public trust material이지만 private key는 아니다.
|
|
|
|
안전 확인:
|
|
|
|
```bash
|
|
for benchmark_secret_file in token/.iop-bench token/.claude token/.gemini token/.gpt; do
|
|
test -f "$benchmark_secret_file"
|
|
test "$(stat -c '%a' "$benchmark_secret_file")" = 600
|
|
done
|
|
test -f token/iop-dev-ca.pem
|
|
```
|
|
|
|
macOS에서 동일 검사를 수행할 때는 BSD `stat` 문법을 사용한다. 어떤 경우에도 `cat`, `echo`, shell tracing(`set -x`)으로 secret 내용을 출력하지 않는다.
|
|
|
|
## 5. Provider와 managed credential 설정
|
|
|
|
현재 dev 구성은 다음 절차로 만들어졌다.
|
|
|
|
1. Control Plane credential plane용 CA, workload certificate, at-rest keyring과 lease issuer/recipient key를 operator-owned untracked 경로에 생성했다.
|
|
2. Control Plane, Edge와 각 Node에 role/name-bound mTLS identity를 배치했다.
|
|
3. Edge public ingress를 HTTPS로 구성하고 benchmark runner에 CA certificate만 전달했다.
|
|
4. benchmark principal을 bootstrap하고 one-time token을 `token/.iop-bench`에 저장했다.
|
|
5. `token/.claude`, `token/.gemini`, `token/.gpt`의 raw provider key를 credential HTTPS request body로 직접 등록했다. command argument, YAML, tracked docs나 task evidence에는 넣지 않았다.
|
|
6. credential slot과 public route를 별도로 생성하고 principal projection에 direct route와 hybrid preset stage authorization을 연결했다.
|
|
7. Control Plane → Edge → Nodes 순서로 bounded restart하고 fresh projection, sealed lease, route revision과 no-fallback 동작을 검증했다.
|
|
|
|
재구성이 필요하면 `docs/edge-local-dev-guide.md`의 “Managed credential plane and TLS startup”과 “Safe slot lifecycle”을 따른다. 실제 slot id, alias, revision과 lease id는 운영 상태이므로 이 문서에 복사하지 않는다.
|
|
|
|
## 6. Caller별 연결 방식
|
|
|
|
| Caller | Edge surface | Child 설정 | 중요한 제한 |
|
|
|---|---|---|---|
|
|
| Claude Code | Anthropic-compatible Messages | `ANTHROPIC_BASE_URL`, `ANTHROPIC_API_KEY`; `--bare --print --verbose --output-format stream-json --no-session-persistence --permission-mode dontAsk --tools Read,Write,Edit --allowedTools Read,Write,Edit` | task는 stdin으로 한 번 제출한다. network/shell 도구 없이 격리 workspace 파일 작업만 허용한다. model/effort는 manifest 값을 그대로 전달한다. |
|
|
| agy | Gemini-native `streamGenerateContent` | fresh session `HOME`, `GOOGLE_GEMINI_BASE_URL`, `GEMINI_API_KEY`; `--sandbox --output-format stream-json --model ... --print ...` | `agy 1.1.12`만 승인된다. API-key provider에 `--effort`를 전달하지 않으며 ambient user config를 읽지 않는다. |
|
|
| Codex | OpenAI-compatible Responses | fresh session `HOME`, isolated `iop_benchmark` provider override, `IOP_BENCHMARK_API_KEY`; `exec --sandbox workspace-write --json --ephemeral --ignore-user-config --strict-config` | user config를 읽지 않고 격리 workspace에만 쓸 수 있다. current adapter effort는 `xhigh`; caller binding event가 없으면 admitted config binding을 사용하고, 보고된 mismatch는 거부한다. |
|
|
|
|
세 caller child에는 필요한 `PATH`와 CA 변수만 allowlist로 전달한다. parent의 caller/provider 설정이나 unrelated secret은 상속하지 않는다.
|
|
|
|
agy의 public base는 adapter가 cell별로 다음처럼 route-qualified 한다.
|
|
|
|
```text
|
|
<edge-origin>/gemini/<route-id>
|
|
```
|
|
|
|
`GEMINI_BASE_URL`, `AGY_PROVIDER`, `AGY_OPENAI_BASE_URL`, `AGY_OPENAI_API_KEY`는 이 benchmark transport가 아니다.
|
|
|
|
official agy planner가 Gemini `generationConfig.responseMimeType`과 `responseSchema` 또는 `responseJsonSchema`를 보내면 Edge는 이를 기존 Chat `response_format`으로 변환한다. function declaration은 official SDK가 사용하는 `parameters`와 `parametersJsonSchema` 표기 중 정확히 하나를 허용한다. 동의어 필드가 동시에 있거나 schema가 JSON object가 아니면 provider dispatch 전에 거부한다. 구조가 유효한 agy `result.status=ERROR`는 stream parser 오류로 바꾸지 않고 caller process 실패로 기록한다.
|
|
|
|
## 7. Benchmark 고정 설정
|
|
|
|
| 설정 | 값 |
|
|
|---|---|
|
|
| pipeline version | `2` |
|
|
| environment | `dev` |
|
|
| execution seed | `bench-02-c01-c09-v1` |
|
|
| repetitions | `1` |
|
|
| session policy | `fresh` |
|
|
| setup cache policy | `isolated` |
|
|
| run timeout | 300 seconds |
|
|
| idle timeout | 30 seconds |
|
|
| quiet window | 10 seconds |
|
|
| cleanup grace | 5 seconds |
|
|
| desktop viewport | `1920x1080` |
|
|
| mobile viewport | `375x812` |
|
|
| output root | `agent-test/runs/bench-02` |
|
|
| rubric | `one-shot-agent-comparison-v1` |
|
|
|
|
execution seed로 결정되는 현재 slot 순서는 다음과 같다. 표의 C 번호 순서와 실제 실행 순서는 다르다.
|
|
|
|
1. C02 Claude→Gemini direct
|
|
2. C05 Codex→GPT direct
|
|
3. C03 agy→Gemini direct
|
|
4. C06 Claude→Gemini hybrid
|
|
5. C08 Claude→GPT hybrid
|
|
6. C09 Codex→GPT hybrid
|
|
7. C01 Claude→Sonnet direct
|
|
8. C07 agy→Gemini hybrid
|
|
9. C04 Claude→GPT direct
|
|
|
|
## 8. C01-C09 matrix와 route binding
|
|
|
|
| Cell | Caller | Route kind/id | Requested model/effort | Effective stage binding |
|
|
|---|---|---|---|---|
|
|
| C01 | Claude | direct / `claude-sonnet-5` | `claude-sonnet-5` / `max` | request=`claude-sonnet-5` max |
|
|
| C02 | Claude | direct / `gemini-3.6-flash` | `gemini-3.6-flash` / `high` | request=`gemini-3.6-flash` high |
|
|
| C03 | agy | direct / `gemini-3.6-flash` | `gemini-3.6-flash` / `high` | request=`gemini-3.6-flash` high |
|
|
| C04 | Claude | direct / `gpt-5.6-luna` | `gpt-5.6-luna` / `xhigh` | request=`gpt-5.6-luna` xhigh |
|
|
| C05 | Codex | direct / `gpt-5.6-luna` | `gpt-5.6-luna` / `xhigh` | request=`gpt-5.6-luna` xhigh |
|
|
| C06 | Claude | preset / `gemini-hybrid` | `gemini-hybrid` / `high` | selector/plan/review/repair=`gemini-3.6-flash` high; work=`ornith-fast` |
|
|
| C07 | agy | preset / `gemini-hybrid` | `gemini-hybrid` / `high` | selector/plan/review/repair=`gemini-3.6-flash` high; work=`ornith-fast` |
|
|
| C08 | Claude | preset / `gpt-hybrid` | `gpt-hybrid` / `xhigh` | selector/plan/review/repair=`gpt-5.6-terra` high; work=`ornith-fast` |
|
|
| C09 | Codex | preset / `gpt-hybrid` | `gpt-hybrid` / `xhigh` | selector/plan/review/repair=`gpt-5.6-terra` high; work=`ornith-fast` |
|
|
|
|
hybrid preset은 caller가 stage를 따로 호출하는 구조가 아니다. 하나의 caller request 안에서 Edge가 selector/plan/work/review/repair를 소유한다.
|
|
|
|
## 9. Fixture와 결과 조건
|
|
|
|
공통 task는 fictional product “Lumen Atlas”의 responsive one-page landing page다.
|
|
|
|
- prompt: `scripts/fixtures/agent-comparison-benchmark/prompt.md`
|
|
- copy: `scripts/fixtures/agent-comparison-benchmark/reference.txt`
|
|
- images: `aurora-grid.svg`, `orbit-rings.svg`
|
|
- 생성 파일: workspace root의 `index.html`, `styles.css`, `script.js` 정확히 세 개
|
|
- 외부 asset, framework, package manager, build tool, analytics와 network dependency 금지
|
|
- desktop/mobile responsive, semantic HTML, focus/contrast/accessibility 요구
|
|
- 각 attempt는 fresh workspace/session에서 task를 한 번만 제출
|
|
|
|
fixture checksum은 manifest의 값이 유일한 기준이다. prompt나 asset을 변경하면 기존 run과 비교하지 말고 manifest/version/checksum을 함께 갱신하는 별도 작업으로 처리한다.
|
|
|
|
## 10. Process environment 준비
|
|
|
|
다음은 value를 출력하지 않는 process-local 예시다. `<edge-host>`를 문서에 실제 값으로 치환하지 말고 실행 환경에서만 주입한다.
|
|
|
|
Public live registry가 소비하는 environment contract는 다음과 같다.
|
|
|
|
| Variable | 값/의미 | Durable evidence |
|
|
|---|---|---|
|
|
| `IOP_BENCH_CLAUDE_BASE_URL` | managed Edge HTTPS origin | raw 값 금지; endpoint digest만 허용 |
|
|
| `IOP_BENCH_AGY_BASE_URL` | managed Edge HTTPS origin; adapter가 `/gemini/<route-id>`를 추가 | raw 값 금지; endpoint digest만 허용 |
|
|
| `IOP_BENCH_CODEX_BASE_URL` | managed Edge OpenAI-compatible `/v1` base | raw 값 금지; endpoint digest만 허용 |
|
|
| `IOP_BENCH_CLAUDE_SECRET_ENV` | Claude가 사용할 secret-bearing variable 이름 | variable 이름만 허용 |
|
|
| `IOP_BENCH_AGY_SECRET_ENV` | agy가 사용할 secret-bearing variable 이름 | variable 이름만 허용 |
|
|
| `IOP_BENCH_CODEX_SECRET_ENV` | Codex가 사용할 secret-bearing variable 이름 | variable 이름만 허용 |
|
|
| `IOP_BENCH_SHARED_TOKEN` | 이 가이드 예시의 secret-bearing variable | 값 기록 금지 |
|
|
| `SSL_CERT_FILE` | dev Edge HTTPS CA certificate path | repository-relative file reference만 허용 |
|
|
| `NODE_EXTRA_CA_CERTS` | Node.js caller용 동일 CA certificate path | repository-relative file reference만 허용 |
|
|
| `IOP_BENCH_CONFIG_OBSERVATION_ENV` | config JSON을 보유한 variable 이름 | variable 이름만 허용 |
|
|
| `BENCH_CONFIG` | schema v1 route/model/stage observation JSON | secret은 없지만 runtime과 일치하는 canonical digest만 evidence에 기록 |
|
|
| `PATH` | caller binary resolution | resolved executable path/version만 preflight에서 확인 |
|
|
|
|
Child adapter가 내부적으로 만드는 값은 caller별로 격리된다.
|
|
|
|
- Claude: `ANTHROPIC_BASE_URL`, `ANTHROPIC_API_KEY`, restricted `Read,Write,Edit`, nonessential traffic/autoupdater disable flags와 CA variables
|
|
- agy: fresh session `HOME`, `GOOGLE_GEMINI_BASE_URL`, `GEMINI_API_KEY`, `LANG=C.UTF-8`, `LC_ALL=C.UTF-8`, `TZ=UTC`와 CA variables
|
|
- Codex: fresh session `HOME`, `IOP_BENCHMARK_API_KEY`, strict ephemeral provider override와 CA variables
|
|
|
|
이 child variable은 사용자가 별도로 준비할 값이 아니다. live registry가 위 public contract에서 파생하며, parent의 같은 이름 값을 그대로 신뢰하거나 상속하지 않는다.
|
|
|
|
```bash
|
|
set -euo pipefail
|
|
|
|
benchmark_edge_origin="${BENCHMARK_EDGE_ORIGIN:?set BENCHMARK_EDGE_ORIGIN to the managed dev Edge HTTPS origin}"
|
|
read -r IOP_BENCH_SHARED_TOKEN < token/.iop-bench
|
|
export IOP_BENCH_SHARED_TOKEN
|
|
|
|
export IOP_BENCH_CLAUDE_BASE_URL="$benchmark_edge_origin"
|
|
export IOP_BENCH_AGY_BASE_URL="$benchmark_edge_origin"
|
|
export IOP_BENCH_CODEX_BASE_URL="$benchmark_edge_origin/v1"
|
|
|
|
export IOP_BENCH_CLAUDE_SECRET_ENV=IOP_BENCH_SHARED_TOKEN
|
|
export IOP_BENCH_AGY_SECRET_ENV=IOP_BENCH_SHARED_TOKEN
|
|
export IOP_BENCH_CODEX_SECRET_ENV=IOP_BENCH_SHARED_TOKEN
|
|
|
|
export SSL_CERT_FILE="$PWD/token/iop-dev-ca.pem"
|
|
export NODE_EXTRA_CA_CERTS="$PWD/token/iop-dev-ca.pem"
|
|
|
|
export IOP_BENCH_CONFIG_OBSERVATION_ENV=BENCH_CONFIG
|
|
BENCH_CONFIG="$(python3 - <<'PY'
|
|
import json
|
|
|
|
routes = [
|
|
{"route_kind":"direct","route_id":"claude-sonnet-5","model":"claude-sonnet-5","bindings":[{"stage":"request","model":"claude-sonnet-5","effort":"max"}]},
|
|
{"route_kind":"direct","route_id":"gemini-3.6-flash","model":"gemini-3.6-flash","bindings":[{"stage":"request","model":"gemini-3.6-flash","effort":"high"}]},
|
|
{"route_kind":"direct","route_id":"gpt-5.6-luna","model":"gpt-5.6-luna","bindings":[{"stage":"request","model":"gpt-5.6-luna","effort":"xhigh"}]},
|
|
{"route_kind":"execution_preset","route_id":"gemini-hybrid","model":"gemini-hybrid","bindings":[{"stage":"selector","model":"gemini-3.6-flash","effort":"high"},{"stage":"plan","model":"gemini-3.6-flash","effort":"high"},{"stage":"work","model":"ornith-fast","effort":None},{"stage":"review","model":"gemini-3.6-flash","effort":"high"},{"stage":"repair","model":"gemini-3.6-flash","effort":"high"}]},
|
|
{"route_kind":"execution_preset","route_id":"gpt-hybrid","model":"gpt-hybrid","bindings":[{"stage":"selector","model":"gpt-5.6-terra","effort":"high"},{"stage":"plan","model":"gpt-5.6-terra","effort":"high"},{"stage":"work","model":"ornith-fast","effort":None},{"stage":"review","model":"gpt-5.6-terra","effort":"high"},{"stage":"repair","model":"gpt-5.6-terra","effort":"high"}]},
|
|
]
|
|
print(json.dumps({"schema_version":"1","routes":routes}, separators=(",",":")))
|
|
PY
|
|
)"
|
|
export BENCH_CONFIG
|
|
```
|
|
|
|
중요한 의미:
|
|
|
|
- `IOP_BENCH_*_SECRET_ENV`의 값은 secret이 아니라 실제 secret을 보유한 environment variable의 이름이다.
|
|
- 세 caller는 같은 IOP principal을 사용하지만 서로 다른 protocol base를 받는다.
|
|
- `BENCH_CONFIG`는 runtime에서 독립적으로 확인한 route/stage snapshot이어야 한다. manifest를 보고 임의 합성한 값을 live readiness evidence로 사용하면 안 된다.
|
|
- endpoint와 config는 evidence에 raw 값 대신 digest identity로만 남는다.
|
|
|
|
작업 후에는 같은 shell에서 다음 변수를 제거한다.
|
|
|
|
```bash
|
|
unset IOP_BENCH_SHARED_TOKEN
|
|
unset IOP_BENCH_CLAUDE_BASE_URL IOP_BENCH_AGY_BASE_URL IOP_BENCH_CODEX_BASE_URL
|
|
unset IOP_BENCH_CLAUDE_SECRET_ENV IOP_BENCH_AGY_SECRET_ENV IOP_BENCH_CODEX_SECRET_ENV
|
|
unset IOP_BENCH_CONFIG_OBSERVATION_ENV BENCH_CONFIG
|
|
unset SSL_CERT_FILE NODE_EXTRA_CA_CERTS
|
|
unset BENCHMARK_EDGE_ORIGIN benchmark_edge_origin
|
|
```
|
|
|
|
## 11. 실행 절차
|
|
|
|
manifest path는 모든 명령에서 동일하게 사용한다.
|
|
|
|
```bash
|
|
benchmark_manifest=scripts/fixtures/agent-comparison-benchmark-iop-one-shot.json
|
|
```
|
|
|
|
### 11.1 Source와 deterministic verification
|
|
|
|
```bash
|
|
python3 -m unittest scripts.agent_benchmark.codex_iop_test scripts.agent_benchmark.connectivity_integration_test
|
|
python3 -m unittest discover -s scripts/agent_benchmark -p '*_test.py'
|
|
python3 scripts/agent_comparison_benchmark.py validate --manifest "$benchmark_manifest"
|
|
git diff --check
|
|
```
|
|
|
|
필요하면 변경 범위에 따라 Go tests와 managed credential qualification도 실행한다.
|
|
|
|
```bash
|
|
go test -count=1 ./...
|
|
credential_smoke_parent="$(mktemp -d /tmp/iop-benchmark-credential.XXXXXX)"
|
|
TMPDIR="$credential_smoke_parent" make test-credential-slot-smoke
|
|
rmdir "$credential_smoke_parent"
|
|
```
|
|
|
|
### 11.2 Public preflight
|
|
|
|
10절의 환경을 같은 shell에 준비한 뒤 실행한다.
|
|
|
|
```bash
|
|
python3 scripts/agent_comparison_benchmark.py preflight --manifest "$benchmark_manifest"
|
|
```
|
|
|
|
성공 조건:
|
|
|
|
```text
|
|
status=ready ready=9 registration_required=0 implementation_gap=0
|
|
```
|
|
|
|
preflight는 다음을 함께 확인한다.
|
|
|
|
- caller binary/version/help
|
|
- Edge model catalog
|
|
- principal auth와 endpoint compatibility
|
|
- direct/preset route 존재
|
|
- requested model/effort
|
|
- exact stage binding과 order
|
|
- official agy transport capability
|
|
|
|
`registration_required` 또는 `implementation_gap`이면 즉시 중단한다. alias, model, effort, route나 caller를 대체하지 않는다. preflight-only run root는 evidence이므로 삭제하지 않는다.
|
|
|
|
### 11.3 Scored execution
|
|
|
|
fresh preflight와 명시적 실행 권한이 있는 현재 plan에서만 다음 명령을 한 번 호출한다.
|
|
|
|
```bash
|
|
python3 scripts/agent_comparison_benchmark.py run --manifest "$benchmark_manifest"
|
|
```
|
|
|
|
- direct CLI stdout/stderr와 exit code를 보존한다.
|
|
- CLI가 출력한 canonical `run-...` id만 이후 `status`, `score`, `report`에 사용한다.
|
|
- command가 nonzero여도 같은 plan에서 `run`을 다시 호출하지 않는다.
|
|
- CLI가 run id를 출력하지 않으면 임의 id나 성공 pointer를 만들지 않는다.
|
|
- caller나 provider를 CLI 밖에서 별도로 호출해 scored result를 보충하지 않는다.
|
|
|
|
### 11.4 Status
|
|
|
|
```bash
|
|
python3 scripts/agent_comparison_benchmark.py status \
|
|
--manifest "$benchmark_manifest" \
|
|
--run-id "$benchmark_run_id"
|
|
```
|
|
|
|
현재 comparison execution 완료 조건:
|
|
|
|
- `success + failed + timed_out + cancelled = 9`
|
|
- `running = 0`
|
|
- `interrupted = 0`
|
|
- 각 cell/repetition에 retained terminal attempt가 존재
|
|
|
|
실패/timed-out/cancelled는 terminal evidence지만 성공 결과가 아니므로 scoring eligibility와 최종 비교에서 별도로 표시된다.
|
|
|
|
### 11.5 Blind scoring
|
|
|
|
유효한 execution run에 대해서만 수행한다.
|
|
|
|
```bash
|
|
python3 scripts/agent_comparison_benchmark.py score \
|
|
--manifest "$benchmark_manifest" \
|
|
--run-id "$benchmark_run_id"
|
|
```
|
|
|
|
evaluator는 Codex→`gpt-5.6-luna` xhigh direct route다. identity가 제거된 blind workspace만 보며 source cell identity mapping은 blind tree 밖에 유지한다.
|
|
|
|
automatic gate 실패나 lifecycle failure는 `unscored`이고 0점으로 바꾸지 않는다. `scoring_failed`도 명시적 `--retry-scoring-failed` 권한 없이 재시도하지 않는다.
|
|
|
|
### 11.6 Report
|
|
|
|
```bash
|
|
python3 scripts/agent_comparison_benchmark.py report \
|
|
--manifest "$benchmark_manifest" \
|
|
--run-id "$benchmark_run_id"
|
|
```
|
|
|
|
report는 run root의 immutable evidence를 읽어 idempotent `report.md`를 만든다. 기존 report 내용과 새 projection이 다르면 덮어쓰지 않고 실패한다.
|
|
|
|
## 12. 수집 evidence
|
|
|
|
각 attempt는 다음 범주의 evidence를 가진다.
|
|
|
|
| 범주 | 내용 |
|
|
|---|---|
|
|
| lifecycle | submission, first output, finish, idle, exit, quiet, cleanup와 terminal reason |
|
|
| timeline | submitted, first output, first workspace write observation/mtime, total duration |
|
|
| usage | input/output/reasoning/cache read/cache write/total tokens, model/tool calls와 duration |
|
|
| workspace | fresh session identity, fixture checksum, testbed provenance, generated file tree |
|
|
| web validation | generated files, static safety, images, network, console, responsive, accessibility |
|
|
| screenshots | desktop `1920x1080`, mobile `375x812` |
|
|
| scoring | eligibility, blind allocation, rubric worksheet, score status |
|
|
|
|
caller가 보고하지 않은 metric은 0으로 만들지 않고 `unavailable`과 reason/source를 보존한다.
|
|
|
|
Codex current usage mapping:
|
|
|
|
| Caller JSONL field | Canonical metric |
|
|
|---|---|
|
|
| `input_tokens` | `input_tokens` |
|
|
| `cached_input_tokens` | `cached_input_tokens` |
|
|
| `cache_write_input_tokens` | `cache_write_tokens` |
|
|
| `output_tokens` | `output_tokens` |
|
|
| `reasoning_output_tokens` | `reasoning_tokens` |
|
|
| `total_tokens` | `total_tokens` |
|
|
|
|
누락된 `total_tokens`는 하위 category 합으로 재구성하지 않는다.
|
|
|
|
## 13. 자동 gate와 100점 rubric
|
|
|
|
automatic web gates는 scoring eligibility만 결정하고 점수에 포함되지 않는다.
|
|
|
|
| Gate |
|
|
|---|
|
|
| generated files |
|
|
| static safety |
|
|
| local images |
|
|
| no external network dependency |
|
|
| console safety |
|
|
| responsive layout |
|
|
| accessibility |
|
|
|
|
quality rubric:
|
|
|
|
| Category | Max |
|
|
|---|---:|
|
|
| requirements fidelity | 25 |
|
|
| visual completeness | 25 |
|
|
| responsive accessibility | 15 |
|
|
| image/detail usage | 10 |
|
|
| behavior stability | 10 |
|
|
| code quality | 10 |
|
|
| self verification | 5 |
|
|
| Total | 100 |
|
|
|
|
## 14. 실패 처리와 재개 원칙
|
|
|
|
| 상황 | 조치 |
|
|
|---|---|
|
|
| manifest invalid | source/manifest를 수정하고 validate부터 다시 시작한다. run을 만들지 않는다. |
|
|
| preflight not ready | blocker를 해결하고 fresh preflight한다. attempt를 할당하지 않는다. |
|
|
| caller launch 전 interruption | retained evidence를 보존한다. run tree를 직접 수정하지 않는다. |
|
|
| lifecycle/parser failure | exact retained output으로 source 원인을 수정하고 deterministic regression을 추가한다. |
|
|
| terminal failed/timed_out/cancelled | evidence로 보존한다. 암묵 retry하지 않는다. |
|
|
| state가 `running`이지만 process가 없음 | manual JSON 수정/삭제/reconcile을 하지 않는다. reviewer evidence로 남기고 승인된 새 plan에서만 다음 상태를 결정한다. |
|
|
| scoring_failed | 0점 처리하지 않는다. 명시적 retry 권한 없이는 중단한다. |
|
|
| report unavailable | run evidence를 수정하거나 report를 수작업 생성하지 않는다. |
|
|
|
|
현재 milestone의 원칙은 old incomplete/failed run을 `resume --retry-failed`하지 않고 distinct fresh run을 만드는 것이다. 일반 CLI가 `resume`을 지원한다는 사실이 현재 benchmark에서 사용 권한을 뜻하지 않는다.
|
|
|
|
## 15. Secret-safe 기록 규칙
|
|
|
|
다음 값은 tracked docs, task review, run metadata, log, metric label과 command argument에 남기지 않는다.
|
|
|
|
- IOP principal token
|
|
- raw provider credential
|
|
- private key, at-rest key, issuer/recipient private key
|
|
- slot alias와 lease id
|
|
- credential-bearing URL
|
|
- raw prompt/response, tool input/output
|
|
- caller/provider session content
|
|
|
|
허용되는 내용:
|
|
|
|
- secret file의 상대 path와 존재/mode
|
|
- route/model/stage 이름
|
|
- safe credential slot reference/revision
|
|
- hashed endpoint/config/spec identity
|
|
- redacted lifecycle 상태, duration과 usage count
|
|
- run id와 attempt identity가 필요한 controller evidence
|
|
|
|
incident evidence를 보존하기 전에 repository와 run output에서 secret 원문이 없는지 확인한다. 의심되는 artifact는 내용을 복사하지 말고 path와 redaction failure만 보고한다.
|
|
|
|
## 16. 실행 전 체크리스트
|
|
|
|
- [ ] 현재 manifest validation 통과
|
|
- [ ] focused/full deterministic tests fresh PASS
|
|
- [ ] benchmark runner와 `../iop-s2` provenance 확인
|
|
- [ ] Claude/agy/Codex command와 current version/help 확인
|
|
- [ ] `token/.iop-bench`, CA와 provider provisioning source 존재/mode 확인; 내용 출력 없음
|
|
- [ ] dev runtime source/build identity, process와 listener 확인
|
|
- [ ] managed projection, provider slot/route와 no-legacy-fallback 확인
|
|
- [ ] config observation이 runtime route/stage와 정확히 일치
|
|
- [ ] public preflight `ready=9`
|
|
- [ ] 현재 plan이 exactly one scored run을 소유하고 사용자 권한이 명확함
|
|
- [ ] old run resume/retry/state edit 계획 없음
|
|
- [ ] run 이후 status, scoring, report의 run id 전달 경로 준비
|
|
|
|
## 17. 관련 구현
|
|
|
|
- controller/state: `scripts/agent_benchmark/attempts.py`
|
|
- manifest: `scripts/agent_benchmark/manifest.py`
|
|
- lifecycle: `scripts/agent_benchmark/lifecycle.py`
|
|
- workspace isolation: `scripts/agent_benchmark/workspace.py`
|
|
- live routing/admission: `scripts/agent_benchmark/live_iop.py`
|
|
- Claude adapter: `scripts/agent_benchmark/claude_iop.py`
|
|
- agy adapter: `scripts/agent_benchmark/agy_iop.py`
|
|
- Codex adapter: `scripts/agent_benchmark/codex_iop.py`
|
|
- measurement: `scripts/agent_benchmark/measurement.py`
|
|
- browser/web gate: `scripts/agent_benchmark/web_validation.py`
|
|
- blind scoring: `scripts/agent_benchmark/scoring.py`
|
|
- rubric: `scripts/agent_benchmark/rubric.py`
|
|
- report: `scripts/agent_benchmark/reporting.py`
|