Metadata-Version: 2.4
Name: geo-crawler
Version: 0.1.0
Summary: AI-powered geographic information crawler with multi-model support
Author: GEO Crawler Team
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: beautifulsoup4>=4.0.0
Requires-Dist: browser-use-volcengine>=0.12.6
Requires-Dist: markdownify>=1.0.0
Requires-Dist: openai>=2.0.0
Requires-Dist: openpyxl>=3.0.0
Requires-Dist: pandas>=2.0.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: rich>=14.0.0
Requires-Dist: typer>=0.20.0
Requires-Dist: volcengine-python-sdk>=4.0.34
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: hypothesis>=6.0.0; extra == "dev"

# GEO 实验调度器（GEO Experiment Orchestrator）

GEO 实验调度器是一个轻量级的实验执行系统，用于在单个 AI 平台上执行标准化查询任务并自动评估结果。

## 功能特性

- 📊 **数据集加载**：支持从 JSON 文件加载或交互式输入查询任务
- 🤖 **查询执行**：在 AI 平台上自动执行查询任务
- ✅ **自动评估**：使用规则评判器自动评估查询结果的正确性
- 📈 **指标计算**：计算准确率等关键指标
- 💾 **结果保存**：保存完整的实验产物到运行目录
- 🏷️ **品牌评估**：通过5个核心指标量化品牌在AI回答中的表现（被引用率、首次提及位置、信息深度覆盖、话语份额、平台份额）

## 快速开始

### 1. 环境准备与安装

项目已配置为标准 Python 包（使用 `pyproject.toml`），确保已安装 **Python 3.11+**：

```bash
# 安装基础依赖
uv sync

# 开发环境：安装所有依赖（包括测试工具）
uv sync --all-extras --dev
```

这将自动安装所有依赖并将 `src/` 目录配置为可导入的包。

#### 依赖安装说明

- `uv sync` - 只安装基础运行依赖
- `uv sync --extra dev` - 安装开发依赖（pytest、hypothesis 等）
- `uv sync --all-extras` - 安装所有可选依赖组
- `uv sync --all-extras --dev` - 安装所有依赖和开发工具（推荐用于开发环境）

#### 检查安装状态

```bash
# 查看已安装的包
uv pip list

# 查看项目依赖树
uv pip show --tree
```

**主要依赖：**
- `browser-use`：浏览器自动化
- `pydantic`：数据验证
- `python-dotenv`：环境变量管理
- `beautifulsoup4`：HTML 解析
- `openpyxl`：Excel 报告生成

**安装优势：**
- ✅ 无需 `sys.path.insert` 操作
- ✅ 代码修改后无需重新安装（可编辑模式）
- ✅ IDE 获得更好的代码补全和类型检查
- ✅ 符合 Python 包管理最佳实践

### 1.1 包导入

安装后，可以在项目任何位置直接导入模块：

```python
from doubao.query import query_doubao
from kimi.query import query_kimi
from deepseek.query import query_deepseek
from yuanbao.query import query_yuanbao
from orchestrator.runner import Runner
from orchestrator.dataset import load_dataset_interactive
```

### 2. 初始化浏览器登录（首次使用必须）

在运行实验之前，需要先初始化各平台的浏览器登录状态：

```bash
# 初始化所有平台登录（推荐）
geo-crawler init-profile all

# 或单独初始化某个平台
geo-crawler init-profile kimi
geo-crawler init-profile doubao
geo-crawler init-profile yuanbao
geo-crawler init-profile deepseek

# 检查登录状态
geo-crawler init-profile all --check

# 强制重新登录
geo-crawler init-profile kimi --force
```

登录状态会保存在 `browser_profiles/` 目录中，下次运行时自动使用。

### 3. 运行采样

使用 GEO CLI 工具运行纯采样：

