Skip to content

Commit 2e5eb6c

Browse files
committed
feat(evaluation): 完成第五阶段报告与审计闭环
1 parent 1504625 commit 2e5eb6c

8 files changed

Lines changed: 792 additions & 20 deletions

File tree

examples/optimization/eval_optimize_loop/README.md

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
# Evaluation + Optimization Loop — Stage 4
1+
# Evaluation + Optimization Loop — Stage 5
22

33
This example provides an auditable evaluation and prompt-optimization loop.
44
Stages 1–3 prepare an isolated prompt workspace, run baseline and candidate
55
evaluations on both train and validation datasets, normalize failures, build
66
case diffs, detect overfitting, and apply an independent Gate. Stage 4 adds a
77
common Candidate Provider boundary, an `AgentOptimizer` adapter, and guarded
8-
source-prompt writeback.
8+
source-prompt writeback. Stage 5 publishes a complete JSON/Markdown report and
9+
artifact index, or preserves a standalone failure report when a run fails.
910

1011
The deterministic fake mode still runs without a model, API key, judge, or
1112
optimizer. Its built-in scenarios produce ACCEPT for `improve` and REJECT for
1213
both `no_improvement` and `overfit`:
1314

1415
```bash
1516
python examples/optimization/eval_optimize_loop/run_pipeline.py \
16-
--run-id local_stage4 \
17+
--run-id local_stage5 \
1718
--scenario improve
1819
```
1920

@@ -38,7 +39,8 @@ credential reuse the environment without writing their resolved values to run
3839
artifacts. `--run-real` is mandatory so an accidental command cannot spend API
3940
quota. This entry always uses `pipeline.real.json`, where source writeback is
4041
disabled, and reports ACCEPT or REJECT without treating REJECT as a process
41-
failure.
42+
failure. Both CLIs print the paths of the JSON report, Markdown report, and
43+
artifact index after a completed run.
4244

4345
Applications with a custom agent can still use the Python integration point.
4446
Set `execution.mode` to `real`, prepare the run, and inject an async
@@ -67,6 +69,45 @@ pipeline evaluations: baseline train/validation and candidate train/validation.
6769
scores, and configuration snapshot are retained under `runs/<run-id>/optimizer/`
6870
when `artifacts.retain_optimizer_native_artifacts` is enabled.
6971

72+
## Report artifacts
73+
74+
A completed run atomically publishes the formal bundle at
75+
`runs/<run-id>/report/`:
76+
77+
```text
78+
report/
79+
├── optimization_report.json
80+
├── optimization_report.md
81+
├── artifact_index.json
82+
├── inputs/
83+
│ ├── pipeline_config.json
84+
│ ├── optimizer_config.json
85+
│ ├── train_evalset.json
86+
│ └── validation_evalset.json
87+
├── evaluations/
88+
│ ├── baseline_train.json
89+
│ ├── baseline_validation.json
90+
│ ├── candidate_train.json
91+
│ └── candidate_validation.json
92+
└── prompts/
93+
├── baseline/
94+
└── candidate/
95+
```
96+
97+
The formal `report/` directory is visible only after all required artifacts
98+
have been written and validated. If any post-preparation phase fails, the
99+
pipeline does not leave a partial formal report; it atomically writes
100+
`runs/<run-id>/failure_report.json` with the failed phase, completed phases,
101+
sanitized error information, source Prompt hashes, and already existing
102+
artifacts. A failure-report write error is surfaced together with the original
103+
pipeline error.
104+
105+
`artifact_index.json` records each artifact's relative path, SHA-256 hash, byte
106+
size, producer phase, and availability. Consumers can use those fields to
107+
verify that an artifact has not drifted since publication. Input copies are
108+
validated against the preparation snapshot, and sensitive resolved credentials
109+
are not accepted into the report bundle.
110+
70111
Source prompts are updated only when all of these conditions hold:
71112

72113
- Gate returns ACCEPT;
@@ -81,13 +122,15 @@ returned as failed. If rollback integrity cannot be proven, the pipeline raises
81122
an error instead of claiming the source is safe. The checked-in configuration
82123
keeps writeback disabled by default.
83124

