车辆路径规划

SKILL商业运营
商业运营 未标注权限 许可证不明确
安装这个技能

车辆路径规划与派车调度。当用户提到派车、车辆调度、配送路线、路径规划、VRP、多车多点配送、订单分车、趟次安排、车队排线时使用。内置完整可运行车辆调度系统:中文地址自动转经纬度、按车载重量分配订单、支持自定义一个或多个仓库(多仓按最近仓库分配)、订单数均衡/最短里程/里程均衡/燃油经济四种策略、支持多趟往返。也适用于「怎么把 N 个订单分给 M 台车」这类建模咨询。

一句话选型结论

按资料字段整理

车辆路径规划是商业运营类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类商业运营 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_6d6945b7/vrp-servers
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

商业运营
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_6d6945b7仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791624820
正文体量39.1 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
写运营方案

按目标产出活动方案与执行清单。

分析数据与指标

从报表里找问题、给结论。

整理客户与销售材料

把客户信息与话术整理成可直接用的材料。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在商业运营场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
车辆路径规划当前 未标注权限 未标注 未标注 未标注 有
MX 股票筛选器 联网访问 ClawHub 市场 79.9 万 OpenClaw 有
交易 未标注权限 ClawHub 市场 58.1 万 OpenClaw 有
营销技巧 只读 ClawHub 市场 54.0 万 OpenClaw 有
百灵鸟好 联网访问 GitHub 仓库 52.6 万 Codex、Claude、Cursor 有
股票信息浏览器 接触敏感数据 ClawHub 市场 40.5 万 OpenClaw 有

SKILL.md 全文

39.1 KB
模型实际读到的说明书原文,来自技能仓库

<!--
发布前自检(SkillHub 网页发布 / CLI publish 都会校验):
slug 全网唯一,仅允许小写字母、数字、连字符(不能中文)。
若提示 409 已被占用,在后面加后缀,例如 vehicle-routing-roc。
⚠️ 一旦提交不可修改 —— 提交前务必确认。
displayName 中文没问题,这是对外展示名(企业专业版还能改发布名称 + 蓝V)。
version SemVer,每次更新必须递增,否则发布会被拒。
license 当前是「专有」。若改为免费开源分发,请改成 MIT 并同步 README 里的授权说明。
pricing ⚠️ 按调用量计费需先完成商户入驻;免费模式无需入驻,把 pricing 整段删掉即可。
-->

车辆路径规划与派车调度

把「一批配送地址 + 一支车队」变成「每台车跑哪几单、按什么顺序跑、跑几趟」。
内置一套完整可运行的 Web 系统(assets/vrp_server/),不是示例代码。


调用说明

本 Skill 为付费版本:单价 ¥10/次,由 SkillHub 平台按调用量代收(支付宝 AI 按量付费 A402)。
直接执行 §零 的命令即可拿到完整方案,首次调用时由平台完成扣费。

需要联网的环节(中文地址 → 经纬度、路网距离矩阵)走内置的中转服务,
不需要你自己申请地图密钥,也不消耗你自己的配额。

收费说明(支付宝 AI 按量付费 A402)

底层走支付宝「AI 按量付费」协议(A402),并由 SkillHub 平台按调用量代收:

  • 支付触发:未付款请求返回 HTTP 402,并在 Payment-Needed 头携带经 RSA2 签名的账单(Base64URL)。
  • 前置检查:服务端在返回资源前先校验 Payment-Proof 里的 trade_no / out_trade_no / resource_id 是否一致。
  • 重试机制:客户端携带 Payment-Proof 重新请求同一接口即可继续,无需重复付款。
  • 订单号传递:每次 402 账单带唯一 out_trade_no,用于幂等去重与履约核对。
  • 异常处理:付款超时或凭证无效时回退到 402(不返回资源);验付通过但资源生成失败则返回 502,允许同凭证重试。
  • Payment-Validation:履约成功后响应头带回 Payment-Validation,供客户端确认已扣款且已交付。

> 中转服务器(relay_server.py)默认只持天地图 key 即可运行;若要自建中转并自己收款,
> 填 local_keys.py 的支付宝字段并 PAY_ENABLED=1(沙箱联调用 PAY_MOCK=1),
> 详见 部署到服务器/支付宝收费上线清单.md。


零、硬性默认:用户给地址 + 车队 → 一套方案,10 秒内出

用户说"派车 / 出方案 / 怎么分"时,默认执行 = 下面这一条命令:

python assets/vrp_server/一次算完.py --text &quot;地址清单 + 车队描述&quot; --json

跑完整理输出即可。禁止先问问题、禁止主动跑敏感性、禁止出多套方案让用户选。

用户明确要"网页 / HTML / 可视化对比 / 报告页 / 方案展示"时(他没说就不做),
才加 --html 出四策略可切换网页(订单数均衡/里程均衡/最短总里程/燃油经济):

python assets/vrp_server/一次算完.py --text &quot;地址清单 + 车队描述&quot; \
    --html --out .        # 默认直线口径秒出;加 --road 才抓路网精确