```bash
# 直接传入问题
geo-crawler sample run \
  --platform kimi \
  --platform qianwen \
  --query "20万左右电动车推荐" \
  --mode quick \
  --repeat 3 \
  --api-key-env OPENAI_API_KEY

# 使用数据集
geo-crawler sample run \
  --platform kimi \
  --dataset examples/dataset_test_single.json \
  --env-file .env
```

采样将自动执行以下步骤：
1. 加载数据集
2. 在 AI 平台上执行查询
3. 保存原始采样产物到 `runs/` 目录

### 4. 查看结果

采样完成后，结果将保存在 `runs/run-YYYYMMDD-HHMMSS-xxxxxx/` 目录中：

```
runs/run-20251203-143025-a1b2c3/
├── manifest.json       # 采样元信息
├── inputs.json         # 输入数据集快照
├── outputs.json        # 查询执行结果
├── summary.json        # 成功和失败数量
└── DONE                # 完成标记
```

## 使用示例

### 示例 1：直接传入问题

```bash
python examples/geo_cli.py sample run \
  --platform kimi \
  --platform qianwen \
  --query "20万左右电动车推荐" \
  --mode quick \
  --repeat 3 \
  --api-key-env OPENAI_API_KEY \
  --format json
```

### 示例 2：使用采样配置文件

```bash
python examples/geo_cli.py sample run --config sampling.json
```

配置文件示例：

```json
{
  "platforms": ["kimi", "qianwen"],
  "inputs": [
    {"id": "q1", "query": "20万左右电动车推荐"}
  ],
  "mode": "quick",
  "repeat": 3,
  "out": "runs",
  "controller": {
    "model": "gpt-4.1-mini",
    "api_key_env": "OPENAI_API_KEY"
  },
  "browser": {
    "profile_dir": "browser_profiles",
    "use_judge": true
  }
}
```

## 数据集格式

数据集使用 JSON 格式，包含一个查询任务数组。每个任务必须包含以下字段：

### 必需字段

- `id` (string)：查询任务的唯一标识符
- `query` (string)：查询内容

### 示例数据集

品牌评估场景示例（`examples/dataset_test_single.json`）：

```json
[
  {
    "id": "q1",
    "query": "推荐一款30万左右的新能源SUV"
  },
  {
    "id": "q2",
    "query": "哪款电动车续航最长？"
  },
  {
    "id": "q3",
    "query": "适合家用的智能电动车有哪些？"
  }
]
```

### 数据集验证规则

系统会自动验证数据集：
- ✅ 顶层必须是数组
- ✅ 每个任务必须是字典对象
- ✅ 必须包含 `id` 和 `query` 字段
- ✅ 字段值必须是非空字符串
- ⚠️ 无效任务会被跳过并记录警告

## 实验配置参数

### 完整配置说明

```json
{
  "id": "实验唯一标识符",
  "dataset": "数据集文件路径",
  "platforms": ["平台列表"],
  "judges": [
    {
      "type": "评判器类路径",
      "params": {
        "judge_id": "评判器ID",
        "target_brand": "目标品牌",
        "key_dimensions": ["关键维度列表"],
        "use_llm": true
      }
    }
  ],
  "metrics": [
    {
      "type": "指标类路径",
      "params": {
        "metric_id": "指标ID",
        "target_brand": "目标品牌",
        "key_dimensions": ["关键维度列表"]
      }
    }
  ],
  "repeat_count": 1,
  "thinking_mode": "quick"
}
```

### 参数详解

#### id
- **类型**：`string`
- **说明**：实验的唯一标识符
- **示例**：`"exp_xiaopeng_evaluation"`

#### dataset
- **类型**：`string`
- **说明**：数据集文件路径（相对于项目根目录）
- **示例**：`"examples/dataset_test_single.json"`

#### platforms
- **类型**：`list[string]`
- **说明**：要评估的 AI 平台列表
- **可选值**：`"kimi"`, `"doubao"`, `"deepseek"`, `"yuanbao"`
- **示例**：
  - 单平台：`["kimi"]`
  - 多平台：`["kimi", "doubao", "deepseek", "yuanbao"]`

