mcpField
R
route-researcher

route-researcher

github.com/dreamiurg生产力3538 次安装16 周前更新
代码仓库

关于 route-researcher

route-researcher 是用于研究北美山峰并生成路线攻略报告的技能,面向登山计划制定者。它汇集登山网站、行程报告和天气等状况数据,整理路线与风险信息。

主要功能

  1. 01识别山峰并检索详情
  2. 02获取天气与雪崩状况
  3. 03汇总登山信息来源
  4. 04整理行程报告与风险
  5. 05生成路线攻略报告

适用场景

  1. 01攀登前研究指定山峰
  2. 02查看路线、风险和近期状况
  3. 03规划登山行程时间与安排

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dreamiurg/claude-mountaineering-skills --skill route-researcher

安装后在 Claude Code 中输入/route-researcher确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/
report-template.md
trip-report-template.md
docs/
architecture.md
examples/
2025-10-23-mount-si.md
2025-11-06-mount-adams.md
2025-11-06-wolf-peak.md
2026-01-29-mount-shuksan.md
2026-01-29-tinkham-peak.md
tools/
.python-version
cloudscrape.py
fetch_conditions.py
pyproject.toml
README.md
test_cloudscrape.py
test_fetch_conditions_unit.py
test_fetch_conditions.py
test_geodata_fetchers.py
test_phase4.py
test_phase5.py
uv.lock

详细介绍

路线研究员

研究北美各地的山峰,并生成全面的路线攻略报告,整合来自多个来源的数据,包括 PeakBagger、SummitPost、WTA、AllTrails、天气预报、雪崩状况和行程报告。

数据来源: 此技能汇总了专业登山网站的信息(PeakBagger、SummitPost、Washington Trails Association、AllTrails、The Mountaineers 和区域雪崩中心)。生成报告的质量取决于这些来源所提供信息的完整程度。如果目标山峰在这些网站上的资料较少,报告可能只包含有限的详细信息。此技能最适用于北美资料丰富的山峰。

何时使用此技能

用户提出以下请求时,请使用此技能:

  • 研究某座特定山峰
  • 获取路线攻略或攀登信息
  • 获取山峰行程规划信息
  • 查询登山目标的当前状况

示例:

  • “研究 Mt Baker”
  • “我计划下个月攀登 Sahale Peak,你能研究一下路线吗?”
  • “生成 Forbidden Peak 的路线攻略”

进度检查清单

研究进度:

  • 阶段 1:山峰识别(已验证山峰,已获取 ID)
  • 阶段 2:山峰信息检索(已获取坐标和详细信息)
  • 阶段 3:数据收集(并行执行)
    • 阶段 3a:通过 Python 获取状况(天气、空气质量、日照时长、雪崩、PeakBagger 统计数据/攀登记录)
    • 阶段 3b:研究 Agent(3 个并行执行——网络来源和行程报告)
    • 阶段 3c:汇总结果
    • 阶段 3d:通行/许可证(内联 WebSearch)
  • 阶段 4:路线分析(综合路线、关键难点和危险因素)
  • 阶段 5:报告生成(报告撰写 Agent)
  • 阶段 6:报告审查与验证(报告审查 Agent)
  • 阶段 7:完成(通知用户并提供后续步骤)

编排工作流

第 1 阶段:山峰识别

目标: 确定并验证要研究的具体山峰。

  1. 从用户消息中提取山峰名称

    • 查找山峰名称或攀登目标
    • 常见示例:“Mt Baker”“Mount Rainier”“Sahale Peak”等
  2. 使用 peakbagger-cli 搜索 PeakBagger:

    uvx --with patchright --from "git+https://github.com/dreamiurg/[email protected]" peakbagger peak search "{peak_name}" --format json
    
    • 解析 JSON 输出,提取匹配的山峰
    • 每条结果包含:peak_id、名称、海拔(英尺/米)、位置、URL
  3. 处理多个匹配结果:

    • 如果找到多个山峰:使用 AskUserQuestion 列出选项

      • 对每个选项,显示:山峰名称、海拔、位置以及 PeakBagger URL
      • 将每个选项的描述格式设为:“[山峰名称]([海拔],[位置])- [PeakBagger URL]”
      • 这样用户可以点击链接并确认正确的山峰
      • 让用户选择正确的山峰
      • 如果都不匹配,提供“其他”选项
    • 如果找到单个匹配结果:向用户确认

      • 显示包含山峰详情和 PeakBagger 链接的确认消息
      • 显示:“已找到:[山峰名称]([海拔],[位置])”
      • 在消息中包含 PeakBagger URL,供用户核实:“[PeakBagger URL]”
      • 使用 AskUserQuestion 提问:“这是正确的山峰吗?你可以在 [PeakBagger URL] 查看确认”
    • 如果没有找到匹配结果:

      • 系统地尝试山峰名称的不同变体(参见“山峰名称变体”部分):
        • 颠倒词序:“Mountain Pratt” → “Pratt Mountain”
        • **称谓变体:**Mt/Mount、St/Saint
        • **添加位置:**包含州名或山脉名称
        • **移除称谓:**仅尝试核心名称
      • 使用不同变体并行运行多次搜索
      • 合并结果,并向用户展示最合适的匹配结果
      • 如果仍无结果,使用 AskUserQuestion 询问用户提供:
        • 其他山峰名称变体
        • 直接提供 PeakBagger 山峰 ID 或 URL
        • 通用 PeakBagger 搜索
  4. 提取山峰 ID:

    • 从搜索结果 JSON 中提取 peak_id 字段
    • 保存该值,供后续 peakbagger-cli 命令使用
    • 同时保存 PeakBagger URL,供参考链接使用
第 2 阶段:获取山峰信息

目标: 获取详细的山峰信息和坐标,以便收集基于位置的数据。

此阶段必须在第 3 阶段之前完成,因为天气、日照和雪崩数据都需要坐标。

使用第 1 阶段获取的山峰 ID 检索详细的山峰信息:

uvx --with patchright --from "git+https://github.com/dreamiurg/[email protected]" peakbagger peak show {peak_id} --format json

