---
name: vrchat-world-queries
description: "VRChat world: new worlds, recommend, lore, X creators."
version: 1.0.0
metadata:
  hermes:
    tags: [vrchat, gaming, worlds, queries, lore]
    related_skills: [vrc-monitor-agent, vrchat-social-queries]
---

# VRChat 世界域 — 挑世界/推荐/情报挖掘/X 博主

本 skill 覆盖 **vrc-monitor 的世界域**：挑新世界、待逛列表（backlog）、世界推荐、PlanetVRC 检索、世界情报/彩蛋/攻略挖掘、X 博主世界推荐，以及地图列表展示格式。

> ⚠️ **工具表唯一权威在 vrc-monitor-agent skill**。本 skill 只写工作流与域内细节，不复制工具表。通用 MCP 调用陷阱见 vrc-monitor-agent「常见陷阱」。
> MCP 端点：`http://127.0.0.1:8799/mcp`；**开图用 open_world 的完整参数/回退语义见 vrchat-social-queries §11.4**（跨域指针，本域只记调用行）。

## 1. 挑新世界（「没去过的 + 适合聊天的」）

用户说「挑个新世界 / 没去过的 / 适合聊天」时，四步：

1. **拉候选**：`get_new_worlds {onlyUnvisited: true, limit: 50, sortBy: 'favorites'}` → 未逛新世界按收藏降序（⚠️ 字段名 `worldName` 不是 `name`；limit 上限 50，不够可翻页/换 sortBy）
2. **按名粗筛**：剔除明显的游戏图（名字含 Game/战斗/恐怖/Roleplay/赌场等），留氛围向（神殿/旅馆/月亮/安静小屋/咖啡厅）
3. **补简介**：候选逐个 `get_world_name {worldId}` 拿 description + capacity 判断聊天氛围
4. **开图**：用户拍板后调 MCP `open_world {worldId, type:'hidden', region:'jp'}` → 自动建 jp 隐藏房；管道直发优先、不可用静默回退 API 邀请（响应 `method: "api"` = 回退态，非失败）

**用户偏好（通用建议）**：给 3-5 个候选表格（名称/人气/在线/氛围一句话）让用户选；用户说「随机挑一个」用 `random.choice`；用户说「去过某个」就跳过继续推。

**数据坑**：
- **`get_new_worlds` 的 favorites 是扫描时快照，会迅速过期**——展示人气时对候选逐个 `get_world_name` 刷新（缓存命中快），别直接用扫描值
- **`visited` 标记可能不可靠**：`get_new_worlds {onlyUnvisited:true}` 仍可能返回逛过的世界——排除已逛世界时交叉核对 `get_world_name` 返回的 `note` 字段（逛过的世界常留了 note）
- **主题化挑世界（睡觉/聊天/氛围等）**：`search_worlds` 用日英关键词效果很好（如 `おやすみ`/`sleep`）——主题关键词走 API 可靠（区别于中文世界名搜索的坑）

### 世界待逛列表（backlog）

用户说「把这张图加进待逛/待办」时走 3 工具：

- `add_to_backlog {worldId, reason?, priority?}` — 加入待逛列表（本地待办，不动云端收藏；幂等，priority 0-2；世界不在 world_kb 时插兜底行，并自动回填元数据：先读本地 world_cache，缺失才走 `GET /worlds/{id}` 写回缓存 + world_kb（含 created_at），失败只记日志不阻断写入）
- `get_backlog {status? (pending|visited|all 默认 pending), sortBy? (added_at|priority|favorites 默认 added_at), limit?}` — 查看待逛列表；**逛完自动从未逛区消失**（visited 后 pending 不再显示，记录保留在 visited 历史）
- `remove_from_backlog {worldId}` — 移出待逛列表（只清 backlog 标记，保留行，幂等）

## 2. 世界推荐 recommend_worlds

`recommend_worlds`：多源融合（本地新世界池 × PlanetVRC × 官方主题搜索 × 用户反馈）+ 本地状态感知（visited/sleep_ok/note）+ 可解释 reasons 数组 + 一键 open_world 闭环。

