trip-decider
一个旅行规划 AI,它给的行程你能直接照着走——每一段路都写清:坐什么车、几点发、在哪上车、多少钱、末班几点。查不到的它明说,绝不用“前往 XX(车程约 1 小时)”这种话糊弄你。
你一定遇到过这种事
城市之间其实好办。高铁谁都会查,12306 上车次、时刻、票价一目了然。
真正让一趟旅行崩掉的,往往是最后一段:县城怎么到景区,景区之间怎么走,当天还能不能回来。
这类班车信息散、旧、难搜。地图 App 里的班次可能是几年前的;搜索引擎搜出来的攻略互相抄;最后还是得去小红书翻“有没有人真的坐过”。通用 AI 到这里通常只剩一句:
上午前往篁岭。
怎么去?在哪上车?几点有车?回程末班几点?它不知道,但它也不说它不知道。等你到了县城,才发现计划里最关键的一段根本没法执行。
trip-decider 在第十次真实复测里,同一段给出的信息是:
高铁站—篁岭旅游班车:6:40–17:40,每约 20 分钟一班;沿线停靠李坑、汪口、江湾;全程 ¥19/人;回程末班 17:00。
如果班次没有可靠的固定答案,它不会补一个“约半小时一班”,而会直接写:
班车时刻随季节浮动,建议出发前一天在“赣悦行”小程序确认班次。
不确定本身也是可执行的信息。重要的不是把每个格子填满,而是让你知道这段能不能照着走;不能的话,出发前要去哪里确认、到了该问谁。
以上数字来自真实宿主复测记录,原始过程保存在docs/field-reports/。
它长什么样
逐日行程:交通列才是主角
| 时段 | 行程 | 怎么走 | 票价与返程边界 |
|---|---|---|---|
| 当天 | 婺源高铁站 → 篁岭 | 旅游班车;6:40–17:40,每约 20 分钟一班;沿线停李坑、汪口、江湾 | ¥19/人;回程末班 17:00 |
| 出发前一天 | 复核季节班次 | 打开“赣悦行”小程序确认当天发车表 | 未确认前保持“待核验” |
📷 截图占位:第十次复测完整逐日行程表需要露出每行的交通方式、上下车点、班次、票价、末班和待核验动作。
预算:金额和可信状态放在一起
| 项目 | 金额 | 状态 | 你该怎么做 |
|---|---|---|---|
| 高铁站—篁岭旅游班车 | ¥19/人 | 已核验 | 按行程乘坐 |
| 季节班次 | — | 出发前复核 | 前一天查“赣悦行” |
| 没有可靠来源的费用 | — | 未核验 | 不计入“已知预算” |
📷 截图占位:第十次复测预算表需要同时露出金额、状态和未计入总额的未知项。
核验:把“看起来很完整”拆开逐条对账
一次真实对账中,第三方行程写了 4 条铁路断言。trip-decider 逐条查询 12306 后给出:
| 结果 | 数量 | 例子 |
|---|---|---|
| 有据 | 0 | — |
| 冲突 | 1 | G1992:第三方写 15:35、¥340;12306 实查为 15:02、¥202.5 |
| 查无实据 | 3 | 只说明当前组合查不到,不擅自判成“车次不存在” |
📷 截图占位:核验对账页需要露出第三方断言、12306 实查值、判定和“查无实据不等于错误”的说明。
完整快照见verify-2026-08-04-third-party-vs-12306.md。
两个用法
排:一句话变成能执行的行程
真实宿主测试用的是这样一句话:
我想 8 月 4 号从武汉出发去婺源上饶那边玩到 8 月 7 号,两个人,预算 6000,节奏慢一点,想看自然和古村。帮我安排一下。
trip-decider 会先确认硬条件,再查往返铁路、地点身份和当地交通,最后给出从出发到返程的逐日表。缺哪一段就点名缺哪一段,不会拿一段散文冒充计划。
验:把别人排的丢进来,逐条查账
你: 帮我核一下这份行程里的车次、时刻和票价。trip-decider: 4 条断言:0 条有据、1 条冲突、3 条查无实据。G1992 的发车时刻相差 33 分钟,二等座票价相差 ¥137.5;其余 3 条建议出发前确认。
“冲突”表示查到了但对不上;“查无实据”表示当前没查到。它不会把后者偷换成“假的”,也不会把第三方行程整份打一个模糊分数。
快速开始
要求 Python >=3.11,<3.12。当前一键启动脚本在 Windows PowerShell 上验证。
py -3.11 -m venv .venv
.\.venv\Scripts\python.exe -m pip install --requirement .\requirements-dev.lock
当地地点和路线查询需要高德 Web 服务 key:
$env:AMAP_WEB_SERVICE_KEY = "<your-amap-web-service-key>"
.\scripts\run_product.ps1
浏览器会打开 http://127.0.0.1:8765/。真实 key 只通过进程环境传入,不要写进仓库、配置示例、日志或截图。
如果要在 Claude Desktop 里直接对话使用,按docs/usage.md 配置 MCP。完整离线测试:
$env:PYTHONPATH = "$PWD\src"
.\.venv\Scripts\python.exe -m pytest -q
为什么它做得到
因为能直查的交通不是让模型回忆:跨城铁路查 12306,当地路线查高德;外部补充的班车信息也必须带来源和采集时刻。
每个事实分开记录两件事:有没有可靠支持,以及到你读取时还新不新。前者可以保存,后者每次读取现算,所以昨天可信的班次不会永远挂着“已核验”。规则见 证据两轴模型 和新鲜度策略。
查不到就标未核验,冲突就把两边都摆出来。系统层面禁止静默编造:这套约束从最初13 条机器核验继续扩展到当前的 I15,覆盖持久化、未知与冲突、读时计算、规划消费和 MCP 调用边界,详见 不变式契约。
evidence-runtime 是实现这件事的底层:它决定什么事实可以进入规划。但它是手段,不是目的;目的始终是让你拿到一份能照着走的行程。
它诚实到什么程度
- 当前完成端到端真实复测的主链路是武汉到婺源区域,不声称已经覆盖全国;
- 12306、高德和地方班车都会变化,旧快照不会被包装成今天仍然有效;
- 酒店成交价、实时可订状态、天气和部分景点信息仍可能是未知;
- 它不订票、不订房,也不会替你登录任何账号;
- 本地 Web 服务没有认证,只应绑定
127.0.0.1,不能直接暴露到公网。
所有已确认边界见 KNOWN_ISSUES.md。真实宿主用坏它、卡住它、发现数据对不上的过程没有藏起来,全部留在docs/field-reports/。
License 与作者
MIT License · © 2026 Hugin-Z