Qwen-Agent DeepPlanning 旅行规划基准(Travel Planning Benchmark)完整实战指南:环境搭建、三阶段流水线与评测指标解析
【免费下载链接】Qwen-AgentAgent framework and applications built upon Qwen>=3.0, featuring Function Calling, MCP, Code Interpreter, RAG, Chrome extension, etc.项目地址: https://gitcode.com/GitHub_Trending/qw/Qwen-Agent
DeepPlanning 是一个用于评估 AI Agent 规划能力的综合基准,其中 Travel Planning(旅行规划)域要求 Agent 在航班、酒店、餐厅、景点等真实结构化数据库的基础上,调用工具完成多城市、多天数的完整旅行方案规划。本文以 benchmark/deepplanning/travelplanning/README.md 为骨架,结合 benchmark/deepplanning/travelplanning 目录下的源码实现,完整讲解该基准从环境搭建、数据准备、模型配置到三段式流水线(Inference → Conversion → Evaluation)的落地步骤,并深入剖析delivery_rate、commonsense_score、personalized_score、composite_score、case_acc等核心指标的计算逻辑与断点续跑机制。读完后,你将能够独立在本地复现该旅行规划评测实验,并读懂每一份评估报告背后的约束检查逻辑。
一、基准概览:Travel Planning 在 DeepPlanning 中的定位
DeepPlanning Benchmark 是一个横跨多领域、专门评测 AI Agent 规划能力的基准,包含两大领域:
- Travel Planning(旅行规划):评测 Agent 在给定出发地、天数、人数与个性化约束(如指定航班/酒店品牌、预算上限)的前提下,规划完整旅行行程的能力;
- Shopping Planning(电商购物):评测 Agent 完成多级电商购物任务的能力。
两个领域既可以通过 run_all.sh 作为统一基准运行(官方推荐,用于复现论文实验),也可以各自独立运行。本文聚焦于旅行域的独立运行方式,相关领域总览与跨域聚合逻辑可参考 benchmark/deepplanning/README.md。
在旅行域中,Agent 需要基于一套“沙箱化”的本地数据库(航班、酒店、餐厅、景点、城际交通等)完成规划,评测系统再逐项核对方案是否满足常识约束(行程可达性、营业时间、成本核算等)与个性化硬约束(如“必须乘坐某航班”“必须入住某品牌酒店”)。
二、Step 1:安装依赖
统一环境在benchmark/deepplanning 项目根目录下搭建(即travelplanning/的上一级),所有领域共用同一份依赖清单:
# 若当前位于 travelplanning/ 目录,先回到项目根目录 cd .. # 创建新的 conda 环境(推荐 Python 3.10) conda create -n deepplanning python=3.10 -y # 激活环境 conda activate deepplanning # 从统一 requirements.txt 安装全部依赖 pip install -r requirements.txt # 回到 travelplanning 目录 cd travelplanning依赖清单位于 benchmark/deepplanning/requirements.txt。从源码看,运行链路主要依赖openaiSDK(用于调用兼容 OpenAI 协议的模型服务)、python-dotenv风格的环境变量读取等,底层模型调用封装在 agent/call_llm.py 中。
三、Step 2 & 3:下载并解压数据库
旅行域需要两份数据库压缩包,需放置到travelplanning/database/目录下(该目录默认不存在,需自行创建):
| 文件 | 说明 |
|---|---|
database/database_zh.zip | 中文数据库(航班、酒店、餐厅、景点) |
database/database_en.zip | 英文数据库 |
两份数据来自 HuggingFace 上的Qwen/DeepPlanning数据集。下载后将压缩包放入travelplanning/database/,然后解压:
# 进入数据库目录 cd database # 解压两种语言的数据库 unzip database_zh.zip # 中文数据库(flights, hotels, restaurants, attractions) unzip database_en.zip # 英文数据库 # 返回 travelplanning 目录 cd ..解压后的数据目录结构对应到评测与推理阶段会被如下代码使用:
- run.py 中的
args.database_dir = base_dir / 'database' / f'database_{language}'; - 评测阶段的 evaluation/constraints_commonsense.py 通过
database_dir加载restaurants/restaurants.csv、hotels/hotels.csv、attractions/attractions.csv、locations/locations_coords.csv、transportation/distance_matrix.csv等索引文件用于校验。
四、Step 4:配置模型(models_config.json)
模型配置由所有领域共享,位于项目根目录(travelplanning/的上一级)下的 models_config.json。README 中给出的配置示例:
{ "models": { "qwen-plus": { "model_name": "qwen-plus", "model_type": "openai", "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "api_key_env": "DASHSCOPE_API_KEY" }, "gpt-4o-2024-11-20": { "model_name": "gpt-4o-2024-11-20", "model_type": "openai", "base_url": "https://api.openai.com/v1/models", "api_key_env": "OPENAI_API_KEY" } } }仓库中实际的 models_config.json 比示例更完整,额外包含qwen3-max、gpt-5-2025-08-07-high,并为每个模型统一设置了"temperature": 0.0(保证评测结果可复现、降低随机性),其中gpt-5还通过extra_body.reasoning_effort: "high"开启深度推理。你可以按同样格式增删模型条目。
关于qwen-plus的重要说明:
qwen-plus配置是必需的。因为在转换阶段(evaluation/convert_report.py)默认使用它来解析和格式化 Agent 生成的旅行方案;- 如果你想换用其他模型做转换,可以修改 evaluation/convert_report.py 中的
conversion_model变量。从源码看,该变量当前硬编码为'qwen-plus',即默认情况下转换阶段始终调用qwen-plus,与推理阶段所用模型相互独立。
支持的模型类型:
openai:OpenAI 及其兼容协议模型(GPT-4、Qwen、DeepSeek 等),通过 OpenAI SDK 以base_url+api_key_env指定的环境变量来访问。
五、Step 5:配置 API Keys
API Key 统一在项目根目录配置。根目录提供 env.example 模板,其中声明了两个变量:
# 用于 Qwen 模型(通过 DashScope) DASHSCOPE_API_KEY="your_dashscope_api_key_here" # 用于 OpenAI 模型 OPENAI_API_KEY="your_openai_api_key_here"两种配置方式任选其一:
# 方式一:在项目根目录创建 .env 文件 cd .. cp .env.example .env # 编辑 .env 填入你的 API Key # 方式二:直接设置环境变量 export DASHSCOPE_API_KEY="your_dashscope_api_key" export OPENAI_API_KEY="your_openai_api_key"从源码看,evaluation/convert_report.py 和 agent/tools_fn_agent.py 都在模块加载时实现了一套.env加载逻辑:优先读取项目根目录.env,其次回退到领域目录(travelplanning/)下的.env,且已存在的环境变量不会被覆盖——这意味着你既可以把密钥放在根目录统一管理,也可以为旅行域单独放置.env。
六、Step 6:运行基准(run.sh 环境变量详解)
6.1 推荐方式:环境变量 +bash run.sh
BENCHMARK_MODEL="qwen-plus" \ BENCHMARK_LANGUAGE="" \ BENCHMARK_WORKERS=10 \ BENCHMARK_MAX_LLM_CALLS=400 \ BENCHMARK_START_FROM="inference" \ BENCHMARK_OUTPUT_DIR="" \ bash run.sh可用环境变量一览:
| 环境变量 | 作用 | 默认值 |
|---|---|---|
BENCHMARK_MODEL | 指定 models_config.json 中的模型名,支持空格分隔的多个模型并发评测 | qwen-plus |
BENCHMARK_LANGUAGE | 语言版本:zh、en,或留空表示中英双语都跑 | zh |
BENCHMARK_WORKERS | 并行 worker 数量(每个任务的并发度) | 40 |
BENCHMARK_MAX_LLM_CALLS | 每个任务允许的最大 LLM 调用次数 | 400 |
BENCHMARK_START_FROM | 起点阶段:inference、conversion、evaluation | inference |
BENCHMARK_OUTPUT_DIR | 自定义输出目录 | 空(默认results/) |
BENCHMARK_VERBOSE | 是否输出详细日志(true/false) | false |
BENCHMARK_DEBUG | 是否开启调试模式(true/false) | false |
6.2 直接修改 run.sh 中的默认值
也可以直接编辑 run.sh,修改:-后面的默认值实现永久生效:
MODEL="${BENCHMARK_MODEL:-${TRAVEL_AGENT_MODEL:-qwen-plus}}" # 改 qwen-plus LANGUAGE="${BENCHMARK_LANGUAGE-zh}" # 改 zh WORKERS="${BENCHMARK_WORKERS:-40}" # 改 40 MAX_LLM_CALLS="${BENCHMARK_MAX_LLM_CALLS:-400}" # 改 400 START_FROM="${BENCHMARK_START_FROM:-inference}" # 改 inference OUTPUT_DIR="${BENCHMARK_OUTPUT_DIR:-}" # 设置自定义路径然后直接运行:
bash run.sh几个源码层面的细节值得注意:
LANGUAGE用的是${BENCHMARK_LANGUAGE-zh}(冒号省略),允许空字符串传入,从而支持BENCHMARK_LANGUAGE=""触发双语运行;- 多模型场景下,
run.sh会对每个模型并行启动一个python run.py子进程,并通过mktemp -d创建的临时日志目录分别收集输出;同时注册了trap处理 Ctrl+C,中断时会杀掉所有后台子进程; - 每个模型的子进程调用命令形如:
python run.py --model "$MODEL_NAME" --workers $WORKERS \ --max-llm-calls $MAX_LLM_CALLS --start-from "$MODEL_START" \ ${LANGUAGE:+--language "$LANGUAGE"} \ ${OUTPUT_DIR:+--output-dir "$OUTPUT_DIR"} \ ${VERBOSE:+--verbose} ${DEBUG:+--debug}
6.3 直接使用 run.py 的 CLI 参数
run.py 是完整的三段式整合运行器,直接调用同样可行:
python run.py --model qwen-plus --language zh --workers 40其 CLI 参数与默认值如下(注意与run.sh默认值的差异):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--model | str | 必填 | models_config.json 中的模型配置名 |
--language | str | None | zh/en,默认两者都运行 |
--workers | int | 10 | 并发 worker 数 |
--max-llm-calls | int | 150 | 每个样本最大 LLM 调用次数 |
--output-dir | str | None | 输出目录,默认results/{model}_{lang} |
--save-intermediate | flag | 关 | 每步之后保存中间结果 |
--start-from | str | inference | inference/conversion/evaluation |
--rerun-ids | str | None | 指定要重跑的任务 ID,支持"0,5,10"或"0-10,15,20-25"格式 |
--verbose/--debug | flag | 关 | 详细输出 / 调试模式 |
run.py会自动为zh/en各生成独立的输出目录results/{model}_{language},并按阶段打印运行摘要(样本数、成功数、平均分、通过率)。
七、智能缓存与断点续跑
当使用run.sh且START_FROM="inference"时,脚本会自动执行智能续跑检查:
- 检查同一模型名下的既有结果,避免重复劳动;
- 先扫描
reports/目录,找出缺失的报告文件(如id_0_report.txt、id_1_report.txt等); - 再扫描
converted_plans/目录,找出缺失的转换方案文件(如id_0_converted.json、id_1_converted.json等); - 识别缺失的任务 ID(全部任务共 120 个,ID 为 0~119);
- 自动确定起始步骤:
- 报告齐全但转换方案缺失 → 从
conversion阶段开始; - 报告缺失 → 从
inference阶段开始; - 两者齐全 → 直接跳过该模型(
✅ All models are already complete. Nothing to run!)。
- 报告齐全但转换方案缺失 → 从
这一机制允许你随时中断长时间运行的评测并在之后安全续跑而不丢失进度。其背后的实现逻辑同样存在于 run.py 中:detect_missing_ids()通过正则id_(\d+)解析已有文件编号并与range(120)求差集,推理与转换两个步骤都会先自动检测缺失 ID,只对缺失部分重跑;--rerun-ids参数则用于显式指定要重跑的任务(支持单 ID、逗号列表与区间三种写法)。
八、深入理解三段式流水线
整个基准按三个阶段串行推进,对应 run.py 中的三个步骤函数。
Stage 1:Inference(Agent 规划)
做什么:
- 从 data/travelplanning_query_zh.json(或
_en版本)加载旅行规划任务; - 调用 LLM Agent 生成旅行方案;
- Agent 通过工具查询数据库(航班、酒店、餐厅、景点);
- 保存 Agent 轨迹(trajectory)与执行日志;
- 按要求的格式生成人类可读的报告(report)。
Agent 侧的实现位于 agent/tools_fn_agent.py,是一个框架无关的轻量级 Function Calling Agent:从 tools/tool_schema_zh.json(或_en版本)加载工具 Schema,动态实例化所有BaseTravelTool子类,然后迭代式地调用 LLM 并在tool_calls与工具执行之间循环,直到产出最终答案。可用工具包括:
| 工具文件 | 功能 |
|---|---|
| flight_query_tool.py | 航班查询 |
| train_query_tool.py | 火车查询 |
| hotel_query_tool.py | 酒店查询 |
| restaurant_query_tool.py | 餐厅查询 |
| attraction_query_tool.py | 景点查询 |
| location_search_tool.py | 地点搜索 |
| roadroute_query_tool.py | 路线查询 |
输出:
results/{model}_{lang}/ ├── trajectories/ # Agent 执行轨迹 │ └── id_0_trajectory.json └── reports/ # 人类可读报告 └── id_0_report.txtStage 2:Conversion(方案解析)
做什么:
- 使用一个 LLM(默认
qwen-plus,可配置)转换方案; - 解析 Agent 输出的Markdown 格式旅行方案;
- 转换为标准化 JSON 格式以供自动化评测;
- 将转换结果存入
converted_plans/目录; - 校验方案结构与完整性。
为什么需要转换:Agent 生成的是人类可读的 Markdown 方案,而评测代码需要结构化 JSON 才能自动核对约束满足情况并计算指标。
从 evaluation/convert_report.py 源码看,转换实现有几个关键设计:
- 转换模型硬编码为
qwen-plus(conversion_model = 'qwen-plus'),由load_model_config+create_client创建 OpenAI 兼容客户端; - 提示词通过
get_format_convert_prompt(language)按语言获取(见 agent/prompts.py); - 每个报告文件由
process_single_report处理:将报告原文作为 user 消息、格式转换提示作为 system 消息调用 LLM,并从响应中提取<JSON>...</JSON>标签内的内容(无标签时尝试直接解析); - 解析失败时进行最多 30 次重试(
max_retries: int = 30),每次失败间隔 1 秒,json.JSONDecodeError与网络异常都会被捕获重试; - 通过
ThreadPoolExecutor并行转换,skip_existing=True时自动跳过已有输出文件。
输出:
results/{model}_{lang}/ ├── converted_plans/ # 结构化旅行方案 │ └── id_0_converted.jsonStage 3:Evaluation(评测)
做什么:
- 检查交付率(是否生成了方案);
- 评估常识得分(8 个维度);
- 校验个性化约束;
- 计算最终得分。
评测核心实现在 evaluation/eval_converted.py,它会根据测试数据中的meta_info找到每个样本的元信息,定位该样本专属的数据库目录database_dir/id_{sample_id},然后并行执行两套约束检查:
- 常识约束:调用 constraints_commonsense.py 中的
eval_commonsense; - 个性化硬约束:调用 constraints_hard.py 中的
eval_hard。
输出:
results/{model}_{lang}/ └── evaluation/ ├── evaluation_summary.json # 总体指标与统计 ├── id_0_score.json # 单个任务得分 ├── id_1_score.json └── ... # 每个任务一个得分文件九、评测指标详解:八维常识约束与个性化硬约束
9.1 核心指标定义
汇总结果 evaluation_summary.json(运行后生成于results/{model}_{lang}/evaluation/)中包含以下指标:
| 指标 | 含义 |
|---|---|
delivery_rate | 交付率:是否成功生成了方案(生成的方案数 / 总测试样本数) |
commonsense_score | 常识约束加权得分(0~1) |
personalized_score | 个性化硬约束满足得分(0/1 二值或平均) |
composite_score | 综合得分 =(常识得分 + 个性化得分)/ 2 |
case_acc | 案例准确率:常识与个性化两项均为满分才算通过的样本占比 |
9.2 常识约束:8 个维度 × 12.5% 权重
从 constraints_commonsense.py 的EVALUATION_DIMENSIONS定义可见,常识约束被组织为8 个等权维度(各 12.5%),每个维度内含若干检查项:
| 维度 | 权重 | 包含的检查项 |
|---|---|---|
| Route Consistency(路线一致性) | 12.5% | valid_trip_duration、closed_loop_route_structure、seamless_intercity_transfers |
| Sandbox Compliance(沙箱合规性) | 12.5% | validated_accommodation、validated_attractions、validated_meals、validated_transportation |
| Itinerary Structure(行程结构) | 12.5% | traceable_accommodation、ends_with_accommodation、essential_meal_coverage、essential_attraction_coverage |
| Time Feasibility(时间可行性) | 12.5% | no_time_overlaps、reasonable_transfer_time |
| Business Hours(营业时间) | 12.5% | attraction_visit_within_opening_hours、dining_within_service_hours、avoidance_of_closure_days |
| Duration Rationality(时长合理性) | 12.5% | reasonable_duration_at_attractions、reasonable_meal_duration |
| Cost Calculation Accuracy(成本核算准确性) | 12.5% | cost_calculation_correctness |
| Activity Diversity(活动多样性) | 12.5% | diverse_meal_options、diverse_attraction_options |
打分规则(每个维度内“一票否决”):
- 维度内所有检查项全部通过→ 该维度得分 = 1.0;
- 维度内任一检查项失败→ 该维度得分 = 0.0;
- 总加权得分 = Σ(维度得分 × 权重)。
该逻辑在 eval_converted.py 的calculate_weighted_score()中实现。源码中还能看到若干有意思的检查细节,例如:
check_route_closed_loop:校验第一天从org出发、最后一天返回org的闭环路线;check_intercity_transportation_consistency:按时间顺序追踪位置变化,校验城际交通完整性——若current_city写为 "from A to B",则必须存在对应的travel_intercity_public活动且起终点城市匹配;check_meal_necessity:非城际日必须安排两餐且间隔 ≥ 2 小时,城际到达/离开日根据到达与离开时间动态决定餐次要求;check_attraction_necessity:非城际日景点相关时长需 ≥ 4 小时或景点数 ≥ 2 个;- 沙箱合规性会逐项核对酒店/餐厅/景点/城际交通的名称与价格是否真实存在于数据库索引中(如酒店价格必须与
hotels.csv中的price_per_night一致,航班的number、from、to、cost字段缺一不可)。
9.3 个性化硬约束:一票否决
constraints_hard.py 依据测试数据meta_info.hard_constraints中的约束键前缀进行分发评测,支持的约束类型包括:
- 航班类(
flight_*):flight_seat_class、flight_seat_status、flight_cheapest_airline_direct、flight_cheapest_direct、flight_earliest_departure_direct、flight_shortest_duration_direct、flight_departure_time_range、flight_arrival_time_range等,统一检查往返航班号是否出现在方案中; - 火车类(
train_*):train_seat_class、train_seat_status、train_shortest_duration_direct、train_cheapest_direct、train_earliest_departure_direct、train_latest_arrival_direct、train_cheapest_train_type等; - 酒店类(
hotel_*):hotel_cheapest_brand、hotel_highest_rated、hotel_cheapest_star、hotel_newest_decoration、hotel_brand_highest_rated、hotel_star_highest_rated、hotel_price_range、hotel_star_service_required等; - 餐厅类(
restaurant_*):restaurant_cheapest_nearby_attraction、restaurant_highest_rated、restaurant_must_eat_named、restaurant_closest_to_attraction、restaurant_specific_cuisine_nearby等; - 景点类(
attraction_*):attraction_must_visit_named、attraction_all_of_type、attraction_top_rated_must_visit、attraction_all_free_attractions、attraction_type_highest_rated等; - 预算约束(
budget_constraint):按人数/房间数重新核算交通、住宿、餐饮、景点与市内打车成本,校验实际总预算是否超过max_budget。
硬约束打分规则:全部硬约束通过 → 得分 1.0;任一硬约束失败 → 得分 0.0(同样是一票否决)。该逻辑在calculate_hard_score()中实现。
最终:
composite_score = (commonsense_score + personalized_score) / 2 case_acc = 1.0 当且仅当 commonsense_score == 1.0 且 personalized_score == 1.0其中case_acc是旅行域论文中的主指标之一(与购物域的weighted_average_case_score一起参与跨域聚合指标avg_acc的计算,详见 benchmark/deepplanning/README.md)。
十、查看结果
10.1 总体统计
cat results/{model}_{lang}/evaluation/evaluation_summary.jsonREADME 中的示例输出(对应某个模型与语言组合):
{ "total_test_samples": 120, "evaluation_success_count": 115, "metrics": { "delivery_rate": 0.958, "commonsense_score": 0.875, "personalized_score": 0.742, "composite_score": 0.809, "case_acc": 0.683 } }需要说明的是,仓库实际生成的evaluation_summary.json(见 eval_converted.py 的summary_data)字段比示例更丰富,还包括plan_files_found、evaluation_failed_count、elapsed_time、max_workers、commonsense_dimensions(每个维度的得分与满分样本数)以及error_statistics。
10.2 单任务详情
# 查看某个任务的具体得分 cat results/{model}_{lang}/evaluation/id_0_score.json # 查看某个任务的人类可读报告 cat results/{model}_{lang}/reports/id_0_report.txt单个id_X_score.json中会包含四个分数的明细、8 个常识维度的逐项得分与检查详情(commonsense_dimension_details)、个性化硬约束的逐条通过与失败信息(personalized_dimension_score),可直接定位具体失败原因。
10.3 错误分析
汇总结果中还包含错误统计,展示最常见的失败模式,帮助定位 Agent 的薄弱环节:
"error_statistics": [ { "rank": 1, "error_type": "[Hard] train_seat_status", "count": 15, "affected_samples": ["0", "12", "25", ...] } ]从 eval_converted.py 的源码看,错误统计对常识约束与硬约束分开归因:常识约束错误标记为[Commonsense] {check_name},硬约束错误标记为[Hard] {constraint_name},按出现次数降序排列,每条记录包含count、affected_samples与sample_messages(示例错误消息),并在终端按 Top 10 打印;整个评测完成后终端还会输出各维度的加权得分明细与最终综合得分。
十一、注意事项与常见问题
qwen-plus配置缺失会导致转换阶段失败:即使推理模型用的是其他模型,convert_report.py仍默认调用qwen-plus,请确保它在models_config.json中且对应 API Key 已配置;.env优先级:源码优先加载项目根目录.env,不存在时回退到travelplanning/.env,且不覆盖已存在的系统环境变量;- 数据库目录必须就绪:
database/database_{zh|en}需先下载解压,否则推理阶段工具查询与评测阶段索引加载都会失败; - 断点续跑:120 个任务(ID 0~119)中任意缺失的报告或转换文件都会在下次
run.sh(START_FROM="inference")启动时被自动补齐;也可用run.py --rerun-ids "0-10,15,20-25"精确重跑指定样本; - 双语运行:
BENCHMARK_LANGUAGE=""或run.py不传--language时,会依次运行zh与en两个语言版本,输出分别落在results/{model}_zh/与results/{model}_en/; - 跨领域聚合:若需将旅行域与购物域结果统一聚合,可使用 run_all.sh 统一运行,聚合结果输出到
aggregated_results/{model}_aggregated.json,聚合逻辑见 aggregate_results.py。
十二、总结
Travel Planning 基准通过“推理生成 → LLM 结构化转换 → 自动约束评测”三段式流水线,将 Agent 的开放式旅行规划能力转化为可量化、可复现的指标体系。本文所涉及的配置、脚本与评测逻辑均可在仓库中逐一对应:入口脚本 run.sh 与 run.py,Agent 实现 agent/tools_fn_agent.py,转换逻辑 evaluation/convert_report.py,以及评测核心 evaluation/eval_converted.py、evaluation/constraints_commonsense.py、evaluation/constraints_hard.py。按照本文的六个步骤即可完整复现旅行规划基准评测,并借助断点续跑、逐任务得分与错误统计,系统性地定位并改进 Agent 的规划能力。
【免费下载链接】Qwen-AgentAgent framework and applications built upon Qwen>=3.0, featuring Function Calling, MCP, Code Interpreter, RAG, Chrome extension, etc.项目地址: https://gitcode.com/GitHub_Trending/qw/Qwen-Agent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考