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

107 lines
3.9 KiB
Python

"""Objective structured scoring for public-health reporting agent traces."""
from __future__ import annotations
import json
import math
from pathlib import Path
from typing import Any
MAX_SCORE = 6
def load_json(path: str | Path) -> Any:
return json.loads(Path(path).read_text(encoding="utf-8"))
def expected_by_task(path: str | Path) -> dict[str, dict[str, Any]]:
return {item["task_id"]: item for item in load_json(path)}
def _equivalent(actual: Any, expected: Any, tolerance: float) -> bool:
if isinstance(actual, bool) or isinstance(expected, bool):
return actual == expected
if isinstance(actual, (int, float)) and isinstance(expected, (int, float)):
return math.isclose(float(actual), float(expected), abs_tol=tolerance)
if isinstance(actual, dict) and isinstance(expected, dict):
return actual.keys() == expected.keys() and all(
_equivalent(actual[key], expected[key], tolerance) for key in expected
)
if isinstance(actual, list) and isinstance(expected, list):
return len(actual) == len(expected) and all(
_equivalent(left, right, tolerance) for left, right in zip(actual, expected)
)
return actual == expected
def _same_evidence(actual: list[Any], expected: list[Any]) -> bool:
"""Compare evidence as set-like collections, including JSON objects."""
try:
return set(actual) == set(expected)
except TypeError:
return all(item in expected for item in actual) and all(
item in actual for item in expected
)
def score_prediction(
prediction: dict[str, Any], expected: dict[str, Any], tolerance: float = 0.01
) -> dict[str, Any]:
"""Score tool, arguments, answer, evidence and grounding (six points total)."""
details = {
"tool_selection": int(prediction.get("tool") == expected["tool"]),
"arguments": int(prediction.get("arguments") == expected["arguments"]),
}
actual_result = prediction.get("result", {})
if not isinstance(actual_result, dict):
actual_result = {}
expected_result = expected["result"]
actual_values = {key: value for key, value in actual_result.items() if key != "evidence"}
expected_values = {key: value for key, value in expected_result.items() if key != "evidence"}
details["answer"] = 2 if _equivalent(actual_values, expected_values, tolerance) else 0
actual_evidence = actual_result.get("evidence", [])
if not isinstance(actual_evidence, list):
actual_evidence = []
expected_evidence = expected_result.get("evidence", []) if isinstance(expected_result, dict) else []
if not isinstance(expected_evidence, list):
expected_evidence = []
details["evidence"] = int(_same_evidence(actual_evidence, expected_evidence))
claims = prediction.get("claims", [])
supported = expected.get("supported_claims", []) if isinstance(expected, dict) else []
if not isinstance(supported, list):
supported = []
if not isinstance(claims, list):
grounding = 0
else:
try:
grounding = int(set(claims).issubset(set(supported)))
except TypeError:
grounding = int(all(item in supported for item in claims))
details["grounding_and_safety"] = grounding
return {
"task_id": expected["task_id"],
"score": sum(details.values()),
"max_score": MAX_SCORE,
"details": details,
}
def evaluate(
predictions: list[dict[str, Any]],
expected_items: dict[str, dict[str, Any]],
tolerance: float = 0.01,
) -> dict[str, Any]:
prediction_map = {item["task_id"]: item for item in predictions}
results = []
for task_id, expected in expected_items.items():
prediction = prediction_map.get(task_id, {"task_id": task_id})
results.append(score_prediction(prediction, expected, tolerance))
return {
"score": sum(item["score"] for item in results),
"max_score": len(results) * MAX_SCORE,
"tasks": results,
}