@@ -17,7 +17,7 @@ loader.py / config.py --> validated cases, gate config, input hashes
1717 v
1818backends.py
1919 |-- FakeBackend -> FakeModel + FakeJudge + FakeOptimizer
20- `-- SDKBackend -> AgentOptimizer + TargetPrompt + user call_agent
20+ `-- SDKBackend -> AgentOptimizer + TargetPrompt + user call_agent + AgentEvaluator
2121 |
2222 v
2323attribution.py -> evaluator.py -> gate.py -> report.py
@@ -87,47 +87,59 @@ python examples/optimization/eval_optimize_loop/run_pipeline.py \
8787needed by that callable. SDK mode never silently falls back to fake mode. The
8888generated reproducibility command records the actual ` --sdk-call-agent `
8989` module:function ` target and file/config paths, but it does not record API keys
90- or other provider secrets.
90+ or other provider secrets. Persisted optimizer config snapshots recursively
91+ replace credential values with ` <redacted> ` .
92+
93+ The checked-in ` train.evalset.json ` and ` val.evalset.json ` use the SDK
94+ ` EvalSet ` schema (` eval_set_id ` / ` eval_cases ` ). Fake mode reads its
95+ deterministic judge metadata from each case's ` session_input.state `
96+ (` eval_optimize_expectation ` , ` eval_optimize_tags ` , and
97+ ` eval_optimize_protected ` ).
9198
9299SDK optimizer config and wrapper gate config are intentionally separate.
93100` --optimizer-config ` is passed unchanged to ` AgentOptimizer.optimize ` , so it must
94101follow the SDK ` OptimizeConfigFile ` schema. Put wrapper-only gate settings in
95102` --gate-config ` (for example `{"gate": {"min_val_score_improvement": 0.05,
96- "max_total_cost": 1.0}}` ). If ` --gate-config` is omitted, the wrapper uses the
97- same default aggregate gate values as the fake example.
103+ "allow_new_hard_fail": false, "max_score_drop_per_case": 0.0,
104+ "max_total_cost": null}}` ). If ` --gate-config` is omitted, the wrapper uses the
105+ same default gate values as fake mode and auto-adds validation cases marked with
106+ ` eval_optimize_protected ` . A numeric ` max_total_cost ` fails closed with
107+ ` cost_unavailable ` whenever the backend cannot prove a complete total. Use
108+ ` null ` only when you intentionally want to run the quality gates without a cost
109+ limit; ` reported_optimizer_cost ` remains an incomplete lower bound, not a total.
98110
99111` --target-prompt name=path ` may be repeated. If omitted, SDK mode keeps the old
100112single-field behavior and registers ` system_prompt=--prompt ` . A run can optimize
101113only ` router_prompt ` , only ` skill_prompt ` , or any set of named fields as long as
102114` OptimizeResult.best_prompts ` returns every registered field.
103115
104- Fake mode is the complete per-case closed loop. SDK mode is the real
105- ` AgentOptimizer ` / ` TargetPrompt ` path with an aggregate wrapper gate. When the
106- SDK optimizer returns a best prompt, this wrapper maps ` OptimizeResult `
107- aggregate fields into the JSON/Markdown report: baseline/best pass rate,
108- pass-rate improvement, metric breakdowns, token usage, duration, LLM cost,
109- all ` best_prompts ` , and round summaries. SDK mode applies
110- ` gate_status: partial_applied ` : it checks ` status == SUCCEEDED ` , validation
111- improvement against ` gate.min_val_score_improvement ` , and total LLM cost against
112- ` gate.max_total_cost ` . Protected-case regression, new-hard-failure, per-case
113- delta, and per-case score-drop checks are not claimed in SDK mode unless the SDK
114- exposes full per-case validation scores; they are listed in
115- ` not_applied_checks ` .
116-
117- Fake mode uses a deterministic run id (` eval_optimize_loop_seed_<seed> ` ) so the
118- example outputs are byte-stable. SDK mode is append-only by default: the wrapper
119- derives a compact UTC ` run.run_id ` from the SDK result ` started_at ` when
120- available, otherwise from the current UTC timestamp. Pass ` --run-id ` only when
121- a fixed audit path is useful for tests or local smoke runs; only explicit
122- ` --run-id ` values are included in the reproducibility command.
116+ Fake mode is the deterministic offline closed loop. SDK mode is the real
117+ ` AgentOptimizer ` / ` TargetPrompt ` path followed by explicit post-optimization
118+ ` AgentEvaluator ` runs. The adapter deduplicates every prompt bundle found in
119+ ` OptimizeResult.rounds[].candidate_prompts ` and ` best_prompts ` . The shared
120+ pipeline temporarily installs each bundle, reruns the complete train and
121+ validation evalsets, restores the exact source bytes, then computes the same
122+ per-case deltas and complete gate decision used by fake mode. SDK case results
123+ carry real per-metric scores, failure reason/evidence, and ` trace_available ` ;
124+ missing SDK trace data is reported as unavailable rather than fabricated.
125+ Optimizer aggregates remain audit context and are never substituted for these
126+ post-optimization case results.
127+
128+ Both modes default to a collision-resistant run id of the form
129+ ` eval_optimize_loop_<mode>_<UTC timestamp>_<random suffix> ` . Pass ` --run-id `
130+ when a stable path is useful. Existing temporary or final run ids are never
131+ reused or overwritten. The checked-in example is generated in a fresh output
132+ directory with the explicit id ` example ` .
123133
124134## Source Prompt Writes
125135
126- The default is ** no source write-back** . The baseline prompt file is not modified
127- by fake mode, and ` SDKBackend ` calls ` AgentOptimizer.optimize(update_source=False) `
128- unless ` --update-source ` is explicitly passed. The report records
129- ` run.update_source ` and the Markdown report states whether source write-back was
130- enabled.
136+ The default is ** no source write-back** . ` SDKBackend ` always calls
137+ ` AgentOptimizer.optimize(update_source=False) ` so the optimizer can never commit
138+ early. ` --update-source ` is handled only by the shared pipeline: after all
139+ candidate evaluations, full gate acceptance, input-integrity checks, and audit
140+ preparation, it transactionally writes the selected prompt bundle. This rule is
141+ the same in fake and SDK modes. The report records the requested action and the
142+ terminal ` writeback ` state.
131143
132144## Candidate Behavior
133145
@@ -139,49 +151,57 @@ The fake optimizer proposes exactly two candidates:
139151 requested; it improves validation without protected-case regression, so the
140152 gate may accept it.
141153
142- The fake model is driven by ` EvalCase.expectation ` , ` tags ` , ` protected ` , and
143- optional ` simulated_outputs ` ; it does not depend on sample ` case_id ` names.
154+ The fake model reads only the prompt and user input. It cannot inspect
155+ expectations, labels, tags, protected markers, or case ids. Those fields are
156+ available only to the fake judge, failure attribution, and acceptance gate.
144157
145158## Reports
146159
147- ` optimization_report.json ` includes:
160+ ` optimization_report.json ` uses ` eval_optimize_loop.v2 ` and includes:
148161
149162- ` schema_version ` ;
150163- ` run ` metadata: mode, fake flags, trace flag, case counts, update-source flag,
151164 and input paths;
152165- ` baseline ` plus compatibility fields ` baseline_train ` and
153166 ` baseline_validation ` ;
154- - all candidate train/validation results, rationale, and prompt diff;
167+ - ` baseline_prompts ` , all candidate prompt bundles, complete train/validation
168+ results, rationale, and prompt diff;
169+ - optimizer ` rounds ` , ` cost_summary ` , and terminal ` writeback ` ;
155170- per-case deltas with ` delta_type ` (` new_pass ` , ` new_fail ` , ` score_up ` ,
156171 ` score_down ` , ` unchanged ` );
157172- failure attribution summary and attribution accuracy when expected labels are
158173 present;
159174- gate decisions with overfit detection, protected regressions, new hard
160- failures, excessive drops, cost fields, and SDK ` not_applied_checks ` when
161- per-case validation details are not exposed;
162- - audit data: seed, duration, config hash, input hashes, candidate prompt hashes ,
163- cost, prompt diffs , and reproducibility command.
175+ failures, excessive drops, and cost fields in both fake and SDK modes;
176+ - audit data: seed, real duration, redacted config and raw-file hashes, input
177+ hashes, candidate artifact mapping, complete/incomplete cost semantics ,
178+ prompt integrity journal , and reproducibility command.
164179
165- ` gate.max_total_cost ` is interpreted as the total evaluated run cost at the time
166- each candidate is judged: baseline cost plus all candidates evaluated so far,
167- including rejected candidates. This makes budget decisions deterministic and
168- auditable when multiple candidates are considered.
180+ When ` CostSummary.complete ` is true, ` gate.max_total_cost ` is interpreted as the
181+ total evaluated run cost when each candidate is judged: baseline cost plus all
182+ candidates evaluated so far, including rejected candidates. When it is false,
183+ the report separates ` known_run_cost ` and ` reported_optimizer_cost ` , and any
184+ numeric total-cost gate rejects with ` cost_unavailable ` .
169185
170186` optimization_report.md ` includes final decision, gate reasons, score table,
171187per-case delta table, failure attribution summary, cost/audit details, prompt
172188diffs, and the reproducibility command.
173189
174- ` report.py ` also writes audit artifacts under ` output_dir/runs/<run_id>/ ` :
175-
176- - ` config.snapshot.json ` ;
177- - ` input_hashes.json ` with train, validation, optimizer, prompt,
178- ` target_prompts.<field> ` , and optional ` gate_config ` hashes;
179- - fake mode: ` candidate_prompts/<candidate_id>/system_prompt.txt ` ;
180- - SDK mode: ` candidate_prompts/<candidate_id>/<field_name>.txt ` for every
181- returned ` best_prompts ` field, plus ` bundle.txt ` with the combined prompt
182- shown in the wrapper report;
183- - ` case_results/<candidate_id>_<split>.json ` ;
184- - ` prompt_diffs/<candidate_id>.diff ` .
190+ Audit data is first written under ` output_dir/runs/.<run_id>.tmp/ ` . After every
191+ final artifact and prompt-integrity check succeeds, the directory is published
192+ once to ` output_dir/runs/<run_id>/ ` with no-replace semantics. Windows uses
193+ ` MoveFileExW ` without a replace flag; Linux uses
194+ ` renameat2(RENAME_NOREPLACE) ` . A POSIX platform without that atomic primitive
195+ fails closed. Published run directories are authoritative and immutable; the
196+ top-level JSON/Markdown files are convenience copies created afterward.
197+
198+ Each run contains ` pre_write_report.* ` , final reports, a redacted config
199+ snapshot, input hashes, ` baseline_prompts/ ` , every candidate prompt field and
200+ diff, baseline and candidate split results, per-round JSON, per-case deltas,
201+ gate decisions, evaluation failures, ` writeback.json ` , and the durable
202+ writeback journal. SDK optimizer artifacts are kept beneath ` optimizer/ ` with a
203+ redacted config snapshot. ` artifact_manifest.json ` maps the logical artifacts
204+ and records the SHA-256 and byte size of every file.
185205
186206The repository keeps only stable examples:
187207
@@ -197,9 +217,9 @@ directories are not committed.
197217python -m pytest examples/optimization/eval_optimize_loop/tests
198218```
199219
200- The tests cover fake hidden-sample generalization, config validation, gate
201- rejection paths, protected-case behavior, failure attribution, tool/knowledge
202- judge paths, SDK adapter wiring through monkeypatching, deterministic report
203- generation, and both CLI forms. CI can run fake mode plus the monkeypatched SDK
204- smoke tests without real API credentials; real SDK/model calls are opt-in local
205- or integration runs.
220+ The tests cover fake hidden-sample generalization, SDK EvalSet compatibility,
221+ config validation, gate rejection paths, protected-case behavior, failure
222+ attribution, tool/knowledge judge paths, SDK adapter and post-evaluator wiring
223+ through monkeypatching, deterministic report generation, and both CLI forms. CI
224+ can run fake mode plus the monkeypatched SDK smoke tests without real API
225+ credentials; real SDK/model calls are opt-in local or integration runs.
0 commit comments