# coopani-world-journey 中文文档

规范地址：https://npmcn.com/package/coopani-world-journey/

版本：0\.5\.26
npm 元数据更新时间：2026-11-03T07:25:14.913Z
中文译文更新时间：2026-11-03T09:19:42.922Z
软件包许可：MIT

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/coopani-world-journey
原文对照：https://npmcn.com/package/coopani-world-journey/?lang=original

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：756 kB，36 个文件
- 模块格式：ES Module
- TypeScript 类型：未声明
- Node.js 要求：\>=22
- 运行依赖：0 个
- 首次发布：2026-10-31T10:26:02.461Z
- 最新版本发布：2026-11-03T07:25:14.777Z
- 已发布版本数：43（近 12 个月 43 个）

## 中文简介

陪着她真的出门旅行（需 Coopani 0.2.17+）。一百二十四座城，每座都有真实地标，不是随机抽地名。从你设定的家出发，她自己挑去哪、坐什么去，路上真的耗掉那么久——选了火车就在车上待十一小时，中间不打扰你。到了按当地时间吃饭、看日出、住店、写日记；路线是一条线，连着几天说的地名在地图上连得起来。雨天取消日出，钱不够会停下来打几天工，回家是一站一站走回去。没有机场的地方先落到能飞的城市再转。装了「一起听歌」，你放的歌会跟着她走；也能拍照，但要自己填一个生图接口。

## 安装

```sh
npm install coopani-world-journey
```

也可以使用：`pnpm add coopani-world-journey`、`yarn add coopani-world-journey`、`bun add coopani-world-journey`

## README

# 大肥鱼环游世界 · coopani-world-journey

让她**真的出门走一趟**，而不是演一段旅行给你看。

她从你设定的家出发，自己挑地方、自己挑交通方式，路上耗掉的**是真实的时间**； 到了之后按当地时间过日子——吃饭、看日出、回酒店、写日记。日记放在你指定的同步盘里， 换台电脑她还记得自己去过哪。

## 现在能做到什么

| 能做 | 说明 |
| --- | --- |
| 124 座城，每城都有真地标 | 中国全境 + 东亚、东南亚、欧洲、北美、拉美、大洋洲。每城六到九个具体去处，凑不出真名字的城市宁可不写 |
| 自己选目的地 | 按距离分近/中/远三档掷，没去过的优先，不会连着三次都去山城；够不着的地方不进候选 |
| 九种交通方式 | 步行、自行车、电瓶车、摩托车、自驾、长途汽车、火车、轮船、飞机 |
| 用时是真算的 | 同一段距离，步行与飞机差两个数量级；短途飞机只快一小时，因为两头各要三小时 |
| 不会出现「步行去巴黎」 | 每种方式有距离上限，船必须两端都有客运码头 |
| 到站按当地时间 | 时区靠 `Intl` 算，夏令时也对；日出日落用 NOAA 公式本地算 |
| 一天有作息 | 三个饭点固定；晚八点后没夜间活动就回酒店；有日出条件才排凌晨起床 |
| 酒店掷骰 | 六成普通连锁、四成景观房、不到百分之一是那种住一晚就后悔的小店 |
| 日记跟着人走 | 一个月一个 Markdown，可放同步盘；冲突副本按天合并，旧的一天一篇会自动并进来 |
| 半年上限 | 一次最多连续在外几个月（可调），到了自动回家，回家休息几天（可调） |
| 有些地方在作品里出现过 | 排到那儿的时候她会知道这是哪部作品的哪个场景 —— 但**不知道剧情**，也不会补清单外的地名。首批 10 处，札幌和里约故意空着 |
| 能跟作者说两句 | 面板底部「说两句」：报 bug、提想加的地点。默认就指向作者，装了直接能报 |

## 为什么不用高德

**能用，但只当尺子，不当剧本。**

