Files
liqiang b119135836
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
2026-08-20 13:12:50 +00:00

150 lines
5.7 KiB
Python

#!/usr/bin/env python3
"""Derive canonical Experiment 7-3 evidence from the completed 7-4 campaign.
The completed 7-4 report ran the Experiment 7-3 judge on every one of its
60 cases and three memory systems. This validator creates a small, auditable
index without changing, adding, or re-judging any paid API trajectory.
"""
from __future__ import annotations
import argparse
import hashlib
import json
from collections import Counter, defaultdict
from datetime import datetime, timezone
from pathlib import Path
REQUIRED_DIMENSIONS = {"precision", "recall", "reasoning", "proactivity"}
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
"--source",
type=Path,
default=Path(__file__).with_name("results") / "full_7_4_60_cases_costed.json",
)
parser.add_argument(
"--output",
type=Path,
default=Path(__file__).with_name("results") / "full_7_3_structured_rubric_evidence.json",
)
args = parser.parse_args()
source = json.loads(args.source.read_text(encoding="utf-8"))
records = source.get("records", [])
valid = []
errors = []
by_layer_system = defaultdict(lambda: Counter(records=0, passed=0, vetoes=0))
for index, row in enumerate(records):
dimensions = row.get("rubric_details") or {}
numeric = row.get("rubric_dimensions") or {}
hallucination = row.get("hallucination_detail")
problems = []
if row.get("status") != "ok":
problems.append(f"status={row.get('status')!r}")
if set(dimensions) != REQUIRED_DIMENSIONS:
problems.append(f"rubric_details={sorted(dimensions)}")
if set(numeric) != REQUIRED_DIMENSIONS:
problems.append(f"rubric_dimensions={sorted(numeric)}")
if any(not 1 <= int(value) <= 4 for value in numeric.values()):
problems.append("rubric score outside 1..4")
if not isinstance(hallucination, dict) or "detected" not in hallucination:
problems.append("missing hallucination verdict")
for name, detail in dimensions.items():
# A concise direct answer can legitimately have no affirmative
# proactivity evidence. In that boundary case the judge must name
# the applied boundary explicitly instead of inventing evidence.
if (
not isinstance(detail, dict)
or not detail.get("reasoning")
or not (detail.get("evidence") or detail.get("boundary_case"))
):
problems.append(f"{name} lacks reasoning and evidence/boundary")
if problems:
errors.append({
"record_index": index,
"test_id": row.get("test_id"),
"system": row.get("system"),
"problems": problems,
})
continue
valid.append(row)
bucket = by_layer_system[(row["layer"], row["system"])]
bucket["records"] += 1
bucket["passed"] += int(bool(row.get("success")))
bucket["vetoes"] += int(bool(row.get("hallucination_veto")))
distinct_cases = sorted({row.get("test_id") for row in valid})
systems = sorted({row.get("system") for row in valid})
layers = sorted({row.get("layer") for row in valid})
complete = (
not errors
and len(records) == 180
and len(valid) == 180
and len(distinct_cases) == 60
and len(systems) == 3
and layers == ["layer1", "layer2", "layer3"]
and all(counter["records"] == 20 for counter in by_layer_system.values())
and len(by_layer_system) == 9
)
report = {
"schema_version": "1.0",
"experiment": "7-3",
"generated_at_utc": datetime.now(timezone.utc).isoformat(),
"evidence_lineage": {
"source_file": str(args.source),
"source_sha256": sha256(args.source),
"source_experiment": source.get("experiment"),
"source_generated_at_utc": source.get("generated_at_utc"),
"transformation": (
"Validation/index only: no API records, answers, scores, or verdicts were added, "
"removed, or changed."
),
},
"rubric_contract": {
"dimensions": sorted(REQUIRED_DIMENSIONS),
"scale": "1..4 with concrete reasoning and cited evidence",
"hallucination": "independent hard veto",
},
"run_scope": {
"distinct_test_cases": len(distinct_cases),
"layers": layers,
"systems": systems,
"records_expected": 180,
"records_validated": len(valid),
"all_60_cases_covered": len(distinct_cases) == 60,
"validation_scope": "full" if complete else "incomplete",
},
"summary": [
{"layer": layer, "system": system, **dict(counter)}
for (layer, system), counter in sorted(by_layer_system.items())
],
"errors": errors,
"status": "complete" if complete else "incomplete",
}
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
print(json.dumps({
"status": report["status"],
"records_validated": len(valid),
"distinct_cases": len(distinct_cases),
"errors": len(errors),
"output": str(args.output),
}, ensure_ascii=False))
return 0 if complete else 1
if __name__ == "__main__":
raise SystemExit(main())