> ## Documentation Index
> Fetch the complete documentation index at: https://agent-compass.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Analyzer 集成

当你需要对执行结果进行诊断或统计，但不应改变 Benchmark 的权威评测结果时，新增一个 Analyzer。

Analyzer 在一次任务尝试产生 `RunResult` 后执行。它可以检查规范化答案、轨迹、指标、状态、错误和产物，并返回 `AnalysisResult`。它不能重新运行 agent、改变 Benchmark 的正确性判定，也不能替代应在 `BaseBenchmark.evaluate()` 中实现的评分逻辑。

## 实现一个公开 Analyzer

下面的示例基于 [Benchmark 的 Harness 驱动教程](/zh/developer_guide/extensions/benchmark/code_implementation/harness_driven)中的公开 Benchmark `example_exact_match`。它用于标记空答案或异常长的最终答案，不依赖任何 Environment provider 实现：

```python theme={"system"}
from agentcompass.runtime import (
    ANALYZERS,
    AnalysisResult,
    AnalyzerCategory,
    BaseAnalyzer,
    RunResult,
)


@ANALYZERS.register()
class ExampleAnswerLengthAnalyzer(BaseAnalyzer):
    id = "ExampleAnswerLengthAnalyzer"
    description = "Flag empty or unusually long example final answers."
    category = AnalyzerCategory.BEHAVIOR
    datasets = ["example_exact_match"]
    data_requirements = ["$.final_answer"]
    conf = {
        "only_incorrect": False,
        "max_characters": 1000,
    }
    distribution_fields = {
        "answer_characters": "numeric_stats",
    }

    async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
        _ = prepared, req, plan
        answer = str(result.final_answer or "").strip()
        answer_characters = len(answer)
        max_characters = max(1, int(self.conf.get("max_characters", 1000)))

        return AnalysisResult(
            task_id=task.task_id,
            is_badcase=not answer or answer_characters > max_characters,
            details={
                "answer_characters": answer_characters,
                "max_characters": max_characters,
                "empty_answer": not answer,
            },
        )
```

基于规则的 Analyzer 应保持确定性。如果 Analyzer 需要调用 Model，应公开并验证其配置，且不得修改被评测结果。分析失败应记录为 Analyzer 输出，不能修改任务状态或得分。

## `BaseAnalyzer` 契约

| 属性或方法                 | 在当前 runtime 中的含义                                                             |
| --------------------- | ---------------------------------------------------------------------------- |
| `id`                  | 唯一注册 ID 和默认输出系列键                                                             |
| `description`         | `agentcompass list analyzer` 显示的说明文字                                         |
| `category`            | 通过 `AnalyzerCategory` 声明的分类元数据；当前持久化和运行级汇总不会按该字段分组                           |
| `datasets`            | 可运行的 Benchmark ID；空列表匹配所有 Benchmark                                          |
| `data_requirements`   | 用于检查 `RunResult.json` 的 JSONPath 表达式；缺少任一必需数据时跳过该次尝试的 Analyzer               |
| `conf`                | Analyzer 默认配置；`execution.analysis_params` 下与 `<AnalyzerId>` 对应的对象会覆盖该实例中的对应值 |
| `distribution_fields` | 使用 `numeric_stats` 或 `value_counts` 聚合的 `details` 字段                         |
| `base_analyzer`       | 专用实现可指定的基础系列 ID                                                              |
| `priority`            | 用于选择同一系列内的实现；数值越高，优先级越高                                                      |
| `analysis()`          | 为一次尝试返回 `AnalysisResult` 的异步方法                                               |

基类还提供 `matches_dataset()`、`check_requirements()`、`should_skip()` 和 `is_threshold_badcase()`。共享的 `should_skip()` 能识别 `conf.only_incorrect`；自定义配置仍由 Analyzer 自己处理。

`datasets` 必须使用准确的已注册 Benchmark ID。`data_requirements` 只应包含调用 `analysis()` 前确实必需的数据；可选字段应在方法内处理，避免因为缺失而静默移除整个 Analyzer 输出。

## 注册与导出

将实现放在 `src/agentcompass/analyzers/` 下；确定性规则通常放在 `analyzers/basic/`。使用 `@ANALYZERS.register()` 装饰具体类，并通过各级软件包 `__init__.py` 导入其模块，确保导入 `agentcompass.analyzers` 时执行装饰器。

注册表会拒绝重复 ID。为了方便 Python 导入，可以选择在顶层重新导出名称，但用于触发注册的逐级模块导入不可省略。运行以下命令确认发现结果：

```bash theme={"system"}
uv run agentcompass list analyzer
```

Analyzer 专属值通过 `execution.analysis_params` 传入；`agentcompass config docs` 当前只记录 Benchmark、Harness 和 Environment 配置数据类，不记录 Analyzer `conf` 字典。必须显式记录每个支持的 Analyzer 键及其默认值。

## 选择与系列解析

启用分析后，runtime 会为每次尝试执行以下步骤：