- 参数：`theme` / `excludeTheme` / `sources` / `excludeVisited`
- 评分模型：热度 + 新鲜度 + 主题 + 作者画像（30 天窗口熟客）
- 输出带 `reasons`（如 `熟悉度39 + 黄金区63% + group+3` 粒度）
- 用户反馈工具：`rate_world {worldId, rating: 1|-1|0}`（好图加权/烂图降权）、`mark_world_visited {worldId}`（事件驱动 visited 会漏记，开图闭环手动确认）、`favorite_world {worldId, tag?}`（云端收藏，tag 为 `worldsN` / `vrcPlusWorldsN` 动态分组，含 VRC+ 专属收藏夹，写操作需确认）、`move_world_group {worldId, toGroup}`（世界在收藏分组间移动）、`update_favorite_group {group, displayName?}`（重命名分组）、`clear_favorite_group {group}`（清空分组内收藏，destructive 需 confirm）

**作者维度**：作者画像按 `author_id` 聚合（个人偏好 > 圈层热度 > 纯热度，3:1:1 起步），⚠️ **必须用 author_id 匹配不用 author_name**（作者可改名）。

### 按作者列出全部图（get_worlds_by_author）

用户说「把这个作者的全部图找出来 / 全部加权重」时：

1. `get_world_name {worldId, forceRefresh: true}` → 拿当前图 `authorId`（⚠️ 缓存命中的旧行可能无 authorId，用 forceRefresh 走 API 才拿全）
2. `get_worlds_by_author {authorId}` → 列出该作者全部图（worldId/名称/收藏/浏览/容量/标签/发布时间，顺带写 world_cache 含 author_id）；也可直接传 `authorName` 内部经 `/users` 解析
3. 批量操作：遍历 `worlds` 逐个 `rate_world {worldId, rating}` 加权重，或展示给用户选

⚠️ 用 `authorId` 匹配不用 `author_name`（作者可改名）；`limit` 默认 100（「最多 100 张」，不是必然全量），作者图多时显式调大（上限 500）。

## 3. PlanetVRC 地图检索

`search_planet_worlds` / `recommend_planet_worlds`：planetvrchat.net 日文世界目录，适合 VRChat API 搜不到的日文/小众图。

- `search_planet_worlds {query}`：关键词搜索 → 世界名/wrld_id/平台/分类/收藏数；limit 最大 8（每个结果抓详情页补 wrld_id，约 1-2s/个）
- `recommend_planet_worlds {sort}`：sort=popular（访问者数最多）/ new / updated → 世界+wrld_id+最大人数+访问量+收藏数+公开日
- ⚠️ 返回字段是 **`wrldId`**（驼峰）不是 `wrld_id`——反查详情时写错键拿 None（实测踩坑）

## 4. 世界情报/彩蛋/攻略挖掘

用户在世界里发现可疑元素（电话亭/神秘符号/隐藏区域）说"帮我搜一下这图的秘密/攻略"时，**三源组合拳**：

1. **PlanetVRC 收录页**：`https://planetvrchat.net/archives/{postId}`（postId 来自 `recommend_planet_worlds`）——页面**自带 AI 摘要**，常直接点破地图设定，还带作者/分类/发布日/访客收藏数
2. **DuckDuckGo HTML 版**：`https://html.duckduckgo.com/html/?q=<关键词>`——国内直连稳定，正则解析 `result__a`/`result__snippet`；**X 博主的实况介绍推文常被索引**
3. **X oEmbed API**：`https://publish.twitter.com/oembed?url=https://x.com/<user>/status/<id>`——**免登录拿推文完整文本**（`html` 字段剥标签即正文）

**已验证的坑**：
- ❌ Bing 搜索结果页反爬；`r.jina.ai` 渲染代理 403；**X 搜索页面=登录墙**：未登录返回 200 但内容全空——**X 不登录不给任何搜索结果**；要搜 X 必须登录态 cookie（见下文方案）
- **攻略现状判断**：搜"XX 攻略/解け/見つけた"看有没有人公开解出——没人解出就明说"全网暂无攻略，可能是前几个解开的人"
- **解谜进展闭环**：用户每发现一个机关/交互，持续 `set_world_note` 追加到该世界 note（note 即探索笔记，下次带人/续解直接翻）

