旅行手账(Travel Handbook)
把零散旅行素材,一键变成整趟一篇、手机友好的图文游记,并内联一张真实驾车路线图(有条件必做)。
模型由 WorkBuddy 内置模型自动完成,无需你配置任何 API 密钥;仅「真实路线图」模块需自备腾讯地图 Key。
六条铁律(本 Skill 的核心规则)
> 交互模式(默认):图说 / 草稿 / 风格三个「确认门」默认关闭——直接生成定稿,事后由用户在对话里点对点修改(2026-09-20 用户明确)。仅当用户主动要求「先给我看 / 再确认」时,才逐门确认。
- 固定结构:成稿结构锁死(见下「固定结构」),任何旅行都套用,不按天流水账、不增减章节。
- 风格由 agent 直接选定(可事后更换):agent 依据旅行调性从
config.THEMES直接选定最贴合的主题并渲染,不再逐门征询;用户事后若要换风,重跑render.py --theme <新主题>即可。绝不默认小红书红等特定平台风。 - 图说与文本直接定稿(不逐条确认):agent 用视觉能力为每张照片写图说(≤15 字,写景不虚构),直接写入成稿;看不清 / 无法识别的仍标注「待你确认」,绝不编造或硬猜。图说同步写回
materials.json的photos[].caption与各的 alt。 - 照片集中为图集(顶部轮播):成稿用
标记每张照片;render.py自动把所有照片收成「图集」,集中放封面标题下方,横向滚动自动播放(带相框),正文不再内嵌任何图片。成稿里的图说文字即图集图说。 - 严格依据素材、不添加任何记录外内容:路线、天数、地点、费用、避坑、天气、人名店名等所有事实必须来自用户提供的行程记录/笔记/照片图说/语音转写,禁止凭空添加或发挥任何素材未提及的信息;成稿后逐项回查,凡素材未提及的内容一律删除。
- 有条件必须制作路线图:只要本机具备条件(Node.js + 腾讯地图 Key + 随附 polygon-clipping),默认必须生成「真实驾车路线图」并内联进成稿,不得省略;若依赖不完整,必须明确指引用户如何安装(见「真实路线图」节),只有在用户明确表示不安装 / 不需要时,才可不制作。停靠点须包含关键途经地标。
能力
- 输入:照片(你选图)、文字笔记、行程单/票务、语音备忘录
- 输出:图文游记 Markdown + 手机友好 HTML 预览 + 真实驾车路线图(内联)+ 图文卡片(PNG,可选)
- 文风:干货攻略风(信息密度高、避坑指南、实用优先)
- 语言:中文为主,外文地名/店名保留原文
- 篇幅:整趟一篇,不设字数上下限——篇幅由素材量决定:素材多则展开、素材少则精简;不许为凑字数灌水,也不许为压字数漏掉信息(
config.TARGET_LENGTH仅作参考量级) - 图说:由 WorkBuddy 内置视觉能力自动看图直接写定(≤15 字,写景不虚构;默认不逐条确认,仅用户主动要求时才集中把关);看不清的标注「待确认」(无需视觉 API 密钥)
- 模型:WorkBuddy 内置模型(自动),无需额外密钥
固定结构(锁死,章节标题一字不改)
# 标题(封面标题,带 emoji)
> 开篇导语(1–2 句钩子)
## 行程总览
## 必玩(景点)
## 必吃(美食)
## 住(本次经验)
## 避坑指南
## 花费参考
## 结语
#话题标签
详见 references/prompt_zh.md(各段格式与要点的权威定义)。
各部分制作规范(v3)
> 本表与 references/prompt_zh.md 是同一套规范的两份副本:改格式/口径必须两处同步,并同步 scripts/lint.py 顶部常量(三处一致,否则体检与写作脱节)。
篇幅总原则:不设字数上下限。 篇幅由素材量决定——素材多则展开、素材少则精简;不许为凑字数灌水,也不许为压字数漏掉信息。各段的字数与条数,lint.py 只打印出来供参考,不判对错。
| 部分 | 规范要点 |
|---|---|
| 全局文风 | 干货攻略风、先结论后细节;第一人称复数;禁用「绝美/美哭了/yyds/一生必去/天花板/封神/据说/听说」等零信息或模糊表达 |
| 封面标题 | 1 个 emoji 置首;含「天数」或「路线关键词」+ 一句卖点;禁「XX游记」平铺式 |
| 开篇导语 | 1–2 句;首句抛最强卖点,次句交代规模(天数/人数/方式) |
| 行程总览 | 三件套=路线串 + 行程对照表(日期|行程|里程·车程|住宿,行数 = 天数)+ 起止日期与总里程;总里程须与路线图 TOTAL_KM 一致(差 >5% 须说明);写里程须注明口径——是全天合计,还是素材只记了其中几段 |
| 必玩(景点) | 每站 ### 站名 ★n;每站三段式=亮点 + 实用信息(导航点/门票/停车/时长)+ 关键站点给 1 个提示框(> [!TIP]/> [!WARNING]);覆盖素材全部景点;★ 为主观评级,素材未提不给星 |
| 必吃(美食) | 按城市 ###;店名 + 招牌 + 一句口味(人均仅素材有才写);无素材的城市写「未记录」 |
| 住(本次经验) | 表格(住宿|位置|点评一句|参考价);参考价仅写素材有的,无则「—」;覆盖「值得说」与「要避开」 |
| 避坑指南 | 有序列表,每条=场景 + 后果 + 做法;只写素材中真实出现的坑,条数由素材决定(严禁为凑数编造),禁补通用常识 |
| 花费参考 | ① 分类 barchart(类别,金额,单位统一为元)必做,带合计条;② 明细表(项目|单价|数量|金额|数量依据);③ 「本次未记录项」提示块,列出素材未提及的费用并提示用户可补充。口径见下「花费参考口径」 |
| 结语 | 1–3 句;主观感受 + 互动引导;不编造后续行程 |
| 话题标签 | 文末单独一行 #话题;目的地 + 玩法 + 人群 + 季节 |
| 图集图说 | 图说写景不虚构、保持简短;全部集中顶部轮播、正文零内嵌;渲染后核验张数 |
| 缺素材处理 | 章节保留、不省略、不合并,写「本次未记录/未体验」;严禁搬通用攻略填空 |
| 可验收 | scripts/lint.py 一键体检,结论分三类:【必错】章节缺失/照片路径失配/路线图天数与行程表不符;【提醒】禁用词/花费表与图对不上/总里程不符;【参考】各段字数与条数。必错项必须先修再渲染 |
花费参考口径(v3 新增)
- 每笔写成「单价 × 数量 = 金额」,数量须写明依据,且依据可追溯:
- 人次=素材人数;租车天数=取车日→还车日;住宿晚数=每晚算一晚;门票张数=实际去的人数(没去就不计)。 - 可以算:素材给了单价 → 乘可推数量 → 得金额(纯算术,不是编造)。
不可以算:素材未给单价 → 不得反推(如机票只写"比直飞便宜多少")→ 列入「未记录项」。 - 币种统一:非人民币按汇率折算为人民币,成稿须注明汇率值 + 日期(如「按 1 港币≈0.918 元人民币(2026-09-21)」)。
- 展示:分类条形图(单位统一为元、可求和、带合计条)+ 明细表 + 未记录项清单。
- 只含已记录部分时,须明确标注「仅含已记录项,非全程总花费」。
第 0 阶段:环境预检(轻量、非阻塞)
生成前可选跑一次预检,确认本地依赖是否齐备、缺什么、影响什么、怎么补。检测不阻断流程——缺失只降级对应能力,按提示继续即可;仅核心依赖 Pillow 缺失会阻断图片处理。
python scripts/preflight.py # 人类可读
python scripts/preflight.py --json # 供 AI 解析
python scripts/preflight.py --skip-network # 网络受限时只判存在性
检测项与影响(参考 travel-planning-expert 第 0 阶段执行流程):
| 依赖 | 级别 | 缺失影响 |
|---|---|---|
| Pillow | 核心(必需) | 照片 EXIF/压缩不可用,collect/render 图片处理被阻断 → 须先 pip install Pillow |
| pillow-heif | 可选 | HEIC 照片裂图;JPG/PNG 不受影响 |
| faster-whisper | 可选 | 语音不转写,改用 --notes 粘贴文字 |
| playwright | 可选 | 不能 --png 导出卡片,改截图/打印 PDF |
| Node.js | 路线图必需(规则 6) | 无法生成真实路线图 → 必须指引安装 Node.js(见「真实路线图」节) |
| polygon-clipping(随附) | 路线图必需(已内置) | 真实几何渲染缺省界叠加(文件已随附,无需安装) |
| 腾讯地图 Key | 路线图必需(规则 6) | 真实路网几何缺失,mapgen 降级「示意版」直线图 → 必须指引申请 Key |
退出码:0 = 核心依赖齐备;1 = Pillow 缺失(须先装);2 = 脚本错误。Node / Key 缺失不改变退出码,但按规则 6 须指引用户安装,不得静默省略路线图。
工作流(agent-native,无需密钥)
- 收集素材:运行
collect.py,传入照片目录、笔记、行程单、语音。
python scripts/collect.py --photos <照片目录> --notes <笔记.txt> --itinerary <行程.txt> --voice <语音.m4a> --name "新疆自驾游"
产出output/materials.json(照片已按 EXIF 时间排序)。 - 看图写图说:用 Read 工具查看
materials.json中每张照片,为每张写一句中文图说(≤15 字,写景不虚构)。看不清 / 无法识别的标注「待你确认」,绝不编造或硬猜。 - 图说写回:把图说写入
materials.json的photos[].caption(成稿的![图说]以此为准)。默认无需逐条确认;仅当用户要求「先给我看」时才集中展示请其把关。 - 成稿(锁结构 + 照片标记):阅读
references/prompt_zh.md,结合素材,写出output/<旅程名>.md:
- 严格按「固定结构」八段 + 标签;
- 为每张照片写一行(路径用源文件绝对/相对路径,位置随意;render.py会统一抽成顶部图集,不留在正文);
- 正文纯文字,不把照片插到具体段落。
- 成稿后先跑规范体检(必须):python scripts/lint.py --md output/<旅程名>.md;【必错】项(章节缺失/照片路径失配/路线图天数与行程表不符)必须先修,【提醒】与【参考】项视情处理后再渲染。 - 生成真实路线图(规则 6·有条件必做):写
output/route_stops.txt(按行程顺序,每天用# DAY n分隔,含途经地标)→ 跑mapgen.py出route.svg。缺 Node.js / 地图 Key 时,先按「真实路线图」节指引用户安装;仅当用户明确拒绝安装时才省略。
python scripts/mapgen.py --stops output/route_stops.txt --out output/route.svg --title "XX 自驾环线" - 风格直接选定(规则 2):分析旅行记录 + 照片调性,从
config.THEMES(清旅/品牌红/湖蓝/大漠金/森系绿/极简)直接选定最贴合的主题,不再逐门征询。 - 渲染:运行
render.py,带上主题与路线图。
python scripts/render.py --md output/<旅程名>.md --theme <主题> --route output/route.svg
加--png可导出图文卡片(需 Playwright)。
- 图集默认手动滑动(不再自动播放);分享给他人才不依赖 assets 文件夹时,加--self-contained把图片内联为 base64 单文件,可独立转发。
- 渲染后必须核验图集张数:图集数应等于成稿引用的有效照片数;少图多为源照片被改名 / 路径失配(render.py对不存在路径静默跳过)→ 逐个核对![...]路径与磁盘真实文件名后重渲。
- 复跑规范体检(带路线图):python scripts/lint.py --md <成稿.md> --route output/route.svg,确认总里程与「行程总览」一致(差 >5% 须说明或修正成稿)。 - 交付:展示 HTML 预览。页面顶部为封面标题 + 照片图集(横向滚动/自动播放/可点圆点跳转);其下为纯文字游记;底部一个按钮——「📰 复制富文本」复制已渲染 HTML(已内联样式,保留排版,可直贴公众号/Word,粘贴不再出现源码)。图文长图可截图/打印 PDF,或用
--png导出。
风格与「旅游规划专家」产物同构:顶部「行程速览卡」(照片数/主题/里程)、章节序号圆徽 + 品牌下划线、表头品牌浅底、.tip/.note/.warn提示框(正文支持> [!TIP]/> [!NOTE]/> [!WARNING]语法)、底部「章节导航」吸顶跳转;复制按钮为实心品牌色胶囊。正文还支持```barchart分类条形图:在成稿用代码围栏写类别,金额每行一项,渲染为按类别用不同颜色区分、宽度按「占合计比例」归一的横向条形图;合计严格 = 各项之和,以满宽条形图展示并置于分类条之后(去掉纯文字合计行),适合「花费参考」等分类占比展示;页面为自适应布局(手机满宽、桌面自动加宽至约 860px 居中,不再锁死手机窄列)。
- 可选·事实自检:成稿后运行python scripts/selfcheck.py --md output/<旅程名>.md,自动比对成稿中数字/日期/价格/地名是否出自素材,列出需人工核对的行(落实规则#5)。
可选:自备密钥的 API 批量模式(非默认)
若你想脱离 agent、用自备密钥的模型 API 批量生成,可配置 .env 后运行:
python scripts/generate.py # 用 materials.json 真实调用模型(需 .env 密钥)
python scripts/generate.py --mock # 无密钥自测占位稿
默认推荐上面的 agent-native 流程,无需任何密钥。
首次使用
- 无需配置密钥(模型自动)。
- 依赖安装(推荐一次性装齐):
pip install -r requirements.txt。其中 Pillow 为必需(照片 EXIF 读取、HEIC 转 JPEG、大图压缩);pillow-heif建议 iPhone 用户安装,否则 HEIC 照片图集会裂图;faster-whisper、playwright为可选增强。 - 语音转写默认
whisper(本地离线,无密钥):需pip install faster-whisper(首次会自动下模型);若未装,collect 会跳过语音并提示你用--notes粘贴转写文字。 - 行程单/票务图片或 PDF 的 OCR 为可选增强:装
paddleocr+pymupdf后 collect 会自动尝试识别,未装则提示用 txt 粘贴。 - 导出 PNG 需:
pip install playwright && playwright install chromium。 - 真实路线图(规则 6 有条件必做)需:Node.js ≥18 + 腾讯地图 WebService Key(放
~/.workbuddy/tencent_map_key或设环境变量MAP_KEY);polygon-clipping已随附,无需安装。 - 密钥安全:仅在「自备密钥 API 批量模式」才需
.env,参照env.example.md填写;.env只存本地,切勿提交、切勿打进发布包。
真实路线图(规则 6:有条件必须制作)
在游记正文下方生成一张「真实驾车路线图」:站点按真实经纬度落点,路线为腾讯地图真实驾车导航几何,底图为民政部标准地图边界(阿里云 DataV GeoAtlas)。这是本 Skill 唯一会联网的模块。
有条件必须制作:只要本机具备 Node.js + 腾讯地图 Key + 随附 polygon-clipping,就必须生成并内联;依赖不完整时先指引用户安装(见下表),仅当用户明确拒绝安装 / 不需要时才可省略。
依赖与安装指引:
| 依赖 | 作用 | 缺失时如何安装 |
|---|---|---|
| Node.js(≥18) | 跑 route_node/ 真实几何管线 |
安装 Node;或让脚本用 managed node(~/.workbuddy/binaries/node/versions/*/node.exe,mapgen.py 已自动探测) |
| 腾讯地图 WebService Key | geocode + 真实驾车几何 | 到腾讯位置服务申请 Key,写入 ~/.workbuddy/tencent_map_key(一行)或设环境变量 MAP_KEY |
| polygon-clipping(随附) | 省界叠加 | 已内置 scripts/route_node/lib/polygon-clipping.umd.min.js,无需安装 |
| 网络 | geocode / 驾车几何 / 省界下载 | 需可访问 apis.map.qq.com 与 geo.datav.aliyun.com |
前置·Key(二选一,绝不写进脚本 / 产物):环境变量 MAP_KEY,或文件 ~/.workbuddy/tencent_map_key(一行 Key)。
步骤:
1. 写停靠点文件 output/route_stops.txt,每行一个,按行程顺序(城镇写名称;景区 / 地标给经纬度 名称,经度,纬度)。必须用 # DAY n 给每天打标记:
# DAY 2
西宁
门源
祁连山大草原,100.947232,37.958797
七彩丹霞,100.065859,38.972676
# DAY 3
嘉峪关
敦煌
# DAY 6
大柴旦
# DAY 7
U型公路(315国道),94.965896,37.389371
水上雅丹,93.774248,37.624059
德令哈
# DAY 8
茶卡盐湖,99.070321,36.712523
青海湖(江西沟),100.277557,36.618719
西宁
- 天分组(必须):# DAY n(也认 # 第N天)表示其后站点属于第 n 天,直到出现下一个标记;普通 # 注释照旧忽略。每段路归「终点站」所在天,图例显示「第N天 起点→终点」;同一天走多段会自动合并成一行。
- 严禁拿段号当天数:这是 2026-09-21 修掉的真实缺陷——12 段被当成 12 天,图例一路排到「第12天」,而行程只有 9 天(详见 route_node/pitfalls.md 第 34 条)。没有开车路段的天不写标记(航班日、市区活动日),图例会自动跳过,这些天在图下注脚里说明。
- 城镇只写名称(走 geocoder 解析);景区 / 网红地标必须给经纬度,否则 geocoder 常误判行政中心(如「祁连」→祁连县城,而非「祁连山大草原」)或直接报「参数错误」(如「水上雅丹」)。可先用 placeSuggestion 核实坐标。
- 站点名用实际游玩地点:实际玩「七彩丹霞」(临泽县)就不要写「张掖」(市区);停靠点文件、路线图、成稿与行程表三处必须同名,否则看起来就像两处对不上(详见 pitfalls.md 第 35 条)。
- 途经地标要落点:如「U型公路(315国道)」位于 大柴旦 → 水上雅丹 之间的 G315 上(坐标 37.389371,94.965896)。腾讯对 G315 等国道默认会绕高速,把该地标作为独立停靠点(或 SEGS[].waypoint)即可强制走国道。
2. 生成路线图 SVG(有 Key → 真实路网;无 Key → 站点直线「示意版」,需站点自带经纬度):
python scripts/mapgen.py --stops output/route_stops.txt --out output/route.svg --title "深圳→长沙→武汉 自驾"
# 可选:--provs 630000,620000 指定省界(不填则按站点 geocode 的 ad_info.adcode 自动推断)
3. 渲染时带上 --route:
python scripts/render.py --md output/<旅程名>.md --theme <主题> --route output/route.svg
踩坑(脚本内已处理 / 需注意):
- 天分组必须写:SEGS[].day 取「终点站」所属天,禁止用段号顶替(用段号会让同一天多段撑大天数,图例与行程表对不上)。lint.py 已内置「图例天数 vs 行程表天数」校验,超范围直接判必错。
- 腾讯 Key 有每秒请求上限:mapgen.py 已对 geocode 做指数退避重试 + 站点间限速;驾车几何由 fetch_route.js 自带 350ms 节流。
- geocoder 的 adcode 在 result.ad_info.adcode(不在 address_components);脚本已正确读取,取错会导致省界底图不出(PROVS 为空)。
- geocoder 对景区名常返回行政中心 → 关键节点用 placeSuggestion 核实后给经纬度(详见 scripts/route_node/pitfalls.md)。
- 重抓前先备份 routes.json(景观公路几何可能不可逆丢失)。
合规与红线(与 geo-map-compliance-guard 一致):Key 仅本机读取、绝不写入产物 HTML/日志;省界仅用民政部标准地图数据;坐标为 GCJ-02,不绘制敏感坐标。真实几何由已验证的 Node 管线(scripts/route_node/)解码,本脚本只做编排。
分享给朋友
本文件夹即一个完整 Skill。朋友拿到后放到 WorkBuddy 的 skills 目录
(用户级:~/.workbuddy/skills/;项目级:<工程>/.workbuddy/skills/),即可直接用,无需任何密钥。数据全程只在自己机器处理。
说明 / 边界
- 一键发布小红书/公众号:本期不做(个人无开放 API)。纯文字用底部按钮复制后手动发;配图发布请截图 HTML 图集或导出
--png图文卡片后手动发。 - 行程单为图片/PDF 时:本期不做 OCR,请用 txt 粘贴或用
--notes提供文字。 - 除「真实路线图」模块会联网(腾讯地图
apis.map.qq.com+ 阿里 DataV 省界)外,其余处理全部本地完成、不上传数据。
```