#### judges
- **类型**：`list[dict]`
- **说明**：评判器配置列表，支持多个评判器
- **结构**：
  - `type`：评判器类的完整路径
  - `params`：评判器参数
    - `judge_id`：评判器唯一标识
    - `target_brand`：目标品牌名称
    - `key_dimensions`：关键维度列表
    - `use_llm`：是否使用 LLM 语义判断（推荐 `true`）
    - `competitor_brands`：竞品列表（仅关键词模式需要）
    - `dimension_keywords`：维度关键词映射（仅关键词模式需要）

#### metrics
- **类型**：`list[dict]`
- **说明**：指标计算器配置列表，支持多个指标
- **结构**：
  - `type`：指标类的完整路径
  - `params`：指标参数
    - `metric_id`：指标唯一标识
    - `target_brand`：目标品牌名称
    - `key_dimensions`：关键维度列表

#### repeat_count
- **类型**：`integer`
- **说明**：每个查询在每个平台上重复执行的次数
- **默认值**：`1`
- **用途**：用于稳定性测试和方差分析
- **示例**：`5`（每个查询执行 5 次）

#### thinking_mode
- **类型**：`string`
- **说明**：AI 平台的思考模式
- **可选值**：
  - `"quick"`：快速模式，响应更快，适合批量测试
  - `"deep"`：深度模式，思考更深入，结果更准确
- **默认值**：`"quick"`
- **示例**：`"deep"`

## 输出说明

### 运行目录结构

每次实验执行后，会在 `runs/` 目录下创建一个带时间戳的运行目录：

```
runs/
└── run-20251203-143025-a1b2c3/
    ├── manifest.json
    ├── inputs.json
    ├── outputs.json
    ├── evals.json
    ├── metrics.json
    ├── costs.json
    ├── report.xlsx        # Excel 报告（品牌评估实验）
    └── DONE
```

### 文件说明

#### manifest.json
实验元信息和配置：

```json
{
  "run_id": "run-20251203-143025-a1b2c3",
  "dataset": "dataset.json",
  "platform": "kimi",
  "judges": ["rule_v1"],
  "metrics": ["accuracy"],
  "repeat_count": 1,
  "thinking_mode": "quick",
  "started_at": "2025-12-03T14:30:25Z"
}
```

#### inputs.json
输入数据集的快照：

```json
[
  {
    "id": "q1",
    "query": "巴黎塔多高？"
  }
]
```

#### outputs.json
查询执行结果列表：

```json
[
  {
    "id": "q1",
    "query": "巴黎塔多高？",
    "platform": "kimi",
    "mode": "quick",
    "start_time": "2025-12-03 14:30:25",
    "end_time": "2025-12-03 14:30:28",
    "final_answer": "埃菲尔铁塔高度约为 330 米",
    "thinking_process": "思考过程...",
    "references": "参考资料...",
    "token_usage": {
      "prompt_tokens": 1200,
      "completion_tokens": 300,
      "total_tokens": 1500,
      "model": "gpt-4o-mini",
      "by_model": {
        "gpt-4o-mini": {
          "prompt_tokens": 1200,
          "completion_tokens": 300,
          "total_tokens": 1500
        }
      }
    }
  }
]
```

#### evals.json
评估结果列表：

```json
[
  {
    "id": "q1",
    "label": "correct",
    "score": 1
  }
]
```

#### metrics.json
聚合指标：

```json
{
  "accuracy": 0.75,
  "correct_count": 3,
  "total_count": 4
}
```

#### costs.json
Token 用量和费用汇总。费用单位固定为人民币，价格来自 `.env` 中“每百万 tokens 的人民币价格”配置，不做汇率转换：

```json
{
  "currency": "CNY",
  "unit": "RMB per 1M tokens",
  "total_prompt_tokens": 1400,
  "total_completion_tokens": 400,
  "total_tokens": 1800,
  "total_cost_rmb": 0.0036,
  "records": [],
  "by_source": {
    "query": {
      "prompt_tokens": 1200,
      "completion_tokens": 300,
      "total_tokens": 1500,
      "cost_rmb": 0.003
    }
  }
}
```