1. 如果存在 `analyzers` 允许列表，则只保留其中列出的 Analyzer；否则应用 `exclude_analyzers` 排除列表。
2. 构造每个剩余的已注册 Analyzer，并使用按 ID 提供的配置覆盖默认值。
3. 检查 `datasets`、`data_requirements` 和 `only_incorrect` 是否满足。
4. 按 `base_analyzer` 分组；未设置时使用自身 `id` 作为系列。
5. 选择各系列中 `priority` 严格最高的实现并调用其 `analysis()` 方法。

同一系列中符合条件的成员优先级相同时，较早注册的实现仍会入选。专用 Analyzer 只有在扩展同一套诊断契约和输出系列时，才应设置 `base_analyzer = "<generic-id>"`、更高的 `priority` 和范围更窄的 `datasets`。无关 Analyzer 应保留 `base_analyzer = None`，使它们可以独立运行。

这一优先级行为只属于 Analyzer。Recipe 的优先级当前不会影响 Recipe 应用顺序。

## 返回 `AnalysisResult`

`analysis()` 返回以下字段：

| 字段           | 含义                                               |
| ------------ | ------------------------------------------------ |
| `task_id`    | 当前任务 ID；持久化任务记录已包含该标识                            |
| `is_badcase` | 检测结果明确时使用 `True` 或 `False`；纯统计输出或结果不确定时使用 `None` |
| `details`    | 为每次尝试保留的可 JSON 序列化诊断数据                           |
| `score`      | 可选的 Analyzer 数值得分，与 Benchmark 得分分开聚合             |
| `error`      | 无法完成分析时的可选说明                                     |
| `extra`      | 可选的其他可 JSON 序列化元数据                               |

runtime 将获选系列的结果保存到：

```text theme={"system"}
attempts.<attempt-index>.analysis_result.<analyzer-family>
```

保存的数据始终包含 `is_badcase` 和 `details`，并只在有值时包含 `score`、`error` 和 `extra`。`AnalysisResult.task_id` 不会在该系列数据中重复保存。如果 `analysis()` 抛出异常，runtime 会记录该系列的错误，将判断标记为不确定并继续执行，不会改变 Benchmark 已有结果。

只能声明支持的聚合方式：

```python theme={"system"}
distribution_fields = {
    "answer_characters": "numeric_stats",  # int 或 float
    "error_types": "value_counts",         # str 或 list[str]
}
```

只要至少一个持久化任务尝试包含可聚合的分析输出，运行级聚合就可以写入 `analysis_summary.json` 和 `analysis_summary.md`。若检测器显式返回 `is_badcase`，但异常样本与分析错误均为零，该结果仍会保留在对应任务尝试的详情记录中。此时，它不会进入运行级摘要。持久化格式见[汇总与分析结果](/zh/user_guide/other_features/results/summary_analysis)。

## 使用一个公开任务验证

完成 Benchmark 示例和 [Harness 实现教程](/zh/developer_guide/extensions/harness/code_implementation)中的 `example_exact_match` 与 `example_answer` 后，使用示例 Analyzer 运行对应的确定性任务。该流程不需要 Model 端点或凭证：

```bash theme={"system"}
uv run agentcompass run example_exact_match example_answer unused-model \
  --env host_process \
  --benchmark-params '{"sample_ids":["capital-france"]}' \
  --harness-params '{"answer":"Paris"}' \
  --task-concurrency 1 \
  --max-retries 0 \
  --enable-analysis \
  --analysis-params '{
    "analyzers": ["ExampleAnswerLengthAnalyzer"],
    "ExampleAnswerLengthAnalyzer": {"max_characters": 4}
  }' \
  --results-dir results-dev \
  --run-name analyzer-smoke
```

应验证：

* 该次尝试的 `analysis_result` 下只出现请求指定的系列；
* `details.answer_characters` 与已保存的 `final_answer` 一致；
* 配置的上限能覆盖类默认值，且不会修改后续 Analyzer 实例。五个字符的答案会被四字符上限标记为异常样本；
* 其他 Benchmark 会因为 `datasets` 被跳过；
* 缺少必需数据时跳过 Analyzer，而 `analysis()` 内部异常会产生系列错误；
* 输出可聚合时，`analysis_summary.json` 和 `analysis_summary.md` 包含声明的数值分布。

还应使用 [`agentcompass analysis`](/zh/user_guide/using_agentcompass/cli/analysis#重新分析已有结果) 在已有结果的副本上重新运行 Analyzer，验证它与持久化 `RunResult` 重建逻辑兼容。完整代码仓库检查和 PR 证据见[测试与验证](/zh/developer_guide/contributing/testing)。

## 完成检查表

* 新逻辑用于诊断已有结果，不替代 Benchmark 评分。
* ID、说明、类别、数据集范围、必需字段和配置默认值均已明确。
* 从 `agentcompass.analyzers` 导入时能够完成注册，且 `agentcompass list analyzer` 显示该 ID。
* 系列与优先级设置不会导致无关 Analyzer 被排除。
* `AnalysisResult` 和 `details` 可 JSON 序列化，声明的分布使用支持的值类型。
* 已在公开数据上检查运行期间分析、结果副本重新分析、跳过、错误和聚合输出。
* 面向用户的配置和输出字段均已用中英文记录。