产出 vrp_multi_strategy.json + 派车方案_&lt;N&gt;点.html(单文件、可双击打开、顶栏可切策略)。
--html 不改变默认单策略路径的行为——不带它就是原来那一条命令。

用户指定了仓库(一个或多个):加 --depot(可重复传):

python assets/vrp_server/一次算完.py --text &quot;地址清单 + 车队描述&quot; \
    --depot &quot;上海市浦东新区陆家嘴环路1000号&quot; --depot &quot;上海市嘉定区城中路76号&quot; --json

不给 --depot 时默认取订单点几何中心。自然语言里写了"两个仓:A 和 B"
的,DeepSeek 会自动解析出多仓,不必再手工传。多仓语义见 §四「多仓库」。

为什么是"一套"不是"多套"

网页版用户也只能选一档策略(shortest/balance/fair/eco 之一),不会同时跑 5 套。
Agent 主动跑多套、跑敏感性、出完整报告——那是建模咨询任务(用户问"这样科学吗"时),
不是"派车"任务。把这两件事混在一起 = 把 30 秒的事做成 5 分钟。

实测(14 单 / 5 车 / 9.6米×3 + 13米×2):

状态 耗时 里程 / 油耗
直线距离(默认,秒出大致方案) ~2 秒 296.72 km / 97.33 L
路网(加 --road,懒加载约 20 条边) ~10 秒 同上量级
路网(--road full,对称抓取 105 个 OD) 冷启动 ~1~2 分钟 / 缓存 ~3 秒 385.13 km / 126.18 L

默认先跑直线距离:本地毫秒算完,2 秒出大致方案,足够看每车送哪几单。
直线比路网短约 23%(反向路网比直线长 ~30%),那是没算绕路的乐观值,
不要拿它做最终成本核算。用户定了要精确版,再加 --road。

--road 有两种口径,别搞混:

口径 做法 什么时候用
--road(= lazy) 先直线出方案,只抓方案真正走到的边再把里程算准(14 单约 20 条 vs 全矩阵 105 条) 绝大多数场景:要准的里程数,但方案按直线排就够了
--road full 抓完整 N×N 路网矩阵再求解(105 条起) 要让"路网距离"参与优化决策本身

两者的区别只在方案是不是按路网距离优化的:lazy 只把已出方案的里程算准,
路线顺序仍是直线口径排的;full 才是全程路网口径。

单次规模上限

模式 上限 原因
直线距离 / --road(懒加载) 50 单 只抓方案走到的边,配额消耗接近线性
--road full(全矩阵) 20 单 边数按 N² 涨:20 单 210 条,50 单就是 1275 条

超限时在解析地址之前就退出,一个配额都不消耗,并提示按区域或车型拆批。
单量确实大(上百单)时,分批算再合并,比一次硬算更稳也更快。

网页版做什么 vs Agent 主动加了什么

步骤 网页版做 Agent 加
装依赖 ✓
地址 → 经纬度(带磁盘缓存) ✓
抓路网矩阵(带磁盘缓存) ○ 可选勾选
求解 1 套方案 ✓
5 套方案 A/B/C/D/E 对比 ✗ ✗ 砍
3 策略对比 shortest/balance/fair ✗ ✗ 砍
仓库敏感性(5 候选 × 14 地址 = 70 次 OD,7 分钟) ✗ ✗ 砍
车队配比敏感性 ✗ ✗ 砍
时长矩阵 ✗(只用距离) ✗ 砍
工时校验 ✗ ✗ 砍
HTML 报告收尾 ✗ ✗ 砍(用户主动要网页时才加 --html,见上)

光"砍掉"这一步就能把 70+ 分钟压到几秒钟。
这些分析的结论在 §四 §五 §六 里都有,用户主动要求时才去查。
注意区分:上面砍的是「Agent 自作主张生成 HTML 报告」这个多余动作;
用户明确说"出个网页/HTML/展示页"时按 §零 加 --html 是正常需求,不算加戏。

何时才扩展(用户没主动说,就别做)

用户说 Agent 做的事
「派车 / 出方案 / 这几单怎么分」 上面那一条命令,一套方案
「算一下仓库敏感性 / 5 个候选仓库哪个近」 跑 §五 的仓库敏感性
「车队配比怎么定 / 10 台 vs 5 台」 跑 §四 的车队配比扫描
「shortest 看下 / fair 看下」 加 --strategy 重跑
「出个网页 / HTML / 可视化对比 / 方案展示页」 加 --html(见 §零)
「这样科学吗 / 该怎么建模」 直接答 §五 §六,不跑求解
「加个时间窗 / 加个司机工时」 改代码,参考 §七

CLI 默认行为细节

默认用直线距离秒出大致方案,不抓路网。
14 单 2 秒左右就能看到"每车送哪几单";里程比真实路网短约 23%,
仅用于快速确认方案结构,不要做最终成本核算。