#### report.xlsx
Excel 格式的品牌评估报告（仅在品牌评估实验中生成）：

包含以下内容：
- HIQ（用户查询问题）
- 品牌名
- 关键维度列表
- 响应数
- 品牌指标表格（按平台展示）
  - Model（platform）
  - Inclusion Rate（被引用率）
  - Share of Voice（话语份额）
  - Share of Model（模型份额）
  - First Mention Position（首次提及位置）
  - First Mention Percentage（首次提及百分比）
  - Depth Coverage（深度覆盖）

#### DONE
空文件，标记实验成功完成。

### 控制台输出

实验执行过程中，控制台会显示详细的进度信息：

```
============================================================
开始执行实验
============================================================
步骤 1/5: 加载数据集
✓ 数据集加载完成 - 任务数: 5
步骤 2/5: 执行查询
✓ 查询执行完成 - 结果数: 5
步骤 3/5: 评估结果
✓ 结果评估完成 - 评估数: 5
步骤 4/5: 计算指标
✓ 指标计算完成 - 准确率: 80.00%
步骤 5/5: 保存产物
✓ 产物保存完成 - 运行目录: runs/run-20251203-143025-a1b2c3
============================================================
实验执行完成
============================================================
运行目录: runs/run-20251203-143025-a1b2c3
关键指标摘要:
  - 准确率: 80.00%
  - 正确数: 4/5
============================================================
```

## 日志系统

### 日志配置

系统使用 Python 标准 logging 模块，日志会同时输出到：
- 控制台（标准输出）
- 日志文件（`logs/experiment.log`）

### 日志级别

- **INFO**：关键步骤和进度信息
- **WARNING**：警告信息（如跳过无效任务）
- **ERROR**：错误信息和异常堆栈
- **DEBUG**：详细的调试信息（单个查询处理）

### 自定义日志配置

使用命令行参数指定日志级别：

```bash
# 使用 DEBUG 级别
python examples/geo_cli.py sample run \
  --platform kimi \
  --query "推荐一款30万左右的新能源SUV" \
  --log-level DEBUG

# 使用 INFO 级别（默认）
python examples/geo_cli.py sample run \
  --platform kimi \
  --dataset examples/dataset_test_single.json \
  --log-level INFO
```

或在代码中配置：

```python
from orchestrator.runner import configure_logging

# 配置日志级别和输出文件
configure_logging(
    level="DEBUG",                    # 日志级别
    log_file="logs/my_experiment.log" # 日志文件路径
)
```

## 常见问题

### Q: 首次使用需要做什么？

**A:** 首次使用必须先初始化浏览器登录：

```bash
python examples/geo_cli.py init-profile all
```

按照提示在浏览器中登录各个平台，登录状态会自动保存。

### Q: 如何添加新的查询任务？

**A:** 创建或编辑数据集 JSON 文件，添加新的查询对象：

```json
[
  {
    "id": "q1",
    "query": "推荐一款30万左右的新能源SUV"
  },
  {
    "id": "q2",
    "query": "你的新查询内容"
  }
]
```

### Q: 如何查看详细的执行日志？

**A:** 查看 `logs/brand_experiment.log` 文件，或使用 DEBUG 日志级别：

```bash
python examples/geo_cli.py sample run \
  --platform kimi \
  --query "推荐一款30万左右的新能源SUV" \
  --log-level DEBUG
```

### Q: 实验失败后如何重试？

**A:** 直接重新运行相同的命令，系统会创建新的运行目录。每次运行都是独立的。

### Q: 如何自定义评判规则？

**A:** 继承 `orchestrator.base_judge.Judge` 抽象基类，实现自定义的评判逻辑。参考 `src/orchestrator/judges/brand_dimension_judge.py` 示例。