这将返回结构化的 JSON,其中包含:

  • 山峰名称和别名
  • 海拔(英尺和米)
  • 地形突起度(英尺和米)
  • 隔离距离(英里和公里)
  • 坐标(十进制度表示的纬度和经度)
  • 位置(县、州、国家)
  • 路线(如有):步道起点、距离、爬升高度
  • 所属山峰列表及排名
  • 标准路线描述(如路线数据中提供)

错误处理:

  • 如果 peakbagger-cli 失败:改用 WebSearch/WebFetch,并在“信息缺口”中注明
  • 如果 JSON 中缺少特定字段:在缺口部分标记为“不可用”
  • 速率限制:peakbagger-cli 内置速率限制(默认延迟 2 秒)

从此步骤获取坐标后,立即进入第 3 阶段。

第 3 阶段:收集数据

目标: 从所有可用来源收集全面的路线信息。

执行策略: 运行 Python 脚本获取确定性的 API 数据,同时并行调度专用 Agent 进行网络研究。这种混合方式既能最大限度地减少令牌用量,又能提高并行处理能力。

步骤 3A:获取状况数据(Python 脚本)

运行状况数据获取脚本,收集所有基于 API 的数据:

cd "{repo_root}/skills/route-researcher/tools"
uv run python fetch_conditions.py \
  --coordinates "{latitude},{longitude}" \
  --elevation {elevation_m} \
  --peak-name "{peak_name}" \
  --peak-id {peak_id} \
  --trailhead "{trailhead_lat},{trailhead_lon}" \
  --distance-mi {round_trip_distance_mi} \
  --gain-ft {total_gain_ft} \
  --start-time "{HH:MM}" \
  --waypoint "{lat1},{lon1}" --waypoint "{lat2},{lon2}"

可选参数:--trailhead 启用多县路径采样(登山口→山顶);医院/护林站查询始终从山顶开始;--distance-mi/--gain-ft 启用 time_estimates;--start-time(需同时提供距离和爬升高度)启用 itinerary;--waypoint(2 个或以上)启用 bearings。

此脚本会返回 JSON,包含:

  • weather:7 天预报,含温度、降水、冰冻高度;每天均包含 snow_line_note(以易读的方式说明冰冻高度与雪线的关系)和 near_summit(布尔值:冰冻高度距山顶 2000 ft 以内时为 true)
  • air_quality:AQI 评级及任何相关风险
  • daylight:完整的曙暮光时刻表 — astronomical_dawn、nautical_dawn、civil_twilight(黎明)、sunrise、sunset、civil_dusk、nautical_dusk、astronomical_dusk;在高纬度地区,当太阳未达到阈值(白夜)时,这些值为 null;daylight_hours、timezone
  • time_estimates:使用绳索/不使用绳索的时间估算,以及 3 档速度估算(roped_hr、unroped_hr、fast_hr、moderate_hr、leisurely_hr)— 仅当提供 --distance-mi 和 --gain-ft CLI 参数时才会包含
  • itinerary:行程安排及安全提示(start_time、summit_eta、turnaround_by、return_eta、total_hr、after_dark 布尔值、dusk_cutoff、note)— 仅当同时提供 --start-time、--distance-mi 和 --gain-ft 时才会包含;after_dark: true 是一项必须醒目呈现的安全警告;total_hr 是往返总时长(小时)
  • bearings:航点间的导航方位(segments[] 包含 bearing_deg、distance_mi、cumulative_distance_mi;total_distance_mi)— 仅当提供 2 个或以上 --waypoint "lat,lon" 参数时才会包含
  • avalanche:NWAC 区域及供手动查询的 URL
  • peakbagger:攀登统计数据及近期攀登记录(如果提供了 peak_id)
  • counties:从登山口到山顶经过的县(counties[] 包含 county_name、county_fips、state_name、state_code);sampled 布尔值和 sample_points 整数表示是否运行了路径采样(需要 --trailhead);如果未提供 --trailhead,则仅返回山顶所在县
  • nearest_hospital:最近的医院/急诊室(hospitals[] 包含 name、lat、lon、distance_miles、emergency,以及 OSM 中提供的 phone/website/address);优先排列急诊室,然后按距离排序;最多 3 家
  • ranger_station:最近的护林站(stations[] 包含 name、lat、lon、distance_miles,以及有提供时的 phone/website/address)+ 可选的 admin_district(如果山顶坐标与 USFS 护林区重叠,则包含 district_name、forest_name、region)
  • campgrounds:约 12 mi(20 km)范围内已建成的露营地(campgrounds[] 包含 name、lat、lon、distance_miles、camp_type、backcountry、operator,以及有提供时的 website);不包括野外/高海拔营地 — 请从行程报告中提取此类信息
  • gaps:报告中注明的任何 API 失败

请与步骤 3B 并行运行 — 在同一轮回复中同时包含 fetch_conditions.py 的 Bash 命令和全部 3 个 Task 调用,以最大限度提高并行度。

步骤 3B:分派研究 Agent(并行)

在一条消息中分派 3 个研究 Agent(同时发出全部 Task 调用)。每个 Agent 负责研究指定来源,并直接获取行程报告内容。

Agent 1:PeakBagger + SummitPost

