geo-crawler 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- geo_crawler-0.1.0/PKG-INFO +1080 -0
- geo_crawler-0.1.0/README.md +1057 -0
- geo_crawler-0.1.0/commands/__init__.py +22 -0
- geo_crawler-0.1.0/commands/analyze.py +935 -0
- geo_crawler-0.1.0/commands/common.py +211 -0
- geo_crawler-0.1.0/commands/init_profile.py +623 -0
- geo_crawler-0.1.0/commands/sample.py +286 -0
- geo_crawler-0.1.0/geo_crawler/__init__.py +4 -0
- geo_crawler-0.1.0/geo_crawler/__main__.py +6 -0
- geo_crawler-0.1.0/geo_crawler/cli.py +68 -0
- geo_crawler-0.1.0/geo_crawler.egg-info/PKG-INFO +1080 -0
- geo_crawler-0.1.0/geo_crawler.egg-info/SOURCES.txt +130 -0
- geo_crawler-0.1.0/geo_crawler.egg-info/dependency_links.txt +1 -0
- geo_crawler-0.1.0/geo_crawler.egg-info/entry_points.txt +2 -0
- geo_crawler-0.1.0/geo_crawler.egg-info/requires.txt +16 -0
- geo_crawler-0.1.0/geo_crawler.egg-info/top_level.txt +12 -0
- geo_crawler-0.1.0/pyproject.toml +96 -0
- geo_crawler-0.1.0/setup.cfg +4 -0
- geo_crawler-0.1.0/src/__init__.py +5 -0
- geo_crawler-0.1.0/src/browser_config.py +48 -0
- geo_crawler-0.1.0/src/browser_providers/__init__.py +27 -0
- geo_crawler-0.1.0/src/browser_providers/volcengine.py +275 -0
- geo_crawler-0.1.0/src/browser_providers/volcengine_sandbox.py +316 -0
- geo_crawler-0.1.0/src/browser_runtime/__init__.py +5 -0
- geo_crawler-0.1.0/src/browser_runtime/config.py +104 -0
- geo_crawler-0.1.0/src/browser_runtime/session.py +177 -0
- geo_crawler-0.1.0/src/deepseek/README.md +202 -0
- geo_crawler-0.1.0/src/deepseek/__init__.py +18 -0
- geo_crawler-0.1.0/src/deepseek/prompt_deep.md +22 -0
- geo_crawler-0.1.0/src/deepseek/prompt_quick.md +22 -0
- geo_crawler-0.1.0/src/deepseek/query.py +102 -0
- geo_crawler-0.1.0/src/deepseek/schema.py +420 -0
- geo_crawler-0.1.0/src/deepseek/tools.py +309 -0
- geo_crawler-0.1.0/src/doubao/README.md +192 -0
- geo_crawler-0.1.0/src/doubao/__init__.py +18 -0
- geo_crawler-0.1.0/src/doubao/prompt_deep.md +22 -0
- geo_crawler-0.1.0/src/doubao/prompt_quick.md +22 -0
- geo_crawler-0.1.0/src/doubao/query.py +102 -0
- geo_crawler-0.1.0/src/doubao/schema.py +330 -0
- geo_crawler-0.1.0/src/doubao/tools.py +585 -0
- geo_crawler-0.1.0/src/kimi/README.md +141 -0
- geo_crawler-0.1.0/src/kimi/__init__.py +18 -0
- geo_crawler-0.1.0/src/kimi/prompt_deep.md +22 -0
- geo_crawler-0.1.0/src/kimi/prompt_quick.md +22 -0
- geo_crawler-0.1.0/src/kimi/query.py +101 -0
- geo_crawler-0.1.0/src/kimi/schema.py +562 -0
- geo_crawler-0.1.0/src/kimi/tools.py +330 -0
- geo_crawler-0.1.0/src/orchestrator/README.md +494 -0
- geo_crawler-0.1.0/src/orchestrator/__init__.py +41 -0
- geo_crawler-0.1.0/src/orchestrator/agent.py +237 -0
- geo_crawler-0.1.0/src/orchestrator/base_judge.py +156 -0
- geo_crawler-0.1.0/src/orchestrator/base_metric.py +99 -0
- geo_crawler-0.1.0/src/orchestrator/cost_models.py +194 -0
- geo_crawler-0.1.0/src/orchestrator/dataset.py +180 -0
- geo_crawler-0.1.0/src/orchestrator/judge.py +127 -0
- geo_crawler-0.1.0/src/orchestrator/judges/__init__.py +13 -0
- geo_crawler-0.1.0/src/orchestrator/judges/brand_dimension_judge.py +892 -0
- geo_crawler-0.1.0/src/orchestrator/judges/llm_response_models.py +118 -0
- geo_crawler-0.1.0/src/orchestrator/llm_config.py +137 -0
- geo_crawler-0.1.0/src/orchestrator/llm_wrapper.py +61 -0
- geo_crawler-0.1.0/src/orchestrator/metric.py +121 -0
- geo_crawler-0.1.0/src/orchestrator/metrics/__init__.py +5 -0
- geo_crawler-0.1.0/src/orchestrator/metrics/brand_metric.py +451 -0
- geo_crawler-0.1.0/src/orchestrator/models.py +132 -0
- geo_crawler-0.1.0/src/orchestrator/query_executor.py +134 -0
- geo_crawler-0.1.0/src/orchestrator/reporter.py +584 -0
- geo_crawler-0.1.0/src/orchestrator/runner.py +1023 -0
- geo_crawler-0.1.0/src/orchestrator/storage.py +169 -0
- geo_crawler-0.1.0/src/qianwen/README.md +35 -0
- geo_crawler-0.1.0/src/qianwen/__init__.py +9 -0
- geo_crawler-0.1.0/src/qianwen/prompt_deep.md +26 -0
- geo_crawler-0.1.0/src/qianwen/prompt_quick.md +25 -0
- geo_crawler-0.1.0/src/qianwen/query.py +60 -0
- geo_crawler-0.1.0/src/qianwen/schema.py +295 -0
- geo_crawler-0.1.0/src/qianwen/tools.py +360 -0
- geo_crawler-0.1.0/src/sampling/__init__.py +11 -0
- geo_crawler-0.1.0/src/sampling/config.py +254 -0
- geo_crawler-0.1.0/src/sampling/runner.py +136 -0
- geo_crawler-0.1.0/src/sampling/storage.py +64 -0
- geo_crawler-0.1.0/src/yuanbao/README.md +195 -0
- geo_crawler-0.1.0/src/yuanbao/__init__.py +18 -0
- geo_crawler-0.1.0/src/yuanbao/prompt_deep.md +21 -0
- geo_crawler-0.1.0/src/yuanbao/prompt_quick.md +21 -0
- geo_crawler-0.1.0/src/yuanbao/query.py +102 -0
- geo_crawler-0.1.0/src/yuanbao/schema.py +452 -0
- geo_crawler-0.1.0/src/yuanbao/tools.py +586 -0
- geo_crawler-0.1.0/tests/test_agent_cost_tracking.py +103 -0
- geo_crawler-0.1.0/tests/test_base_judge_properties.py +723 -0
- geo_crawler-0.1.0/tests/test_base_metric_properties.py +387 -0
- geo_crawler-0.1.0/tests/test_brand_dimension_judge.py +416 -0
- geo_crawler-0.1.0/tests/test_brand_dimension_judge_error_handling.py +520 -0
- geo_crawler-0.1.0/tests/test_brand_dimension_judge_properties.py +558 -0
- geo_crawler-0.1.0/tests/test_brand_integration.py +526 -0
- geo_crawler-0.1.0/tests/test_brand_llm_integration.py +363 -0
- geo_crawler-0.1.0/tests/test_brand_metric.py +329 -0
- geo_crawler-0.1.0/tests/test_brand_metric_llm_compatibility.py +372 -0
- geo_crawler-0.1.0/tests/test_brand_metric_properties.py +500 -0
- geo_crawler-0.1.0/tests/test_browser_config.py +38 -0
- geo_crawler-0.1.0/tests/test_browser_runtime.py +190 -0
- geo_crawler-0.1.0/tests/test_cli_init_profile_auto.py +74 -0
- geo_crawler-0.1.0/tests/test_cli_sample.py +289 -0
- geo_crawler-0.1.0/tests/test_cost_models.py +89 -0
- geo_crawler-0.1.0/tests/test_data_models.py +409 -0
- geo_crawler-0.1.0/tests/test_dataset.py +146 -0
- geo_crawler-0.1.0/tests/test_dataset_interactive.py +152 -0
- geo_crawler-0.1.0/tests/test_deprecation_warnings.py +83 -0
- geo_crawler-0.1.0/tests/test_error_logging.py +345 -0
- geo_crawler-0.1.0/tests/test_evaluate_mode_switching.py +155 -0
- geo_crawler-0.1.0/tests/test_geo_cli_entrypoint.py +36 -0
- geo_crawler-0.1.0/tests/test_integration.py +644 -0
- geo_crawler-0.1.0/tests/test_integration_new_format.py +338 -0
- geo_crawler-0.1.0/tests/test_judge_interface.py +251 -0
- geo_crawler-0.1.0/tests/test_judge_metric_id_properties.py +313 -0
- geo_crawler-0.1.0/tests/test_llm_config.py +28 -0
- geo_crawler-0.1.0/tests/test_llm_dimension_coverage.py +187 -0
- geo_crawler-0.1.0/tests/test_llm_integration_e2e.py +450 -0
- geo_crawler-0.1.0/tests/test_llm_response_models.py +199 -0
- geo_crawler-0.1.0/tests/test_llm_timeout.py +63 -0
- geo_crawler-0.1.0/tests/test_llm_wrapper.py +103 -0
- geo_crawler-0.1.0/tests/test_metric_interface.py +332 -0
- geo_crawler-0.1.0/tests/test_multi_platform_integration.py +394 -0
- geo_crawler-0.1.0/tests/test_platform_context_fields.py +416 -0
- geo_crawler-0.1.0/tests/test_platform_prompts.py +136 -0
- geo_crawler-0.1.0/tests/test_query_executor.py +373 -0
- geo_crawler-0.1.0/tests/test_reporter_new_format.py +240 -0
- geo_crawler-0.1.0/tests/test_runner_cost_tracking.py +113 -0
- geo_crawler-0.1.0/tests/test_runner_properties.py +412 -0
- geo_crawler-0.1.0/tests/test_storage_new_format.py +287 -0
- geo_crawler-0.1.0/tests/test_storage_state_export.py +41 -0
- geo_crawler-0.1.0/tests/test_volcengine_browser_provider.py +264 -0
- geo_crawler-0.1.0/tests/test_volcengine_browser_validation.py +377 -0
- geo_crawler-0.1.0/tests/test_volcengine_sandbox_provider.py +133 -0
|
@@ -0,0 +1,1080 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: geo-crawler
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AI-powered geographic information crawler with multi-model support
|
|
5
|
+
Author: GEO Crawler Team
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: beautifulsoup4>=4.0.0
|
|
9
|
+
Requires-Dist: browser-use-volcengine>=0.12.6
|
|
10
|
+
Requires-Dist: markdownify>=1.0.0
|
|
11
|
+
Requires-Dist: openai>=2.0.0
|
|
12
|
+
Requires-Dist: openpyxl>=3.0.0
|
|
13
|
+
Requires-Dist: pandas>=2.0.0
|
|
14
|
+
Requires-Dist: pydantic>=2.0.0
|
|
15
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
16
|
+
Requires-Dist: rich>=14.0.0
|
|
17
|
+
Requires-Dist: typer>=0.20.0
|
|
18
|
+
Requires-Dist: volcengine-python-sdk>=4.0.34
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
21
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
22
|
+
Requires-Dist: hypothesis>=6.0.0; extra == "dev"
|
|
23
|
+
|
|
24
|
+
# GEO 实验调度器(GEO Experiment Orchestrator)
|
|
25
|
+
|
|
26
|
+
GEO 实验调度器是一个轻量级的实验执行系统,用于在单个 AI 平台上执行标准化查询任务并自动评估结果。
|
|
27
|
+
|
|
28
|
+
## 功能特性
|
|
29
|
+
|
|
30
|
+
- 📊 **数据集加载**:支持从 JSON 文件加载或交互式输入查询任务
|
|
31
|
+
- 🤖 **查询执行**:在 AI 平台上自动执行查询任务
|
|
32
|
+
- ✅ **自动评估**:使用规则评判器自动评估查询结果的正确性
|
|
33
|
+
- 📈 **指标计算**:计算准确率等关键指标
|
|
34
|
+
- 💾 **结果保存**:保存完整的实验产物到运行目录
|
|
35
|
+
- 🏷️ **品牌评估**:通过5个核心指标量化品牌在AI回答中的表现(被引用率、首次提及位置、信息深度覆盖、话语份额、平台份额)
|
|
36
|
+
|
|
37
|
+
## 快速开始
|
|
38
|
+
|
|
39
|
+
### 1. 环境准备与安装
|
|
40
|
+
|
|
41
|
+
项目已配置为标准 Python 包(使用 `pyproject.toml`),确保已安装 **Python 3.11+**:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# 安装基础依赖
|
|
45
|
+
uv sync
|
|
46
|
+
|
|
47
|
+
# 开发环境:安装所有依赖(包括测试工具)
|
|
48
|
+
uv sync --all-extras --dev
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
这将自动安装所有依赖并将 `src/` 目录配置为可导入的包。
|
|
52
|
+
|
|
53
|
+
#### 依赖安装说明
|
|
54
|
+
|
|
55
|
+
- `uv sync` - 只安装基础运行依赖
|
|
56
|
+
- `uv sync --extra dev` - 安装开发依赖(pytest、hypothesis 等)
|
|
57
|
+
- `uv sync --all-extras` - 安装所有可选依赖组
|
|
58
|
+
- `uv sync --all-extras --dev` - 安装所有依赖和开发工具(推荐用于开发环境)
|
|
59
|
+
|
|
60
|
+
#### 检查安装状态
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# 查看已安装的包
|
|
64
|
+
uv pip list
|
|
65
|
+
|
|
66
|
+
# 查看项目依赖树
|
|
67
|
+
uv pip show --tree
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**主要依赖:**
|
|
71
|
+
- `browser-use`:浏览器自动化
|
|
72
|
+
- `pydantic`:数据验证
|
|
73
|
+
- `python-dotenv`:环境变量管理
|
|
74
|
+
- `beautifulsoup4`:HTML 解析
|
|
75
|
+
- `openpyxl`:Excel 报告生成
|
|
76
|
+
|
|
77
|
+
**安装优势:**
|
|
78
|
+
- ✅ 无需 `sys.path.insert` 操作
|
|
79
|
+
- ✅ 代码修改后无需重新安装(可编辑模式)
|
|
80
|
+
- ✅ IDE 获得更好的代码补全和类型检查
|
|
81
|
+
- ✅ 符合 Python 包管理最佳实践
|
|
82
|
+
|
|
83
|
+
### 1.1 包导入
|
|
84
|
+
|
|
85
|
+
安装后,可以在项目任何位置直接导入模块:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from doubao.query import query_doubao
|
|
89
|
+
from kimi.query import query_kimi
|
|
90
|
+
from deepseek.query import query_deepseek
|
|
91
|
+
from yuanbao.query import query_yuanbao
|
|
92
|
+
from orchestrator.runner import Runner
|
|
93
|
+
from orchestrator.dataset import load_dataset_interactive
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 2. 初始化浏览器登录(首次使用必须)
|
|
97
|
+
|
|
98
|
+
在运行实验之前,需要先初始化各平台的浏览器登录状态:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# 初始化所有平台登录(推荐)
|
|
102
|
+
geo-crawler init-profile all
|
|
103
|
+
|
|
104
|
+
# 或单独初始化某个平台
|
|
105
|
+
geo-crawler init-profile kimi
|
|
106
|
+
geo-crawler init-profile doubao
|
|
107
|
+
geo-crawler init-profile yuanbao
|
|
108
|
+
geo-crawler init-profile deepseek
|
|
109
|
+
|
|
110
|
+
# 检查登录状态
|
|
111
|
+
geo-crawler init-profile all --check
|
|
112
|
+
|
|
113
|
+
# 强制重新登录
|
|
114
|
+
geo-crawler init-profile kimi --force
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
登录状态会保存在 `browser_profiles/` 目录中,下次运行时自动使用。
|
|
118
|
+
|
|
119
|
+
### 3. 运行采样
|
|
120
|
+
|
|
121
|
+
使用 GEO CLI 工具运行纯采样:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# 直接传入问题
|
|
125
|
+
geo-crawler sample run \
|
|
126
|
+
--platform kimi \
|
|
127
|
+
--platform qianwen \
|
|
128
|
+
--query "20万左右电动车推荐" \
|
|
129
|
+
--mode quick \
|
|
130
|
+
--repeat 3 \
|
|
131
|
+
--api-key-env OPENAI_API_KEY
|
|
132
|
+
|
|
133
|
+
# 使用数据集
|
|
134
|
+
geo-crawler sample run \
|
|
135
|
+
--platform kimi \
|
|
136
|
+
--dataset examples/dataset_test_single.json \
|
|
137
|
+
--env-file .env
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
采样将自动执行以下步骤:
|
|
141
|
+
1. 加载数据集
|
|
142
|
+
2. 在 AI 平台上执行查询
|
|
143
|
+
3. 保存原始采样产物到 `runs/` 目录
|
|
144
|
+
|
|
145
|
+
### 4. 查看结果
|
|
146
|
+
|
|
147
|
+
采样完成后,结果将保存在 `runs/run-YYYYMMDD-HHMMSS-xxxxxx/` 目录中:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
runs/run-20251203-143025-a1b2c3/
|
|
151
|
+
├── manifest.json # 采样元信息
|
|
152
|
+
├── inputs.json # 输入数据集快照
|
|
153
|
+
├── outputs.json # 查询执行结果
|
|
154
|
+
├── summary.json # 成功和失败数量
|
|
155
|
+
└── DONE # 完成标记
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## 使用示例
|
|
159
|
+
|
|
160
|
+
### 示例 1:直接传入问题
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
python examples/geo_cli.py sample run \
|
|
164
|
+
--platform kimi \
|
|
165
|
+
--platform qianwen \
|
|
166
|
+
--query "20万左右电动车推荐" \
|
|
167
|
+
--mode quick \
|
|
168
|
+
--repeat 3 \
|
|
169
|
+
--api-key-env OPENAI_API_KEY \
|
|
170
|
+
--format json
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 示例 2:使用采样配置文件
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
python examples/geo_cli.py sample run --config sampling.json
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
配置文件示例:
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"platforms": ["kimi", "qianwen"],
|
|
184
|
+
"inputs": [
|
|
185
|
+
{"id": "q1", "query": "20万左右电动车推荐"}
|
|
186
|
+
],
|
|
187
|
+
"mode": "quick",
|
|
188
|
+
"repeat": 3,
|
|
189
|
+
"out": "runs",
|
|
190
|
+
"controller": {
|
|
191
|
+
"model": "gpt-4.1-mini",
|
|
192
|
+
"api_key_env": "OPENAI_API_KEY"
|
|
193
|
+
},
|
|
194
|
+
"browser": {
|
|
195
|
+
"profile_dir": "browser_profiles",
|
|
196
|
+
"use_judge": true
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## 数据集格式
|
|
202
|
+
|
|
203
|
+
数据集使用 JSON 格式,包含一个查询任务数组。每个任务必须包含以下字段:
|
|
204
|
+
|
|
205
|
+
### 必需字段
|
|
206
|
+
|
|
207
|
+
- `id` (string):查询任务的唯一标识符
|
|
208
|
+
- `query` (string):查询内容
|
|
209
|
+
|
|
210
|
+
### 示例数据集
|
|
211
|
+
|
|
212
|
+
品牌评估场景示例(`examples/dataset_test_single.json`):
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
[
|
|
216
|
+
{
|
|
217
|
+
"id": "q1",
|
|
218
|
+
"query": "推荐一款30万左右的新能源SUV"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"id": "q2",
|
|
222
|
+
"query": "哪款电动车续航最长?"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"id": "q3",
|
|
226
|
+
"query": "适合家用的智能电动车有哪些?"
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### 数据集验证规则
|
|
232
|
+
|
|
233
|
+
系统会自动验证数据集:
|
|
234
|
+
- ✅ 顶层必须是数组
|
|
235
|
+
- ✅ 每个任务必须是字典对象
|
|
236
|
+
- ✅ 必须包含 `id` 和 `query` 字段
|
|
237
|
+
- ✅ 字段值必须是非空字符串
|
|
238
|
+
- ⚠️ 无效任务会被跳过并记录警告
|
|
239
|
+
|
|
240
|
+
## 实验配置参数
|
|
241
|
+
|
|
242
|
+
### 完整配置说明
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"id": "实验唯一标识符",
|
|
247
|
+
"dataset": "数据集文件路径",
|
|
248
|
+
"platforms": ["平台列表"],
|
|
249
|
+
"judges": [
|
|
250
|
+
{
|
|
251
|
+
"type": "评判器类路径",
|
|
252
|
+
"params": {
|
|
253
|
+
"judge_id": "评判器ID",
|
|
254
|
+
"target_brand": "目标品牌",
|
|
255
|
+
"key_dimensions": ["关键维度列表"],
|
|
256
|
+
"use_llm": true
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
],
|
|
260
|
+
"metrics": [
|
|
261
|
+
{
|
|
262
|
+
"type": "指标类路径",
|
|
263
|
+
"params": {
|
|
264
|
+
"metric_id": "指标ID",
|
|
265
|
+
"target_brand": "目标品牌",
|
|
266
|
+
"key_dimensions": ["关键维度列表"]
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
],
|
|
270
|
+
"repeat_count": 1,
|
|
271
|
+
"thinking_mode": "quick"
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### 参数详解
|
|
276
|
+
|
|
277
|
+
#### id
|
|
278
|
+
- **类型**:`string`
|
|
279
|
+
- **说明**:实验的唯一标识符
|
|
280
|
+
- **示例**:`"exp_xiaopeng_evaluation"`
|
|
281
|
+
|
|
282
|
+
#### dataset
|
|
283
|
+
- **类型**:`string`
|
|
284
|
+
- **说明**:数据集文件路径(相对于项目根目录)
|
|
285
|
+
- **示例**:`"examples/dataset_test_single.json"`
|
|
286
|
+
|
|
287
|
+
#### platforms
|
|
288
|
+
- **类型**:`list[string]`
|
|
289
|
+
- **说明**:要评估的 AI 平台列表
|
|
290
|
+
- **可选值**:`"kimi"`, `"doubao"`, `"deepseek"`, `"yuanbao"`
|
|
291
|
+
- **示例**:
|
|
292
|
+
- 单平台:`["kimi"]`
|
|
293
|
+
- 多平台:`["kimi", "doubao", "deepseek", "yuanbao"]`
|
|
294
|
+
|
|
295
|
+
#### judges
|
|
296
|
+
- **类型**:`list[dict]`
|
|
297
|
+
- **说明**:评判器配置列表,支持多个评判器
|
|
298
|
+
- **结构**:
|
|
299
|
+
- `type`:评判器类的完整路径
|
|
300
|
+
- `params`:评判器参数
|
|
301
|
+
- `judge_id`:评判器唯一标识
|
|
302
|
+
- `target_brand`:目标品牌名称
|
|
303
|
+
- `key_dimensions`:关键维度列表
|
|
304
|
+
- `use_llm`:是否使用 LLM 语义判断(推荐 `true`)
|
|
305
|
+
- `competitor_brands`:竞品列表(仅关键词模式需要)
|
|
306
|
+
- `dimension_keywords`:维度关键词映射(仅关键词模式需要)
|
|
307
|
+
|
|
308
|
+
#### metrics
|
|
309
|
+
- **类型**:`list[dict]`
|
|
310
|
+
- **说明**:指标计算器配置列表,支持多个指标
|
|
311
|
+
- **结构**:
|
|
312
|
+
- `type`:指标类的完整路径
|
|
313
|
+
- `params`:指标参数
|
|
314
|
+
- `metric_id`:指标唯一标识
|
|
315
|
+
- `target_brand`:目标品牌名称
|
|
316
|
+
- `key_dimensions`:关键维度列表
|
|
317
|
+
|
|
318
|
+
#### repeat_count
|
|
319
|
+
- **类型**:`integer`
|
|
320
|
+
- **说明**:每个查询在每个平台上重复执行的次数
|
|
321
|
+
- **默认值**:`1`
|
|
322
|
+
- **用途**:用于稳定性测试和方差分析
|
|
323
|
+
- **示例**:`5`(每个查询执行 5 次)
|
|
324
|
+
|
|
325
|
+
#### thinking_mode
|
|
326
|
+
- **类型**:`string`
|
|
327
|
+
- **说明**:AI 平台的思考模式
|
|
328
|
+
- **可选值**:
|
|
329
|
+
- `"quick"`:快速模式,响应更快,适合批量测试
|
|
330
|
+
- `"deep"`:深度模式,思考更深入,结果更准确
|
|
331
|
+
- **默认值**:`"quick"`
|
|
332
|
+
- **示例**:`"deep"`
|
|
333
|
+
|
|
334
|
+
## 输出说明
|
|
335
|
+
|
|
336
|
+
### 运行目录结构
|
|
337
|
+
|
|
338
|
+
每次实验执行后,会在 `runs/` 目录下创建一个带时间戳的运行目录:
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
runs/
|
|
342
|
+
└── run-20251203-143025-a1b2c3/
|
|
343
|
+
├── manifest.json
|
|
344
|
+
├── inputs.json
|
|
345
|
+
├── outputs.json
|
|
346
|
+
├── evals.json
|
|
347
|
+
├── metrics.json
|
|
348
|
+
├── costs.json
|
|
349
|
+
├── report.xlsx # Excel 报告(品牌评估实验)
|
|
350
|
+
└── DONE
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 文件说明
|
|
354
|
+
|
|
355
|
+
#### manifest.json
|
|
356
|
+
实验元信息和配置:
|
|
357
|
+
|
|
358
|
+
```json
|
|
359
|
+
{
|
|
360
|
+
"run_id": "run-20251203-143025-a1b2c3",
|
|
361
|
+
"dataset": "dataset.json",
|
|
362
|
+
"platform": "kimi",
|
|
363
|
+
"judges": ["rule_v1"],
|
|
364
|
+
"metrics": ["accuracy"],
|
|
365
|
+
"repeat_count": 1,
|
|
366
|
+
"thinking_mode": "quick",
|
|
367
|
+
"started_at": "2025-12-03T14:30:25Z"
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
#### inputs.json
|
|
372
|
+
输入数据集的快照:
|
|
373
|
+
|
|
374
|
+
```json
|
|
375
|
+
[
|
|
376
|
+
{
|
|
377
|
+
"id": "q1",
|
|
378
|
+
"query": "巴黎塔多高?"
|
|
379
|
+
}
|
|
380
|
+
]
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
#### outputs.json
|
|
384
|
+
查询执行结果列表:
|
|
385
|
+
|
|
386
|
+
```json
|
|
387
|
+
[
|
|
388
|
+
{
|
|
389
|
+
"id": "q1",
|
|
390
|
+
"query": "巴黎塔多高?",
|
|
391
|
+
"platform": "kimi",
|
|
392
|
+
"mode": "quick",
|
|
393
|
+
"start_time": "2025-12-03 14:30:25",
|
|
394
|
+
"end_time": "2025-12-03 14:30:28",
|
|
395
|
+
"final_answer": "埃菲尔铁塔高度约为 330 米",
|
|
396
|
+
"thinking_process": "思考过程...",
|
|
397
|
+
"references": "参考资料...",
|
|
398
|
+
"token_usage": {
|
|
399
|
+
"prompt_tokens": 1200,
|
|
400
|
+
"completion_tokens": 300,
|
|
401
|
+
"total_tokens": 1500,
|
|
402
|
+
"model": "gpt-4o-mini",
|
|
403
|
+
"by_model": {
|
|
404
|
+
"gpt-4o-mini": {
|
|
405
|
+
"prompt_tokens": 1200,
|
|
406
|
+
"completion_tokens": 300,
|
|
407
|
+
"total_tokens": 1500
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
]
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
#### evals.json
|
|
416
|
+
评估结果列表:
|
|
417
|
+
|
|
418
|
+
```json
|
|
419
|
+
[
|
|
420
|
+
{
|
|
421
|
+
"id": "q1",
|
|
422
|
+
"label": "correct",
|
|
423
|
+
"score": 1
|
|
424
|
+
}
|
|
425
|
+
]
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
#### metrics.json
|
|
429
|
+
聚合指标:
|
|
430
|
+
|
|
431
|
+
```json
|
|
432
|
+
{
|
|
433
|
+
"accuracy": 0.75,
|
|
434
|
+
"correct_count": 3,
|
|
435
|
+
"total_count": 4
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
#### costs.json
|
|
440
|
+
Token 用量和费用汇总。费用单位固定为人民币,价格来自 `.env` 中“每百万 tokens 的人民币价格”配置,不做汇率转换:
|
|
441
|
+
|
|
442
|
+
```json
|
|
443
|
+
{
|
|
444
|
+
"currency": "CNY",
|
|
445
|
+
"unit": "RMB per 1M tokens",
|
|
446
|
+
"total_prompt_tokens": 1400,
|
|
447
|
+
"total_completion_tokens": 400,
|
|
448
|
+
"total_tokens": 1800,
|
|
449
|
+
"total_cost_rmb": 0.0036,
|
|
450
|
+
"records": [],
|
|
451
|
+
"by_source": {
|
|
452
|
+
"query": {
|
|
453
|
+
"prompt_tokens": 1200,
|
|
454
|
+
"completion_tokens": 300,
|
|
455
|
+
"total_tokens": 1500,
|
|
456
|
+
"cost_rmb": 0.003
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
#### report.xlsx
|
|
463
|
+
Excel 格式的品牌评估报告(仅在品牌评估实验中生成):
|
|
464
|
+
|
|
465
|
+
包含以下内容:
|
|
466
|
+
- HIQ(用户查询问题)
|
|
467
|
+
- 品牌名
|
|
468
|
+
- 关键维度列表
|
|
469
|
+
- 响应数
|
|
470
|
+
- 品牌指标表格(按平台展示)
|
|
471
|
+
- Model(platform)
|
|
472
|
+
- Inclusion Rate(被引用率)
|
|
473
|
+
- Share of Voice(话语份额)
|
|
474
|
+
- Share of Model(模型份额)
|
|
475
|
+
- First Mention Position(首次提及位置)
|
|
476
|
+
- First Mention Percentage(首次提及百分比)
|
|
477
|
+
- Depth Coverage(深度覆盖)
|
|
478
|
+
|
|
479
|
+
#### DONE
|
|
480
|
+
空文件,标记实验成功完成。
|
|
481
|
+
|
|
482
|
+
### 控制台输出
|
|
483
|
+
|
|
484
|
+
实验执行过程中,控制台会显示详细的进度信息:
|
|
485
|
+
|
|
486
|
+
```
|
|
487
|
+
============================================================
|
|
488
|
+
开始执行实验
|
|
489
|
+
============================================================
|
|
490
|
+
步骤 1/5: 加载数据集
|
|
491
|
+
✓ 数据集加载完成 - 任务数: 5
|
|
492
|
+
步骤 2/5: 执行查询
|
|
493
|
+
✓ 查询执行完成 - 结果数: 5
|
|
494
|
+
步骤 3/5: 评估结果
|
|
495
|
+
✓ 结果评估完成 - 评估数: 5
|
|
496
|
+
步骤 4/5: 计算指标
|
|
497
|
+
✓ 指标计算完成 - 准确率: 80.00%
|
|
498
|
+
步骤 5/5: 保存产物
|
|
499
|
+
✓ 产物保存完成 - 运行目录: runs/run-20251203-143025-a1b2c3
|
|
500
|
+
============================================================
|
|
501
|
+
实验执行完成
|
|
502
|
+
============================================================
|
|
503
|
+
运行目录: runs/run-20251203-143025-a1b2c3
|
|
504
|
+
关键指标摘要:
|
|
505
|
+
- 准确率: 80.00%
|
|
506
|
+
- 正确数: 4/5
|
|
507
|
+
============================================================
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
## 日志系统
|
|
511
|
+
|
|
512
|
+
### 日志配置
|
|
513
|
+
|
|
514
|
+
系统使用 Python 标准 logging 模块,日志会同时输出到:
|
|
515
|
+
- 控制台(标准输出)
|
|
516
|
+
- 日志文件(`logs/experiment.log`)
|
|
517
|
+
|
|
518
|
+
### 日志级别
|
|
519
|
+
|
|
520
|
+
- **INFO**:关键步骤和进度信息
|
|
521
|
+
- **WARNING**:警告信息(如跳过无效任务)
|
|
522
|
+
- **ERROR**:错误信息和异常堆栈
|
|
523
|
+
- **DEBUG**:详细的调试信息(单个查询处理)
|
|
524
|
+
|
|
525
|
+
### 自定义日志配置
|
|
526
|
+
|
|
527
|
+
使用命令行参数指定日志级别:
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
# 使用 DEBUG 级别
|
|
531
|
+
python examples/geo_cli.py sample run \
|
|
532
|
+
--platform kimi \
|
|
533
|
+
--query "推荐一款30万左右的新能源SUV" \
|
|
534
|
+
--log-level DEBUG
|
|
535
|
+
|
|
536
|
+
# 使用 INFO 级别(默认)
|
|
537
|
+
python examples/geo_cli.py sample run \
|
|
538
|
+
--platform kimi \
|
|
539
|
+
--dataset examples/dataset_test_single.json \
|
|
540
|
+
--log-level INFO
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
或在代码中配置:
|
|
544
|
+
|
|
545
|
+
```python
|
|
546
|
+
from orchestrator.runner import configure_logging
|
|
547
|
+
|
|
548
|
+
# 配置日志级别和输出文件
|
|
549
|
+
configure_logging(
|
|
550
|
+
level="DEBUG", # 日志级别
|
|
551
|
+
log_file="logs/my_experiment.log" # 日志文件路径
|
|
552
|
+
)
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
## 常见问题
|
|
556
|
+
|
|
557
|
+
### Q: 首次使用需要做什么?
|
|
558
|
+
|
|
559
|
+
**A:** 首次使用必须先初始化浏览器登录:
|
|
560
|
+
|
|
561
|
+
```bash
|
|
562
|
+
python examples/geo_cli.py init-profile all
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
按照提示在浏览器中登录各个平台,登录状态会自动保存。
|
|
566
|
+
|
|
567
|
+
### Q: 如何添加新的查询任务?
|
|
568
|
+
|
|
569
|
+
**A:** 创建或编辑数据集 JSON 文件,添加新的查询对象:
|
|
570
|
+
|
|
571
|
+
```json
|
|
572
|
+
[
|
|
573
|
+
{
|
|
574
|
+
"id": "q1",
|
|
575
|
+
"query": "推荐一款30万左右的新能源SUV"
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
"id": "q2",
|
|
579
|
+
"query": "你的新查询内容"
|
|
580
|
+
}
|
|
581
|
+
]
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### Q: 如何查看详细的执行日志?
|
|
585
|
+
|
|
586
|
+
**A:** 查看 `logs/brand_experiment.log` 文件,或使用 DEBUG 日志级别:
|
|
587
|
+
|
|
588
|
+
```bash
|
|
589
|
+
python examples/geo_cli.py sample run \
|
|
590
|
+
--platform kimi \
|
|
591
|
+
--query "推荐一款30万左右的新能源SUV" \
|
|
592
|
+
--log-level DEBUG
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
### Q: 实验失败后如何重试?
|
|
596
|
+
|
|
597
|
+
**A:** 直接重新运行相同的命令,系统会创建新的运行目录。每次运行都是独立的。
|
|
598
|
+
|
|
599
|
+
### Q: 如何自定义评判规则?
|
|
600
|
+
|
|
601
|
+
**A:** 继承 `orchestrator.base_judge.Judge` 抽象基类,实现自定义的评判逻辑。参考 `src/orchestrator/judges/brand_dimension_judge.py` 示例。
|
|
602
|
+
|
|
603
|
+
详见:`src/orchestrator/README.md`
|
|
604
|
+
|
|
605
|
+
### Q: 运行目录的命名规则是什么?
|
|
606
|
+
|
|
607
|
+
**A:** 格式为 `run-YYYYMMDD-HHMMSS-xxxxxx`,其中:
|
|
608
|
+
- `YYYYMMDD`:日期(年月日)
|
|
609
|
+
- `HHMMSS`:时间(时分秒,UTC 时间)
|
|
610
|
+
- `xxxxxx`:6 位随机十六进制后缀(避免并发冲突)
|
|
611
|
+
|
|
612
|
+
### Q: LLM 模式和关键词模式有什么区别?
|
|
613
|
+
|
|
614
|
+
**A:**
|
|
615
|
+
- **LLM 模式(推荐)**:使用大语言模型进行语义判断,自动识别品牌提及和维度覆盖,无需手动配置竞品列表和关键词
|
|
616
|
+
- **关键词模式**:基于关键词匹配,需要手动配置竞品列表和维度关键词,准确性较低
|
|
617
|
+
|
|
618
|
+
推荐使用 LLM 模式(示例 1-7),配置更简单,效果更好。
|
|
619
|
+
|
|
620
|
+
## 项目结构
|
|
621
|
+
|
|
622
|
+
```
|
|
623
|
+
geo-crawler/
|
|
624
|
+
├── src/ # 源代码目录
|
|
625
|
+
│ ├── doubao/ # 豆包 AI 集成模块
|
|
626
|
+
│ ├── kimi/ # Kimi AI 集成模块
|
|
627
|
+
│ ├── deepseek/ # DeepSeek AI 集成模块
|
|
628
|
+
│ ├── yuanbao/ # 元宝 AI 集成模块
|
|
629
|
+
│ └── orchestrator/ # 实验编排与评估框架
|
|
630
|
+
│ ├── __init__.py
|
|
631
|
+
│ ├── runner.py # 实验编排器
|
|
632
|
+
│ ├── agent.py # Agent 抽象层
|
|
633
|
+
│ ├── base_judge.py # Judge 抽象基类(推荐)
|
|
634
|
+
│ ├── base_metric.py # Metric 抽象基类(推荐)
|
|
635
|
+
│ ├── models.py # 数据模型(EvalRecord、MetricResult)
|
|
636
|
+
│ ├── judge.py # ⚠️ 已废弃,请使用 base_judge.py
|
|
637
|
+
│ ├── metric.py # ⚠️ 已废弃,请使用 base_metric.py
|
|
638
|
+
│ ├── storage.py # 存储管理
|
|
639
|
+
│ ├── dataset.py # 数据集管理
|
|
640
|
+
│ ├── reporter.py # 报告生成器
|
|
641
|
+
│ ├── llm_config.py # LLM 配置
|
|
642
|
+
│ ├── llm_wrapper.py # LLM 包装器
|
|
643
|
+
│ ├── judges/ # 评判器实现模块
|
|
644
|
+
│ │ ├── brand_dimension_judge.py # 品牌维度评判器
|
|
645
|
+
│ │ └── llm_response_models.py # LLM 响应模型
|
|
646
|
+
│ └── metrics/ # 指标实现模块
|
|
647
|
+
│ └── brand_metric.py # 品牌指标计算器
|
|
648
|
+
├── tests/ # 测试目录
|
|
649
|
+
├── examples/ # 示例脚本目录
|
|
650
|
+
│ ├── geo_cli.py # CLI 兼容转发壳
|
|
651
|
+
│ ├── dataset_test_single.json # 示例数据集
|
|
652
|
+
│ └── experiment_multi_judge_metric.json # 示例配置
|
|
653
|
+
├── experiments/ # 实验配置目录
|
|
654
|
+
├── runs/ # 运行结果目录
|
|
655
|
+
├── output/ # 查询输出目录
|
|
656
|
+
├── logs/ # 日志文件目录
|
|
657
|
+
├── browser_profiles/ # 浏览器配置目录
|
|
658
|
+
├── pyproject.toml # 项目配置
|
|
659
|
+
├── requirements.txt # 依赖列表
|
|
660
|
+
└── README.md # 本文档
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
## 架构说明
|
|
664
|
+
|
|
665
|
+
### Judge 和 Metric 抽象基类
|
|
666
|
+
|
|
667
|
+
从 v2.0 开始,系统采用新的抽象基类架构:
|
|
668
|
+
|
|
669
|
+
#### Judge 抽象基类(`base_judge.py`)
|
|
670
|
+
|
|
671
|
+
所有评判器都应继承 `Judge` 抽象基类并实现以下方法:
|
|
672
|
+
|
|
673
|
+
- `evaluate(item, output) -> EvalRecord`:评判单个 output
|
|
674
|
+
- `evaluate_batch(items, outputs) -> List[EvalRecord]`:批量评判(可选优化)
|
|
675
|
+
|
|
676
|
+
**特性:**
|
|
677
|
+
- ✅ 每个 output 生成独立的 EvalRecord
|
|
678
|
+
- ✅ 标准化的评估记录格式(包含 eval_id、output_id、query_id、platform、timestamp、result)
|
|
679
|
+
- ✅ 内置错误处理和日志记录
|
|
680
|
+
|
|
681
|
+
**示例:**
|
|
682
|
+
|
|
683
|
+
```python
|
|
684
|
+
from orchestrator.base_judge import Judge, EvalRecord
|
|
685
|
+
|
|
686
|
+
class MyCustomJudge(Judge):
|
|
687
|
+
async def evaluate(self, item: Dict[str, Any], output: Dict[str, Any]) -> EvalRecord:
|
|
688
|
+
# 实现评判逻辑
|
|
689
|
+
result = {"score": 1.0, "label": "correct"}
|
|
690
|
+
return EvalRecord(
|
|
691
|
+
eval_id=self._generate_eval_id(),
|
|
692
|
+
output_id=output.get("id"),
|
|
693
|
+
query_id=item.get("id"),
|
|
694
|
+
platform=output.get("platform"),
|
|
695
|
+
timestamp=datetime.now(UTC).isoformat(),
|
|
696
|
+
result=result
|
|
697
|
+
)
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
#### Metric 抽象基类(`base_metric.py`)
|
|
701
|
+
|
|
702
|
+
所有指标计算器都应继承 `Metric` 抽象基类并实现以下方法:
|
|
703
|
+
|
|
704
|
+
- `compute(eval_records) -> Dict[str, Any]`:计算总体指标
|
|
705
|
+
- `compute_by_group(eval_records, group_by) -> Dict[str, Dict[str, Any]]`:按分组计算指标
|
|
706
|
+
|
|
707
|
+
**特性:**
|
|
708
|
+
- ✅ 支持总体和分组两层统计
|
|
709
|
+
- ✅ 标准化的指标结果格式(MetricResult 包含 total 和 by_platform)
|
|
710
|
+
- ✅ 灵活的分组机制(按 platform、model 等)
|
|
711
|
+
|
|
712
|
+
**示例:**
|
|
713
|
+
|
|
714
|
+
```python
|
|
715
|
+
from orchestrator.base_metric import Metric, MetricResult
|
|
716
|
+
|
|
717
|
+
class MyCustomMetric(Metric):
|
|
718
|
+
def compute(self, eval_records: List[EvalRecord]) -> Dict[str, Any]:
|
|
719
|
+
# 实现总体指标计算
|
|
720
|
+
return {"accuracy": 0.85, "total_count": 100}
|
|
721
|
+
|
|
722
|
+
def compute_by_group(self, eval_records: List[EvalRecord], group_by: str = "platform") -> Dict[str, Dict[str, Any]]:
|
|
723
|
+
# 实现分组指标计算
|
|
724
|
+
return {
|
|
725
|
+
"kimi": {"accuracy": 0.90, "count": 50},
|
|
726
|
+
"doubao": {"accuracy": 0.80, "count": 50}
|
|
727
|
+
}
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
### 数据模型
|
|
731
|
+
|
|
732
|
+
系统使用 Pydantic 模型确保类型安全:
|
|
733
|
+
|
|
734
|
+
- **EvalRecord**:评估记录的标准格式
|
|
735
|
+
- `eval_id`:唯一的评估记录 ID
|
|
736
|
+
- `output_id`:对应的 output ID
|
|
737
|
+
- `query_id`:对应的查询任务 ID
|
|
738
|
+
- `platform`:AI 平台名称
|
|
739
|
+
- `timestamp`:评判时间戳
|
|
740
|
+
- `result`:具体的评判结果
|
|
741
|
+
- `error`:错误信息(如果评判失败)
|
|
742
|
+
|
|
743
|
+
- **MetricResult**:指标结果的标准格式
|
|
744
|
+
- `total`:总体指标
|
|
745
|
+
- `by_platform`:按平台分组的指标
|
|
746
|
+
|
|
747
|
+
### 迁移指南
|
|
748
|
+
|
|
749
|
+
⚠️ **旧的 `judge.py` 和 `metric.py` 已废弃**
|
|
750
|
+
|
|
751
|
+
如果您的代码仍在使用旧的基类,请按以下步骤迁移:
|
|
752
|
+
|
|
753
|
+
1. **更新导入语句:**
|
|
754
|
+
```python
|
|
755
|
+
# 旧的方式(已废弃)
|
|
756
|
+
from orchestrator.judge import Judge
|
|
757
|
+
from orchestrator.metric import Metric
|
|
758
|
+
|
|
759
|
+
# 新的方式
|
|
760
|
+
from orchestrator.base_judge import Judge, EvalRecord
|
|
761
|
+
from orchestrator.base_metric import Metric, MetricResult
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
2. **更新 Judge 实现:**
|
|
765
|
+
- 将 `evaluate()` 方法改为返回 `EvalRecord` 对象
|
|
766
|
+
- 为每个 output 生成独立的评估记录
|
|
767
|
+
- 使用 `_generate_eval_id()` 生成唯一 ID
|
|
768
|
+
|
|
769
|
+
3. **更新 Metric 实现:**
|
|
770
|
+
- 实现 `compute()` 方法计算总体指标
|
|
771
|
+
- 实现 `compute_by_group()` 方法计算分组指标
|
|
772
|
+
- 使用 `compute_all()` 方法返回 `MetricResult` 对象
|
|
773
|
+
|
|
774
|
+
4. **更新实验配置:**
|
|
775
|
+
- 确保 judges 和 metrics 配置指向新的实现类
|
|
776
|
+
- 检查结果保存格式是否符合新的数据模型
|
|
777
|
+
|
|
778
|
+
## 技术栈
|
|
779
|
+
|
|
780
|
+
- **Python 3.11+**:核心编程语言
|
|
781
|
+
- **browser-use**:浏览器自动化框架
|
|
782
|
+
- **Pydantic**:数据验证和类型安全
|
|
783
|
+
- **Typer**:CLI 框架
|
|
784
|
+
- **Rich**:终端美化输出
|
|
785
|
+
- **OpenPyxl**:Excel 报告生成
|
|
786
|
+
- **BeautifulSoup4**:HTML 解析
|
|
787
|
+
|
|
788
|
+
## 相关文档
|
|
789
|
+
|
|
790
|
+
- **API 文档**:`src/orchestrator/README.md` - 完整的 API 文档和使用指南
|
|
791
|
+
- **设计文档**:`.kiro/specs/judge-metric-redesign/` - 系统设计和架构说明
|
|
792
|
+
- **测试文档**:`tests/README.md` - 测试套件说明
|
|
793
|
+
|
|
794
|
+
## 许可证
|
|
795
|
+
|
|
796
|
+
本项目遵循 MIT 许可证。
|
|
797
|
+
|
|
798
|
+
## 品牌评估模块
|
|
799
|
+
|
|
800
|
+
GEO 实验调度器支持品牌评估功能,通过5个核心指标量化品牌在AI回答中的表现。
|
|
801
|
+
|
|
802
|
+
### 核心指标
|
|
803
|
+
|
|
804
|
+
1. **被引用率(Inclusion Rate)**:品牌被提及的响应数占总响应数的百分比
|
|
805
|
+
2. **首次提及位置(First Mention Position)**:品牌在响应中首次出现的平均字符位置
|
|
806
|
+
3. **信息深度覆盖(Depth Coverage)**:品牌提及时覆盖关键维度的平均百分比
|
|
807
|
+
4. **话语份额(Share of Voice)**:目标品牌提及次数占所有品牌提及次数的百分比
|
|
808
|
+
5. **平台份额(Platform Shares)**:按平台分组的话语份额
|
|
809
|
+
|
|
810
|
+
### 快速开始
|
|
811
|
+
|
|
812
|
+
使用品牌评估功能只需配置实验参数:
|
|
813
|
+
|
|
814
|
+
```python
|
|
815
|
+
experiment = {
|
|
816
|
+
"id": "exp_brand_evaluation",
|
|
817
|
+
"dataset": "dataset.json",
|
|
818
|
+
"platform": "kimi",
|
|
819
|
+
"judges": [
|
|
820
|
+
{
|
|
821
|
+
"name": "brand_l9",
|
|
822
|
+
"type": "judges.brand_dimension_judge.BrandDimensionJudge",
|
|
823
|
+
"params": {
|
|
824
|
+
"target_brand": "理想L9",
|
|
825
|
+
"competitor_brands": ["蔚来ES8", "问界M9"],
|
|
826
|
+
"key_dimensions": ["长续航", "大空间", "智能驾驶"],
|
|
827
|
+
"dimension_keywords": {
|
|
828
|
+
"长续航": ["续航", "里程", "充电"],
|
|
829
|
+
"大空间": ["空间", "座位", "宽敞"],
|
|
830
|
+
"智能驾驶": ["自动驾驶", "辅助驾驶", "智能"]
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
],
|
|
835
|
+
"metrics": [
|
|
836
|
+
{
|
|
837
|
+
"name": "brand_l9_metrics",
|
|
838
|
+
"type": "metrics.brand_metric.BrandMetric",
|
|
839
|
+
"params": {
|
|
840
|
+
"target_brand": "理想L9",
|
|
841
|
+
"key_dimensions": ["长续航", "大空间", "智能驾驶"]
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
],
|
|
845
|
+
"repeat_count": 1,
|
|
846
|
+
"thinking_mode": "quick"
|
|
847
|
+
}
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
### 示例脚本
|
|
851
|
+
|
|
852
|
+
查看 `geo-crawler` 命令获取完整示例,包括:
|
|
853
|
+
- 单品牌评估
|
|
854
|
+
- 多品牌对比评估
|
|
855
|
+
- 跨平台品牌评估
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
## 架构演进与迁移
|
|
860
|
+
|
|
861
|
+
### 废弃通知
|
|
862
|
+
|
|
863
|
+
从 v2.0 开始,旧的 `judge.py` 和 `metric.py` 基类已废弃,将在 v3.0 移除。请使用新的抽象基类:
|
|
864
|
+
|
|
865
|
+
- **旧类**:`orchestrator.judge.Judge`、`orchestrator.metric.Metric` ⚠️ 已废弃
|
|
866
|
+
- **新类**:`orchestrator.base_judge.Judge`、`orchestrator.base_metric.Metric` ✅ 推荐
|
|
867
|
+
|
|
868
|
+
**快速迁移:**
|
|
869
|
+
|
|
870
|
+
```python
|
|
871
|
+
# 旧方式(已废弃)
|
|
872
|
+
from orchestrator.judge import Judge
|
|
873
|
+
class MyJudge(Judge):
|
|
874
|
+
def evaluate(self, item, output):
|
|
875
|
+
return {"query_id": item["id"], "label": "correct"}
|
|
876
|
+
|
|
877
|
+
# 新方式(推荐)
|
|
878
|
+
from orchestrator.base_judge import Judge, EvalRecord
|
|
879
|
+
from datetime import datetime, UTC
|
|
880
|
+
|
|
881
|
+
class MyJudge(Judge):
|
|
882
|
+
async def evaluate(self, item, output):
|
|
883
|
+
return EvalRecord(
|
|
884
|
+
eval_id=self._generate_eval_id(),
|
|
885
|
+
output_id=output.get("id"),
|
|
886
|
+
query_id=item.get("id"),
|
|
887
|
+
platform=output.get("platform"),
|
|
888
|
+
timestamp=datetime.now(UTC).isoformat(),
|
|
889
|
+
result={"label": "correct"}
|
|
890
|
+
)
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
**新架构优势:**
|
|
894
|
+
- ✅ 每个 output 独立评判,可追溯
|
|
895
|
+
- ✅ 支持总体和分组两层统计
|
|
896
|
+
- ✅ 强类型约束(Pydantic)
|
|
897
|
+
- ✅ 支持异步操作
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
### 多 Judge/Metric 支持
|
|
902
|
+
|
|
903
|
+
从 v2.0 开始支持在一个实验中配置多个 Judge 和 Metric,实现多维度评估:
|
|
904
|
+
|
|
905
|
+
```json
|
|
906
|
+
{
|
|
907
|
+
"judges": [
|
|
908
|
+
{
|
|
909
|
+
"type": "judges.brand_dimension_judge.BrandDimensionJudge",
|
|
910
|
+
"params": {"judge_id": "brand_judge_ideal", "target_brand": "理想汽车"}
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
"type": "judges.brand_dimension_judge.BrandDimensionJudge",
|
|
914
|
+
"params": {"judge_id": "brand_judge_nio", "target_brand": "蔚来"}
|
|
915
|
+
}
|
|
916
|
+
],
|
|
917
|
+
"metrics": [
|
|
918
|
+
{
|
|
919
|
+
"type": "metrics.brand_metric.BrandMetric",
|
|
920
|
+
"params": {"metric_id": "brand_metric_ideal", "target_brand": "理想汽车"}
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
"type": "metrics.brand_metric.BrandMetric",
|
|
924
|
+
"params": {"metric_id": "brand_metric_nio", "target_brand": "蔚来"}
|
|
925
|
+
}
|
|
926
|
+
]
|
|
927
|
+
}
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
**使用场景:**
|
|
931
|
+
- 多品牌对比评估
|
|
932
|
+
- 多维度评估(产品特性、用户体验等)
|
|
933
|
+
- 跨平台品牌表现分析
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
## 日志配置
|
|
938
|
+
|
|
939
|
+
### Browser Use 日志级别
|
|
940
|
+
|
|
941
|
+
在 `.env` 文件中配置:
|
|
942
|
+
|
|
943
|
+
```bash
|
|
944
|
+
# 推荐:只显示警告和错误
|
|
945
|
+
BROWSER_USE_LOGGING_LEVEL=warning
|
|
946
|
+
|
|
947
|
+
# 调试:显示所有信息(包含 Emoji)
|
|
948
|
+
BROWSER_USE_LOGGING_LEVEL=info
|
|
949
|
+
|
|
950
|
+
# 最小:只显示最终结果
|
|
951
|
+
BROWSER_USE_LOGGING_LEVEL=result
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
**日志级别说明:**
|
|
955
|
+
- `debug` - 所有调试信息
|
|
956
|
+
- `info` - 详细信息(包含 Emoji 符号)
|
|
957
|
+
- `warning` - 只显示警告和错误(推荐日常使用)
|
|
958
|
+
- `error` - 只显示错误
|
|
959
|
+
- `result` - 只显示最终结果
|
|
960
|
+
|
|
961
|
+
**Emoji 符号含义:**
|
|
962
|
+
- `🎯` Task(任务)
|
|
963
|
+
- `📍` Step(步骤)
|
|
964
|
+
- `▶️` Action(动作)
|
|
965
|
+
- `👍` Success(成功)
|
|
966
|
+
- `❌` Fail(失败)
|
|
967
|
+
- `📄` Final Result(最终结果)
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
## CLI 工具使用指南
|
|
972
|
+
|
|
973
|
+
### GEO CLI - 品牌评估命令行工具
|
|
974
|
+
|
|
975
|
+
`geo-crawler` 是项目的主入口,`examples/geo_cli.py` 作为兼容转发壳保留。它提供以下功能:
|
|
976
|
+
|
|
977
|
+
#### 1. 初始化浏览器登录
|
|
978
|
+
|
|
979
|
+
```bash
|
|
980
|
+
# 初始化所有平台(推荐)
|
|
981
|
+
geo-crawler init-profile all
|
|
982
|
+
|
|
983
|
+
# 初始化单个平台
|
|
984
|
+
geo-crawler init-profile kimi
|
|
985
|
+
geo-crawler init-profile doubao
|
|
986
|
+
geo-crawler init-profile yuanbao
|
|
987
|
+
geo-crawler init-profile deepseek
|
|
988
|
+
|
|
989
|
+
# 检查登录状态
|
|
990
|
+
geo-crawler init-profile all --check
|
|
991
|
+
|
|
992
|
+
# 强制重新登录
|
|
993
|
+
geo-crawler init-profile kimi --force
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
#### 2. 运行采样
|
|
997
|
+
|
|
998
|
+
```bash
|
|
999
|
+
# 直接传入问题
|
|
1000
|
+
geo-crawler sample run \
|
|
1001
|
+
--platform kimi \
|
|
1002
|
+
--platform qianwen \
|
|
1003
|
+
--query "20万左右电动车推荐" \
|
|
1004
|
+
--mode quick \
|
|
1005
|
+
--repeat 3 \
|
|
1006
|
+
--api-key-env OPENAI_API_KEY
|
|
1007
|
+
|
|
1008
|
+
# 使用数据集
|
|
1009
|
+
geo-crawler sample run \
|
|
1010
|
+
--platform kimi \
|
|
1011
|
+
--dataset examples/dataset_test_single.json \
|
|
1012
|
+
--env-file .env
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
#### 3. 使用采样配置文件
|
|
1016
|
+
|
|
1017
|
+
```bash
|
|
1018
|
+
geo-crawler sample run --config sampling.json
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
采样配置只描述平台、输入、轮次、模型、浏览器和输出位置,不包含 Judge、Metric 或报表配置。
|
|
1022
|
+
|
|
1023
|
+
### LLM 语义判断模式
|
|
1024
|
+
|
|
1025
|
+
支持两种评判模式:
|
|
1026
|
+
|
|
1027
|
+
#### LLM 模式(推荐)
|
|
1028
|
+
|
|
1029
|
+
**优势:**
|
|
1030
|
+
- ✅ 自动识别品牌提及
|
|
1031
|
+
- ✅ 智能理解维度覆盖
|
|
1032
|
+
- ✅ 识别同义词和相关表述
|
|
1033
|
+
- ✅ 无需手动配置竞品列表
|
|
1034
|
+
|
|
1035
|
+
**配置:**
|
|
1036
|
+
|
|
1037
|
+
```bash
|
|
1038
|
+
# .env 文件
|
|
1039
|
+
JUDGE_METRIC_LLM_ENABLED=true
|
|
1040
|
+
JUDGE_METRIC_LLM_API_KEY=your_api_key
|
|
1041
|
+
JUDGE_METRIC_LLM_MODEL=gpt-4o-mini # 可选
|
|
1042
|
+
JUDGE_METRIC_LLM_INPUT_PRICE=0.20 # 每百万 tokens 的人民币价格
|
|
1043
|
+
JUDGE_METRIC_LLM_OUTPUT_PRICE=2.00 # 每百万 tokens 的人民币价格
|
|
1044
|
+
```
|
|
1045
|
+
|
|
1046
|
+
平台查询 LLM 使用 `OPENAI_INPUT_PRICE` 和 `OPENAI_OUTPUT_PRICE` 计算费用,单位同样是人民币/百万 tokens。
|
|
1047
|
+
|
|
1048
|
+
```python
|
|
1049
|
+
# 代码配置
|
|
1050
|
+
judge_params = {
|
|
1051
|
+
"target_brand": "小鹏",
|
|
1052
|
+
"key_dimensions": ["长续航", "快充", "高性价比", "大空间"],
|
|
1053
|
+
"use_llm": True,
|
|
1054
|
+
"llm": judge_llm # LLMWrapper 实例
|
|
1055
|
+
}
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
#### 关键词匹配模式
|
|
1059
|
+
|
|
1060
|
+
**局限性:**
|
|
1061
|
+
- ✗ 需要手动配置竞品列表
|
|
1062
|
+
- ✗ 需要手动配置维度关键词
|
|
1063
|
+
- ✗ 无法识别同义词
|
|
1064
|
+
|
|
1065
|
+
```python
|
|
1066
|
+
judge_params = {
|
|
1067
|
+
"target_brand": "小鹏",
|
|
1068
|
+
"competitor_brands": ["理想", "蔚来", "问界"],
|
|
1069
|
+
"key_dimensions": ["长续航", "快充"],
|
|
1070
|
+
"dimension_keywords": {
|
|
1071
|
+
"长续航": ["续航", "里程", "电池"],
|
|
1072
|
+
"快充": ["快充", "充电", "充电速度"]
|
|
1073
|
+
},
|
|
1074
|
+
"use_llm": False
|
|
1075
|
+
}
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
## 联系方式
|
|
1079
|
+
|
|
1080
|
+
如有问题或建议,请联系项目维护者。
|