详见：`src/orchestrator/README.md`

### Q: 运行目录的命名规则是什么？

**A:** 格式为 `run-YYYYMMDD-HHMMSS-xxxxxx`，其中：
- `YYYYMMDD`：日期（年月日）
- `HHMMSS`：时间（时分秒，UTC 时间）
- `xxxxxx`：6 位随机十六进制后缀（避免并发冲突）

### Q: LLM 模式和关键词模式有什么区别？

**A:** 
- **LLM 模式（推荐）**：使用大语言模型进行语义判断，自动识别品牌提及和维度覆盖，无需手动配置竞品列表和关键词
- **关键词模式**：基于关键词匹配，需要手动配置竞品列表和维度关键词，准确性较低

推荐使用 LLM 模式（示例 1-7），配置更简单，效果更好。

## 项目结构

```
geo-crawler/
├── src/                       # 源代码目录
│   ├── doubao/                # 豆包 AI 集成模块
│   ├── kimi/                  # Kimi AI 集成模块
│   ├── deepseek/              # DeepSeek AI 集成模块
│   ├── yuanbao/               # 元宝 AI 集成模块
│   └── orchestrator/          # 实验编排与评估框架
│       ├── __init__.py
│       ├── runner.py          # 实验编排器
│       ├── agent.py           # Agent 抽象层
│       ├── base_judge.py      # Judge 抽象基类（推荐）
│       ├── base_metric.py     # Metric 抽象基类（推荐）
│       ├── models.py          # 数据模型（EvalRecord、MetricResult）
│       ├── judge.py           # ⚠️ 已废弃，请使用 base_judge.py
│       ├── metric.py          # ⚠️ 已废弃，请使用 base_metric.py
│       ├── storage.py         # 存储管理
│       ├── dataset.py         # 数据集管理
│       ├── reporter.py        # 报告生成器
│       ├── llm_config.py      # LLM 配置
│       ├── llm_wrapper.py     # LLM 包装器
│       ├── judges/            # 评判器实现模块
│       │   ├── brand_dimension_judge.py  # 品牌维度评判器
│       │   └── llm_response_models.py    # LLM 响应模型
│       └── metrics/           # 指标实现模块
│           └── brand_metric.py           # 品牌指标计算器
├── tests/                     # 测试目录
├── examples/                  # 示例脚本目录
│   ├── geo_cli.py             # CLI 兼容转发壳
│   ├── dataset_test_single.json          # 示例数据集
│   └── experiment_multi_judge_metric.json # 示例配置
├── experiments/               # 实验配置目录
├── runs/                      # 运行结果目录
├── output/                    # 查询输出目录
├── logs/                      # 日志文件目录
├── browser_profiles/          # 浏览器配置目录
├── pyproject.toml             # 项目配置
├── requirements.txt           # 依赖列表
└── README.md                  # 本文档
```

## 架构说明

### Judge 和 Metric 抽象基类

从 v2.0 开始，系统采用新的抽象基类架构：

#### Judge 抽象基类（`base_judge.py`）

所有评判器都应继承 `Judge` 抽象基类并实现以下方法：

- `evaluate(item, output) -> EvalRecord`：评判单个 output
- `evaluate_batch(items, outputs) -> List[EvalRecord]`：批量评判（可选优化）

**特性：**
- ✅ 每个 output 生成独立的 EvalRecord
- ✅ 标准化的评估记录格式（包含 eval_id、output_id、query_id、platform、timestamp、result）
- ✅ 内置错误处理和日志记录

**示例：**

```python
from orchestrator.base_judge import Judge, EvalRecord

class MyCustomJudge(Judge):
    async def evaluate(self, item: Dict[str, Any], output: Dict[str, Any]) -> EvalRecord:
        # 实现评判逻辑
        result = {"score": 1.0, "label": "correct"}
        return EvalRecord(
            eval_id=self._generate_eval_id(),
            output_id=output.get("id"),
            query_id=item.get("id"),
            platform=output.get("platform"),
            timestamp=datetime.now(UTC).isoformat(),
            result=result
        )
```