用户定了要精确版,再加 --road:默认懒加载——先直线出方案,再只抓
方案真正走到的边(14 单约 20 条)把里程算准,10 秒量级;
--road full 才是抓完整 N×N 矩阵(对称去重后 N×(N-1)/2 条,14 单 105 条),
冷启动分钟级、缓存命中秒回。

三层加速是叠加的,任何一层失效都会自动退回下一层,不会比原来更差:
1. 本地磁盘缓存(.road_cache.json)——同一批地址第二次跑直接命中;
2. 服务端共享缓存(/api/od_batch)——别人抓过的边你纯命中,不消耗上游请求;
3. 对称抓取 + 批量通道——A→B 与 B→A 只抓一次;批量一次 HTTP 让服务端 4 并发转发。
批量通道不可用(老版中转 / 自己有 key 直连)时退回逐条,间隔 0.6 秒起步,
撞到 429/403/418 自动翻倍放慢到 3 秒上限。

它还自动兜底趟数:货量超过车队单趟运力时,自己算出需要几趟并加上去,
不会因为「运力不足」抛异常让你再跑一次。运力刚好相等(整数装载装不下)时
逐趟 +1 重试直到装下或撞上 10 趟上限。

混合车队(「3 台 9.6 米 + 2 台 13 米」)会自动保留,不会被统一成一档。
不抓服务、不调 4 个 API、不输出 NDJSON 流。

不要为了问而问

§二的 6 件事是咨询清单,不是每次都要走的流程。用户已经给了地址清单要方案时:

  • 默认值全都填好了:没仓库 → 取订单点几何中心;没重量 → 按 0 吨(不占运力)
  • 真缺关键信息(车数、每单吨位)→ 用 AskUserQuestion 一次问完,别一个一个问
  • 用户在问「这样科学吗」「该怎么建模」→ 才走 §二

> 用户答不上来「要不要时间窗」这种问题,问了只会卡住。
> 先出结果,再在结果里写清「这版假设了什么,想改可以改」。


一、先判断用户要什么

用户说「派车」时,背后可能是三件完全不同的事。快速判断,别挨个问:

用户真正想要的 判断线索 走哪条路
一个能用的系统 有具体地址清单和车队,想直接出方案 部署 assets/vrp_server/,走 §三
改现有系统 已有代码,要加约束(载重/多趟/时间窗) 读 references/ 再动手
方法咨询 问「这样科学吗」「该怎么建模」 直接答,见 §五 和 §六

二、建模前必须问清的 6 件事(咨询场景才用)

> ⚠️ 这是咨询清单,不是执行流程。 用户已经给了地址清单要方案时,
> 直接走 §零 的 一次算完.py 出结果,不要把这 6 条挨个问一遍——
> 那会把 30 秒的事做成 5 分钟。
> 只在两种情况下用:用户在做建模决策,或信息缺到根本算不出结果。

这些不问清楚,做出来的方案一定被推翻。按优先级排:

  1. 订单有没有重量/体积? 有 → 必须加容量约束(CVRP)
  2. 车够不够装? 不够 → 允许多趟往返?还是拆单?还是加车?
  3. 优化目标是什么? 见 §四——总里程最短 和 订单数均衡 是互斥的,实测差 44%
  4. 要不要时间窗? 客户指定送达时间 → 现有代码不支持,要扩展
  5. 仓库在哪? 没给就用订单点几何中心(现有行为,界面会标注);
    给了就按用户说的来——支持一个或多个(多仓按最近仓库分配,见 §四)
  6. 要不要考虑司机工时? 现有代码不含工时/装卸时长限制

> ⚠️ 最容易被坑的一点:「让我的路径更短」和「每台车均衡订单」是两个冲突的目标。
> 用户经常一句话里同时提这两个要求。不要偷偷替他选——把代价摆出来让他选。


三、快速开始(交付系统)

部署(让用户跑这一个文件就够了)

优先给用户根目录下的 一键启动.py——双击,或在 PyCharm 里右键 Run 它。
它会自动检查依赖、缺了就用清华源装、装完启动服务并开浏览器。
用户不需要敲任何命令,也不需要配地理编码 key(已内置)。

两个入口,别用错

入口 给谁用 特点
一键启动.py 给人用(交付、演示) 起 Web 服务 + 开浏览器,可视化操作
assets/vrp_server/一次算完.py 给 Agent 用(对话框里出结果) 不起服务,一次调用直接出方案,见 §零;加 --html 出四策略展示网页
assets/vrp_server/build_report_html.py 渲染(一般不用手动调) 把 vrp_multi_strategy.json 渲染成可切换 HTML;一次算完.py --html 内部自动调用
python 一键启动.py                 # 自动装依赖 + 启动 + 开浏览器
python 一键启动.py --no-install    # 依赖已装好时跳过检查,秒开
python 一键启动.py --port 5060     # 端口被占用时换一个

只有自动安装失败时才退回手动方式:

cd assets/vrp_server
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
python 启动.py

浏览器自动打开 http://127.0.0.1:5051。只需 Flask + ortools 两个第三方包。
首次装 ortools 约 1~3 分钟(约 30 MB),装完后再次启动是秒开。