Task(
  subagent_type="general-purpose",
  model="sonnet",
  prompt="""You are a route researcher gathering mountaineering data for {peak_name}.

## Your Assignment
Research from these sources: PeakBagger, SummitPost

**Discover first (web sources):** for SummitPost, run a `site:summitpost.org` WebSearch to get exact URLs, then fetch those (don't WebFetch guessed paths).

## PeakBagger Research
1. Search: "{peak_name} site:peakbagger.com"
2. Extract route descriptions from peak page
3. List recent ascents with trip reports:
   ```bash
   uvx --with patchright --from "git+https://github.com/dreamiurg/[email protected]" peakbagger peak ascents {peak_id} --format json --with-tr --limit 20
  1. 找出包含内容的行程报告(word_count > 0)

  2. 使用以下方式获取最多 5 篇近期行程报告的内容:

    uvx --with patchright --from "git+https://github.com/dreamiurg/[email protected]" peakbagger ascent show {ascent_id} --format json
    

SummitPost 研究

  1. 搜索:"{peak_name} site:summitpost.org"

  2. 使用 WebFetch 提取:路线名称、难度、接近路线、描述、危险

  3. 如果 WebFetch 失败,请使用以下获取流程:

    # Fast path (httpx with browser-like headers, no browser)
    uv run python {repo_root}/skills/route-researcher/tools/cloudscrape.py "{url}"
    
    # If the above returns {"error": ...} or content is blocked/JS-rendered:
    uv run python {repo_root}/skills/route-researcher/tools/cloudscrape.py --render "{url}"
    
    # If --render still returns a Cloudflare challenge page, escalate (needs a display):
    uv run python {repo_root}/skills/route-researcher/tools/cloudscrape.py --render --headed "{url}"
    

行程报告提取

对于获取的每份报告,提取:

  • 日期、作者、路线状况、提及的装备
  • 危险因素(明确并分别提取):
    • 落石区域:路线上的位置、状况、提及的时间建议
    • 冰瀑/冰塔危险:位置、稳定性、黎明前/时间方面的建议
    • 冰檐危险:位置、堆积方向、避让说明
  • 地形详情(如有提及则提取):
    • 下攀路段:位置、难度、是否有下降锚点
    • 河流/溪流渡口:位置、水流状况、涉水难度
    • 水源:有名称的地点、季节性可用情况
    • 有名称的营地或露营点:名称/位置、暴露情况说明

输出格式(严格按以下 JSON 返回)

{
  "sources": ["PeakBagger", "SummitPost"],
  "route_info": [
    {"source": "...", "name": "...", "difficulty": "...", "description": "...", "hazards": [...]}
  ],
  "trip_reports": [
    {"source": "...", "date": "...", "author": "...", "url": "...", "summary": "...", "conditions": "...", "has_gpx": false,
     "rockfall": "...", "icefall": "...", "cornices": "...",
     "downclimbs": "...", "crossings": "...", "water_sources": "...", "camps": "..."}
  ],
  "gaps": ["what couldn't be fetched and why"]
}
```"""
)

Agent 2:WTA + Mountaineers + 区域来源

Task(
  subagent_type="general-purpose",
  model="sonnet",
  prompt="""You are a route researcher gathering mountaineering data for {peak_name}.

## Your Assignment
Research from these sources: WTA, Mountaineers.org, northwesthikers.net, hikeoftheweek.com, Oregon Hikers Field Guide (oregonhikers.org), Cascade Climbers (cascadeclimbers.com), Mountain Project

**Retrieval strategy — discover URLs, then fetch.** For each web source below (except mountaineers.org — use the Mountaineers MCP, see below), FIRST run a `site:` WebSearch (e.g. `"{peak_name} site:wta.org"`, `site:nwhikers.net`, `site:cascadeclimbers.com`) to collect the exact hike-page and individual trip-report URLs. THEN fetch each discovered URL through the fetching ladder. Do not WebFetch a guessed URL — enumerate real URLs first. This recovers reports that one-pass fetching loses to 403/JS blocks.

## WTA Research
1. Search: "{peak_name} site:wta.org"
2. Find the hike page and extract: trail name, difficulty, distance, elevation gain, hazards
3. Get trip reports from AJAX endpoint: {wta_url}/@@related_tripreport_listing
4. Fetch content for up to 5 recent trip reports using the fetching ladder:
   ```bash
   # Fast path first
   uv run python {repo_root}/skills/route-researcher/tools/cloudscrape.py "{trip_report_url}"

   # If output contains {"error": ...} or content is blocked/JS-rendered:
   uv run python {repo_root}/skills/route-researcher/tools/cloudscrape.py --render "{trip_report_url}"

   # If --render still returns a Cloudflare challenge page, escalate (needs a display):
   uv run python {repo_root}/skills/route-researcher/tools/cloudscrape.py --render --headed "{trip_report_url}"

Mountaineers 研究(优先使用 Mountaineers MCP——不要抓取网页)

mountaineers.org 通常会对 WebFetch/cloudscrape 返回 HTTP 403。请改用 Mountaineers MCP 工具——它们会返回结构化数据:

  1. 使用 mcp__mountaineers__search_routes(查询 “{peak_name}”)和 mcp__mountaineers__get_route 获取路线/地点页面(难度、方向、装备)。
  2. 使用 mcp__mountaineers__search_trip_reports(查询 “{peak_name}”);如果已有路线 URL,则使用 mcp__mountaineers__get_route_trip_reports 列出会员行程报告。
  3. 使用 mcp__mountaineers__get_trip_report 获取每份相关报告的正文及结构化字段(日期、作者、结果、道路/状况说明)。
  4. 只有在 MCP 不可用时才记录此缺口——mountaineers.org 通常会对 WebFetch/cloudscrape 返回 HTTP 403,因此抓取网页并非此域名的可行后备方案。

注意:Task 派发的 general-purpose Agent(即本 Agent)可以使用 Mountaineers MCP。请从 MCP 结果中提取路线信息、技术要求和危险因素。

NWHikers 研究(northwesthikers.net / nwhikers.net)

  1. 搜索:“{peak_name} site:nwhikers.net OR site:northwesthikers.net”
  2. 使用 WebFetch 提取第一人称行程报告、GPS 轨迹说明和状况
  3. 如果 WebFetch 失败,使用 cloudscrape.py "{url}"(快速路径通常足够)

Hike of the Week(hikeoftheweek.com——必须使用 --render)

  1. 搜索:“{peak_name} site:hikeoftheweek.com”

  2. 必须使用 --render 标志——该网站受 Cloudflare 保护,会拦截 WebFetch:

    uv run python {repo_root}/skills/route-researcher/tools/cloudscrape.py --render "{url}"
    # If --render still returns a Cloudflare challenge page, escalate (needs a display):
    uv run python {repo_root}/skills/route-researcher/tools/cloudscrape.py --render --headed "{url}"
    
  3. 提取:行程安排、路线叙述、进入路线的说明、登山口方向

Oregon Hikers Field Guide(oregonhikers.org——仅限俄勒冈州目标)

  1. 搜索:“{peak_name} site:oregonhikers.org”
  2. 使用 WebFetch——该网站是静态 MediaWiki HTML,适用于 WebFetch
  3. 提取:路线描述、进入路线的说明、许可证、状况说明

Cascade Climbers(cascadeclimbers.com)

  1. 搜索:“{peak_name} site:cascadeclimbers.com”
  2. 使用 WebFetch;如果被拦截,则使用 cloudscrape.py "{url}"
  3. 提取:技术路线信息、装备清单、行程报告、状况

Mountain Project(适用于技术性/岩石路段)

  1. 搜索:“{peak_name} site:mountainproject.com”
  2. 使用 WebFetch 提取:路线名称、等级、装备、描述、岩石质量
  3. 如果 WebFetch 失败,使用 cloudscrape.py "{url}"

后备方案

如果 WebFetch 无法获取任何页面,请按以下抓取顺序操作:cloudscrape.py "{url}"(快速)→ 对于由 JS 渲染或受 Cloudflare 保护的页面,使用 cloudscrape.py --render "{url}"。

行程报告提取

对于获取的每份报告,提取:

  • 日期、作者、路线状况、提及的装备
  • 危险因素(明确并分别提取):
    • 落石区域:路线上的位置、状况、提及的时间建议
    • 冰瀑/冰塔危险:位置、稳定性、黎明前/时间方面的建议
    • 冰檐危险:位置、堆积方向、避让说明
  • 地形详情(如有提及则提取):
    • 下攀路段:位置、难度、是否有下降锚点
    • 河流/溪流渡口:位置、水流状况、涉水难度
    • 水源:有名称的地点、季节性可用情况
    • 有名称的营地或露营点:名称/位置、暴露情况说明

输出格式(返回 EXACTLY 此 JSON)

{
  "sources": ["WTA", "Mountaineers", "NWHikers", "HikeOfTheWeek", "OregonHikers", "CascadeClimbers", "MountainProject"],
  "route_info": [
    {"source": "...", "name": "...", "difficulty": "...", "description": "...", "hazards": [...]}
  ],
  "trip_reports": [
    {"source": "...", "date": "...", "author": "...", "url": "...", "summary": "...", "conditions": "...", "has_gpx": false,
     "rockfall": "...", "icefall": "...", "cornices": "...",
     "downclimbs": "...", "crossings": "...", "water_sources": "...", "camps": "..."}
  ],
  "gaps": ["what couldn't be fetched and why"]
}
```"""
)

