GhostRunner:把每条街道变成惊悚剧情,本地AI音频跑步引擎

GhostRunner 用编译/运行分离架构,把真实街道变成可离线运行的互动音频惊悚剧舞台。
GhostRunner 是一个开源、本地优先的音频戏剧引擎,让跑者的真实路线成为一部丧尸逃亡或间谍撤离广播剧的舞台。它的核心是编译/运行分离:跑步前用 LLM 一次性生成剧情、地标与配音,运行时只做毫秒级的确定性查找与触发。剧情节拍锚定在路线上的具体米数,地标严格取自 OpenStreetMap,校验器拒绝任何虚构地标。LLM 只担任导演与节拍写手,编排全部由纯 Python 完成;配速则用 TabPFN 分类为四种状态来选择故事变体。声音是唯一按量计费项,按路线渲染一次可无限回放,使一人小生意在成本结构上成立。整个系统可在 4 GB 显存的笔记本 GPU 上运行。
跑步时听播客已经不够刺激了。GhostRunner 给了一个更大胆的设想:让脚下的真实街道成为一部惊悚广播剧的舞台。你选一个题材——丧尸逃亡、赛博朋克快递员、间谍撤离——设定距离和强度,系统会规划出真实路线,识别沿途地标,然后生成一段音频剧情,而你周围的环境就是布景。
据开发者在 Hacktoberfest 开源 AI 挑战赛的提交介绍,GhostRunner 是一款开源、本地优先的音频戏剧引擎。它不仅能作为跑步伴侣,开发者还将其设计为一个一人可运营的小生意:制作并出售主题跑步/步行体验包。

