最近把一个自己维护的在线 AI 酒馆项目升级到了 V1.4,这次更新的主题是“互动跑团”。很多朋友看到版本号第一反应是“又改了 UI 吧”,但实际上,从常规的 AI 角色扮演聊天切换到一张完整的 TRPG 跑团桌,改动面比想象中大得多。角色要立得住、剧情要接得上、行动要有判定、状态要能持久化,这不是单纯换一套主题能解决的问题。
本文会从开发者的视角,把像素酒馆 V1.4 的互动跑团能力拆开讲清楚:它做了哪些架构级的调整、状态和上下文是怎么处理的、更新日志里为什么能沉淀出“100 多项优化”。同时也给出一些可以复用的设计思路和示例代码,方便想自己做 AI 跑团应用、或者正在做 LLM 对话类产品的同学参考。如果你是第一次听说“酒馆”这类项目,也不用担心,前两节会先把基础概念补上。
1. 项目背景与核心概念
1.1 什么是像素酒馆
像素酒馆是一个运行在浏览器里的“AI 角色扮演空间”。你可以把它理解成一个带有人物卡、世界设定、对话历史和剧情分支管理的前端客户端。用户导入一张角色卡,AI 就扮演卡片里定义的人物;用户再配置一个世界设定,AI 就能在这个设定下展开剧情。
这类项目的通用工作方式是:前端把角色设定、历史对话、用户输入拼装成消息列表,调用大模型 API,拿到模型生成结果后渲染到聊天面板。像素风格的美术与交互设计,让整个“房间”更像一个复古 RPG 的冒险场景。
而从工程角度看,它就是一套围绕 LLM API 封装出来的状态管理 + 提示词管理 + 界面渲染系统。它解决的核心问题是:如何让一个无状态的对话模型,在相对长的时间内记住人物性格、任务目标和世界规则,并稳定地产出符合预期的内容。
1.2 从聊天到跑团:V1.4 要解决什么
在早期版本里,AI 的角色扮演更接近“陪你聊剧情”。用户说一句,AI 回一句,整体没有回合制、没有完整状态面板,也没有基于规则的判定机制。这样的体验胜在自由,但当用户想跑一场正规的 TRPG 团时,问题就暴露了:
- 玩家攻击一只怪物,AI 可能随口说“你赢了”,没有伤害计算,也没有怪物剩余血量。
- 战斗持续几轮之后,AI 会忘记玩家还剩多少血、身上有哪些道具。
- 玩家移动、探索、对话、使用道具时,缺少统一的规则约束,导致剧情前后矛盾。
- 对话一旦变长,早期内容被挤出上下文窗口,所有设定都可能被“遗忘”。
V1.4 的互动跑团,本质上是在原有的“角色扮演聊天系统”之上,增加了一套轻量级的游戏状态机。AI 不再只是“扮演角色”,还承担了游戏主持人(GM,Game Master)的职责:描述场景、执行判定、更新角色状态、推进剧情。为了做到这一点,项目必须重新设计数据结构、提示词模板和上下文管理策略。
1.3 核心概念一览
| 概念 | 说明 | 在跑团中的作用 |
|---|---|---|
| 角色卡 | 描述角色性格、背景、说话风格的结构化文本 | 定义玩家扮演的角色外观和言行 |
| 预设 | 一套完整的系统提示词,决定 AI 的角色和回复风格 | 把 AI 设定成“GM”或“旁白” |
| 世界书 | 按关键词触发注入的背景知识库 | 在进入特定场景时补充地图、组织、人物背景 |
| 上下文窗口 | 模型单次请求能接收的最大 Token 数 | 决定历史对话和状态能携带多少信息 |
| 系统提示词 | 消息列表里 role=system 的内容 | 是告诉 AI“你是 GM,必须按规则判定”的关键 |
| 结构化输出 | 让模型返回可解析的 JSON 或 Markdown 块 | 模型把“剧情文本”和“状态变更数据”分开返回 |
| 状态机 | 维护游戏状态的程序模块 | 持久化玩家 HP、MP、道具、任务、敌人状态 |
| GM/NPC | 游戏主持人和非玩家角色 | AI 在跑团模式中的身份 |
2. 技术架构与运行原理
2.1 整体架构
像素酒馆的整体架构并不复杂,大致可以分为三层:
- 前端层:负责角色卡管理、聊天界面、跑团状态面板、设置页。它可以是纯静态网页,也可以是 SPA 应用。
- 代理层:可选。为了保护 API Key、处理跨域和限流,通常会用一个轻量后端做请求转发。
- 模型层:兼容 OpenAI 格式的大模型 API,也可以扩展到其他兼容接口的模型服务。
一次完整的请求链路可以这样描述:
用户输入行动文本 ↓ 前端组装 system + history + user 消息 ↓ 后端代理转发至 LLM API ↓ 模型返回剧情描述 + 状态变更 JSON ↓ 前端解析并更新游戏状态 ↓ 聊天面板和状态面板同步渲染这套链路里最核心的不是网络请求,而是“组装消息”和“解析返回”两个环节。它们直接决定了 AI 能不能稳定地扮演 GM。
2.2 核心模块划分
开发这类项目时,代码最好不要全部堆在一个页面里。按职责拆分模块,后期维护会轻松很多。常见的模块划分如下:
- 角色卡模块:负责导入、校验、导出角色卡,解析卡内的姓名、设定、示例对话等字段。
- 世界书模块:根据用户当前输入或场景关键词,命中对应条目并注入到系统提示词中。
- 上下文管理模块:负责滑动窗口、历史摘要、消息截断,避免请求超过 Token 限制。
- 跑团状态模块:维护玩家、敌人、任务、地图、回合数等结构化状态。
- 解析模块:从模型输出中提取剧情文本和状态更新片段,并做容错。
- API 适配层:统一不同模型服务的请求格式、错误码、限流策略。
2.3 为什么需要“跑团状态机”
普通聊天应用只需要保存消息列表,但跑团需要保存“游戏世界事实”。比如:
- 玩家当前所在位置;
- 玩家和每个敌人剩余血量;
- 玩家背包里的道具;
- 当前主线任务和支线任务进度;
- 战斗是否正在进行。
如果这些信息只靠模型从历史对话里“回忆”,结果就是不可控的。更可靠的做法是:程序自己维护一份 JSON 状态,每次请求前把状态序列化后拼进系统提示词,让模型基于这份状态做出判定;模型返回时,再通过结构化输出把变更后的状态回传给程序。这样即使上下文窗口里有大量剧情文本,游戏状态也不会丢失。
3. 环境准备与运行方式
3.1 运行环境要求
如果你只是想体验像素酒馆,最基础的条件就是一台能联网的电脑或手机,以及一个现代浏览器。推荐使用 Chrome、Edge 或 Firefox 的最新版本,因为跑团面板里有大量 DOM 更新和本地缓存操作,旧版浏览器容易出现兼容问题。
如果你准备在本地二次开发,则需要准备:
- Node.js 16 或更高版本(不同项目要求不同,以实际 README 为准);
- npm、pnpm 或 yarn 中的任意一个包管理器;
- 可选:Docker,用来快速构建前端静态资源;
- 可选:一个符合 OpenAI 接口规范的模型服务地址和 API Key。
版本不需要完全照抄本文,重点是把环境跑通,再看项目自己的要求。
3.2 获取项目并启动
这里以常见的前端项目启动方式为例。如果你拿到了像素酒馆的源码仓库,典型步骤如下:
git clone https://example.com/pixel-tavern.git cd pixel-tavern npm install npm run dev启动后,终端会输出一个本地访问地址,通常是http://localhost:5173或类似端口。用浏览器打开后,先到设置页填写模型 API 地址和密钥,再导入或创建一张角色卡,就可以开始对话。
如果你更习惯用 Docker 部署,可以这样操作:
docker build -t pixel-tavern . docker run -p 8080:80 pixel-tavern这里需要注意的是,对外提供在线服务时,不要把 API Key 明文写进前端静态资源。更稳妥的做法是使用后端代理转发请求,或在服务端配置环境变量。
3.3 配置文件示例
下面是一个典型的配置文件示例。它演示了跑团模式下需要关注的配置项:
{ "appName": "pixel-tavern", "apiBase": "https://api.example.com/v1", "model": "gpt-4o-mini", "temperature": 0.8, "maxTokens": 2048, "contextWindow": 8192, "sessionStorage": "indexeddb", "feature": { "trpgMode": true, "structuredOutput": true } }参数说明:
apiBase:模型服务的 API 地址,示例地址不可直接使用,需要替换成你自己可用的服务地址。model:模型名称,具体值由你的模型服务决定。temperature:采样温度。跑团叙事建议设置在 0.7 到 0.9 之间,太低会让回复枯燥,太高容易偏离设定。maxTokens:单次回复的最大 Token 数。contextWindow:模型上下文窗口大小,上下文管理器会根据这个值决定保留多少历史消息。trpgMode:跑团模式开关。structuredOutput:结构化输出开关。开启后,系统提示词会要求模型返回带 JSON 块的文本。
3.4 项目目录结构参考
一个清晰的前端工程目录可能长这样:
pixel-tavern/ ├─ public/ ├─ src/ │ ├─ api/ │ │ └─ llmClient.ts │ ├─ core/ │ │ ├─ contextManager.ts │ │ ├─ gameState.ts │ │ └─ parser.ts │ ├─ modules/ │ │ ├─ characterCard.ts │ │ └─ worldBook.ts │ ├─ panels/ │ │ ├─ ChatPanel.vue │ │ └─ StatePanel.vue │ ├─ main.ts │ └─ config.ts └─ package.json目录职责很明确:api层负责和模型服务通信,core层负责上下文、状态和解析逻辑,panels层是同 UI 组件,config.ts管理用户配置。这样拆分之后,即使界面完全重写,核心游戏逻辑依然可以复用。
4. 互动跑团系统的核心设计
4.1 使用结构化状态保存游戏世界
跑团模式的第一步,是定义一份程序可读、模型也能看懂的“游戏状态 JSON”。之前提到过,状态应该由程序维护,而不是让模型凭记忆输出。下面是一个简化示例:
{ "players": [ { "name": "林澈", "hp": 20, "maxHp": 20, "mp": 6, "maxMp": 6, "attack": 5, "defense": 3, "skills": ["火球术", "侦查"], "inventory": ["治疗药水", "铜钥匙"] } ], "enemies": [ { "name": "狼人", "hp": 12, "attack": 4, "defense": 1 } ], "location": "阴雾森林北侧", "quests": ["寻找失踪的商队"], "turn": 1, "combatActive": true }这份 JSON 的价值在于:它可以用极少的 Token 量描述“当前世界发生了什么”。把这份状态 JSON 序列化后拼进系统提示词,模型就知道了玩家的血量、位置、敌人状态和任务进度。
这里有一个设计要点:不要把完整的技能描述、道具说明全塞进状态 JSON,否则上下文会被大量占满。状态 JSON 只保存“事实”,具体规则和效果说明可以放到世界书里,按需触发。
4.2 系统提示词模板设计
跑团模式能否成立,很大程度上取决于系统提示词的质量。下面是一个简化的 GM 提示词模板:
你是一位 TRPG 游戏主持人(GM),请严格遵循以下规则: 1. 当前游戏状态如下: {gameStateJson} 2. 每回合开始时,先用一段不超过 80 字的文字描述场景,再等待玩家行动。 3. 当玩家提出行动时,需要判断行动是否可行。如果行动涉及攻击、技能、道具、移动,要给出判定依据和结果。 4. 如果行动改变了游戏状态,必须输出一个 JSON 块,格式如下: ```json { "narrative": "剧情描述文本", "stateUpdate": { "players": [...], "enemies": [...], "location": "...", "quests": [...] } }- 不要跳过判定,不要替玩家做决定。
- 保持像素酒馆的复古冒险氛围,语言简洁有画面感。
这个模板的关键是“固定输出格式”。模型发挥不稳定是常态,但如果我们在系统提示词里要求它输出固定结构,再在代码层做解析兜底,稳定性就会大大提高。 ### 4.3 调用模型与解析返回内容 下面给出一个前端调用模型接口的示例。这里使用 `fetch` 直接请求 OpenAI 兼容接口: ```javascript // 文件路径:src/api/llmClient.ts async function requestGMReply(userInput, gameState, history) { const systemPrompt = buildGamePrompt(gameState); const messages = [ { role: 'system', content: systemPrompt }, ...history.slice(-20), { role: 'user', content: userInput } ]; const resp = await fetch(config.apiBase + '/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + config.apiKey }, body: JSON.stringify({ model: config.model, temperature: config.temperature, max_tokens: config.maxTokens, messages }) }); const data = await resp.json(); return data.choices[0].message.content; }请求发出后,模型返回的内容通常是“剧情描述 + 一个 JSON 块”。接下来需要解析这个混合文本。这里给出一个容错解析函数:
// 文件路径:src/core/parser.ts function parseGMOutput(rawText) { const jsonMatch = rawText.match(/```json\n([\s\S]*?)\n```/); if (!jsonMatch) { // 没有 JSON 块时,把整段文本当作剧情描述 return { narrative: rawText.trim(), stateUpdate: null }; } try { const parsed = JSON.parse(jsonMatch[1]); return { narrative: parsed.narrative || rawText.trim(), stateUpdate: parsed.stateUpdate || null }; } catch (e) { // 解析失败时不打断对话,只展示原文 return { narrative: rawText.trim(), stateUpdate: null }; } }解析之后,需要根据stateUpdate更新游戏状态。这里使用一个简化实现:
// 文件路径:src/core/gameState.ts function applyStateUpdate(gameState, stateUpdate) { if (!stateUpdate) { return gameState; } const next = JSON.parse(JSON.stringify(gameState)); if (stateUpdate.players) { next.players = stateUpdate.players; } if (stateUpdate.enemies) { next.enemies = stateUpdate.enemies; } if (stateUpdate.location) { next.location = stateUpdate.location; } if (stateUpdate.quests) { next.quests = stateUpdate.quests; } if (typeof stateUpdate.turn === 'number') { next.turn = stateUpdate.turn; } return next; }示例代码的思路是“宁可保守,也不能让状态错乱”。如果模型返回的状态字段缺少某个数组,程序就保留旧值。这样即使某次模型只返回了剧情文本,也不会导致玩家血量被清空。
4.4 长对话记忆与上下文裁剪
跑团对话非常容易超长。一个冒险团可能连续跑两个小时,产生几万 Token 的历史记录。如果每次请求都把全部历史发给模型,费用和延迟都无法接受。
常见的做法是“最近 N 轮 + 历史摘要”:
// 文件路径:src/core/contextManager.ts function buildMessages(history, gameStateSummary) { const recent = history.slice(-20); if (history.length <= 20) { return recent; } return [ { role: 'system', content: `以下是之前的冒险摘要,请作为背景参考:${gameStateSummary}` }, ...recent ]; }这里有一个容易被忽略的点:历史摘要是“背景”,不是“当前事实”。真正的当前事实必须通过写进系统提示词里的gameStateJson来保证。否则模型会优先参考最近剧情,然后可能把玩家已经受伤的状态忘掉。
如果 Token 仍然不够,可以再把摘要进一步压缩:定期把早期对话发送给模型,让模型生成一段更简洁的“章节回顾”,再存入本地缓存。这个回顾可以按剧情章节、战斗场次或现实时间切分,灵活度比较高。
4.5 解析失败与重试策略
模型输出不可控是常态,所以稳定性设计比功能设计更重要。具体来说,需要处理以下几种情况:
| 异常情况 | 推荐处理方式 |
|---|---|
| 模型没有输出 JSON 块 | 把整段文本当作剧情展示,不更新状态 |
| JSON 块格式错误 | 捕获解析异常,保留原始剧情 |
| JSON 字段与状态结构不匹配 | 丢弃对应字段,保留其他字段 |
| API 请求超时 | 使用指数退避重试,最多重试 3 次 |
| 单次回复 Token 超限 | 减小maxTokens,或提示用户拆分输入 |
重试逻辑参考:
async function callWithRetry(fn, retries = 3) { for (let i = 0; i < retries; i++) { try { return await fn(); } catch (e) { if (i === retries - 1) { throw e; } await sleep(500 * Math.pow(2, i)); } } }指数退避的核心是避免在模型服务端过载时继续加压。第一次失败等 0.5 秒,第二次等 1 秒,第三次等 2 秒。如果是限流错误,这个策略能明显提高成功率。
5. V1.4 升级亮点与 100 多项优化解读
官方更新说明里提到 V1.4 累积了 100 多项优化。由于本文不是更新公告的复读机,这里从工程实现角度把这类大规模优化拆成几个方向,帮助大家理解“升级到底在升什么”。
5.1 用户体验类优化
跑团应用的界面信息密度比普通聊天要高很多。除了聊天文本,还需要展示状态面板、任务列表、骰子结果、背包图标等。所以用户体验优化通常会集中在:
- 首屏加载速度优化,减少白屏时间;
- 聊天气泡与状态面板的联动渲染;
- 移动端布局适配,尤其是横竖屏切换;
- 像素字体渲染优化,避免锯齿;
- 角色状态变更时的视觉反馈,例如掉血数字浮动;
- 本地缓存策略优化,重新打开页面能恢复上一次会话。
这类优化数量最多,也最容易让用户感知到“变流畅了”。
5.2 稳定性与容错优化
跑团模式的稳定性压力远大于普通聊天模式。普通聊天里 AI 一次回复不符合预期,用户只会觉得“怪”;跑团模式如果状态错乱,整个局就废了。因此版本升级会重点加强:
- 模型返回 JSON 结构校验;
- 请求超时与重试机制;
- 连续请求防抖,避免用户重复点击;
- 本地存储写入失败时的降级方案;
- 长对话超限前的自动提示。
只有这些底层逻辑足够稳,跑团体验才敢放给普通用户使用。
5.3 模型适配优化
不同模型对“结构化输出”的支持程度不同。有的模型很听话,你让它输出 JSON 它就输出 JSON;有的模型则更喜欢混着 Markdown 一起输出。适配层需要解决:
- 统一各家模型服务的请求格式;
- 自动识别模型上下文窗口大小;
- 根据模型能力决定是否开启结构化输出;
- 对不支持 JSON 输出的模型,使用正则或二次提示做兜底。
这部分优化在更新日志里不会太显眼,但直接关系到用户能不能把项目接到自己的模型上。
5.4 跑团玩法优化
跑团模式本身也有大量迭代空间,例如:
- 骰子系统:支持
1d20、2d6+3这类骰子表达式; - 战斗结算:根据攻击、防御计算伤害,并同步到状态面板;
- 任务追踪:把当前任务和目标拆分展示;
- NPC 档案:与角色卡联动,自动记录 NPC 对玩家的态度;
- 场景切换:根据位置变化自动切换背景和可交互元素。
5.5 工程与安全优化
最后是开发者和部署者最关心的部分:
- API Key 从明文配置改为服务端环境变量管理;
- 增加请求日志脱敏,避免把用户消息原样打出;
- 导入导出用户数据时增加 JSON Schema 校验;
- 增加简单限流,防止单用户异常请求耗尽配额;
- 提供数据迁移脚本,让旧版本角色卡能平滑升级。
需要说明的是,上述方向是这类应用升级时常见的工作分类,并不代表 V1.4 的更新说明中一定逐条包含这些内容。如果你想了解精确的优化清单,最好的方式是查看项目的 Release Notes 和 Git 提交记录。
6. 常见问题与排查思路
跑团模式上线后,用户反馈最多的问题通常集中在“模型不按格式输出”和“状态不更新”两大类。下面整理一份排查表:
| 问题现象 | 可能原因 | 解决思路 |
|---|---|---|
| 请求返回 401 | API Key 错误或未配置 | 检查配置文件、环境变量,确认模型服务地址正确 |
| 返回内容没有 JSON | 模型未遵守系统提示词 | 调低温度,增加 few-shot 示例,开启结构化输出 |
| 跑团状态不更新 | 解析模块没有提取到 stateUpdate | 查看原始响应日志,手动测试 JSON 解析函数 |
| 对话太长导致请求失败 | 历史消息超出上下文窗口 | 减少保留轮数,启用历史摘要,清理世界书命中条目 |
| 浏览器直接请求 API 报跨域 | API 服务允许跨域或未走代理 | 使用后端代理转发请求,或配置服务端 CORS 白名单 |
| 界面卡顿严重 | 渲染了过多历史消息节点 | 使用虚拟滚动、分页加载,历史记录只渲染可视区域 |
| 跑团过程中模型忘记设定 | 系统提示词太长被截断 | 压缩状态 JSON,把详细的规则移到世界书按需触发 |
| 多人操作同一个状态冲突 | 本地状态没有同步机制 | 引入 WebSocket 或服务端状态同步,加操作锁 |
调试这类问题时,最有效的方法是打开浏览器开发者工具,切换到 Network 面板,查看实际发送出去的消息数组。重点检查三部分:
system消息里的gameStateJson是否完整;- 历史消息是否被正确裁剪;
- 模型返回的原始内容到底是什么。
很多所谓“AI 失忆”问题,在 Network 面板里一眼就能看出原因,并不是模型真的变笨了,而是程序压根没把状态发给它。
此外,建议在解析模块加一个“原始输出”日志入口。当用户报告状态不更新时,先让用户导出日志,你直接在本地跑一遍parseGMOutput()函数,通常就能复现问题。
7. 最佳实践与工程建议
7.1 提示词工程建议
跑团模式下,提示词设计需要遵循“状态优先、规则其次、叙事最后”的顺序。
第一,把当前游戏状态 JSON 放在系统提示词靠前的位置,确保模型在生成回复时能看到。第二,把不可协商的规则写成固定条款,例如“不要替玩家做决定”“战斗必须基于攻击和防御计算”。第三,再把风格要求放在最后,例如“语言简洁”“保留像素复古感”。
还要注意,不要指望模型每次都能稳定输出 JSON。即便系统提示词写得很清楚,模型也可能在某一次突然不遵守。所以在代码层必须提供“无 JSON 也能继续运行”的兜底路径。宁可在某个回合不更新状态,也不能把整个对话流程卡死。
7.2 安全与合规边界
这类 AI 对话应用涉及到密钥管理、用户数据和模型服务合规,有几点必须重视:
- API Key 不要出现在前端代码、浏览器 localStorage 或公开仓库里。推荐使用后端代理、环境变量或类似方案保管。
- 用户输入的文本和模型输出不能直接完整打印到日志。必要时要对姓名、联系方式、地理位置等信息做脱敏处理。
- 上线前要检查模型服务的使用条款,确认允许的调用场景、速率限制和内容审核要求。
- 涉及用户数据删除、存储、导出时,要明确告知用户数据的保存位置和保留周期。
- 在任何生产环境变更前,先在测试环境验证,并使用最小权限原则配置服务账号。
7.3 性能与可维护性建议
从工程维护角度看,跑团模式的核心逻辑一定要和 UI 分离。也就是说,状态管理、上下文裁剪、解析器不应该依赖具体的前端框架。这样做的原因是:Vue、React、小程序、桌面端随时可能切换,但“游戏状态维护”和“模型返回解析”的逻辑是稳定的,抽成独立模块后可以无缝复用。
另外,建议为状态结构增加一个version字段。当后续版本调整状态结构时,可以对旧数据进行迁移:
{ "version": 1, "players": [] }这看起来只是一个很小的设计,但在长期迭代中能避免大量兼容性问题。
7.4 版本升级时的数据迁移
像素酒馆升级到 V1.4 后,用户手里的旧角色卡和旧对话记录不一定能直接兼容。此时需要提供导入导出数据的校验与迁移逻辑。建议做法是:
- 导出数据时携带版本号和生成时间;
- 导入时先校验版本号;
- 旧版本数据通过映射函数转换到新结构;
- 转换失败时给出明确错误提示,而不是静默丢弃。
数据迁移脚本应该独立于主业务逻辑,单独编写测试用例,覆盖“空字段”“旧字段”“缺失字段”等情况。
8. 总结与下一步学习方向
这次 V1.4 升级真正难的地方,不是把聊天界面改好看,而是让 AI 从“陪你说话”变成“帮你维持一个游戏世界”。两个版本之间,背后是数据结构、提示词模板、上下文管理策略和容错机制的全面调整。
如果你也想做一个类似的跑团或角色扮演应用,建议优先解决两个问题:一是如何用结构化状态保存游戏事实,二是在模型不稳定输出时如何优雅降级。UI 特效和背景音乐可以后面再加,但游戏状态一旦混乱,整局跑团就会失去可信度。
接下来可以继续研究的方向包括:
- 长上下文记忆压缩:把早期对话交给模型生成章节摘要,再按需加载。
- 多 Agent 跑团:AI 同时扮演 GM、多个 NPC,并分别维护不同记忆。
- 多模态辅助:让场景描述自动生成像素风背景图。
- 服务端同步:在多人联机跑团时,用 WebSocket 实时同步角色状态与剧情推进。
如果这篇内容对你有帮助,可以收藏备用。跑团系统里的状态同步、上下文裁剪和结构化输出,都是很值得深入琢磨的点。欢迎在评论区聊聊你实现 AI 跑团时踩过的坑,尤其是模型“不听话”的时候,你是怎么让它回到规则里的。