-   距离、时区、日出日落、有没有码头 —— 全部本地算。**没网也能走。**
-   高德只在两处值得用：每天的天气、以及「这个地点是否真的存在」的抽查。
-   它给不了火车班次和节日，也写不出「当地是什么味道」。后者交给她自己写。

半年连续旅行天天调接口，个人开发者配额几天就烧完。配额一没，表现不是报错， 是她开始乱走 —— 所以底座必须内置。

### 天气：两个源，留空也有

| 情况 | 走谁 |
| --- | --- |
| 国内 + 填了高德密钥 | 高德（分昼夜、中文天气名准，但个人 key 有 QPS 限制） |
| 国外（任何情况） | Open-Meteo（高德不覆盖） |
| 国内 + 没填密钥 / key 失效 | Open-Meteo |

**不填密钥不等于没有天气。** 早先设置页写着「留空天气标为未知」，那是文案没跟上实现 —— 0.2.6 起两处统一：留空自动走 Open-Meteo，免密钥、按经纬度直查、覆盖全球， 只是日预报只有一个天气码，夜里沿用白天的值。风、温度、下不下雨这些够用。

key 失效也只是降级，不是故障：下一次查询就落到 Open-Meteo，旅行一步都不耽误。

## 装

1.  需要 **Coopani 0.2.17 以上**。
2.  在 `data/extensions/package.json` 的 `dependencies` 里加一行： `"coopani-world-journey": "link:<这个包的绝对路径>"`
3.  `pnpm install`
4.  `config.json` 的 `worlds.journey.enabled` 设 `true`，或者从 World 页面打开
5.  **重启 Core**（改 `.ts` 之后必须，不能用 `/api/worlds/restart`）

## 打开这个世界：两个页签

**她此刻** —— 她所在的城市、**她那边的**时间、身体感觉、今晚住哪、日出几点。 在路上时多一条进度：走了百分之几、还剩多久、当地几点到站。 下面是一条时间轴：刚过去的那个点（灰的）加接下来四件事，**每一条都有时刻**。

这是这个面板最值钱的地方。别处只能告诉你「配置好了没有」，这里告诉你**她下一件事什么时候来**。

**照片** —— 她路上拍的。一天一张，全是空镜：窗外、街角、傍晚的城墙。**画面里没有她。**

### 她在家的时候：一个开关，一个按钮

她在家时，面板上多一块：

-   **开关「出门等我点头」** —— 打开后她不会自己走，就在家等着你按。
-   **按钮「让她出发」** —— 按一下她就动身，然后按钮锁住，直到她回家。

开关默认**关**。关着的时候她是自己过日子的：歇够了就走，不问。这是这个扩展的主张 —— 打开就能看见它自己在动。想自己掌控节奏的人把开关打开即可，两种人都照顾到了。

按钮锁着的时候会说清为什么：还在歇着（显示歇到哪天）、或者这会儿没找到能去的地方。

**手按的那一下会跳过两个自动门禁**：加载宽限、以及 7–21 点的出门窗口。 你明确说「现在走吧」，再回你一句「现在不是出门的点」就是抬杠。

### 第一次打开会有三句话

不是配置检查清单 —— 这个扩展不配置也能跑。引导讲的是三件最容易被误解的事：

1.  上面那个钟是**她那边**的时间，跟你几点没关系
2.  **下一站她自己挑**，你不用安排，也不用催
3.  你能做的三件事：问她在哪、让她去某个地方、翻她的日记

点「知道了」收起。之后想再看，页脚有「再看一次引导」。

## 四条指示灯

World 列表上，这个扩展名字右边有四个小圆点 —— 那是它自报的链路状态，鼠标悬停看详情。

| 灯 | 绿 | 红 | 灰 |
| --- | --- | --- | --- |
| 天气 | 上次查到了（高德 / Open-Meteo） | 上次没查到 | 还没查过 |
| 日记 | 目录可写 | 写不进去（悬停看原因） | — |
| 照片 | 上一次拍成了 | 上一次没拍成（日志里搜「没拍成」） | 没配接口 / 还没拍过 |
| 听歌 | 在放《歌名》 | — | 没在放，或者没装那个扩展 |

