【评测系列2-1】从零实现 AgentBench评测系统:架构设计与实战

0 阅读10分钟

我自建了一套 agent_bench 评测系统

目标

  1. 按"能力维度"设计(不是任务类型)
  2. 细粒度指标(不只是成功/失败)
  3. 可视化报告(雷达图 + 诊断建议)
  4. 可扩展架构(随时加新维度)

结果

┌─────────────────────────────────────────────────────────┐
│                  AGENT_BENCH 评测系统                    │
├─────────────────────────────────────────────────────────┤
│  代码量:约 9,000 行(36 个 Python 文件)                │
│  评测维度:5 个                                          │
│  评测任务:54 个                                         │
│  评测器:5 个(每个维度一个)                            │
│  报告生成:HTML 可视化                                   │
│  自动化率:100%                                         │
└─────────────────────────────────────────────────────────┘

二、系统架构设计

2.1 整体架构

图 1:AGENT_BENCH 评测系统架构图

​编辑

上图展示了 AGENT_BENCH 的逻辑架构设计,包含 6 个核心组件:

评测基准(Benchmarks)、评测器(Evaluators)、报告生成(Reports)、任务调度、评测环境(Environments)、待评测 Agent。数据流从 benchmarks → evaluators → reports,environments 提供沙箱执行环境。

┌─────────────────────────────────────────────────────────┐
│                   AGENT_BENCH 架构                      │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐ │
│  │   评测基准   │    │   评测器     │    │   报告生成   │ │
│  │  Benchmarks │───▶│  Evaluators │───▶│   Reports   │ │
│  └─────────────┘    └─────────────┘    └─────────────┘ │
│         │                  │                  │         │
│         ▼                  ▼                  ▼         │
│  ┌─────────────────────────────────────────────────────┐│
│  │                  评测任务调度                        ││
│  │              Task Scheduler                         ││
│  └─────────────────────────────────────────────────────┘│
│         │                  │                  │         │
│         ▼                  ▼                  ▼         │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐ │
│  │   评测环境   │    │  待评测 Agent │    │   指标计算   │ │
│  │ Environments│◀──▶│   Agents    │    │   Metrics   │ │
│  └─────────────┘    └─────────────┘    └─────────────┘ │
│                                                         │
└─────────────────────────────────────────────────────────┘

2.2 目录结构

实际项目结构(2026 年 4 月实测):

项目目录结构

AGENT_BENCH/
├── README.md                    # 系统说明
├── INSTALLATION_COMPLETE.md     # 安装指南
├── INTEGRATION_GUIDE.md         # 集成指南
├── INTEGRATION_SUMMARY.md       # 集成总结
├── PROJECT_OVERVIEW.md          # 项目概览
├── requirements.txt             # Python 依赖
│
├── benchmarks/                  # 评测基准(8 个评测集)
│   ├── task_planning/           # 任务规划评测集
│   ├── tool_use/                # 工具使用评测集
│   ├── dialogue/                # 多轮对话评测集
│   ├── coding/                  # 代码能力评测集
│   ├── knowledge/               # 知识应用评测集
│   ├── nl2sql/                  # NL2SQL 评测集
│   ├── llm_as_judge/            # LLM 作为评委
│   └── safety/                  # 安全性评测集
│
├── environments/                # 评测环境
│   ├── sandbox/                 # 沙箱环境
│   ├── tools/                   # 工具模拟
│   └── scenarios/               # 场景配置
│
├── agents/                      # 待评测 Agent(3 个实现)
│   ├── openclaw_agent.py        # OpenClaw Agent
│   ├── langchain_agent.py       # LangChain Agent
│   └── autogen_agent.py         # AutoGen Agent
│
├── evaluators/                  # 评测器(核心)
│   ├── task_planning_evaluator.py   # 任务规划评测器
│   ├── tool_use_evaluator.py        # 工具使用评测器
│   ├── dialogue_evaluator.py        # 多轮对话评测器
│   ├── coding_evaluator.py          # 代码能力评测器
│   ├── knowledge_evaluator.py       # 知识应用评测器
│   └── metrics.py                   # 指标计算核心
│
├── reports/                     # 评测报告
│   ├── results/                 # 原始结果(JSON)
│   ├── performance/             # 性能测试报告
│   ├── quick_eval/              # 快速评测结果
│   ├── real_eval/               # 真实评测结果
│   └── *.html                   # 可视化报告
│
├── scripts/                     # 工具脚本(19 个)
│   ├── run_eval.py              # 运行评测
│   ├── generate_report.py       # 生成报告
│   ├── run_comparison_eval.py   # 对比评测
│   ├── fair_model_comparison.py # 公平对比
│   └── ...
│
├── integrations/                # 第三方集成(3 个)
│   ├── locust_performance_test.py   # 性能测试(Locust)
│   ├── langsmith_tracing.py         # 链路追踪(LangSmith)
│   └── garak_security_scan.py       # 安全扫描(Garak)
│
├── configs/                     # 配置文件
│   └── eval_config.yaml         # 评测配置
│
├── docs/                        # 文档
│   └── ...
│
└── test_integrations.py         # 集成测试