**调查方法论（可复用）**：
- **作者 X 账号定位（最可靠）**：官方 API `GET /users/{authorId}` 的 **`bioLinks` 数组直接给作者 X/Twitter 链接**，比搜索可靠
- **"哪些博主推过这个图"最快入口 = `x_world_recommendations` 表**：sqlite 按 world_id 或 world_name LIKE 反查，creators JSON 数组带推文 ID/链接
- **推文全文+配图三件套**：oEmbed 拿正文；x.com 单推文页 `og:description` 拿完整正文 + `og:image` 拿配图直链（pbs.twimg.com，下载带 Referer + 代理）
- **X 视频直链提取**：x.com 状态页 HTML 内嵌 JSON 里的 `video.twimg.com/.../*.mp4` 多分辨率直链，正则提取，代理下载
- ⚠️ **用户偏好（通用建议）**：视频/实况素材**不要自己抽帧分析**，直接把视频文件发用户自己看；用户催"先给我链接"时，先发链接/文件再补文字分析
- ⚠️ **世界 ID 反查坑**：用户说"昨天玩的那张图"，按名字 LIKE 会混入无关世界（名字含 Archive/Arch 的搜索工具图）——**先用 `note` 字段反查最可靠**（逛过的世界 note 里有正确 worldId）
- **vrcmap 世界档案站**（`vrcmap.com/world/{worldId}`，代理可达）：世界简介 + 收藏/访问排名 + **作者全部作品列表**（⚠️ 现可直接用 `get_worlds_by_author` 拉作者全部作品，见 §2 作者维度）
- **YouTube 实况搜索**：`youtube.com/results?search_query=<世界名+作者>` 提取 videoId；⚠️ 评论区可能关闭（yt-dlp 返回 0 条 ≠ 失败），别反复重试

**X 搜索登录态（cookie→API 直连方案）**：
- ⚠️ **不要用 computer_use 逐元素点浏览器**——用浏览器 cookie 插件（Cookie-Editor 等）复制登录 cookie，agent 直调平台 API
- 需要 `auth_token` + `ct0`（其余 twid/kdt/lang 更稳），请求头 `x-csrf-token: <ct0>` + Cookie 串，直调 X 搜索 API（GraphQL SearchTimeline 或 `search/adaptive.json`）
- ⚠️ guest token 方案已死（`api.twitter.com/1.1/guest/activate.json` 返回 401）；Nitter `/search/rss` 搜索端点不可用——必须登录态 cookie

## 5. X 博主世界推荐（x_world_digest / x_add_creator 系列）

用户说"关注推特上 XX 博主/添加推荐博主"时：

1. **添加**：`x_add_creator {screen_name, name}`——screen_name 是 X 用户名**不带 @**；name 是展示名（可中文/日文，MSYS curl 发中文会乱码，**必须 Python urllib 直发**）。博主清单存 DB config 表 `x_creators`（JSON 数组），添加即时生效无需重启
2. **验证 handle**：`fetchCreatorRss(screenName)`（`core/fetch-x-worlds.js`，需要代理 env）确认 RSS 可抓
3. **抓取网络前提**：Nitter RSS 国内**直连不通**，必须代理（`VRC_MONITOR_HTTP_PROXY`/`HTTPS_PROXY` env；代理关时自动回退直连）。改 env 后**必须重启服务**才生效
4. **扫描**：`x_scan_creators` 抓全部博主推文→提取世界链接→逐个查 VRChat API 收藏/浏览入库（限流 ~2.6s/个）。**⚠️ 数十个世界要几分钟，MCP 客户端会超时但服务端照常跑完**——超时后等几分钟直接查 `x_world_recommendations` 表验证，别重发
5. **查询**：`x_world_digest {days, limit, creator?, refresh?}` 按收藏排序输出，`favoriteVisitRatio ≥ highlightRatio(默认0.2)` 标 ⭐重点。未配置博主（x_creators=[]）返回空 worlds 不是故障

### ⚠️ 世界挖取经验：t.co 短链解包（2026-09 实测，防漏抓核心）

部分博主（探跡家もっけい mokkei_VE、fox_yata9 等）的世界推荐**不是写在推文文本 `World: X By: Y` 里，而是附带 `https://t.co/XXXX` 短链指向 vrchat 世界页**。t.co 现返回 **200 HTML + `<meta http-equiv=refresh content="0;URL=...">`**（非 HTTP 302），必须抓 body 解析 `URL=` 拿真实 wrld_。服务端 `fetchCreatorTweets` 已内置批量解包（`VRC_MONITOR_X_RESOLVE_TCO=0` 关闭）；**若发现某博主"0 推荐"但用户坚持有，先检查推文是否全是 t.co 短链型**，勿用文本匹配结果反驳。

### ⚠️ 各博主推文格式差异（2026-10 实测，防漏抓核心）

四位常用博主的推荐推文格式**各不相同**，解析器必须逐格式兼容（`core/fetch-x-worlds.js` 的 `extractWorldRefs`）：