#### Metric 抽象基类（`base_metric.py`）

所有指标计算器都应继承 `Metric` 抽象基类并实现以下方法：

- `compute(eval_records) -> Dict[str, Any]`：计算总体指标
- `compute_by_group(eval_records, group_by) -> Dict[str, Dict[str, Any]]`：按分组计算指标

**特性：**
- ✅ 支持总体和分组两层统计
- ✅ 标准化的指标结果格式（MetricResult 包含 total 和 by_platform）
- ✅ 灵活的分组机制（按 platform、model 等）

**示例：**

```python
from orchestrator.base_metric import Metric, MetricResult

class MyCustomMetric(Metric):
    def compute(self, eval_records: List[EvalRecord]) -> Dict[str, Any]:
        # 实现总体指标计算
        return {"accuracy": 0.85, "total_count": 100}
    
    def compute_by_group(self, eval_records: List[EvalRecord], group_by: str = "platform") -> Dict[str, Dict[str, Any]]:
        # 实现分组指标计算
        return {
            "kimi": {"accuracy": 0.90, "count": 50},
            "doubao": {"accuracy": 0.80, "count": 50}
        }
```

### 数据模型

系统使用 Pydantic 模型确保类型安全：

- **EvalRecord**：评估记录的标准格式
  - `eval_id`：唯一的评估记录 ID
  - `output_id`：对应的 output ID
  - `query_id`：对应的查询任务 ID
  - `platform`：AI 平台名称
  - `timestamp`：评判时间戳
  - `result`：具体的评判结果
  - `error`：错误信息（如果评判失败）

- **MetricResult**：指标结果的标准格式
  - `total`：总体指标
  - `by_platform`：按平台分组的指标

### 迁移指南

⚠️ **旧的 `judge.py` 和 `metric.py` 已废弃**

如果您的代码仍在使用旧的基类，请按以下步骤迁移：

1. **更新导入语句：**
   ```python
   # 旧的方式（已废弃）
   from orchestrator.judge import Judge
   from orchestrator.metric import Metric
   
   # 新的方式
   from orchestrator.base_judge import Judge, EvalRecord
   from orchestrator.base_metric import Metric, MetricResult
   ```

2. **更新 Judge 实现：**
   - 将 `evaluate()` 方法改为返回 `EvalRecord` 对象
   - 为每个 output 生成独立的评估记录
   - 使用 `_generate_eval_id()` 生成唯一 ID

3. **更新 Metric 实现：**
   - 实现 `compute()` 方法计算总体指标
   - 实现 `compute_by_group()` 方法计算分组指标
   - 使用 `compute_all()` 方法返回 `MetricResult` 对象

4. **更新实验配置：**
   - 确保 judges 和 metrics 配置指向新的实现类
   - 检查结果保存格式是否符合新的数据模型

## 技术栈

- **Python 3.11+**：核心编程语言
- **browser-use**：浏览器自动化框架
- **Pydantic**：数据验证和类型安全
- **Typer**：CLI 框架
- **Rich**：终端美化输出
- **OpenPyxl**：Excel 报告生成
- **BeautifulSoup4**：HTML 解析

## 相关文档

- **API 文档**：`src/orchestrator/README.md` - 完整的 API 文档和使用指南
- **设计文档**：`.kiro/specs/judge-metric-redesign/` - 系统设计和架构说明
- **测试文档**：`tests/README.md` - 测试套件说明

## 许可证

本项目遵循 MIT 许可证。

## 品牌评估模块

GEO 实验调度器支持品牌评估功能，通过5个核心指标量化品牌在AI回答中的表现。

### 核心指标