📊 统计:共 12 个一级目录,36 个 Python 文件,9,148 行代码

2.3 核心模块职责

模块职责实际代码量文件数
benchmarks/定义评测任务(题目)257 行3 个
evaluators/评测逻辑(评分)2,316 行6 个
environments/评测环境(沙箱)待完善3 个子目录
agents/Agent 接口适配369 行3 个
reports/报告生成待完善4 个子目录
scripts/评测脚本和工具5,641 行19 个
integrations/第三方集成~500 行3 个

2.4 代码量统计详情

实际统计结果(2026 年 4 月 16 日实测):

模块文件数代码行数说明
benchmarks/3 个257 行8 个评测集目录,核心文件 3 个
evaluators/6 个2,316 行5 个评测器 + 1 个指标计算
agents/3 个369 行OpenClaw/LangChain/AutoGen
scripts/19 个5,641 行评测脚本和工具
integrations/3 个~500 行Locust/LangSmith/Garak
environments/0 个待完善沙箱/工具/场景目录已创建
reports/0 个待完善结果/性能/快速评测目录已创建
总计36 个约 9,000 行物理行数(含空行和注释)

说明:核心评测逻辑集中在 evaluators/ 模块,scripts/ 包含评测脚本和工具。environments/ 和 reports/ 目录结构已创建,后续会补充实现。


三、核心模块实现详解

3.1 评测器基类设计

所有评测器都继承自一个基类:

Python - evaluators/base_evaluator.py

# evaluators/base_evaluator.py

from abc import ABC, abstractmethod
from typing import Dict, List, Any
from dataclasses import dataclass

@dataclass
class EvalResult:
    """评测结果数据结构"""
    task_id: str
    task_name: str
    difficulty: str
    score: float
    details: Dict[str, Any]
    timestamp: str

class BaseEvaluator(ABC):
    """评测器基类"""
    
    def __init__(self, config: Dict):
        self.config = config
        self.results: List[EvalResult] = []
    
    @abstractmethod
    def evaluate(self, agent, task: Dict) -> EvalResult:
        """执行评测(子类实现)"""
        pass
    
    @abstractmethod
    def calculate_metrics(self, result: EvalResult) -> Dict:
        """计算指标(子类实现)"""
        pass
    
    def run_batch(self, agent, tasks: List[Dict]) -> List[EvalResult]:
        """批量评测"""
        for task in tasks:
            result = self.evaluate(agent, task)
            self.results.append(result)
        return self.results
    
    def generate_summary(self) -> Dict:
        """生成汇总统计"""
        if not self.results:
            return {}
        
        return {
            "total_tasks": len(self.results),
            "avg_score": sum(r.score for r in self.results) / len(self.results),
            "by_difficulty": self._group_by_difficulty(),
            "results": [r.__dict__ for r in self.results]
        }
    
    def _group_by_difficulty(self) -> Dict:
        """按难度分组统计"""
        groups = {}
        for result in self.results:
            diff = result.difficulty
            if diff not in groups:
                groups[diff] = []
            groups[diff].append(result.score)
        
        return {
            diff: {
                "count": len(scores),
                "avg_score": sum(scores) / len(scores)
            }
            for diff, scores in groups.items()
        }

设计亮点

  1. 抽象基类:强制子类实现核心方法
  2. 数据类:统一结果格式
  3. 批量评测:支持一次评测多个任务
  4. 汇总统计:自动生成统计数据

3.2 任务规划评测器实现

这是 5 个评测器中最复杂的一个:

代码实现(前 100 行):

​编辑

​编辑

📸 截图说明:任务规划评测器包含分解覆盖率、分解精度、粒度合理性、依赖识别等 4 个核心指标的计算逻辑。