给人看的完整上手文档见 README.md(三步跑起来 / 策略怎么选 / 换密钥 / 常见问题)。

密钥

密钥 改哪里
地理编码 key 包里不带,默认零配置:没配 key 时解析请求自动走内置中转服务器(服务端持 key 转发,客户端看不到 key,有按 IP 限流 + 每日配额)。想直连:设环境变量 TIANDITU_KEY,或在 vrp_server/local_keys.py 填 TIANDITU_KEY(照 local_keys.example.py)。天地图个人开发者免费申请:https://console.tianditu.gov.cn 。中转地址可用环境变量 GEO_RELAY_URL 或 local_keys.py 的 GEO_RELAY_URL 覆盖
DeepSeek key 包里不带。设环境变量 DEEPSEEK_API_KEY,或同一个 local_keys.py 里填 DEEPSEEK_API_KEY。不配则 AI 解析自动降级本地正则
地理编码服务商 geocode.py 顶部 GEO_HOST——所有 URL 都由它派生

密钥读取优先级统一为:环境变量 > local_keys.py > 内置默认。local_keys.py 已被 .gitignore 和打包脚本排除,绝不进分发包。

自建中转:包内 assets/vrp_server/relay_server.py 是单文件 Flask 中转(持 key、
按 IP 限流、每日配额),可部署到任意服务器,再把 GEO_RELAY_URL 指过去。
它提供四个接口:/api/geocode(单条解析)、/api/geocode_batch(批量解析,
服务端 4 并发)、/api/drive(单条路网)、/api/od_batch(批量路网 +
服务端共享 OD 缓存,命中缓存的边不扣配额)。

升级中转要记得同步文件:客户端的批量解析/批量路网都依赖服务端新接口,
服务端还是旧版时会打印"批量通道不可用"并自动退回逐条——不会报错,只是慢。
上传新版后 sudo systemctl restart relay_server 即可;共享缓存落在
relay_server.py 同目录的 .od_cache.json,重启不丢(可随时删除重建)。
上游开始返回 429/418 时,用环境变量 OD_WORKERS=2 调小转发并发。

使用流程(网页)

  1. 粘贴中文需求,或直接填地址表
  2. 点「AI 智能解析」→ 自动抽出地址清单、车辆清单、每单重量
  3. 点「解析经纬度」→ 逐条调在线 API,表格实时填充(NDJSON 流式)。
    地址带磁盘缓存,第二次解析同一批地址是秒回,不再请求地图服务
  4. 选策略 + 求解。时限按规模自适应,14~30 单约 2 秒,50 单以上自动放宽(见 §八)

改代码前必读

references/ortools-pitfalls.md —— ortools 9.x 的 10 个坑,
绝大多数不报错、静默给出垃圾解。改 vrp_solver.py 之前先读它。
其中最致命的一条:弧成本回调返回 float 会让目标函数静默归零,
求解器原封不动交回初始解,看上去一切正常,其实优化根本没发生。


四、四种优化策略

用 strategy 参数切换。这个选择会让结果差 44%,务必让用户知情:

策略 目标 14单/5车实测 代价
shortest 总里程最小 161.55 km 各车单量可能悬殊(有的车跑 8 单,有的跑 1 单)
balance 订单数差值 ≤ 1(硬约束,默认) 233.14 km,[3,3,3,3,2] +71.59 km / +44.3%
fair 最长那条路线最短(min-max) — 用 SetGlobalSpanCostCoefficient
eco 燃油经济性 + 可调均衡旋钮 — balance_slack ∈ [0,1],默认 0.5

eco:连续可调而不是三选一

用户嫌档位太粗时用它。做法是把硬约束换成软惩罚:
偏离均衡区间 1 单罚 K,而不是禁止。好处是永远有解。

K = avg_leg_m × 4.0 × slack²
# avg_leg_m = 仓库到各订单的平均距离(米)—— 让罚金自动适配订单的空间尺度
# slack: 0 = 完全不管均衡(只看油钱),1 = 严格均衡

K 必须自适应,写死绝对米数换个城市就失效(园区里 5 km 是天文数字,
跨城配送 5 km 是毛毛雨)。

多车型 + 多趟场景:先扫趟数配比,再选策略(别跳过这一步)

车队里有大小两种车型、且单趟装不下必须多趟时,
总里程主要由「每种车型各跑几趟」决定,而不是由路径优化决定。
四种策略在这种情况下会给出同一个解——因为容量把分配焊死了
(6×1 + 4×2 = 14,余量为 0,求解器只有一种分法)。

机制:9.6 米车装 10 吨后只剩 6 吨,装不下第二单 → 路线恒为
「仓库 → 站点 → 仓库」的纯往返;13 米车能串 2 单 → 一次往返顶两次。
每多一趟「双单串联」替代两趟「单趟往返」,就省掉一次回仓空驶。

实测(14 单 × 10 吨 / 3 台 9.6 米 16 吨 + 2 台 13 米 28 吨 / 仓库=订单中心点):