Agent 3:AllTrails

Task(
  subagent_type="general-purpose",
  model="sonnet",
  prompt="""You are a route researcher gathering mountaineering data for {peak_name}.

## Your Assignment
Research from AllTrails

## AllTrails Research
1. Search: "{peak_name} site:alltrails.com"
2. Use WebFetch to extract: trail name, difficulty, distance, elevation gain, route type, best season, hazards
3. If WebFetch fails, use:
   ```bash
   uv run python {repo_root}/skills/route-researcher/tools/cloudscrape.py "{url}"
  1. 从路线描述以及任何可见的评价/评论中提取以下信息(如有):
    • 落石区域、冰瀑/冰塔林风险、雪檐风险
    • 下攀路段、河流/溪流渡口、水源、已命名的营地

输出格式(返回 EXACTLY 此 JSON)

{
  "sources": ["AllTrails"],
  "route_info": [
    {"source": "...", "name": "...", "difficulty": "...", "distance_miles": N, "elevation_gain_ft": N, "description": "...", "hazards": [...],
     "rockfall": "...", "icefall": "...", "cornices": "...",
     "downclimbs": "...", "crossings": "...", "water_sources": "...", "camps": "..."}
  ],
  "trip_reports": [],
  "gaps": ["what couldn't be fetched and why"]
}
```"""
)

在同一条回复中包含所有 Task 调用,并行执行全部 3 个 Agent。

步骤 3C:汇总结果

Python 脚本和所有 Agent 返回后,汇总为统一的数据结构:

{
  "conditions": { /* from fetch_conditions.py */ },
  "route_data": {
    "sources": [ /* merged from all 3 agents */ ],
    "trip_reports": [ /* merged from all agents */ ]
  },
  "gaps": [ /* merged gaps from all sources */ ]
}

部分失败处理:

  • 如果任何 Agent 完全失败,则使用成功 Agent 返回的数据继续处理
  • 在 gaps 数组中注明失败的来源
  • 最低可用要求:路况数据 + 至少一个路线来源
步骤 3D:通行、许可和道路/闸门状态(内联处理)

确定许可证以及通往登山口的当前道路/闸门状态——不要只告诉用户自行查询。主动调查并报告实际状态,同时注明来源和日期。

许可证:

WebSearch: "{peak_name} trailhead access" ; "{peak_name} permit requirements"

道路/闸门状态工作流(先确定通行公路 + 林道 + 管理机构,然后按顺序检查来源;记录每个来源的 URL,以便写入报告):

  1. 州 DOT 垭口报告(WA → WSDOT):获取相关垭口页面,例如 https://wsdot.com/travel/real-time/mountainpasses/mt.-baker(SR-542)或 .../north-cascades(SR-20)。读取 RoadCondition / TravelAdvisoryActive / 限制信息。其他州:WebSearch "{state} DOT mountain pass report {highway}"。
  2. USFS 森林警报/路况:获取 https://www.fs.usda.gov/{region}/{forest-shortname}/alerts(例如 r06/mbs/alerts)和 /conditions;在页面中搜索道路编号/登山口名称 → 封闭里程桩、原因、季节性闸门。还要使用 WebSearch "{forest name} {road or trailhead} road open {year}" 搜索季节性开放公告。
  3. NPS 道路状况(如果路线位于或经过国家公园):获取 https://www.nps.gov/{park-code}/planyourvisit/road-conditions.htm(例如 noca、mora、olym)→ 各道路的开放/关闭状态和里程桩。
  4. WTA 实地信息(PNW):使用 WebSearch "site:wta.org {trail} gate road open closed {year}" 或获取徒步页面;查看最近 3-5 篇行程报告,搜索“gate”/“road open/closed”/“drove to”(如果 WTA 返回 403,使用 cloudscrape.py --render)。
  5. InciWeb 火灾封闭信息(7 月至 10 月):使用 WebSearch "inciweb {area} closure {trailhead} {year}";如果登山口附近有正在发生的事故,阅读其封闭页面。