核心功能

  • 任务拆解解析
  • 覆盖率计算
  • 精度计算
  • 粒度评估
  • 依赖关系识别

3.3 工具使用评测器实现

Python - evaluators/tool_use_evaluator.py

# evaluators/tool_use_evaluator.py

class ToolUseEvaluator(BaseEvaluator):
    """工具使用评测器"""
    
    def evaluate(self, agent, task: Dict) -> EvalResult:
        """评测工具使用能力"""
        # 1. 发送任务
        prompt = task["prompt"]
        agent_response = agent.send(prompt)
        
        # 2. 执行工具调用(在沙箱中)
        tool_calls = self._parse_tool_calls(agent_response)
        execution_results = []
        
        for call in tool_calls:
            result = self._execute_tool(call, task["environment"])
            execution_results.append(result)
        
        # 3. 计算指标
        api_success = self._calc_api_success(execution_results)
        param_accuracy = self._calc_param_accuracy(tool_calls, task["expected_params"])
        efficiency = self._calc_efficiency(tool_calls, task["optimal_calls"])
        
        # 4. 综合评分
        overall_score = (
            api_success * 0.4 +
            param_accuracy * 0.4 +
            efficiency * 0.2
        )
        
        return EvalResult(
            task_id=task["id"],
            task_name=task["name"],
            difficulty=task["difficulty"],
            score=overall_score * 5,
            details={
                "api_success_rate": api_success,
                "parameter_accuracy": param_accuracy,
                "tool_efficiency": efficiency,
                "tool_calls": tool_calls,
                "execution_results": execution_results
            },
            timestamp=datetime.now().isoformat()
        )

核心功能

  • 工具调用解析
  • 沙箱环境执行
  • API 成功率计算
  • 参数准确性验证
  • 工具使用效率评估

3.4 多轮对话评测器实现

Python - evaluators/dialogue_evaluator.py

# evaluators/dialogue_evaluator.py

class DialogueEvaluator(BaseEvaluator):
    """多轮对话评测器"""
    
    def evaluate(self, agent, task: Dict) -> EvalResult:
        """评测多轮对话能力"""
        dialogue_history = []
        scores = []
        
        # 多轮对话
        for turn in task["turns"]:
            # 发送用户输入
            user_input = turn["user_input"]
            agent_response = agent.send(user_input, context=dialogue_history)
            
            # 评测当前轮
            turn_score = self._evaluate_turn(
                agent_response,
                turn["expected_behavior"],
                dialogue_history
            )
            scores.append(turn_score)
            
            # 更新对话历史
            dialogue_history.append({
                "user": user_input,
                "agent": agent_response
            })
        
        # 计算各项指标
        context_retention = self._calc_context_retention(dialogue_history)
        coreference = self._calc_coreference_resolution(dialogue_history)
        intent_recognition = sum(scores) / len(scores)
        
        # 综合评分
        overall_score = (
            context_retention * 0.3 +
            coreference * 0.3 +
            intent_recognition * 0.4
        )
        
        return EvalResult(
            task_id=task["id"],
            task_name=task["name"],
            difficulty=task["difficulty"],
            score=overall_score * 5,
            details={
                "context_retention": context_retention,
                "coreference_resolution": coreference,
                "intent_recognition": intent_recognition,
                "dialogue_history": dialogue_history,
                "turn_scores": scores
            },
            timestamp=datetime.now().isoformat()
        )

核心功能

  • 多轮对话管理
  • 上下文保持率计算
  • 指代消解准确率
  • 意图识别准确率

3.5 指标计算核心

Python - evaluators/metrics.py

# evaluators/metrics.py

class MetricsCalculator:
    """指标计算器"""
    
    @staticmethod
    def normalize_score(raw_score: float, min_val: float, max_val: float) -> float:
        """将原始分数归一化到 1-5 分"""
        if max_val == min_val:
            return 3.0
        normalized = (raw_score - min_val) / (max_val - min_val)
        return 1.0 + normalized * 4.0
    
    @staticmethod
    def calculate_weighted_average(scores: Dict[str, float], weights: Dict[str, float]) -> float:
        """计算加权平均"""
        total_weight = sum(weights.values())
        weighted_sum = sum(scores[k] * weights[k] for k in scores if k in weights)
        return weighted_sum / total_weight
    
    @staticmethod
    def calculate_confidence_interval(scores: List[float], confidence: float = 0.95) -> Dict:
        """计算置信区间"""
        import numpy as np
        
        mean = np.mean(scores)
        std = np.std(scores)
        n = len(scores)
        
        # t 分布临界值(简化)
        t_critical = 1.96 if n > 30 else 2.0
        
        margin = t_critical * std / np.sqrt(n)
        
        return {
            "mean": mean,
            "std": std,
            "ci_lower": mean - margin,
            "ci_upper": mean + margin,
            "confidence": confidence
        }
    
    @staticmethod
    def generate_radar_data(dimension_scores: Dict[str, float]) -> List[Dict]:
        """生成雷达图数据"""
        labels = list(dimension_scores.keys())
        values = list(dimension_scores.values())
        
        # 闭合雷达图
        labels.append(labels[0])
        values.append(values[0])
        
        return [
            {"label": label, "value": value}
            for label, value in zip(labels, values)
        ]

