| import argparse |
| import csv |
| import json |
| import os |
| import re |
| from pathlib import Path |
|
|
|
|
| DEFAULT_FILE = ( |
| "/225040511/project/Biomni-ReAct/LAB-bench/" |
| "cloningscenarios_cloning_20260524_163903/cloningscenarios_results.jsonl" |
| ) |
|
|
|
|
| def normalize_choice(value): |
| """Return the first A-E option from a model/gold answer string.""" |
| if value is None: |
| return "" |
| text = str(value).strip().upper() |
| if text in {"A", "B", "C", "D", "E"}: |
| return text |
| match = re.search(r"\b([A-E])\b", text) |
| return match.group(1) if match else text |
|
|
|
|
| def compute_accuracy_from_jsonl(file_path, print_errors=False, dedup=True): |
| """ |
| 从 JSONL 文件中读取数据,计算 agent_answer 与 answer 的准确率。 |
| |
| Args: |
| file_path (str): JSONL 文件路径。 |
| print_errors (bool): 是否打印错误条目。 |
| dedup (bool): 是否按 task_id/id 去重,保留最后一次结果。 |
| |
| Returns: |
| dict: accuracy/count/error metrics. |
| """ |
| if not os.path.exists(file_path): |
| raise FileNotFoundError(f"文件不存在: {file_path}") |
|
|
| records = [] |
| seen = {} |
| duplicate_count = 0 |
|
|
| with open(file_path, "r", encoding="utf-8") as f: |
| for line_num, line in enumerate(f, 1): |
| line = line.strip() |
| if not line: |
| continue |
| try: |
| data = json.loads(line) |
| except json.JSONDecodeError as e: |
| print(f"警告: 第 {line_num} 行 JSON 解析错误: {e},跳过") |
| continue |
|
|
| data["_line_num"] = line_num |
| key = data.get("task_id") or data.get("id") |
| if dedup and key: |
| if key in seen: |
| duplicate_count += 1 |
| seen[key] = data |
| else: |
| records.append(data) |
|
|
| if dedup: |
| records = list(seen.values()) + records |
|
|
| correct = 0 |
| total = 0 |
| missing_answer = 0 |
| error_count = 0 |
| error_records = [] |
|
|
| for data in records: |
| line_num = data.get("_line_num", "N/A") |
| answer = normalize_choice(data.get("answer")) |
| agent_answer = normalize_choice(data.get("agent_answer")) |
| if not answer: |
| print(f"警告: 第 {line_num} 行缺少 'answer' 字段,跳过") |
| continue |
|
|
| total += 1 |
| if not agent_answer: |
| missing_answer += 1 |
| if data.get("error"): |
| error_count += 1 |
|
|
| if answer == agent_answer: |
| correct += 1 |
| else: |
| error_records.append( |
| { |
| "line_num": line_num, |
| "task_id": data.get("task_id", data.get("id", "N/A")), |
| "answer": answer, |
| "agent_answer": agent_answer, |
| "error": data.get("error", ""), |
| "question": data.get("question", "N/A"), |
| } |
| ) |
|
|
| accuracy = correct / total if total > 0 else 0.0 |
| metrics = { |
| "file": str(file_path), |
| "total": total, |
| "correct": correct, |
| "wrong": total - correct, |
| "accuracy": accuracy, |
| "accuracy_percent": accuracy * 100, |
| "missing_agent_answer": missing_answer, |
| "error_count": error_count, |
| "duplicate_count": duplicate_count, |
| "error_records": error_records, |
| } |
| |
| if print_errors and error_records: |
| print("\n" + "="*80) |
| print("错误条目列表 (answer != agent_answer):") |
| print("="*80) |
| for idx, err in enumerate(error_records, 1): |
| print(f"[{idx:03d}] 行号: {err['line_num']}") |
| print(f" Task ID: {err['task_id']}") |
| print(f" 问题: {err['question'][:100]}..." if len(err['question']) > 100 else f" 问题: {err['question']}") |
| print(f" 正确答案: {err['answer']}") |
| print(f" Agent答案: {err['agent_answer']}") |
| if err["error"]: |
| print(f" Error: {err['error']}") |
| print("-"*80) |
| |
| return metrics |
|
|
|
|
| def print_metrics(metrics): |
| print(f"\n文件: {metrics['file']}") |
| print(f"总样本数: {metrics['total']}") |
| print(f"正确数: {metrics['correct']}") |
| print(f"错误数: {metrics['wrong']}") |
| print(f"准确率: {metrics['accuracy']:.4f} ({metrics['accuracy_percent']:.2f}%)") |
| print(f"空答案数: {metrics['missing_agent_answer']}") |
| print(f"带 error 字段数: {metrics['error_count']}") |
| print(f"重复 task_id 行数: {metrics['duplicate_count']}") |
|
|
|
|
| def write_error_csv(metrics, out_path): |
| fields = ["line_num", "task_id", "answer", "agent_answer", "error", "question"] |
| out_path = Path(out_path) |
| out_path.parent.mkdir(parents=True, exist_ok=True) |
| with out_path.open("w", encoding="utf-8", newline="") as handle: |
| writer = csv.DictWriter(handle, fieldnames=fields) |
| writer.writeheader() |
| writer.writerows(metrics["error_records"]) |
|
|
|
|
| if __name__ == "__main__": |
| parser = argparse.ArgumentParser(description="Calculate LAB-Bench MCQ accuracy from JSONL results.") |
| parser.add_argument("file", nargs="?", default=DEFAULT_FILE, help="Path to *_results.jsonl.") |
| parser.add_argument("--print-errors", action="store_true", help="Print wrong records.") |
| parser.add_argument("--no-dedup", action="store_true", help="Do not deduplicate by task_id/id.") |
| parser.add_argument("--error-csv", default="", help="Optional CSV path for wrong records.") |
| args = parser.parse_args() |
|
|
| try: |
| metrics = compute_accuracy_from_jsonl( |
| args.file, |
| print_errors=args.print_errors, |
| dedup=not args.no_dedup, |
| ) |
| print_metrics(metrics) |
| if args.error_csv: |
| write_error_csv(metrics, args.error_csv) |
| print(f"\n错误样本 CSV 已保存: {args.error_csv}") |
| except FileNotFoundError as e: |
| print(f"错误: {e}") |
| except Exception as e: |
| print(f"发生未预期的错误: {e}") |
|
|