核心机制:空间精确、地标真实、零运行时推理
GhostRunner 之所以不只是"地图 API 套壳提示词",在于三点设计:
空间精确。 每一个剧情节拍都锚定在路线上的具体距离。关于转弯的台词会在转弯前播放,而不是过后。
有据可依。 叙述者只能提及路线上真实存在于 OpenStreetMap 中的地标,校验器会拒绝任何凭空捏造的地标。
足够快。 因为剧情不是在跑步过程中实时生成的——故事在执行前就已编译并配音完毕。播放一句台词只是查找,而不是推理。
这一切能在仅 4 GB 显存的笔记本 GPU 上运行。
"右转上橡树街。看到左边那栋老砖楼了吗?东西就藏在那里。"
路线以"链age"(沿路径的米数)为坐标系统:转弯在 1240 米处,喷泉在 1310 米处、在左侧。这个选择让触发器基于距离而非时间工作,预编译的节拍在任何配速下都有效。
OpenStreetMap(OSM) 是一个由全球志愿者共同维护的开源地理数据库,任何人都可以自由下载、使用和修改其中的地图数据。与 Google Maps 等商业地图不同,OSM 提供完整的原始矢量数据(道路、建筑、兴趣点、步道等),并以 ODbL 开放许可发布。GhostRunner 使用 Overpass API 按路线走廊查询 OSM 中的 POI(兴趣点),例如砖楼、喷泉、街角商店等地标,再把它们交给 LLM 作为叙述素材。这正是它能做到"有据可依"的基础:地标不是模型编出来的,而是从真实地图数据里检索出来的,校验器据此建立白名单,拒绝任何不在列表中的地标名。
编译/运行分离:延迟与信任问题的解法
跑者移动速度约 3 米/秒,而小显存 GPU 上的 LLM 每句需要数秒,TTS 还要额外时间。如果反应式生成,关于钟楼的台词会在你经过之后才到。小模型还会自信地胡编。
GhostRunner 按"工作何时发生"拆分系统:
- 编译阶段(跑步前及过程中):规划 → Valhalla 路线 → Overpass 走廊 POI → 路线模型(链age)→ 导演(LLM 一次调用)→ 节拍规划器 → 节拍写手(每节拍 3 个变体)→ 校验器 → TTS 预渲染(滚动窗口)
- 运行阶段(每 tick,正常路径上无 LLM):GPS → 配速分类器(TabPFN)→ 路线定位 → 触发检查 → 变体选择 → 播放
两个 LangGraph 图分工明确:compile_graph 承担重活,runtime_graph 是若干毫秒级完成的确定性节点。这个拆分也是商业模式成立的关键——所有昂贵操作只在编译时发生一次,结果可反复复用。
LangGraph 是 LangChain 团队推出的一个用于构建有状态、多步骤 AI 工作流的编排框架,把任务建模为图上的节点与边。在 GhostRunner 中,compile_graph 是一个较重的图,串联了路线规划、POI 抓取、导演 LLM、节拍写手、校验器和 TTS 渲染等节点;runtime_graph 则是一个几乎不含模型调用的轻量图,每 tick 只做 GPS 读取、配速分类、触发检查等确定性操作。把两者拆分为独立的图,是为了让"重活"与"快查"拥有不同的执行频率、失败模式和资源占用,从而在跑步这种实时性强的场景里保证响应速度。
LLM 负责叙述,不负责编排
小模型在工具调用上不可靠,且每次工具调用往返都要消耗跑者耗不起的延迟。因此所有决策都是纯 Python:哪个节拍去何处、用哪个地标、机动文本是什么、提前多远触发。LLM 只做两件事:
- 导演(一次结构化调用):产出三幕(含链age 范围)、3-5 个目标地标、反派前提,以及一份"故事圣经"(呼号、母题、语气)。
- 节拍写手:为每个节拍基于结构化简报写出最多 30 词的三个变体(巡航、加速、力竭)。
校验器强制词数、地标名白名单和安全规则(不横穿马路、不擅闯)。失败时带违规说明重试一次,再回退到确定性模板。模型藏在 LLMClient 接口后,更换只是一行配置。
用 TabPFN 做配速感知
配速被当作一个小型表格分类问题。每个 tick 构建特征:30 秒/2 分钟/5 分钟滚动速度、速度变化与方差、坡度、距上次停顿时间,以及当前速度相对前两分钟个人基线的比值。
TabPFN 将跑者分类为 CRUISING、ACCELERATING、EXHAUSTED 或 STOPPED,并用 3-tick 滞回过滤器避免剧情来回横跳。它跑在 CPU 上,节流至每 5 秒一次,把所有显存留给 LLM。
开发者坦言一个诚实的注意事项:这里没有标注数据集,训练标签来自对模拟跑步轨迹的透明启发式规则。TabPFN 的价值在于快速、校准良好、秒级重训。如果加载失败,规则分类器会自动接管。
TabPFN 是一种基于 Transformer 的表格数据分类器,它的特点是不需要在你的数据集上做梯度训练即可进行小样本预测——模型已经在大量合成表格任务上预训练过,推理时把训练样本作为上下文直接送入前向计算,类似于表格领域的上下文学习。对 GhostRunner 这类没有标注数据集、特征维度低(滚动速度、方差、坡度等)的场景,TabPFN 能给出校准良好的概率输出,且在 CPU 上即可运行、重训成本几乎为零。滞回过滤器则是状态机中常用的去抖机制:只有当新状态连续若干 tick 都稳定出现时才切换,避免跑者在两个类别之间来回抖动导致剧情变体频繁跳变。
声音与成本:唯一按量计费的部分
ElevenLabs 为每句台词配音,通过未来三个触发器的滚动窗口提前渲染。音频片段按内容寻址(sha256(voice + text)),重复台词零成本。反应式时刻(偏离路线、加快、检查点倒计时)使用预渲染的"C 级储备库",50 毫秒内从磁盘播放。
开发者对单位经济做了坦诚拆解:
- 故事文本:本地、无计量。 一条路线约 25 节拍 × 3 变体 × 约 30 词,约 7-8k tokens,即使走 API 也便宜。本地模型真正买到的是"无计量、无限流"的反复重编译与调参自由。
- 路由、地标、地图:开放数据,无按次费用。 Valhalla 跑在本机,公共 Overpass 有合理使用限制,真实服务应自托管,H3 缓存让地标只抓一次。
- 声音:唯一按量计费成本。 一个完整包约 25 节拍 × 3 变体 × 约 150 字符,约 1.1 万字符。因为包渲染一次反复回放,声音成本是按路线而非按听众计算的。
这种成本结构让一人小生意成为可能:每条路线固定成本,每次跑步近乎零成本。
内容寻址存储 是一种以内容哈希(这里是 sha256(voice + text))作为键的存储方式,相同内容必定得到相同的地址,因此天然去重,重复台词无需重新渲染或重新计费。滚动窗口预渲染 则是在运行时始终提前渲染未来若干个触发点(例如接下来三个节拍)的音频,当跑者真正抵达触发距离时音频已经就绪,避免 TTS 请求的数百毫秒到数秒延迟。两者结合,让声音成本只在编译期按路线发生一次,而非按听众或按播放次数发生,这也是"每条路线固定成本、每次跑步近零成本"单位经济成立的关键工程手段。
开放创新为何重要
对于可能被付费购买的项目,开放的部分决定生意能否成立。
客户位置数据留在他自己掌控中。 跑者 GPS 轨迹是敏感的,而目标客户(俱乐部、学校、雇主、旅游运营商)会问数据去向。在 GhostRunner 中,实时轨迹从不离开运行应用的机器,会话结束后原始轨迹删除。"我们永远看不到你跑者的位置"是一句可以诚实说出的话。
可变更部分无锁定。 叙述者在 LLMClient 接口后,声音在 TTSProvider 接口后,配有 mock provider 让整条流水线在无任何厂商的情况下运行。
利基是一条街,开放数据填得上。 通用 API 处理最差的就是"这栋街角的砖楼",模型再大也不知道,OpenStreetMap 知道。客户端自有地标通过一个 demo_pois.json 文件即可加入。
无信号也能用。 已保存任务在 LLM、路由器和 TTS 全部关闭时回放,正是公园、步道或会议大厅演示所需要的。
商品化路径与现状
销售单位是任务包:一条路线、一个编译好的故事和配音音频,制作一次,无限次回放。潜在买家包括跑步俱乐部、步行导览主办方、酒店和校园。
开发者对外坦白了当前状态:尚未运行付费试点或验证定价,目前保存的任务只能从本机回放。让产品可售的部件(一次编译、永久回放、无按听众成本、按客户更换地标和声音)已经建成。将任务包导出为顾客手机无需服务器即可播放的独立包,是下一步。
社区可从 GitHub 仓库 获取代码,本地运行只需 conda 环境加 docker compose 启动 Valhalla 和 Postgres,再运行 python tools/demo.py。
小结
GhostRunner 展示了一个务实的设计范式:把昂贵的 AI 工作前置到编译期,让运行时只做确定性的毫秒级查找。本地优先不仅解决了延迟问题,还顺带解决了隐私、离线可用与单位经济性。它尚不是一个已验证的生意,但它清晰地证明了:当一个创作者愿意把 2-4B 小模型的约束当作设计条件而非缺陷时,能做出什么。
相关推荐

前端转AI Agent开发:一线架构师解析职业路径与红利
一线AI架构师解析前端转AI Agent开发的行业变化与落地路径:纯前端岗位为何消失、Agent+全栈为何成为招聘主流,以及Agent开发工程师、FDE、远程接单三条转型路线和对应学习策略。

AI Agent开发课程怎么选?五条避坑指南
机械转行开发者踩坑两年总结的AI Agent开发课程筛选清单:完整交付链路、可写进简历的项目、真实开发视角、更新频率、陪跑反馈五个维度,附具体判断方法和避坑要点。

扣子(Coze)智能体搭建入门:零代码开发AI Agent实战指南
扣子(Coze)是字节跳动推出的一站式AI应用开发平台,支持零代码拖拽搭建AI智能体。本文基于B站教程梳理扣子的核心概念、三大优势、国内版与国际版差异,以及智能体与应用的区别,帮助初学者快速入门AI Agent开发。