核心功能

  • 分数归一化
  • 加权平均计算
  • 置信区间计算
  • 雷达图数据生成

四、一键运行评测

4.1 命令行使用

实际执行命令

$ python3 eval_nl2sql.py

============================================================
NL2SQL 评测结果
============================================================
总用例数:30
通过数:25
通过率:83.33%

按难度统计:
  Easy: 10/10 (100.00%)
  Medium: 11/12 (91.67%)
  Hard: 4/8 (50.00%)
============================================================

📸 截图说明:上图是 NL2SQL 评测的实际执行输出,30 道题整体通过率 83.33%,Easy 难度 100% 通过,Hard 难度仅 50% 通过。

4.2 Python API 使用

Python - API 调用示例

from agents import OpenClawAgent
from evaluators import TaskPlanningEvaluator, ToolUseEvaluator
from benchmarks import load_benchmark

# 1. 初始化 Agent
agent = OpenClawAgent(
    model="qwen3.5-plus",
    api_key="your-api-key"
)

# 2. 加载评测基准
tasks = load_benchmark("task_planning", difficulty=["easy", "medium", "hard"])

# 3. 初始化评测器
evaluator = TaskPlanningEvaluator(config={})

# 4. 运行评测
results = evaluator.run_batch(agent, tasks)

# 5. 获取汇总
summary = evaluator.generate_summary()

print(f"总任务数:{summary['total_tasks']}")
print(f"平均得分:{summary['avg_score']:.2f}/5")
print(f"按难度统计:{summary['by_difficulty']}")

4.3 配置文件

YAML - configs/eval_config.yaml

# configs/eval_config.yaml

agent:
  type: openclaw_agent
  model: qwen3.5-plus
  api_key: ${OPENCLAW_API_KEY}  # 从环境变量读取

evaluator:
  dimensions:
    - task_planning
    - tool_use
    - dialogue
    - coding
    - knowledge
  
  difficulty:
    - easy
    - medium
    - hard

report:
  format: html
  include_radar: true
  include_bar_chart: true
  include_details: true
  output_dir: reports/results


五、评测报告生成

5.1 HTML 报告结构

HTML - 报告模板

<!DOCTYPE html>
<html>
<head>
    <title>Agent 评测报告</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
</head>
<body>
    <div class="container">
        <h1>Agent 评测报告</h1>
        
        <!-- 综合得分 -->
        <div class="summary">
            <h2>综合得分:3.47/5 (C 级)</h2>
        </div>
        
        <!-- 5 维度雷达图 -->
        <div id="radar-chart"></div>
        
        <!-- 各维度详细得分 -->
        <div class="dimensions">
            <h3>各维度得分</h3>
            <div class="dimension">
                <span>任务规划</span>
                <div class="bar">
                    <div class="fill" style="width: 67%"></div>
                </div>
                <span>3.37/5</span>
            </div>
        </div>
        
        <!-- 按难度分级统计 -->
        <div class="difficulty-stats">
            <h3>按难度统计</h3>
            <table>
                <tr><th>难度</th><th>任务数</th><th>平均得分</th><th>通过率</th></tr>
                <tr><td>Easy</td><td>12</td><td>4.05/5</td><td>85%</td></tr>
                <tr><td>Medium</td><td>28</td><td>3.42/5</td><td>65%</td></tr>
                <tr><td>Hard</td><td>14</td><td>2.89/5</td><td>45%</td></tr>
            </table>
        </div>
        
        <!-- 改进建议 -->
        <div class="recommendations">
            <h3>改进建议</h3>
            <ul>
                <li>工具使用能力待加强(2.64/5)</li>
                <li>增强异常处理规划</li>
                <li>加强长上下文记忆机制</li>
            </ul>
        </div>
    </div>