**「没启用」是灰的，不是红的** —— 没配生图接口不算故障，红点会让人以为坏了。 反过来，**灯说的是「上一次发生了什么」，不是体检报告**：没网的时候天气也会红， 那跟配置错了是两回事。

## 和「一起听歌」联动

装了 `coopani-world-music` 的话：

-   面板上多一行「**在放 · 歌名 · 歌手**」。这一行同时是**联通性指示灯** —— 有歌，说明这边读到了那个扩展写出来的快照；一直是空，就是它换了路径、或者你没在放。
-   她带着你正在放的歌出门，出发和路上会提一句。
-   **歌名里带地名就当场进「想去」**（《成都》《回到拉萨》），不用等出发那一刻 —— 面板上会多一行「想去 · 成都」。下次挑目的地，那个地方权重大得多。
-   **这边不抢话。** 换歌时她不专门开口 —— 那是「一起听歌」自己的事（它换歌必说一句）。 这边的活儿是让她心里多一个想去的地方，等合适的时候自己提。

**这是读文件实现的，不是接口。** 宿主没有让两个 World 互查状态的通道， 所以这边只读 `coopani-nowplaying.json`，不碰那个扩展的进程。 代价是路径约定必须对齐 —— 2026-10-31 它把快照搬到了 `<core>/data/tmp/`， 这边还在读系统临时目录，**联动静默失效了七个小时**，用户是靠「好像没了（」发现的。 现在会从 World 目录逐层向上找，几个候选里取最新的那份。

## 设置项

设置页按「用户来这儿想干什么」分三组，不是按技术分类 —— **最常改的在最上面，一辈子不动一次的沉到底**。

### 出发与节奏

| 项 | 默认 | 说明 |
| --- | --- | --- |
| 从哪儿出发 | 空 | 填城市名（兰州 / 西安）。**改它等于搬家**：她在家就跟着搬过去，在外面就把回家的目标换成新家 |
| 时间倍速 | 1 | 1 = 真实速度。4 = 一天压缩成六小时。24 = 一天一小时 |
| 路上消息间隔 | 2 | 路上隔多久冒一句（服务区、过站、餐车）。0 = 一路安静，只在出发和到站开口。一趟最多三条 |
| 出门等你点头 | 关 | 打开后她不会自己走，只在面板上按「让她出发」。关着就自己按节奏走 |
| 回家后歇几天 | 3 | 0 = 当天就能再走 |
| 一次最多在外多久 | 6 | 到了强制回家 |

### 她的日子

| 项 | 默认 | 说明 |
| --- | --- | --- |
| 让你跟着一块走 | 关 | 打开后她说「我们」，日记里会提到你。**只是叙事，她并不知道你此刻在做什么** |
| 记花销 | 关 | 打开才累计交通、房费、三餐 |
| 预算 | 0 | 0 = 不限。见底时她会自己降级 |
| 日记保存位置 | 空 | 留空跟本机。填同步盘则多设备共用 |

### 可选接口

| 项 | 默认 | 说明 |
| --- | --- | --- |
| 高德密钥 | 空 | 填了用高德（分昼夜、中文天气名准），**还能查到真实的酒店名**。**留空自动用 Open-Meteo**，一样有天气，只是酒店只说类别 |
| 生图接口 / 密钥 / 模型 | 空 | 留空就把景色写成文字。填好就一天一张空镜（同城同日不重复） |

### 反馈

| 项 | 默认 | 说明 |
| --- | --- | --- |
| 反馈邮箱 | 作者的地址 | 「她此刻」底部那一栏「说两句」发给谁：两个按钮，一个报 bug、一个提想加的地点，点开是封写好的信，版本号和她当时在哪儿已经填进去了。**默认就是作者，装了不用翻设置就能报**；想换个人收就改这里，**清空则整块不显示**。它只在你点了之后才发，不会自己往外送东西 |