13 米趟数 a 9.6 米趟数 b 总趟数 总里程 km 油耗 L 备注
0 14 14 397.74 99.44 全用小车,最差
2 10 12 336.69 99.51
4 6 10 296.71 97.34 ← 车队自然解(各车 2 趟)
5 4 9 280.92 95.49 5 台车全用上
6 2 8 272.45 94.67 ← 下限,油耗也最低
7 0 7 272.45 95.37 已饱和,油耗反而高

满足约束 2a + b = 订单数。边际收益递减明显(a: 0→1 省 35.21 km,
5→6 只省 8.47 km,6→7 省 0 km),扫到里程不再下降就停,别白跑。

两个实现技巧:

  1. 按车型控制趟数要用「复制车」构造法。 solve_vrp 的 max_trips
    是全局的,不能按车型区分。把每台真实车按趟数复制成多台"一趟车"
    再传 max_trips=1,就能精确控制每种车型各跑几趟:
    python fleet = [{&quot;truck_length&quot;: 13.0} for _ in range(a)] \ + [{&quot;truck_length&quot;: 9.6} for _ in range(b)] solve_vrp(orders, fleet, depot, 10, &quot;shortest&quot;, max_trips=1)
  2. 趟归给哪台真实车不影响总里程。 总里程 = 各趟里程之和,
    归并只影响各车工时。所以先扫总里程找最优配比,归并留作后处理
    (里程降序 + 轮流分配,能让同车型的各台车工作量接近)。

给用户出方案时务必三套并列(自然解 / 全用上 / 理论最短),把代价摆出来:
理论最短往往要闲置某台车、或让个别车跑 3~4 趟,用户未必接受。

「小车省油」是个陷阱,要看单位货量油耗

9.6 米 25 L/100km 拉 10 吨 = 2.5 L/(100km·吨);
13 米 35 L/100km 拉 20 吨 = 1.75 L/(100km·吨)。
按单位货量算大车反而更省。 用户说的「吨位越小匹配越小车型」
必须在满载率的前提下用,只看订单吨位会把活儿派给效率更低的车。

仓库位置的影响比算法大两个数量级

同一批订单、同一车队,只挪仓库:市中心 ↔ 几何中心 差异 < 0.1%,
挪到边角(外高桥 / 松江)总里程 +71% ~ +75%(272 → 467~478 km)。
而求解器时限 3s / 10s / 25s 结果逐位一致,算法误差 < 0.5%。
用户没给仓库时(默认取订单几何中心)必须主动提醒:
这是当前最大的不确定性,要绝对值请先填真实仓库。

几何中心是理论下界,真实仓库只会更差

上海 14 单 × 10 吨实测(路网口径,方案 B = 13米6趟 + 9.6米2趟):

仓库 方案A 方案B 方案E vs 几何中心
订单几何中心(基线) 364.31 353.71 409.85 基准
嘉定江桥镇 479.91 454.04 561.31 +28%
临港新片区 516.15 487.28 587.03 +38%
青浦华新镇 608.70 569.33 749.49 +61%
外高桥保税区 681.04 642.83 793.65 +82%
松江工业区 803.77 755.64 1108.15 +114%

几何中心在数学上最小化到各点距离之和,而真实仓库都在城市边缘,必然更远。
报数时要说清"这是下界,真实仓库预期 +30%~+110%",
别让用户拿 353 km 去做预算、实际跑出 700 km。

做仓库敏感性只补抓「仓库→各单」这 N 条——订单↔订单子矩阵已经抓过,
换仓库只影响矩阵第 0 行/列。5 个候选仓库 = 5×14 = 70 次调用,约 3 分钟。

多仓库:自定义 1 个或多个仓库

三个入口都已支持,语义一致:

入口 用法
CLI 一次算完.py --depot 仓A --depot 仓B(可重复;不给则几何中心)
自然语言 "两个仓:浦东A路 和 嘉定B路" → DeepSeek 解析出 depots 数组(解析字段名 depots,兼容旧 depot)
网页 仓库栏「添加仓库」可加多行,每行一个仓库地址;解析经纬度时逐仓解析

建模口径:订单按 haversine 最近仓库聚类 → 车队按轮询分给各仓(保证
有单的仓都有车)→ 各仓独立复用单仓 solve_vrp → 结果合并汇总。
不用 OR-Tools 原生多 depot 建模:业务上一次行程通常不允许跨仓取货,
且分仓后每仓规模变小、求解更快更稳。

要点:

  • 没分到订单的仓库自动闲置(不报错);跨仓运力不能互借——某仓爆单
    不会从别的仓借车。warnings 里写明实际参与配送的仓库数。
  • 每条趟次带 depot 字段标明起点仓;HTML / 网页地图为每个仓库画星标
    (多仓时标「仓库N」),每条折线从本趟起点仓出发回本趟起点仓。
  • 多仓 + --road 路网精确:暂不支持按仓拆分抓矩阵,会明确告警并
    降级为直线距离——不静默出错。
  • 结构向后兼容:depots 数组、trip.depot、summary.multi_depot
    只在多仓时出现;单仓/无仓的返回结构与旧版逐位一致,老渲染脚本不用改。
  • 核心实现在 vrp_solver.py 的 solve_multi_depot()(solve_vrp 之后、
    __main__ 之前的独立 section),合并逻辑在 _merge_multi_depot_results()。