1. **被引用率（Inclusion Rate）**：品牌被提及的响应数占总响应数的百分比
2. **首次提及位置（First Mention Position）**：品牌在响应中首次出现的平均字符位置
3. **信息深度覆盖（Depth Coverage）**：品牌提及时覆盖关键维度的平均百分比
4. **话语份额（Share of Voice）**：目标品牌提及次数占所有品牌提及次数的百分比
5. **平台份额（Platform Shares）**：按平台分组的话语份额

### 快速开始

使用品牌评估功能只需配置实验参数：

```python
experiment = {
    "id": "exp_brand_evaluation",
    "dataset": "dataset.json",
    "platform": "kimi",
    "judges": [
        {
            "name": "brand_l9",
            "type": "judges.brand_dimension_judge.BrandDimensionJudge",
            "params": {
                "target_brand": "理想L9",
                "competitor_brands": ["蔚来ES8", "问界M9"],
                "key_dimensions": ["长续航", "大空间", "智能驾驶"],
                "dimension_keywords": {
                    "长续航": ["续航", "里程", "充电"],
                    "大空间": ["空间", "座位", "宽敞"],
                    "智能驾驶": ["自动驾驶", "辅助驾驶", "智能"]
                }
            }
        }
    ],
    "metrics": [
        {
            "name": "brand_l9_metrics",
            "type": "metrics.brand_metric.BrandMetric",
            "params": {
                "target_brand": "理想L9",
                "key_dimensions": ["长续航", "大空间", "智能驾驶"]
            }
        }
    ],
    "repeat_count": 1,
    "thinking_mode": "quick"
}
```

### 示例脚本

查看 `geo-crawler` 命令获取完整示例，包括：
- 单品牌评估
- 多品牌对比评估
- 跨平台品牌评估



## 架构演进与迁移

### 废弃通知

从 v2.0 开始，旧的 `judge.py` 和 `metric.py` 基类已废弃，将在 v3.0 移除。请使用新的抽象基类：

- **旧类**：`orchestrator.judge.Judge`、`orchestrator.metric.Metric` ⚠️ 已废弃
- **新类**：`orchestrator.base_judge.Judge`、`orchestrator.base_metric.Metric` ✅ 推荐

**快速迁移：**

```python
# 旧方式（已废弃）
from orchestrator.judge import Judge
class MyJudge(Judge):
    def evaluate(self, item, output):
        return {"query_id": item["id"], "label": "correct"}

# 新方式（推荐）
from orchestrator.base_judge import Judge, EvalRecord
from datetime import datetime, UTC

class MyJudge(Judge):
    async def evaluate(self, item, output):
        return EvalRecord(
            eval_id=self._generate_eval_id(),
            output_id=output.get("id"),
            query_id=item.get("id"),
            platform=output.get("platform"),
            timestamp=datetime.now(UTC).isoformat(),
            result={"label": "correct"}
        )
```

**新架构优势：**
- ✅ 每个 output 独立评判，可追溯
- ✅ 支持总体和分组两层统计
- ✅ 强类型约束（Pydantic）
- ✅ 支持异步操作



### 多 Judge/Metric 支持

从 v2.0 开始支持在一个实验中配置多个 Judge 和 Metric，实现多维度评估：

```json
{
  "judges": [
    {
      "type": "judges.brand_dimension_judge.BrandDimensionJudge",
      "params": {"judge_id": "brand_judge_ideal", "target_brand": "理想汽车"}
    },
    {
      "type": "judges.brand_dimension_judge.BrandDimensionJudge",
      "params": {"judge_id": "brand_judge_nio", "target_brand": "蔚来"}
    }
  ],
  "metrics": [
    {
      "type": "metrics.brand_metric.BrandMetric",
      "params": {"metric_id": "brand_metric_ideal", "target_brand": "理想汽车"}
    },
    {
      "type": "metrics.brand_metric.BrandMetric",
      "params": {"metric_id": "brand_metric_nio", "target_brand": "蔚来"}
    }
  ]
}
```

**使用场景：**
- 多品牌对比评估
- 多维度评估（产品特性、用户体验等）
- 跨平台品牌表现分析



## 日志配置