页面顶部还有两个徽章，不点任何东西就知道**她现在在哪儿**（「现在 在去天水的路上」）、去过几个地方、花了多少。

### 生图：她真的会拍一张

填好「生图接口」和「生图模型」，她出去逛的时候会顺手拍一张 —— 一天一张，同城同日不重复。

-   **只拍风景，不拍自己。** 没有角色参考图、没有双候选、没有一致性审核， 因为画面里根本没有她。代价是你看不到「她在照片里」，换来的是一致性问题当场消失。
-   **图进宿主的媒体库**（`data/media/`，按内容哈希存，重复的只存一份）， 事件里只带一个 `log:` 句柄，面板拿句柄换图。
-   **失败静默。** 接口挂了、超时、额度没了 —— 旅行一步都不停，只在日志里留一行。
-   **留空就只写字不拍**，相册是空的，别的照旧。

#### 三个框各填什么

| 框 | 填什么 | 举例 |
| --- | --- | --- |
| 生图服务地址 | 服务商的 API 根地址，**填到 `/v1` 或 `/api/v3` 为止** | 硅基流动 `https://api.siliconflow.cn/v1`；火山方舟 `https://ark.cn-beijing.volces.com/api/v3`；OpenAI `https://api.openai.com/v1` |
| 生图密钥 | 那串 API Key，服务商控制台里生成的 | 硅基流动 `sk-...`；方舟的 `ARK_API_KEY`；OpenAI 的 `sk-...` |
| 生图模型名 | 服务商文档里的**图片**模型 ID，各家叫法不同 | 硅基流动 `Kwai-Kolors/Kolors`（免费、中文提示词最顺）、`Tongyi-MAI/Z-Image-Turbo`；方舟 `doubao-seedream-5-0-lite-260128`；OpenAI `gpt-image-1` |

**想先试试又不想折腾？** 硅基流动注册即可用，国内外都能直连，`Kwai-Kolors/Kolors` 那档是免费的， 画质够做旅行空镜。一天一张的用量，收费档也就几分钱。

**别把 `images/generations` 填进地址。** 那个路径程序自己会拼 —— 填进去会变成 `.../images/generations/images/generations`，然后 404。

**填错了不会静默。** 日志里会写明原因（HTTP 状态 + 服务端原话，比如 `这张没拍成（黄昏的麦积山）：服务返回 HTTP 401：Invalid API key`）。 相册空着的时候，先去 `core.log` 里搜「没拍成」。

各家对尺寸和返回格式的写法不一样，程序会**照错误文本换一次再试**，最多试三次：

| 服务挑的东西 | 第二次怎么发 |
| --- | --- |
| 不认 `size`，只认 `image_size`（硅基流动、阿里百炼那一支） | 换成 `image_size`，尺寸值不变 |
| 不认 `response_format`（有的自建兼容层） | 去掉它，回头取 `data[0].url` 也能拿到图 |
| 尺寸值本身不收（OpenAI 要 `1024x1024`，方舟要 `2K`） | 什么尺寸都不带，用服务商的默认值 |

两种字段同时被挑的情况不追 —— 让它报错进日志，比连发五六次把限速打满强。

### 倍速到底影响什么

```
世界时刻 = 基准世界时刻 + (真实时刻 − 基准真实时刻) × 倍速
```

所有旅行判断都在**世界时刻**这条轴上：当地几点、到没到站、待了几天、日出日落。 倍速只决定这条轴跑多快，**不改内容** —— 火车还是走那么久，还是那个城市，还是那个房型。

-   换挡时世界时间**连续**，不会因为 1x 切 24x 就跳过两周
-   巡检间隔跟着倍速自适应（1x→60 秒、24x→37.5 秒），保证一次巡检跨过的世界时间不超过 15 分钟