车队总台数不影响总里程,卡脖子的是"大车能凑出几趟"

用户经常把车队规模说错(实测:先说 5 台,又说 5 台 9.6 + 5 台 13)。
不用纠结口径——只要大车趟数凑得够,总里程与车队规模无关,台数只影响每车跑几趟。
上海 14 单 × 10 吨(需要 6 趟 13 米 + 2 趟 9.6 米):

车队构成 台数 每车最多趟 里程 km
5×9.6 + 5×13 10 2 353.71
3×9.6 + 2×13 5 3 353.71
2×9.6 + 3×13 5 2 353.71
1×9.6 + 4×13 5 2 353.71
4×9.6 + 1×13 5 3 409.85 ← 只有 1 台 13 米,3 趟凑不出 6 趟
5 台全 9.6 米 5 3 514.32

判据:2 台 13 米 × 3 趟 = 6 ✓,3 台 × 2 趟 = 6 ✓,1 台 × 3 趟 = 3 &lt; 6 ✗。
扫配比按「大车台数 × 每车趟数上限」算可行域,别按总台数。
副产品:只允许每车 1 趟时要多付 3%(364.31 vs 353.71),值得单独报给用户。

市区限行:用软惩罚(node_penalties),不要用硬禁

大车禁行区不要写成 VehicleVar().RemoveValue()——运力一紧就直接无解,
用户拿到的是「算不出来」,什么信息都没有。改成给「大车进入受限点」加一个
等价 1000 km 的弧成本惩罚(node_penalties),求解器会极力避开,
真避不开时仍出解并明确告警是哪台车、哪个点。用户拿到的是「合规要付多少代价」。

rules = [{&quot;nodes&quot;: [1, 2, 3], &quot;min_truck_length&quot;: 13.0}]   # 13 米车禁入 1/2/3 号点
res = vrp_solver.solve_vrp(orders, fleet, None, 10, &quot;shortest&quot;,
                           max_trips=1, node_penalties=rules)