| 博主 | 格式 | 解析要点 |
|---|---|---|
| Bradlee1011 | `World name: X` / `By: Y` / `Platform: …` | 标准 By 格式 |
| fox_yata9 | `World:X` / `By:Y`（冒号**无空格**） | 名字正则见下方「世界名正则」 |
| n4rGm5DmrVXXz6I（八谷凛奈） | `世界名\n作者名\n--\n简介`**三行格式** | 前两行分别是名/作者，`--` 分隔描述；作者行支持非 ASCII（日/中/韩作者名） |
| mokkei_VE（探跡家もっけい） | `『日文世界名』`+正文+`https://t.co/xxx` | 名字在『』内，链接全靠 t.co 解包 |

**世界名正则**（`core/fetch-x-worlds.js:924` 现实现）：`/(?:World(?:\s*name)?|ワールド)\s*[:：]\s*…/gi` —— 支持 `World:` / `World name:` / `ワールド：`（**不含** `ワールド名：`）。
⚠️ **历史坑（勿复现）**：曾写 `World\s*name?\s*:`，其中 `name?` 会强制要求 "nam" 字样，导致 `World:X`（fox 格式）漏抓 —— 可选整词须写 `(?:name)?`。**现实现已修正**（此处仅记录曾错形态，勿去「修」一个不存在的 bug）。

**通用标记**：四位常用博主的推荐**实测均带** **`#VRChat_world紹介`** 标签——判断「某条推文是不是世界推荐」时它是很好的判据（比名字/链接正则稳）。服务端三行格式识别即以此标签为前置条件（`looksLikeWorldIntro`）。

> **教训**：曾因解析器只认 `World: X By: Y`，把整批不兼容格式的博主推荐漏抓（八谷三行格式、mokkei 『』格式各漏 20+ 世界），用户拿具体世界名质疑才发现。**「某博主 N 天 0 推荐」≠ 数据真相，先核对推文格式是否被解析器覆盖**。
> 三行格式的作者行**曾**用 `[A-Za-z0-9_\-\.]{2,40}`，把日文/中文作者名（如「よけいっ」「虚拟电波猫」）挡在门外 → 三行分支不命中、整批漏抓；已放宽为 `[^\s\n]{2,40}` 并补单测。

### ⚠️ 世界名解析/筛选：名字+作者搜索兜底（2026-10 实测）

t.co 解包偶尔失败（反爬/超时）或博主只发文字名不给链接时，用 **VRChat 官方 API 名字+作者搜索**找回世界（用户实测验证：**地图名去掉多余标点就能搜到，作者名也能对上**）。以下 URL 为**示意**；服务端实际调用见 `core/fetch-x-worlds.js`（`/worlds?search=` 用 `n=10`、`/users?search=` 用 `n=5`、带 `userId` 的 `/worlds?search=<名>&userId=<id>&n=10`；作者全量作品分页在 `core/tools/events.js`，`n` + `offset`）：

1. **标点清洗（search 前必做）**：去掉 `［Spookality 2026］`/`(IOS対応)`/`（Beta）` 等括号内容、去平台标记（IOS/QUEST/Android対応）、去装饰标点（`․ ˸ ǃ ！ ⁄ ｜ · ・ ．`），再用核心名搜。**带这些符号直接搜搜不到**。
   - 例：`The Dead Forest ［Spookality 2026］(IOS対応)` → 搜 `The Dead Forest`；`Take me away․` → 搜 `Take me away`
2. **名字搜索**：`GET /worlds?search=<清洗后名>&n=10`，逐个比对归一化名（去空格+标点+转小写），要求**精确或长度相近的子串**匹配。⚠️ 长度差大不算命中（避免 `Stalker 2` 误配 `Stalker`——归一化后 `stalker` ⊂ `stalker2` 会假阳，需加长度/尾部数字约束）
3. **作者世界列表法（最可靠，尤其日文/特殊字符名）**：`GET /users?search=<作者名>&n=5` → 取 `userId` → `GET /worlds?search=<名>&userId=<uid>&n=10`（作者过滤）；作者全部作品另走 `/worlds?userId=<uid>` 分页（`n`+`offset`）。作者名能对上时优先用这条（实测恢复了一大批日文世界）
4. **宁缺毋滥**：名字搜索+作者列表都找不到 → 诚实标「待查/NEW」（刚发布的世界 VRChat 索引延迟，属客观查不到），**绝不盲取搜索结果第一条**（用户明确要求防误报）