⚠ **高倍速下会跳点**：巡检要等宿主把事件处理完，模型思考期间世界时间照走。 24x 通常只跳一两个作息节点；**别长期挂 240x**，那是压测用的极端值。 日常建议 1x 或 4x。

## 她怎么决定去哪

三个约束叠在一起。**都不是写死的规则，是权重** —— 写死规则换一对城市就露馅。

### 一、方向：在外面逛不走回头路

她记着上一段往哪走（`heading`，一个方位角）。下一站顺着这个方向加权：

| 和上一段的夹角 | 权重 |
| --- | --- |
| ≤55°（顺着） | ×1.6 |
| 55～100°（侧向） | ×1.0 |
| 100～140°（侧后方） | ×0.5 |
| \>140°（原路折返） | ×0.12 |

夹角超过 150° 直接从候选里剔掉 —— 但**剔空了再放开**，否则她到了漠河这种角落会出不来。

没有这一层的时候，选点是个纯随机游走：每一段单独看都「合理」，连起来是 九寨沟 → 南宁 → 银川 这种 Z 字。四个字：**局部对，整体错**。

### 二、节奏：远的走完，近的转几个

距离分四档：**周边**（≤300km）30%、**近**（300-800）28%、**中**（800-2500）32%、**远**（>2500）10%。

上一段超过 1400 公里，远档压到 2%、周边提到 55% —— 真实的长途旅行是「一段远的 + 在落脚点周围散开 + 再一段远的」，不是连着横穿。

> 为什么最细一档是 300 而不是 800：**八百公里在中国已经是大跳了**。 原来最细的档是 0-800，等于「到了新地方先在附近转转」这件事根本没有地方发生。

### 三、气候：她是**从你家出发**的鱼

城市表有经纬度，能算距离、时区、日出 —— 但**算不出「她受不受得了」**。 所以另有一层气候档案（八个档：干温 / 干冷 / 高原 / 严寒 / 温带 / 湿温 / 湿热 / 热带）。

**以家为基准，代码里没有一个城市是写死的。** 你改出发地，基准跟着变 —— 同一个南宁，对兰州来的和对南宁本地来的，是两种说法。

（提示词里那句「你从哪儿出来的鱼」也走同一个变量 `{{journey.home}}`， 家那边的体感由城市数据现算成 `{{journey.homeFeel}}`。**公开扩展不该带着作者的家乡。**）

不合胃口的地方**去得少，但不是不去**（权重下限 0.42）。半年在外面本来就该见识 不一样的地方 —— 不习惯的表现应该是「去了以后难受、说得出来」，不是「永远不去」。

她随时说得出受不受得了：`journey_now` 里有一行「身体感觉」， 到达事件里也带一句。但那是**事实**，不是让她念的句子 —— 提示词里明写了「别播报气温，那是天气 App 的话」。

### 回家是**走回去**的，不是传送

决定回去之后：

-   离家 500 公里内才一趟车到家；还在外面就一站一站往回走
-   途中每一站都算真的到过 —— 有路上事件、写日记、有花费，住一晚就走
-   选点三级收敛：先找方向朝家且**明显更近**（≥25%）的，没有就放宽到 8%， 再没有才退回纯距离（死角兜底，宁可绕一点也不能回不了家）
-   depart 文案单独一套：「往〈家〉的方向走。下一站某某，离家还有多少公里」

**回程也是旅行。** 以前是直接把位置改成家 —— 两千公里一步到， 中间的城市一个都不算去过。那段时间在她的日记里是**空的**， 而真实的旅行里，回程常常是想法最多的一段。

## 数据存在哪

| 数据 | 位置 | 能不能跟着人走 |
| --- | --- | --- |
| 行程状态（现在在哪） | `<dataDir>/state.json` | **不能**。两台机器共写会互相覆盖 |
| 日记 | 设置的目录，或 `<dataDir>/diary` | 能。按日期前缀合并冲突副本 |