84-
Elapsed duration is observable. Full monetary cost and token usage remain
85-
`unavailable` because an injected business agent may make calls that the SDK
86-
optimizer does not account for; native optimizer resource fields are retained
87-
without treating missing values as zero. JSON/Markdown reports and an artifact
88-
index are Stage 5 work.
125+
The report separates whole-pipeline resources from optimizer-only observations.
126+
Pipeline duration is observable, while full pipeline monetary cost and token
127+
usage remain `unavailable` because business-agent calls may not expose complete
128+
telemetry. In real mode, optimizer rounds, reflection calls, duration, cost, and
129+
token usage are reported independently from the native optimizer result; an
130+
unreliable or incomplete field stays `unavailable` instead of being treated as
131+
zero. In fake mode, optimizer-only fields are `not_applicable`.
89132

90-
Run the Stage 1–4 tests with:
133+
Run the Stage 1–5 tests with:
91134

92135
```bash
93136
.venv/bin/pytest -q \
@@ -96,5 +139,8 @@ Run the Stage 1–4 tests with:
96139
tests/evaluation/test_eval_optimize_loop_stage3a.py \
97140
tests/evaluation/test_eval_optimize_loop_stage3b.py \
98141
tests/evaluation/test_eval_optimize_loop_stage4.py \
99-
tests/evaluation/test_eval_optimize_loop_real_integration.py
142+
tests/evaluation/test_eval_optimize_loop_real_integration.py \
143+
tests/evaluation/test_eval_optimize_loop_stage5_report_builder.py \
144+
tests/evaluation/test_eval_optimize_loop_stage5_artifacts.py \
145+
tests/evaluation/test_eval_optimize_loop_stage5_pipeline.py
100146
```

examples/optimization/eval_optimize_loop/artifact_writer.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,21 @@
5454
"token",
5555
"xapikey",
5656
}
57+
_SENSITIVE_CONFIG_KEY_SUFFIXES = {
58+
"accesstoken",
59+
"apikey",
60+
"authtoken",
61+
"baseurl",
62+
"bearertoken",
63+
"clientsecret",
64+
"credential",
65+
"credentials",
66+
"endpointurl",
67+
"password",
68+
"passwd",
69+
"privatekey",
70+
"secretkey",
71+
}
5772
_APPROVED_SENSITIVE_VALUES = {
5873
"",
5974
"${TRPC_AGENT_API_KEY}",
@@ -167,7 +182,22 @@ def _normalized_sensitive_key(key: str) -> str:
167182
return key.replace("_", "").replace("-", "").casefold()
168183

169184

185+
def _is_sensitive_config_key(key: str) -> bool:
186+
normalized = _normalized_sensitive_key(key)
187+
return normalized in _SENSITIVE_CONFIG_KEYS or any(
188+
normalized.endswith(suffix)
189+
for suffix in _SENSITIVE_CONFIG_KEY_SUFFIXES
190+
)
191+
192+
170193
def _validate_sensitive_config_values(value: object, *, path: str = "$") -> None:
194+
if isinstance(value, str):
195+
if value.strip().casefold().startswith(("http://", "https://")):
196+
raise ArtifactWriteError(
197+
"sensitive optimizer config value is not an approved "
198+
f"placeholder: {path}"
199+
)
200+
return
171201
if isinstance(value, list):
172202
for index, item in enumerate(value):
173203
_validate_sensitive_config_values(item, path=f"{path}[{index}]")
@@ -176,7 +206,7 @@ def _validate_sensitive_config_values(value: object, *, path: str = "$") -> None
176206
return
177207
for key, item in value.items():
178208
item_path = f"{path}.{key}"
179-
if _normalized_sensitive_key(key) in _SENSITIVE_CONFIG_KEYS:
209+
if _is_sensitive_config_key(key):
180210
if not isinstance(item, str) or item not in _APPROVED_SENSITIVE_VALUES:
181211
raise ArtifactWriteError(
182212
"sensitive optimizer config value is not an approved "

0 commit comments

Comments
 (0)