### ⚠️ VRChat API 认证陷阱（2026-10 实测，静默误判源）

排查「为什么一堆世界待查」时的教训：

- **cookie 过期 → 401 静默误判**：`data/auth_cookie.txt` 的 auth cookie 过期后 API 全部返回 **401**；脚本若把 401/空结果当「世界不存在/未索引」，会把**真实存在的世界整批误判为「待查」**（实测 70 个「待查」里 30+ 是 401 造成的假阴性）
- **自检方法**：拿一个**已知存在**的世界（如 Stalker）直查 `/worlds/<id>`——若连它都 401/404，就是认证问题，不是世界不存在
  ```bash
  COOKIE=$(tr -d '\r\n' < data/auth_cookie.txt)   # 去尾部换行
  curl -s -m 15 "https://api.vrchat.cloud/api/1/worlds/<已知wrld_id>" \
    -H "Cookie: auth=$COOKIE" -H "User-Agent: VRCX-0-Actions-MCP/1.0"
  ```
- ⚠️ **凭据纪律**：cookie 只用变量承载，**绝不 `echo`、绝不写日志/进脚本输出**（同 `AGENTS.md` 对 X cookie 的约束）。
- **UA 建议**：请求带服务同款 `User-Agent: VRCX-0-Actions-MCP/1.0`（裸 `urllib` 默认 UA 曾被 WAF 拦）；排查时先固定 UA 变量，避免与 cookie 问题混淆
- **修复**：重启服务走 OTP 自动登录拿新 cookie（`node start-monitor.js`；服务进程 PATH 无 python 时 OTP 脚本会失败 → 设 env `VRC_MONITOR_PYTHON` 指向 python.exe），或手动 `python scripts/fetch-otp.py <邮箱> <IMAP授权码>` 拿验证码 + 客户端 `_verify2fa` 登录写回 cookie

## 6. 地图列表展示格式（通用推荐）

展示地图列表时按此格式（6 列）：

```
| 序号 | 封面 | 地图名称 | 热度 | 备注 | 地图链接 |
```

- **序号**：列表最前方，从 1 递增
- **封面**：`imageUrl`，Markdown 内嵌 `![短名](url)`
- **热度**：取 `heat` 中优先级最高的非零字段（officialFavorites > occupants > planetVisitors），格式 `<icon><数值><单位>`：🔴≥100万 / 🔵≥1万 / ⚪<1万；数值≥1万缩略为「万」（保留 1 位小数），<1万显示原数
- **备注**：`note`（用户备注，无则省略该列）
- **地图链接**：`https://vrchat.com/home/world/{worldId}`
- **封面列省略规则**：封面无数据 或 QQ Bot 场景（QQ 消息无法渲染外链图片）时省略封面列，其余列不变

### chill/放松向地图推荐过滤规则（2026-08-15 用户修正）

用户要「chill 的室内图」时：
- **排除**随机聊天/轮盘配对社交向（如 No Time Two Talk、omegle 式 roulette、大型交流广场）——主题是 social/chat 的不算 chill
- **聚焦** sleep/onsen/温泉/居家/小憩系，看 author_tag 含 chill/sleep/relax/spa/onsen 的图
- 备注列标注**当前人数**（occupants/capacity）——爆满（occupants > capacity 或接近满）的图即使主题 chill 也要提示人多，chill 推荐优先人少的
- 候选补热度：recommend_worlds 输出的 heat 常为 0，用 get_world_name(worldId) 逐个补查 favorites 再按展示格式输出

## 7. 域内陷阱

### 世界改名 → 缓存陈旧

VRChat 世界**可以改名**，`world_cache` 里的旧名会一直赖着（cache-first 永不刷新）。**策略（2026-08-09 改懒刷新）**：去掉 TTL，缓存命中直接返回，只有 `forceRefresh: true` 才走 API（配合 `get_world_history` 手动刷新时变化一目了然）。

**排查流程（用户否认世界名时）**：不要争论，直接 API 验证：

```bash
COOKIE=$(tr -d '\r\n' < <项目目录>/data/auth_cookie.txt)
curl -s -m 15 "https://api.vrchat.cloud/api/1/worlds/<worldId>" \
  -H "Cookie: auth=$COOKIE" \
  -H "User-Agent: <你的UA>"   # ⚠️ 必须带 UA，否则 WAF 403
```

看 `name` + `updated_at`：`updated_at` 在今天 = 刚改过名。手动修复：`get_world_name` 带 `forceRefresh: true`。