### Browser Use 日志级别

在 `.env` 文件中配置：

```bash
# 推荐：只显示警告和错误
BROWSER_USE_LOGGING_LEVEL=warning

# 调试：显示所有信息（包含 Emoji）
BROWSER_USE_LOGGING_LEVEL=info

# 最小：只显示最终结果
BROWSER_USE_LOGGING_LEVEL=result
```

**日志级别说明：**
- `debug` - 所有调试信息
- `info` - 详细信息（包含 Emoji 符号）
- `warning` - 只显示警告和错误（推荐日常使用）
- `error` - 只显示错误
- `result` - 只显示最终结果

**Emoji 符号含义：**
- `🎯` Task（任务）
- `📍` Step（步骤）
- `▶️` Action（动作）
- `👍` Success（成功）
- `❌` Fail（失败）
- `📄` Final Result（最终结果）



## CLI 工具使用指南

### GEO CLI - 品牌评估命令行工具

`geo-crawler` 是项目的主入口，`examples/geo_cli.py` 作为兼容转发壳保留。它提供以下功能：

#### 1. 初始化浏览器登录

```bash
# 初始化所有平台（推荐）
geo-crawler init-profile all

# 初始化单个平台
geo-crawler init-profile kimi
geo-crawler init-profile doubao
geo-crawler init-profile yuanbao
geo-crawler init-profile deepseek

# 检查登录状态
geo-crawler init-profile all --check

# 强制重新登录
geo-crawler init-profile kimi --force
```

#### 2. 运行采样

```bash
# 直接传入问题
geo-crawler sample run \
  --platform kimi \
  --platform qianwen \
  --query "20万左右电动车推荐" \
  --mode quick \
  --repeat 3 \
  --api-key-env OPENAI_API_KEY

# 使用数据集
geo-crawler sample run \
  --platform kimi \
  --dataset examples/dataset_test_single.json \
  --env-file .env
```

#### 3. 使用采样配置文件

```bash
geo-crawler sample run --config sampling.json
```

采样配置只描述平台、输入、轮次、模型、浏览器和输出位置，不包含 Judge、Metric 或报表配置。

### LLM 语义判断模式

支持两种评判模式：

#### LLM 模式（推荐）

**优势：**
- ✅ 自动识别品牌提及
- ✅ 智能理解维度覆盖
- ✅ 识别同义词和相关表述
- ✅ 无需手动配置竞品列表

**配置：**

```bash
# .env 文件
JUDGE_METRIC_LLM_ENABLED=true
JUDGE_METRIC_LLM_API_KEY=your_api_key
JUDGE_METRIC_LLM_MODEL=gpt-4o-mini  # 可选
JUDGE_METRIC_LLM_INPUT_PRICE=0.20    # 每百万 tokens 的人民币价格
JUDGE_METRIC_LLM_OUTPUT_PRICE=2.00   # 每百万 tokens 的人民币价格
```

平台查询 LLM 使用 `OPENAI_INPUT_PRICE` 和 `OPENAI_OUTPUT_PRICE` 计算费用，单位同样是人民币/百万 tokens。

```python
# 代码配置
judge_params = {
    "target_brand": "小鹏",
    "key_dimensions": ["长续航", "快充", "高性价比", "大空间"],
    "use_llm": True,
    "llm": judge_llm  # LLMWrapper 实例
}
```

#### 关键词匹配模式

**局限性：**
- ✗ 需要手动配置竞品列表
- ✗ 需要手动配置维度关键词
- ✗ 无法识别同义词

```python
judge_params = {
    "target_brand": "小鹏",
    "competitor_brands": ["理想", "蔚来", "问界"],
    "key_dimensions": ["长续航", "快充"],
    "dimension_keywords": {
        "长续航": ["续航", "里程", "电池"],
        "快充": ["快充", "充电", "充电速度"]
    },
    "use_llm": False
}
```

## 联系方式

如有问题或建议，请联系项目维护者。