两个实现要点(踩过的坑):
- 只惩罚「进入」节点,不要进出都罚——否则惩罚随串联长度线性放大,
会把「串 2 单」这个最省里程的动作也一起罚掉,结果失真。
- 自定义弧成本回调必须返回 int(见 references/ortools-pitfalls.md #6),
返回 float 会让目标函数静默归零。

判定口径可以粗:内环内 ≈ 距市中心 < 6 km 就够用,边界点单独标注让用户核。
实测(上海 14 单):合规后 272.45 → 318.01 km(+16.7%),但里程极差反而
从 158.80 降到 108.26 km
——大车被关在远郊、小车包揽市区,天然更均衡。

燃油经济性不是事后统计,是折进优化目标

给每台车注册按油耗加权的弧成本,让求解器自己把活儿往小车上挪:

弧成本 = 距离(米) × (该车 L/100km ÷ 25.0)

9.6 米车(25 L)乘数 1.0,13 米车(35 L)乘数 1.4。
实测:eco 比 shortest 里程更长但油耗更低(41.30 km/10.83 L vs 37.13 km/10.93 L)。


五、结果有多准(重要,别跳过)

优先换成路网距离,别停在直线估算

求解器支持外部距离矩阵(road_matrix 参数),所以只要能拿到路网距离,
就应该拿——多花几分钟抓一次矩阵,比事后拿系数乘靠谱得多。

驾车路径规划 API(和地理编码同一个 key、同一个域名,无需额外申请):

import json, urllib.parse, urllib.request
from geocode import GEO_KEY, GEO_HOST          # 与地址解析共用同一份配置

post = {&quot;orig&quot;: &quot;121.50272,31.24215&quot;, &quot;dest&quot;: &quot;121.11812,31.15162&quot;, &quot;style&quot;: &quot;0&quot;}
url = (&quot;http://%s/drive?tk=%s&amp;type=0&amp;postStr=%s&quot;
       % (GEO_HOST, GEO_KEY,
          urllib.parse.quote(json.dumps(post, ensure_ascii=False))))
root = &quot;https://www.&quot; + GEO_HOST.split(&quot;.&quot;, 1)[1]
raw = urllib.request.urlopen(urllib.request.Request(
        url, headers={&quot;User-Agent&quot;: &quot;Mozilla/5.0&quot;,
                      &quot;Referer&quot;: root + &quot;/&quot;}), timeout=25).read()
  • 返回 XML:&lt;distance&gt; 单位公里、&lt;duration&gt; 单位秒、
    &lt;routelatlon&gt; 是 lng,lat;lng,lat;... 折线(可直接拿去画真实道路)。
  • style 参数:0 最快 / 1 最短 / 2 避高速 / 3 避收费。
    实测差异不大(同一 OD 40.1~43.6 km),但避高速对货车往往更贴近现实,
    建议两种都抓、在报告里并列展示。
  • N 个订单点含仓库共 N+1 个节点,全矩阵 = N×(N+1) 次调用。14 单 = 210 次。

三个必踩的坑:

坑 现象 处理
单位 求解器内部按米吃矩阵;传公里会让目标函数缩小 1000 倍,输出一个看着完全正常的方案 传前 ×1000;求解器已加护栏(见下)
反爬 418 连打几十次后 HTTP Error 418,静默返回非预期内容 已实测:间隔 1.5~3 s + 轮换 UA + 带 Referer 可稳定跑完 210 次;失败时不要静默用直线值兜底,要单独记下来补抓
限流恢复 418 之后停几分钟就恢复 写个 repair_*.py 只补抓失败单元,别全量重跑

> 护栏已内置:_normalize_road_matrix() 会校验方阵尺寸、对角线为零、
> 非负、以及「单位是否像公里」。传错单位直接 ValueError,
> 而不是产出一个静默错误的方案——这类错误比崩溃危险得多。

实测:直线 vs 路网(上海 14 点,n=208)

指标 值
路网/直线修正系数 中位数 1.253
修正系数均值 1.291
系数范围 1.02 ~ 1.50

结论:之前文档里写的「低估 20~25%」偏保守,实测中位 +25.3%,
且倍数随距离增大而减小(短途跨江 1.46×,长途 1.12×)——
所以「统一乘 1.3 修正」在数学上仍然站不住,只能用于量级估计。

换成路网后,什么会变、什么不会变

实测四套方案全部重算:

  • 每趟送哪几单:100% 不变。 站点分组对距离口径不敏感,
    所以选型结论(用哪套方案、哪台车跑哪几单)在路网口径下依然成立,不用重新决策。
  • 趟内送达顺序:11 趟变了。 直线口径分不出「先近后远」和「先远后近」,
    路网能分——跨江、绕行的成本只有在真实路网上才看得出来。

> 给用户的说法:用哪台车、每车送哪几单,两种口径都能定;
> 但每趟先送谁后送谁,必须以路网口径为准。

剩余误差(换路网之后)

误差源 量级 方向
地理编码落点偏差 0~5% 双向
油耗模型无载重项 满载 20~30% 低估
算法未收敛 <0.5% —
路网 API 未含实时路况 峰谷 ±20% 双向

建模层没包含的东西

时间窗、装卸货时长、司机工时限制、实时路况、货车限行(限行可用
node_penalties 软规避,见 §四)。业务上重要但当前没有——用户提及时要如实说。


六、做方案咨询时的核心结论

如果用户问「这套科学吗」「该怎么建模」,直接给这几条:

  1. GLS(导向局部搜索)是 VRP 主流元启发式。14 单/5 车下 3s/10s/25s
    结果逐位一致,说明 3 秒已收敛,算法误差 <0.5%。给更长时间纯属空转。
  2. 短板在数据不在算法。换路网距离能消掉 65~80% 的误差,
    调算法参数最多抠出 0.5%。精力要花在数据源上。
  3. 容量约束下,初始解必须自己造。OR-Tools 内置首解启发式不知道你的
    Count/容量硬约束,会构造出违规解然后卡死。用扫描法(sweep)先切扇区。
  4. 总吨数够 ≠ 装得下。单笔订单超过单车载重时,多趟往返救不了——
    多趟是「同一台车跑第二趟」,不是拆单。这类不可行要给出中文可读的原因。

七、扩展指南

要加什么 改哪里 难度
新车型 / 新油耗 truck_spec.py 的对照表 低
时间窗 vrp_solver.py:加 Time 维度 + SetCumulVarSoftUpperBound 中
司机工时上限 加维度设上界 中
换地理编码服务商 geocode.py 的 GEO_HOST + 响应解析 中
路网距离矩阵 build_distance_matrix() 换成调路网 API 中(N² 调用量)
地址缓存 ✅ 已完成,见 §八。geocode_client.py 的 .geocode_cache.json,重复解析 0 耗时
拆单配送 需要重构订单模型(现有模型假设一单不可拆) 高
页脚署名改成自己的 不要改文件,设环境变量即可,见下 低

二次分发:把页脚署名换成你自己

报告页(--html 产出的那个 HTML)底部有一张「服务商署名」联系卡片。
包里默认带的是作者的联系方式。想换成你自己的,不要改 contact.py
(升级包时会被覆盖)——用环境变量覆盖,它优先级最高:

export VRP_CONTACT_NAME=&quot;上海XX物流 · 智能调度&quot;
export VRP_CONTACT_PHONE=&quot;021-1234 5678&quot;
export VRP_CONTACT_EMAIL=&quot;dispatch@example.com&quot;
export VRP_CONTACT_WECHAT=&quot;your-wechat-id&quot;
export VRP_CONTACT_URL=&quot;https://example.com&quot;     # 选填
export VRP_CONTACT_SLOGAN=&quot;车队调度 / 路径优化定制&quot;

Windows PowerShell 用 $env:VRP_CONTACT_NAME=&quot;...&quot;。
另外 VRP_CONTACT_OFF=1 可以彻底关掉这张卡片。

设计上有两条保证:
- 全部留空 = 不渲染,绝不会在页面上留一个空白框;
- contact.py 被误删或写坏时,报告页照常生成,只是没有页脚。

一个产品层面的硬提醒

不要假设用户懂经纬度。 曾经有人建议「没有地图 key 就让客户手工填经纬度」——
真实客户根本不知道那是什么。地址 → 坐标这一步必须由系统完成,
地理编码是硬依赖不是可选增强。工程上正确做法是:
适配器模式(可挂不同服务商)+ 地址缓存(客户地址大多重复,命中缓存零 API 调用)。


八、性能:为什么慢、瓶颈在哪

用户反馈「等结果很久」时,先按这张表定位,不要凭感觉去调算法——
实测下来瓶颈几乎从来不在 OR-Tools。

实测耗时分解(14 单 / 5 车 / max_trips=2,本机)

环节 耗时 说明
import ortools 0.14 s 一次性,服务启动时付过就不再付
地理编码 14 条(冷) 2.8 s 纯网络往返,单条约 0.2 s
求解(旧:写死 5 s) 5.00 s GLS 恒定吃满时限,1 s 就收敛也照样跑满
旧版合计 ≈7.8 s
地理编码 14 条(热,命中缓存) 0.00 s 已加磁盘缓存
求解(新:自适应 2 s) 2.00 s 结果逐位不变
新版合计 ≈2.0 s 提速 74%

三个根因与对应修法

1. GLS 不会提前退出 —— 时限给多少就烧多少秒。

这是最反直觉的一点:OR-Tools 的 Guided Local Search 是元启发式,
找不到更好的解也不会停,会一直搜到时限为止。实测墙钟耗时与设的时限
严格 1:1(设 1s→1.00s,设 10s→10.02s)。

而实测收敛点远早于时限:

规模(单/虚拟车) 首次达到 20 s 结果的时限
14 / 5 1 s
30 / 6 1 s
50 / 8 20 s(真的需要长时间)

→ 已加 vrp_solver.recommend_time_limit(order_count, vehicle_count) 按规模分档,
app.py 调用它而非写死常量。多趟时按虚拟车数(真实车数 × max_trips)算。

2. 地址解析没有缓存 —— 每次重跑都重新打一遍地图 API。

派车场景的常态是同一批地址反复微调重跑(改策略、改趟数、改车型),
地址表其实没变。已加磁盘缓存 geocode_client.py:
- 位置 .geocode_cache.json(与 geocode.py 同级,删除即清空)
- 键 = 地址 + 城市前缀(必须带 city,否则跨城同名门牌会错误命中)
- 只缓存成功结果(失败可能是限流,缓存住会让用户修不好)
- 原子写(临时文件 + os.replace),损坏则静默重建
- Flask 多线程,读写全程加锁
- 上限 5000 条,超出丢弃最旧

3. geocode_many(verify=True) 会让请求数翻倍。

verify_result() 对每个不同的区县前缀额外发一次请求取中心点做落点校验
(前缀有缓存,但地址跨 N 个区就是 N 次额外请求 + N 次节流 sleep)。
脚本里批量解析建议开;Web 端走 geocode_one,默认不带校验。

别碰的东西

  • 不要把 250ms 节流去掉。它会让 key 被打进限流,403 之后整批解析全废——
    省 3 秒换来全部失败,不划算。
  • 不要并发解析。geocode.py 刻意串行,并发只会触发风控。
  • 想再快,唯一安全方向是加缓存和降时限,不是加并发。

九、常见故障

症状 原因
No module named &#x27;flask&#x27; PyCharm 解释器选错,不是没装
装 ortools 极慢 官方源在国外,必须用清华源
地理编码全 403 key 配额耗尽;别为了提速去掉 250ms 节流(见 §八)
坐标明显跑偏 地址太含糊,看 score 字段(<70 不可信)
改了默认策略没生效 默认参数绑死在 def 求值时刻——本项目所有默认值写成 None,函数体内解析
max_trips 设很大还是无解 多趟不拆单;单笔超重永远装不下
求解结果「变慢了但答案一样」 正常,GLS 吃满时限。调 recommend_time_limit 而非加机器
改了地址但解析结果没变 命中了 .geocode_cache.json,删掉该文件即可

完整排查表见 references/deploy-and-deps.md。


附:参考文档

文件 什么时候读
references/ortools-pitfalls.md 改 vrp_solver.py 之前必读。10 个静默失效坑 + 调试心法
references/modeling-and-accuracy.md 距离/油耗怎么算的、四种策略、结果可信度评估
references/deploy-and-deps.md 部署、依赖分层、API 契约、故障排查、回归基线

常见问题

5 问
答案由本站依据条目字段整理
车辆路径规划怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_6d6945b7/vrp-servers。安装方式为未标注。装完刷新技能列表或重开客户端。

车辆路径规划支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

车辆路径规划权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

车辆路径规划收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

车辆路径规划用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。