综合为报告“道路状况”部分撰写注明日期的状态说明:

“闸门/道路状态(截至 {date}):根据 来源,{road} 为 {OPEN/CLOSED/SEASONAL GATE/UNKNOWN}。{如果关闭:闸门位于 {milepost},每程需额外行驶约 {N} 英里。}”

如果没有来源能够确认状态,请明确说明,并提供负责管理的护林站电话号码作为备用联系方式。将登山口名称、许可证、状态说明和所有来源 URL 添加到 route_data。

第 4 阶段:路线分析

目标: 分析收集到的数据,确定路线特征并综合相关信息。

步骤 4A:确定路线类型

根据路线描述、海拔和装备提及内容,将路线分类为:

  • 冰川: 提及冰裂缝、冰川行进,通常海拔 >8000ft
  • 攀岩: 技术攀登、YDS 等级(5.x)、提及保护装备
  • 攀爬: 2-4 级,需要经过暴露地形,但非技术路线
  • 徒步: 1-2 级,以步道为主,暴露地形很少
步骤 4B:综合多个来源的路线信息

目标: 将步骤 3B 调研 Agent 收集的行程报告和路线描述,以及步骤 3A 收集的路况数据,整合为全面的路线攻略。

来源优先级:

  1. 行程报告(步骤 3B Agent)——第一手体验
  2. 路线描述(步骤 3B Agent)——已发布的基础路线攻略
  3. PeakBagger/登顶数据(步骤 3A Python 脚本)——基本信息、规律

路线、关键难点和风险的综合模式:

  1. 从基准信息开始:依据路线描述(标准路线名称、公布的难度)
  2. 结合登山报告细节(地标、具体状况、实际经历)
  3. 标注冲突:登山报告与公布信息不一致时予以说明
  4. 突出共识(“多份报告提到……”)
  5. 纳入具体细节(海拔、地点、引述)
  6. **将每项具体报告的出处链接到其来源。**凡是某个细节来自特定的登山或攀登报告(日期、引述、“某支队伍发现……”、“近期一份报告指出……”),出处必须是指向该报告 URL 的 Markdown 超链接,绝不能使用纯文本。综合时保留每份登山报告的 url(以及日期/作者),以便添加链接;链接文本使用日期/作者。对于共识表述(“多份报告提到……”),在句中链接 2–3 份相关报告。只有不带特定来源的一般性/已发布的路线信息才不需要链接。

示例(路线描述):

“标准路线沿东侧山脊延伸(3 级)。2025 年 9 月的一支队伍发现,在 4,800 英尺处有一条路标堆砌清晰的徒步小径向右分岔——这才是正确的转向点;小径穿过碎石坡,他们称这段路‘乏味’且‘容易扭伤脚踝’。2025 年 10 月的一份报告指出,该路段被积雪覆盖,因此需要穿戴微型冰爪。”

将此模式应用于:

  • **路线:**采用基准结构,补充登山报告中的地标/导航信息,并注明实际用时
  • **关键路段:**描述位置/难度,补充登山报告中的评估,并说明因状况而异的情况
  • **危险:**提取登山报告中的所有危险。按类型整理,并设置明确、独立的小节——不要将落石或冰崩归入笼统的“暴露路段”:
    • **落石:**标注位置、触发因素(其他队伍/冻融/阳光照射山壁)、规避时机(黎明前通过、迅速穿过危险区)
    • **冰崩/冰塔:**标注位置、稳定性评估、规避时机(避开下午、黎明前通过)
    • **雪檐:**标注位置、安全绕行路线、形成状况(堆积方向、季节)
    • 其他危险(冰裂缝、暴露路段、路线辨识、季节性危险)分别列为独立条目
    • 内容务必全面,关系到安全;需包含具体位置和规避策略
  • **地形细节:**如果登山报告/路线信息中提及,请在报告中列出以下内容:
    • 下攀:位置、难度、是否有下降锚点
    • 河流/溪流渡口:位置、季节性水流、涉水难度
    • 水源:具名地点及各季节每天的可用情况
    • 有名称的营地/露营点:名称、位置、暴露情况;注明这些信息来自登山报告,而非露营地数据库

提取关键信息:

从所有综合数据中,识别:

  • 难度评级: YDS 等级、攀爬等级或总体难度(经行程报告验证)
  • 关键难点: 路线中最困难或技术性最强的路段(综合整理自上文)
  • 危险因素: 已识别的所有危险因素(综合整理自上文)
  • 显著装备: 行程报告或路线信息中提及的任何特殊或重要装备(应纳入相关章节,而非单独成节)
  • 起点: 名称及大致位置
  • 距离/爬升: 往返距离和海拔爬升(比较已发布数据与实际行程报告数据)
  • 时间估算: 使用 fetch_conditions.py 输出中的 conditions.time_estimates(键:fast_hr、moderate_hr、leisurely_hr、roped_hr、unroped_hr)——仅当调用时提供了 --distance-mi 和 --gain-ft 才呈现。如果 conditions 数据中没有 time_estimates,请在信息缺口中注明。填充方式: 根据第 3B 步研究得知路线距离/爬升后,使用 --distance-mi {distance} 和 --gain-ft {gain} 重新调用 fetch_conditions.py。同时添加 --start-time HH:MM 参数以获取 itinerary,并添加 --waypoint 参数以获取 bearings——这些可选输出仅在提供相应参数时启用。
  • 冰点高度分析: 比较山峰海拔与预报冰点高度:
    • 包含冰点高度警报的情况: 预报中任意一天的冰点高度距山峰海拔不到 2000 ft
    • 省略警报的情况: 整个预报期间冰点高度始终高于山峰 2000 ft 以上(典型夏季条件)
    • 示例:海拔 5,469 ft 的山峰,冰点高度为 5,000-8,000 ft → 包含警报(条件临界)
    • 示例:海拔 4,000 ft 的山峰,冰点高度为 10,000+ ft → 省略警报(远高于山顶)
第 4C 步:报告中的地理空间数据

在可用时纳入以下地理空间数据字段。报告中提及的每个地点都必须附上链接——请参见下方的链接格式。

