zcbot/evaluation/__main__.py

160 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""命令行入口:`.venv/Scripts/python.exe -m evaluation ...`。"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from .audit import audit_repository, audit_security_repository
from .client import EvalClientError, ZcbotClient
from .combine import combine_reports
from .inspect_bridge import export_inspect_jsonl
from .models import EvalConfigError
from .report import summarize, write_reports
from .runner import RunSettings, run_suite
from .suite import load_suite
ROOT = Path(__file__).resolve().parents[1]
DEFAULT_SUITE = ROOT / "evaluation" / "datasets" / "smoke.json"
DEFAULT_CONFIG = ROOT / "evaluation" / "config.example.json"
def _parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="zcbot 黑盒技术评测")
sub = parser.add_subparsers(dest="command", required=True)
validate = sub.add_parser("validate", help="只校验任务集,不调用 zcbot")
validate.add_argument("--suite", type=Path, default=DEFAULT_SUITE)
audit = sub.add_parser("audit", help="运行本地工程质量审计并生成报告")
audit.add_argument("--suite", type=Path, default=DEFAULT_SUITE)
audit.add_argument("--output", type=Path, default=ROOT / "evaluation" / "reports")
audit.add_argument("--timeout-s", type=float, default=900)
inspect_export = sub.add_parser(
"export-inspect", help="把 suite 导出为 Inspect AI JSONL"
)
inspect_export.add_argument("--suite", type=Path, default=DEFAULT_SUITE)
inspect_export.add_argument("--output", type=Path, required=True)
combine = sub.add_parser("combine", help="合并已有 JSON 报告")
combine.add_argument(
"--report",
type=Path,
action="append",
required=True,
help="输入报告 JSON可重复传入",
)
combine.add_argument(
"--output",
type=Path,
default=ROOT / "evaluation" / "reports" / "latest",
)
run = sub.add_parser("run", help="执行任务集并生成 JSON/Markdown 报告")
run.add_argument("--suite", type=Path, default=DEFAULT_SUITE)
run.add_argument("--config", type=Path, default=DEFAULT_CONFIG)
run.add_argument("--output", type=Path, default=ROOT / "evaluation" / "reports")
run.add_argument("--repetitions", type=int)
run.add_argument("--timeout-s", type=float, default=900)
run.add_argument("--allow-remote", action="store_true")
run.add_argument(
"--with-audit",
action="store_true",
help="在线任务结束后同时执行本地工程审计,补齐 engineering 维度",
)
run.add_argument(
"--execute",
action="store_true",
help="确认实际调用模型并产生费用;缺少此参数时拒绝执行",
)
return parser
def main(argv: list[str] | None = None) -> int:
args = _parser().parse_args(argv)
try:
if args.command == "combine":
summary = combine_reports(args.report)
json_path, md_path = write_reports(summary, args.output)
print(f"[OK] provisional_score={summary['provisional_score']}")
print(f"[OK] total_score={summary['total_score']}")
print(f"[OK] json={json_path}")
print(f"[OK] markdown={md_path}")
return 0
suite = load_suite(args.suite)
if args.command == "validate":
print(
f"[OK] suite={suite.name} version={suite.version} "
f"cases={len(suite.cases)}"
)
return 0
if args.command == "audit":
engineering = audit_repository(
ROOT, timeout_s=args.timeout_s, progress=print
)
security = audit_security_repository(
ROOT, timeout_s=args.timeout_s, progress=print
)
summary = summarize(suite, [engineering, security], repo_root=ROOT)
json_path, md_path = write_reports(
summary, args.output, stem="engineering-audit"
)
print(f"[OK] engineering_score={engineering.score * 100:.2f}")
print(f"[OK] security_score={security.score * 100:.2f}")
print(f"[OK] json={json_path}")
print(f"[OK] markdown={md_path}")
return 0
if args.command == "export-inspect":
count = export_inspect_jsonl(suite, args.output)
print(f"[OK] inspect_jsonl={args.output} samples={count}")
return 0
if not args.execute:
print("[ERR] run 需要显式传 --execute会调用模型并产生费用")
return 2
config = json.loads(args.config.read_text(encoding="utf-8"))
if not isinstance(config, dict):
raise EvalConfigError("config 顶层必须是 JSON object")
with ZcbotClient.from_config(
config, allow_remote=args.allow_remote
) as client:
results = run_suite(
suite,
client,
settings=RunSettings(
repetitions=args.repetitions,
timeout_s=args.timeout_s,
),
progress=print,
)
if args.with_audit:
results.extend(
[
audit_repository(
ROOT, timeout_s=args.timeout_s, progress=print
),
audit_security_repository(
ROOT, timeout_s=args.timeout_s, progress=print
),
]
)
summary = summarize(suite, results, repo_root=ROOT)
json_path, md_path = write_reports(summary, args.output)
print(f"[OK] json={json_path}")
print(f"[OK] markdown={md_path}")
return 0
except (
EvalConfigError,
EvalClientError,
OSError,
ValueError,
json.JSONDecodeError,
) as exc:
print(f"[ERR] {exc}")
return 2
if __name__ == "__main__":
sys.exit(main())