**这条分界不是随便划的。** 放反了不会报错，只会出现「回家打开电脑，看到的是公司那台 三天前的位置」这种幻觉。音乐扩展踩过同一个坑。

⚠ 用网盘时把这个文件夹设成「**始终保留在此设备上**」。网盘的「按需文件」会把人文件留成 占位符，离线读不到，看起来和「记忆丢了」一模一样。

## 已知限制

-   **没填高德密钥时，天气不分昼夜**：夜里沿用当天白天那个预报值。所以「傍晚雨停了」 这种变化她感受不到。填个高德密钥就能补上（见上一节）。
-   **照片只在本机**：媒体库在 `data/media/`，不跟着同步盘走。换台电脑，日记还在，照片没了。
-   **节日没做**（万圣节、春运这些还没进作息表）——见 CHANGELOG 的下一步。
-   **酒店名依赖高德。** 店名从高德的地点库查（真名 + 地址），查不到就只说「县城宾馆」这种类别。 名单里会滤掉「暂停营业」和明显不是住处的地方（实测高德会把餐厅也归进住宿服务）。 它也会过时 —— 高德的库不是实时的，赶上刚关门的店只能认。
-   **没有真实时刻表。** 12306 不向第三方提供数据，航班同理，所以车次号是**按线路和日期生成**的 （同一天同一条线路固定）。它不会自相矛盾，但也不是真的能拿去买票的班次。 同理不做「余票」「晚点」—— 那些必须实时数据，编一个就等于把可信度搭进去。
-   **目的地只有 90 多个**，内置坐标精度到小数点后两位，够算距离，不够导航。
-   **改倍速不影响已经在路上的那一段**，下一段才生效。
-   到达后「城里的一处地方」是**占位描述**，不是具体景点。她要是编一个具体景点出来， 那是她没守住提示词里的第一条约束。

## 测试

```bash
# 152 项：距离、时长、时区、日出、作息表、酒店概率、选路权重、路上节点密度、照片、巡礼、跨海
node "<Coopanion>/resources/app/node_modules/tsx/dist/cli.mjs" --test test/journey.test.mjs

# 三十天压缩模拟：查状态机会不会卡死、会不会重复推送
node "<Coopanion>/resources/app/node_modules/tsx/dist/cli.mjs" test/sim.mts 30 24

# 装载自检：33 项
node "<Coopanion>/resources/app/node_modules/tsx/dist/cli.mjs" \
     "<skill>/scripts/verify-world.mjs" .
```

## 常见问题

### coopani-world-journey 是什么？

陪着她真的出门旅行（需 Coopani 0\.2\.17\+）。一百二十四座城，每座都有真实地标，不是随机抽地名。从你设定的家出发，她自己挑去哪、坐什么去，路上真的耗掉那么久——选了火车就在车上待十一小时，中间不打扰你。到了按当地时间吃饭、看日出、住店、写日记；路线是一条线，连着几天说的地名在地图上连得起来。雨天取消日出，钱不够会停下来打几天工，回家是一站一站走回去。没有机场的地方先落到能飞的城市再转。装了「一起听歌」，你放的歌会跟着她走；也能拍照，但要自己填一个生图接口。

### 如何安装 coopani-world-journey？

在项目目录运行 npm install coopani-world-journey，或使用 pnpm add / yarn add / bun add。

### coopani-world-journey 支持 TypeScript 吗？

该包的 package\.json 没有声明类型入口。需要类型提示时，可以在 npm 上查找社区维护的 @types/coopani-world-journey 是否存在。

### coopani-world-journey 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### coopani-world-journey 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### coopani-world-journey 可以商用吗？

coopani-world-journey 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### coopani-world-journey 还在维护吗？

最新版本 0\.5\.26 发布于 2026/11/03（今天）；至今共发布 43 个版本，其中过去 12 个月发布了 43 个；首次发布于 2026/10/31。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