地点/地图链接格式(根据地点的 lat/lon 和 name 构建):

  • Google Maps 地点(命名实体):https://www.google.com/maps/search/?api=1&query={URL-encoded name + address}——医院、护林站、露营地以及任何命名地点都使用此格式(而非裸坐标),确保链接指向实际地点。仅在坐标有意义时,使用 query={lat},{lon}。
  • Gaia GPS:https://www.gaiagps.com/map/?loc=14/{lon}/{lat}(缩放级别/经度/纬度)。
  • CalTopo:https://caltopo.com/map.html#ll={lat},{lon}&z=14&b=mbt。

展示规则:

  • 县: 在概览中列出 conditions.counties.counties[] 中的 county_name + state_name。为空或为 error → 列入信息缺口。
  • 紧急联系人: 根据 conditions.nearest_hospital.hospitals[] 和 conditions.ranger_station(站点 + 行政区)创建表格。为每个名称添加链接:如果有 website,链接到该网站;否则链接到以 name + address 搜索 Google Maps 的结果。始终包含电话和地址列——如果 OSM 有数据,每个条目现在都带有 phone/website/address/lat/lon;如果缺少 phone 或 address,请先尽力从该实体的官方网站或 Google Maps 列表中查找真实电话/地址,然后再填写“—”。缺失或为 error → 在信息缺口中注明。
  • 露营地: 根据 conditions.campgrounds.campgrounds[] 创建露营表格;为名称添加链接(网站或 Google Maps 地点),并根据每个条目的 lat/lon 添加 Google Maps 和 Gaia 地图链接。
  • 报告中提及的任何命名地点(营地、露营点、高营地、命名地貌、起点):至少附上 Google Maps 和 Gaia 链接,遵循上述格式。对于行程报告中提及但没有坐标的营地,请使用名称搜索 Google Maps,并尽力定位;如果无法定位,请明确说明。偏远地区营地/高营地的信息来自行程报告,而非露营地数据库。
第 4D 步:识别信息缺口

明确记录未找到或不可靠的数据:

  • 缺少登山报告
  • 没有可用的 GPS 轨迹
  • 脚本失败(天气、雪崩、日照时间)
  • 不同来源的信息相互冲突
  • 季节性数据有限
阶段 5:生成报告

目标: 调度报告撰写 Agent,创建全面的 Markdown 文档。

第 5A 步:准备数据包

将收集和分析的所有数据整理成结构化 JSON:

{
  "peak": {
    "name": "{peak_name}",
    "id": {peak_id},
    "elevation_ft": {elevation},
    "coordinates": [{latitude}, {longitude}],
    "location": "{location}",
    "peakbagger_url": "{url}"
  },
  "conditions": {
    // From fetch_conditions.py output
    "weather": {"forecast": [{"date": "...", "snow_line_note": "...", "near_summit": bool, "freezing_level_ft": N, ...}], ...},
    "air_quality": {...},
    "daylight": {"astronomical_dawn": "...", "nautical_dawn": "...", "civil_twilight": "...", "sunrise": "...", "sunset": "...", "civil_dusk": "...", "nautical_dusk": "...", "astronomical_dusk": "...", "daylight_hours": N},
    "avalanche": {...},
    "peakbagger": {...},
    "counties": {"counties": [{"county_name": "...", "county_fips": "...", "state_name": "...", "state_code": "..."}], "sampled": bool, "sample_points": N},  // sampled + sample_points only present when --trailhead was given
    "nearest_hospital": {"hospitals": [{"name": "...", "lat": N, "lon": N, "distance_miles": N, "emergency": "yes|null", "phone": "...", "website": "...", "address": "..." /* phone/website/address optional */}]},
    "ranger_station": {"stations": [{"name": "...", "lat": N, "lon": N, "distance_miles": N, "phone": "...", "website": "...", "address": "..." /* optional */}], "admin_district": {"district_name": "...", "forest_name": "...", "region": "..."}},
    "campgrounds": {"campgrounds": [{"name": "...", "lat": N, "lon": N, "distance_miles": N, "camp_type": "...", "operator": "...", "website": "..." /* optional */}], "note": "..."},
    "time_estimates": {"roped_hr": N, "unroped_hr": N, "fast_hr": N, "moderate_hr": N, "leisurely_hr": N, "note": "..."},
    "itinerary": {"start_time": "HH:MM", "summit_eta": "HH:MM", "turnaround_by": "HH:MM", "return_eta": "HH:MM", "total_hr": N, "after_dark": false, "dusk_cutoff": "9:15 PM" /* 12-hr AM/PM format, unlike other time fields */, "note": "..."},
    "bearings": {"segments": [{"from": 0, "to": 1, "bearing_deg": N, "distance_mi": N, "cumulative_distance_mi": N}], "total_distance_mi": N}
  },
  "route_data": {
    // Merged from all Researcher agents
    "sources": [...],
    "trip_reports": [...]
  },
  "analysis": {
    // From Phase 4
    "route_type": "{hike|scramble|technical|glacier}",
    "difficulty": "{rating}",
    "crux": "{description}",
    "hazards": [...],
    "access": {...}
  },
  "gaps": [...]
}
第 5B 步:调度报告撰写 Agent
Task(
  subagent_type="general-purpose",
  model="sonnet",
  prompt="""You are a Report Writer generating a mountaineering route report.

## Instructions

1. **Read the report template:**
   Use the Read tool to read: {repo_root}/skills/route-researcher/assets/report-template.md

2. **Generate report following template structure exactly:**
   - Header with peak name, elevation, location, date
   - AI disclaimer (prominent safety warning)
   - Overview: route type, difficulty, distance/gain, time estimates
   - Route Description: synthesized from sources, include landmarks
   - Crux: describe hardest section with specifics
   - Known Hazards: comprehensive list
   - Current Conditions: weather forecast, freezing levels, air quality, daylight
   - Trip Reports: links organized by source with dates
   - Information Gaps: explicitly list missing data
   - Data Sources: links to all sources used

3. **Markdown Formatting Rules:**
   - ALWAYS add blank line before lists
   - ALWAYS add blank line after section headers
   - Use `-` for bullets (not `*` or `+`)
   - Use `**text**` for bold emphasis
   - Break paragraphs >4 sentences
   - **Link specific-report attributions.** Any statement attributed to a particular trip/climb report (a date, a quote, "one party…", "a recent report…") MUST be a Markdown link `[date/author](report_url)` to that report's source URL — never plain-text attribution. Pull the URL from the matching `trip_reports[].url` in the data package. Leave only generic/published beta (no specific source) unlinked.

4. **Save the report:**
   Use the Write tool to save to the user's current working directory: {date}-{peak-name-slug}.md

## Data Package

{data_package_json}

## Output Format (return EXACTLY this JSON)
```json
{
  "status": "SUCCESS",
  "file_path": "/absolute/path/to/report.md",
  "filename": "YYYY-MM-DD-peak-name.md",
  "sections_generated": N
}
```"""
)
第 5C 步:记录报告文件路径