</body>
</html>

5.2 报告生成脚本

Python - scripts/generate_report.py

# scripts/generate_report.py

import json
from pathlib import Path
from jinja2 import Template

def generate_html_report(results_dir: str, output: str):
    """生成 HTML 报告"""
    # 1. 加载评测结果
    results = load_results(results_dir)
    
    # 2. 计算汇总数据
    summary = calculate_summary(results)
    
    # 3. 生成雷达图数据
    radar_data = generate_radar_data(summary["dimension_scores"])
    
    # 4. 渲染 HTML 模板
    with open("templates/report.html") as f:
        template = Template(f.read())
    
    html = template.render(
        summary=summary,
        radar_data=radar_data,
        results=results
    )
    
    # 5. 保存报告
    Path(output).write_text(html, encoding="utf-8")
    print(f"报告已生成:{output}")


六、技术亮点

6.1 可扩展架构

插件化设计

Python - 添加新维度

# 添加新维度只需 3 步

# 1. 创建新的评测器
class NewDimensionEvaluator(BaseEvaluator):
    def evaluate(self, agent, task):
        # 实现评测逻辑
        pass

# 2. 创建评测基准
# benchmarks/new_dimension/

# 3. 更新配置文件
# eval_config.yaml
evaluator:
  dimensions:
    - task_planning
    - tool_use
    - dialogue
    - coding
    - knowledge
    - new_dimension  # ← 添加新维度

6.2 沙箱环境

安全的工具执行

Python - environments/sandbox.py

import docker

class SandboxEnvironment:
    """Docker 沙箱环境"""
    
    def __init__(self):
        self.client = docker.from_client()
        self.container = None
    
    def start(self):
        """启动沙箱容器"""
        self.container = self.client.containers.run(
            "python:3.10-slim",
            command="tail -f /dev/null",
            detach=True,
            network_disabled=True,  # 禁用网络
            volumes={
                "/tmp/sandbox": {"bind": "/sandbox", "mode": "rw"}
            }
        )
    
    def execute_code(self, code: str) -> Dict:
        """在沙箱中执行代码"""
        # 写入代码文件
        self.container.exec_run(
            "sh -c 'cat > /sandbox/code.py << EOF\n" + code + "\nEOF'"
        )
        
        # 执行代码
        result = self.container.exec_run(
            "python /sandbox/code.py",
            timeout=30  # 30 秒超时
        )
        
        return {
            "exit_code": result.exit_code,
            "output": result.output.decode(),
            "success": result.exit_code == 0
        }
    
    def stop(self):
        """停止沙箱"""
        if self.container:
            self.container.stop()
            self.container.remove()

6.3 自动化评分

无需人工干预

Python - 自动评分

def auto_score(agent_response: str, expected: Dict) -> float:
    """自动评分"""
    # 1. 解析 Agent 输出
    parsed = parse_response(agent_response)
    
    # 2. 对比标准答案
    if expected["type"] == "exact_match":
        score = 1.0 if parsed == expected["value"] else 0.0
    
    elif expected["type"] == "fuzzy_match":
        score = calculate_similarity(parsed, expected["value"])
    
    elif expected["type"] == "rubric_based":
        score = score_by_rubric(parsed, expected["rubric"])
    
    # 3. 返回分数
    return score


七、总结

7.1 核心成果

完成的工作

  • ✅ 5 个评测器
  • ✅ 54 个评测任务
  • ✅ 自动化评测流程
  • ✅ HTML 可视化报告
  • ✅ 沙箱环境
  • ✅ 插件化架构

技术亮点

  • 可扩展架构(随时加新维度)
  • 细粒度指标(不只是成功/失败)
  • 自动化评分(无需人工干预)
  • 沙箱隔离(安全执行)

7.2 下一步计划

短期(1-2 周)

  1. 增加性能评测模块
  2. 支持多 Agent 对比

你对 Agent 评测系统有什么想法?

A. 架构设计很合理,想试试
B. 有些功能我们也在做,可以交流
C. 有不懂的地方,想请教
D. 想参与开源贡献

下篇预告:评测相关、Hermes 系列、crewAI 系列都有可能,因为我要求自己一定要真实实战,所以哪个先有效果就先分享哪篇;

作者:测试员周周,14 年测试经验,专注 AI+ 测试实