Skip to content

Commit 7c2aaf1

Browse files
committed
fix(evaluation): 补齐异常脱敏与 Trace 错误上下文
1 parent 38fac75 commit 7c2aaf1

5 files changed

Lines changed: 107 additions & 15 deletions

File tree

examples/optimization/eval_optimize_loop/pipeline.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,24 +1043,37 @@ async def _execute_trace_stage(
10431043
phase="candidate", split="validation",
10441044
)
10451045
progress.enter("analysis")
1046-
analysis = build_evaluation_analysis(
1047-
baseline_train=baseline_train,
1048-
baseline_validation=baseline_validation,
1049-
candidate_train=candidate_train,
1050-
candidate_validation=candidate_validation,
1051-
hard_case_ids=set(prepared.config.case_labels.hard_case_ids),
1052-
critical_case_ids=set(prepared.config.case_labels.critical_case_ids),
1053-
severe_case_score_drop=prepared.config.gate.severe_case_score_drop,
1054-
)
1046+
try:
1047+
analysis = build_evaluation_analysis(
1048+
baseline_train=baseline_train,
1049+
baseline_validation=baseline_validation,
1050+
candidate_train=candidate_train,
1051+
candidate_validation=candidate_validation,
1052+
hard_case_ids=set(prepared.config.case_labels.hard_case_ids),
1053+
critical_case_ids=set(prepared.config.case_labels.critical_case_ids),
1054+
severe_case_score_drop=prepared.config.gate.severe_case_score_drop,
1055+
)
1056+
except EvaluationAnalysisError as exc:
1057+
raise PipelineStageExecutionError(
1058+
f"stage 3a analysis failed: {exc}"
1059+
) from exc
10551060
measurements = ResourceMeasurements(
10561061
cost_usd=ObservableValue(status="unavailable", unit="USD", reason="Trace replay does not call a model."),
10571062
total_tokens=ObservableValue(status="unavailable", unit="tokens", reason="Trace replay does not call a model."),
10581063
duration_seconds=ObservableValue(status="available", value=perf_counter() - started_at, unit="seconds"),
10591064
)
10601065
progress.enter("gate")
1061-
gate_decision = evaluate_gate(
1062-
analysis, prepared.config.gate, prepared.config.budget, measurements
1063-
)
1066+
try:
1067+
gate_decision = evaluate_gate(
1068+
analysis,
1069+
prepared.config.gate,
1070+
prepared.config.budget,
1071+
measurements,
1072+
)
1073+
except GateEvaluationError as exc:
1074+
raise PipelineStageExecutionError(
1075+
f"stage 3b gate failed: {exc}"
1076+
) from exc
10641077
progress.enter("writeback")
10651078
writeback = WritebackResult(
10661079
status="skipped", reason="trace_replay", attempted=False

examples/optimization/eval_optimize_loop/report_builder.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ def _not_applicable_optimizer_value(
5050
)
5151

5252

53-
def _redact_error_message(error: Exception) -> str:
53+
def redact_error_message(error: Exception) -> str:
54+
"""移除异常文本中的环境凭据、认证字段和连接地址。"""
5455
message = str(error)
5556
environment_values = {
5657
os.environ.get(name, "")
@@ -152,7 +153,7 @@ def build_failure_report(
152153
return FailureReport(
153154
run_id=prepared.workspace.run_id, execution_mode=prepared.config.execution.mode,
154155
failed_phase=progress.current_phase, exception_type=type(error).__name__,
155-
error_message=_redact_error_message(error), generated_at=generated_at,
156+
error_message=redact_error_message(error), generated_at=generated_at,
156157
input_snapshot=prepared.input_snapshot,
157158
source_prompt_hashes=dict(sorted(source_prompt_hashes.items())),
158159
completed_phases=progress.completed_phases, existing_artifacts=sorted(existing_artifacts),

examples/optimization/eval_optimize_loop/run_real_integration.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from examples.optimization.eval_optimize_loop.real_agent import BusinessModelConfig
2626
from examples.optimization.eval_optimize_loop.real_agent import RealBusinessAgent
2727
from examples.optimization.eval_optimize_loop.real_agent import load_business_model_config
28+
from examples.optimization.eval_optimize_loop.report_builder import redact_error_message
2829
from examples.optimization.eval_optimize_loop.schemas import OptimizerRuntimeParameters
2930
else:
3031
from .config import load_pipeline_config
@@ -33,6 +34,7 @@
3334
from .real_agent import BusinessModelConfig
3435
from .real_agent import RealBusinessAgent
3536
from .real_agent import load_business_model_config
37+
from .report_builder import redact_error_message
3638
from .schemas import OptimizerRuntimeParameters
3739

3840

@@ -116,7 +118,10 @@ def main() -> int:
116118
try:
117119
prepared, result = asyncio.run(_run(args, business_config, parameters))
118120
except Exception as exc:
119-
print(f"Real integration failed: {exc}", file=sys.stderr)
121+
print(
122+
f"Real integration failed: {redact_error_message(exc)}",
123+
file=sys.stderr,
124+
)
120125
return 1
121126

122127
print(f"Completed real-model pipeline: {prepared.workspace.run_dir}")

tests/evaluation/test_eval_optimize_loop_real_integration.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,40 @@ async def fail(*args, **kwargs):
335335
assert "offline failure" in capsys.readouterr().err
336336

337337

338+
def test_real_cli_redacts_environment_secrets_from_pipeline_error(
339+
monkeypatch: pytest.MonkeyPatch,
340+
capsys: pytest.CaptureFixture[str],
341+
):
342+
api_key = "integration-secret-key"
343+
base_url = "https://private-gateway.example.test/v1"
344+
345+
async def fail(*args, **kwargs):
346+
raise RuntimeError(
347+
f"request failed api_key={api_key} base_url={base_url}"
348+
)
349+
350+
monkeypatch.setenv("TRPC_AGENT_API_KEY", api_key)
351+
monkeypatch.setenv("TRPC_AGENT_BASE_URL", base_url)
352+
monkeypatch.setenv("TRPC_AGENT_MODEL_NAME", "business-model")
353+
monkeypatch.setattr(run_real_integration, "_run", fail)
354+
monkeypatch.setattr(
355+
sys,
356+
"argv",
357+
[
358+
"run_real_integration.py",
359+
"--run-real",
360+
"--optimizer-model-name",
361+
"optimizer-model",
362+
],
363+
)
364+
365+
assert run_real_integration.main() == 1
366+
error = capsys.readouterr().err
367+
assert api_key not in error
368+
assert base_url not in error
369+
assert "[REDACTED]" in error
370+
371+
338372
def test_real_cli_requires_explicit_confirmation_before_creating_workspace(tmp_path: Path):
339373
root = _copy_example(tmp_path)
340374
completed = subprocess.run(

tests/evaluation/test_eval_optimize_loop_stage6_trace.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from examples.optimization.eval_optimize_loop import pipeline as pipeline_module
1616
from examples.optimization.eval_optimize_loop.pipeline import prepare_run
17+
from examples.optimization.eval_optimize_loop.pipeline import PipelineStageExecutionError
1718
from examples.optimization.eval_optimize_loop.pipeline import run_trace_stage
1819
from examples.optimization.eval_optimize_loop.schemas import ArtifactIndex
1920
from examples.optimization.eval_optimize_loop.schemas import OptimizationReport
@@ -97,3 +98,41 @@ def __init__(self, *_args, **_kwargs):
9798
artifact_ids = {artifact.artifact_id for artifact in index.artifacts}
9899
assert "input.trace.candidate_train" in artifact_ids
99100
assert "input.trace.candidate_validation" in artifact_ids
101+
102+
103+
@pytest.mark.parametrize(
104+
("target", "error_type", "expected"),
105+
[
106+
(
107+
"build_evaluation_analysis",
108+
pipeline_module.EvaluationAnalysisError,
109+
"stage 3a analysis failed",
110+
),
111+
(
112+
"evaluate_gate",
113+
pipeline_module.GateEvaluationError,
114+
"stage 3b gate failed",
115+
),
116+
],
117+
)
118+
@pytest.mark.asyncio
119+
async def test_trace_stage_wraps_analysis_and_gate_errors_with_phase_context(
120+
tmp_path: Path,
121+
monkeypatch: pytest.MonkeyPatch,
122+
target: str,
123+
error_type: type[Exception],
124+
expected: str,
125+
):
126+
root = _copy_example(tmp_path, f"trace_{target}")
127+
prepared = prepare_run(
128+
root / "pipeline.trace.json",
129+
run_id=f"trace_{target}",
130+
)
131+
132+
def fail(*_args, **_kwargs):
133+
raise error_type("injected failure")
134+
135+
monkeypatch.setattr(pipeline_module, target, fail)
136+
137+
with pytest.raises(PipelineStageExecutionError, match=expected):
138+
await run_trace_stage(prepared, scenario="improve")

0 commit comments

Comments
 (0)