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.
Files changed (132) hide show
  1. geo_crawler-0.1.0/PKG-INFO +1080 -0
  2. geo_crawler-0.1.0/README.md +1057 -0
  3. geo_crawler-0.1.0/commands/__init__.py +22 -0
  4. geo_crawler-0.1.0/commands/analyze.py +935 -0
  5. geo_crawler-0.1.0/commands/common.py +211 -0
  6. geo_crawler-0.1.0/commands/init_profile.py +623 -0
  7. geo_crawler-0.1.0/commands/sample.py +286 -0
  8. geo_crawler-0.1.0/geo_crawler/__init__.py +4 -0
  9. geo_crawler-0.1.0/geo_crawler/__main__.py +6 -0
  10. geo_crawler-0.1.0/geo_crawler/cli.py +68 -0
  11. geo_crawler-0.1.0/geo_crawler.egg-info/PKG-INFO +1080 -0
  12. geo_crawler-0.1.0/geo_crawler.egg-info/SOURCES.txt +130 -0
  13. geo_crawler-0.1.0/geo_crawler.egg-info/dependency_links.txt +1 -0
  14. geo_crawler-0.1.0/geo_crawler.egg-info/entry_points.txt +2 -0
  15. geo_crawler-0.1.0/geo_crawler.egg-info/requires.txt +16 -0
  16. geo_crawler-0.1.0/geo_crawler.egg-info/top_level.txt +12 -0
  17. geo_crawler-0.1.0/pyproject.toml +96 -0
  18. geo_crawler-0.1.0/setup.cfg +4 -0
  19. geo_crawler-0.1.0/src/__init__.py +5 -0
  20. geo_crawler-0.1.0/src/browser_config.py +48 -0
  21. geo_crawler-0.1.0/src/browser_providers/__init__.py +27 -0
  22. geo_crawler-0.1.0/src/browser_providers/volcengine.py +275 -0
  23. geo_crawler-0.1.0/src/browser_providers/volcengine_sandbox.py +316 -0
  24. geo_crawler-0.1.0/src/browser_runtime/__init__.py +5 -0
  25. geo_crawler-0.1.0/src/browser_runtime/config.py +104 -0
  26. geo_crawler-0.1.0/src/browser_runtime/session.py +177 -0
  27. geo_crawler-0.1.0/src/deepseek/README.md +202 -0
  28. geo_crawler-0.1.0/src/deepseek/__init__.py +18 -0
  29. geo_crawler-0.1.0/src/deepseek/prompt_deep.md +22 -0
  30. geo_crawler-0.1.0/src/deepseek/prompt_quick.md +22 -0
  31. geo_crawler-0.1.0/src/deepseek/query.py +102 -0
  32. geo_crawler-0.1.0/src/deepseek/schema.py +420 -0
  33. geo_crawler-0.1.0/src/deepseek/tools.py +309 -0
  34. geo_crawler-0.1.0/src/doubao/README.md +192 -0
  35. geo_crawler-0.1.0/src/doubao/__init__.py +18 -0
  36. geo_crawler-0.1.0/src/doubao/prompt_deep.md +22 -0
  37. geo_crawler-0.1.0/src/doubao/prompt_quick.md +22 -0
  38. geo_crawler-0.1.0/src/doubao/query.py +102 -0
  39. geo_crawler-0.1.0/src/doubao/schema.py +330 -0
  40. geo_crawler-0.1.0/src/doubao/tools.py +585 -0
  41. geo_crawler-0.1.0/src/kimi/README.md +141 -0
  42. geo_crawler-0.1.0/src/kimi/__init__.py +18 -0
  43. geo_crawler-0.1.0/src/kimi/prompt_deep.md +22 -0
  44. geo_crawler-0.1.0/src/kimi/prompt_quick.md +22 -0
  45. geo_crawler-0.1.0/src/kimi/query.py +101 -0
  46. geo_crawler-0.1.0/src/kimi/schema.py +562 -0
  47. geo_crawler-0.1.0/src/kimi/tools.py +330 -0
  48. geo_crawler-0.1.0/src/orchestrator/README.md +494 -0
  49. geo_crawler-0.1.0/src/orchestrator/__init__.py +41 -0
  50. geo_crawler-0.1.0/src/orchestrator/agent.py +237 -0
  51. geo_crawler-0.1.0/src/orchestrator/base_judge.py +156 -0
  52. geo_crawler-0.1.0/src/orchestrator/base_metric.py +99 -0
  53. geo_crawler-0.1.0/src/orchestrator/cost_models.py +194 -0
  54. geo_crawler-0.1.0/src/orchestrator/dataset.py +180 -0
  55. geo_crawler-0.1.0/src/orchestrator/judge.py +127 -0
  56. geo_crawler-0.1.0/src/orchestrator/judges/__init__.py +13 -0
  57. geo_crawler-0.1.0/src/orchestrator/judges/brand_dimension_judge.py +892 -0
  58. geo_crawler-0.1.0/src/orchestrator/judges/llm_response_models.py +118 -0
  59. geo_crawler-0.1.0/src/orchestrator/llm_config.py +137 -0
  60. geo_crawler-0.1.0/src/orchestrator/llm_wrapper.py +61 -0
  61. geo_crawler-0.1.0/src/orchestrator/metric.py +121 -0
  62. geo_crawler-0.1.0/src/orchestrator/metrics/__init__.py +5 -0
  63. geo_crawler-0.1.0/src/orchestrator/metrics/brand_metric.py +451 -0
  64. geo_crawler-0.1.0/src/orchestrator/models.py +132 -0
  65. geo_crawler-0.1.0/src/orchestrator/query_executor.py +134 -0
  66. geo_crawler-0.1.0/src/orchestrator/reporter.py +584 -0
  67. geo_crawler-0.1.0/src/orchestrator/runner.py +1023 -0
  68. geo_crawler-0.1.0/src/orchestrator/storage.py +169 -0
  69. geo_crawler-0.1.0/src/qianwen/README.md +35 -0
  70. geo_crawler-0.1.0/src/qianwen/__init__.py +9 -0
  71. geo_crawler-0.1.0/src/qianwen/prompt_deep.md +26 -0
  72. geo_crawler-0.1.0/src/qianwen/prompt_quick.md +25 -0
  73. geo_crawler-0.1.0/src/qianwen/query.py +60 -0
  74. geo_crawler-0.1.0/src/qianwen/schema.py +295 -0
  75. geo_crawler-0.1.0/src/qianwen/tools.py +360 -0
  76. geo_crawler-0.1.0/src/sampling/__init__.py +11 -0
  77. geo_crawler-0.1.0/src/sampling/config.py +254 -0
  78. geo_crawler-0.1.0/src/sampling/runner.py +136 -0
  79. geo_crawler-0.1.0/src/sampling/storage.py +64 -0
  80. geo_crawler-0.1.0/src/yuanbao/README.md +195 -0
  81. geo_crawler-0.1.0/src/yuanbao/__init__.py +18 -0
  82. geo_crawler-0.1.0/src/yuanbao/prompt_deep.md +21 -0
  83. geo_crawler-0.1.0/src/yuanbao/prompt_quick.md +21 -0
  84. geo_crawler-0.1.0/src/yuanbao/query.py +102 -0
  85. geo_crawler-0.1.0/src/yuanbao/schema.py +452 -0
  86. geo_crawler-0.1.0/src/yuanbao/tools.py +586 -0
  87. geo_crawler-0.1.0/tests/test_agent_cost_tracking.py +103 -0
  88. geo_crawler-0.1.0/tests/test_base_judge_properties.py +723 -0
  89. geo_crawler-0.1.0/tests/test_base_metric_properties.py +387 -0
  90. geo_crawler-0.1.0/tests/test_brand_dimension_judge.py +416 -0
  91. geo_crawler-0.1.0/tests/test_brand_dimension_judge_error_handling.py +520 -0
  92. geo_crawler-0.1.0/tests/test_brand_dimension_judge_properties.py +558 -0
  93. geo_crawler-0.1.0/tests/test_brand_integration.py +526 -0
  94. geo_crawler-0.1.0/tests/test_brand_llm_integration.py +363 -0
  95. geo_crawler-0.1.0/tests/test_brand_metric.py +329 -0
  96. geo_crawler-0.1.0/tests/test_brand_metric_llm_compatibility.py +372 -0
  97. geo_crawler-0.1.0/tests/test_brand_metric_properties.py +500 -0
  98. geo_crawler-0.1.0/tests/test_browser_config.py +38 -0
  99. geo_crawler-0.1.0/tests/test_browser_runtime.py +190 -0
  100. geo_crawler-0.1.0/tests/test_cli_init_profile_auto.py +74 -0
  101. geo_crawler-0.1.0/tests/test_cli_sample.py +289 -0
  102. geo_crawler-0.1.0/tests/test_cost_models.py +89 -0
  103. geo_crawler-0.1.0/tests/test_data_models.py +409 -0
  104. geo_crawler-0.1.0/tests/test_dataset.py +146 -0
  105. geo_crawler-0.1.0/tests/test_dataset_interactive.py +152 -0
  106. geo_crawler-0.1.0/tests/test_deprecation_warnings.py +83 -0
  107. geo_crawler-0.1.0/tests/test_error_logging.py +345 -0
  108. geo_crawler-0.1.0/tests/test_evaluate_mode_switching.py +155 -0
  109. geo_crawler-0.1.0/tests/test_geo_cli_entrypoint.py +36 -0
  110. geo_crawler-0.1.0/tests/test_integration.py +644 -0
  111. geo_crawler-0.1.0/tests/test_integration_new_format.py +338 -0
  112. geo_crawler-0.1.0/tests/test_judge_interface.py +251 -0
  113. geo_crawler-0.1.0/tests/test_judge_metric_id_properties.py +313 -0
  114. geo_crawler-0.1.0/tests/test_llm_config.py +28 -0
  115. geo_crawler-0.1.0/tests/test_llm_dimension_coverage.py +187 -0
  116. geo_crawler-0.1.0/tests/test_llm_integration_e2e.py +450 -0
  117. geo_crawler-0.1.0/tests/test_llm_response_models.py +199 -0
  118. geo_crawler-0.1.0/tests/test_llm_timeout.py +63 -0
  119. geo_crawler-0.1.0/tests/test_llm_wrapper.py +103 -0
  120. geo_crawler-0.1.0/tests/test_metric_interface.py +332 -0
  121. geo_crawler-0.1.0/tests/test_multi_platform_integration.py +394 -0
  122. geo_crawler-0.1.0/tests/test_platform_context_fields.py +416 -0
  123. geo_crawler-0.1.0/tests/test_platform_prompts.py +136 -0
  124. geo_crawler-0.1.0/tests/test_query_executor.py +373 -0
  125. geo_crawler-0.1.0/tests/test_reporter_new_format.py +240 -0
  126. geo_crawler-0.1.0/tests/test_runner_cost_tracking.py +113 -0
  127. geo_crawler-0.1.0/tests/test_runner_properties.py +412 -0
  128. geo_crawler-0.1.0/tests/test_storage_new_format.py +287 -0
  129. geo_crawler-0.1.0/tests/test_storage_state_export.py +41 -0
  130. geo_crawler-0.1.0/tests/test_volcengine_browser_provider.py +264 -0
  131. geo_crawler-0.1.0/tests/test_volcengine_browser_validation.py +377 -0
  132. 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
+ 如有问题或建议,请联系项目维护者。