从 Agent 的 JSON 响应中提取 file_path,供阶段 6 使用。

阶段 6:报告审查与验证

目标: 调度报告审查 Agent,验证报告质量。

第 6A 步:调度报告审查 Agent
Task(
  subagent_type="general-purpose",
  model="opus",
  prompt="""You are a Report Reviewer validating a mountaineering route report.

## Instructions

1. **Read the report:**
   Use the Read tool to read: {report_file_path}

2. **Perform systematic quality checks:**

   **Factual Consistency:**
   - Dates match their stated day-of-week (e.g., "Thu Nov 6, 2025" is actually Thursday)
   - Coordinates, elevations, distances consistent across all mentions
   - Weather forecasts align logically (freezing levels match precipitation types)

   **Mathematical Accuracy:**
   - Elevation gains add up correctly
   - Time estimates reasonable given distance and elevation gain
   - Unit conversions correct (feet to meters, etc.)

   **Internal Logic:**
   - Hazard warnings align with route descriptions
   - Recommendations match current conditions
   - Crux descriptions match overall difficulty rating

   **Completeness:**
   - No placeholder texts like {{peak_name}} or {{YYYY-MM-DD}}
   - All referenced links actually provided
   - Mandatory sections present: Overview, Route, Current Conditions, Trip Reports, Information Gaps, Data Sources

   **Formatting:**
   - Markdown headers properly structured
   - Lists have blank lines before them
   - Tables properly formatted

   **Safety & Responsibility:**
   - AI disclaimer present and prominent
   - Critical hazards properly emphasized
   - Users directed to verify information from primary sources

   **Emergency contacts & location links (verify INDEPENDENTLY):**
   - Each emergency contact (hospital, ranger station) has a working name link (website or a Google Maps place link to the actual entity — NOT bare coordinates), a phone, and an address. Independently confirm the phone/address look right for that named entity (e.g. via its official site / Google Maps); fix or flag mismatches and fill blanks you can confirm.
   - Road/gate status is a dated statement with a cited source, not a "go check it yourself" punt.
   - Every named place in the report (campsite, bivy, high camp, trailhead, named feature) carries map links (Google Maps + Gaia GPS). Flag any named location missing links.
   - Specific trip-report attributions are hyperlinks to their source, not plain text.

3. **Fix issues:**
   - **Critical** (safety errors, factual errors, missing disclaimers): MUST fix using Edit tool
   - **Important** (completeness, consistency): SHOULD fix
   - **Minor** (formatting, polish): FIX if quick

## Output Format (return EXACTLY this JSON)
```json
{
  "status": "PASS" | "PASS_WITH_FIXES" | "FAIL",
  "issues_found": N,
  "fixes_applied": ["description of fix 1", "description of fix 2"],
  "remaining_issues": ["issues that couldn't be fixed"],
  "report_path": "/absolute/path/to/report.md"
}
```"""
)
第 6B 步:处理验证结果

处理审查 Agent 的响应:

  • PASS 或 PASS_WITH_FIXES: 使用 report_path 继续阶段 7
  • FAIL: 向用户展示 remaining_issues 并询问如何处理

报告审查器会自动修复问题并返回修正后的文件路径。

阶段 7:完成

目标: 告知用户任务已完成及后续步骤。

向用户报告:

  1. 成功消息:“{Peak Name} 的路线研究已完成”
  2. **文件位置:**生成的报告的完整绝对路径
  3. **摘要:**简要概述,2–3 句话:
    • 路线类型和难度
    • 主要危险或注意事项
    • 任何重要的信息缺口
  4. **后续步骤:**建议用户:
    • 查看报告
    • 从一手来源核实关键信息
    • 尝试路线前查看当前状况
    • **行程安排与导航:**如果用户需要出发时间安排和/或罗盘方位,请使用 --start-time HH:MM(添加 itinerary 键)和/或 --waypoint lat,lon 标志(2 个或更多航点会添加 bearings 键)重新运行 fetch_conditions.py。将 after_dark: true 作为醒目的安全警告指出。
    • **登顶后登山报告:**登山后,可提供 skills/route-researcher/assets/trip-report-template.md 中的登山报告模板,作为撰写登山报告的起点。

完成消息示例:

Route research complete for Mount Baker!

Report saved to: 2025-10-20-mount-baker.md

Summary: Mount Baker via Coleman-Deming route is a moderate glacier climb (Class 3) with significant crevasse hazards. The route involves 5,000+ ft elevation gain and typically requires an alpine start. Weather and avalanche forecasts are included.

Next steps: Review the report and verify current conditions before your climb. Remember that mountain conditions change rapidly - check recent trip reports and weather forecasts immediately before your trip.

错误处理原则

执行过程中,请遵循以下错误处理指南:

脚本失败
  • **不要因此中断:**如果 Python 脚本失败,请在“信息缺口”中记录并继续
  • **提供替代方案:**附上手动检查链接(Mountain-Forecast.com、NWAC.us)
  • **最多重试一次:**网络超时时重试一次,然后继续
缺失数据
  • **明确说明:**始终记录未找到的内容
  • **提供帮助:**提供手动检查链接
  • **不要猜测:**切勿编造数据来填补缺口
搜索失败
  • **尝试不同写法:**如果未找到山峰,尝试其他名称(Mt 与 Mount)
  • **询问用户:**如果仍未找到,请用户澄清或提供直接 URL
  • **提供指导:**建议用户如何手动搜索 PeakBagger
WebFetch/WebSearch 问题
  • **获取顺序:**先用 WebFetch → cloudscrape.py "{url}"(快速 httpx,不使用浏览器)→ cloudscrape.py --render "{url}"(通过 Patchright 使用真正的 Chrome;等待 Cloudflare 验证挑战结束;对于难以处理的页面,如果有显示器可用,则添加 --headed)
  • **何时使用 --render:**用于 hikeoftheweek.com,以及默认方式在标准输出中返回 {"error": ...} 或内容被屏蔽/由 JavaScript 渲染的任何网站
  • **优雅降级:**缺少一个来源不应导致整个研究停止;cloudscrape.py 失败时会以 0 退出
  • **记录缺口:**注明哪些来源不可用(WebFetch 和所有 cloudscrape.py 路径均失败:快速路径、--render、--render --headed)
  • **安全优先:**如果无法获取关键安全信息(雪崩、危险),请在缺口部分重点说明

执行超时

  • 单个 Python 脚本: API 调用限时 30 秒;提供 --peak-id 时最长 120 秒(peakbagger-cli)
  • WebFetch 操作: 使用默认超时
  • WebSearch 操作: 使用默认超时
  • 技能总执行时间: 目标为 3–5 分钟;综合研究时最长可接受 10 分钟

质量原则

每份生成的报告都必须:

  1. ✅ 在顶部醒目地包含安全免责声明
  2. ✅ 明确记录所有信息缺口
  3. ✅ 附上带链接的来源
  4. ✅ 在文件名和元数据中使用当前日期
  5. ✅ 严格遵循模板结构
  6. ✅ 提供可执行的信息(距离、时间、装备)
  7. ✅ 强调核实信息——这是研究结果,不是绝对准则

实现说明

详细的执行流程、组件概览、数据契约和设计决策,请参阅 skills/route-researcher/docs/architecture.md。

peakbagger-cli 命令参考(v1.10.0,git 源码)

需要 patchright + Google Chrome。 peakbagger-cli 在自身的 uvx 环境中运行,并使用 Patchright 浏览器绕过 Cloudflare,因此每次调用都必须包含 --with patchright。若不包含此参数,命令会因 Error: patchright is required to bypass Cloudflare 而中止。

所有命令都使用 --format json 获取结构化输出。运行方式:

uvx --with patchright --from "git+https://github.com/dreamiurg/[email protected]" peakbagger <command> --format json

可用命令:

  • peak search <query> - 按名称搜索山峰
  • peak show <peak_id> - 获取山峰详细信息(坐标、海拔、路线)
  • peak stats <peak_id> - 获取登顶统计数据和时间模式
    • --within <period> - 按时间段筛选(例如,'1y'、'5y')
    • --after <YYYY-MM-DD> / --before <YYYY-MM-DD> - 日期筛选
  • peak ascents <peak_id> - 列出个人登顶记录及行程报告链接
    • --within <period> - 按时间段筛选(例如,'1y'、'5y')
    • --with-gpx - 仅显示带 GPS 轨迹的登顶记录
    • --with-tr - 仅显示带行程报告的登顶记录
    • --limit <n> - 返回的登顶记录上限(默认值:100)
  • ascent show <ascent_id> - 获取登顶记录详细信息

注意: 要查看完整命令选项,请运行 peakbagger --help 或 peakbagger <command> --help

山峰名称变体

初次搜索失败时,可尝试以下常见变体:

  • 颠倒词序: "Mountain Pratt" → "Pratt Mountain","Peak Sahale" → "Sahale Peak"
  • 扩展名称: "Mt" → "Mount","St" → "Saint"
  • 添加地点: "Baker, WA" 或 "Baker, North Cascades"
  • 移除名称前缀: 使用 "Baker" 代替 "Mt Baker"
  • 组合变体: 尝试颠倒词序并扩展名称(例如,"Mountain Pratt" → "Pratt Mount" + "Pratt Mountain")
导航地图链接
山顶坐标链接

以山顶为中心,使用十进制度数,为报告的概览行构建以下链接。

Google Maps:

https://www.google.com/maps/search/?api=1&query={latitude},{longitude}

CalTopo(MapBuilder Topo 底图,缩放级别 14):

https://caltopo.com/map.html#ll={latitude},{longitude}&z=14&b=mbt

Gaia GPS(顺序为缩放级别/经度/纬度):

https://www.gaiagps.com/map/?loc=14/{longitude}/{latitude}

PeakVisor 徒步地图(缩放级别/纬度/经度;查询参数中的斜杠需 URL 编码为 %2F):

https://peakvisor.com/hiking-map?custom=14%2F{latitude}%2F{longitude}#14/{latitude}/{longitude}

示例(Mt Baker,48.7768,-121.8144):

  • Google Maps:https://www.google.com/maps/search/?api=1&query=48.7768,-121.8144
  • CalTopo:https://caltopo.com/map.html#ll=48.7768,-121.8144&z=14&b=mbt
  • Gaia GPS:https://www.gaiagps.com/map/?loc=14/-121.8144/48.7768
  • PeakVisor:https://peakvisor.com/hiking-map?custom=14%2F48.7768%2F-121.8144#14/48.7768/-121.8144

注意: 使用十进制度数。Gaia GPS 的顺序为缩放级别/经度/纬度;CalTopo 和 PeakVisor 的顺序为纬度/经度。

登山口 Google Maps 链接

如果有坐标(例如,来自 Mountaineers.org 的地点信息):

https://www.google.com/maps/search/?api=1&query={latitude},{longitude}

示例:https://www.google.com/maps/search/?api=1&query=48.5123,-121.0456

如果只有登山口名称:

https://www.google.com/maps/search/?api=1&query={trailhead_name}+{state}

示例:https://www.google.com/maps/search/?api=1&query=Cascade+Pass+Trailhead+WA

注意: 有坐标时优先使用坐标,以获得更精确的位置。


技能版本: 5.1.0 | 最后更新: 2026-05-22

评论

支持 Markdown