有人就能跑。 面向移动端的多人在线 TRPG 应用,目标是由 AI 承担守秘人(KP)的叙事工作。
当前仓库是一个已经完成前后端联调的 MS1 可运行版本,包含 React 前端、TypeScript SDK 和 FastAPI 后端。用户可以完成注册登录、创建或加入房间、选择模组、创建角色、进入大厅、开始游戏和房间内互动等基础流程。
当前版本仍属于阶段性实现:主持人意图理解与叙事支持离线 Fake、OpenAI 和阿里云百炼千问三种模式,默认使用不访问网络的 Fake;复盘摘要等非主链能力仍未实现。账号、房间、角色、模组内容、规则 Runtime、事件和已完成动作均由 SQL Store 持久化。
main 有新提交(包括 PR 合并)后会自动更新持久预览环境,前端入口固定使用
网关端口 10005,地址不会随着部署变化:
该环境只保留面向用户的前端入口,/api 和 /ws 由 Caddy 反向代理到后端;
数据库随容器重建而重置,未配置 Preview 专用 DeepSeek key 时使用 Fake Provider。
| 模块 | 当前实现 |
|---|---|
| 账号 | 注册、登录、退出登录、获取个人信息、修改昵称 |
| 首页 | 创建房间、输入房间码加入、查看我的房间、个人资料 |
| 房间 | 房主选择模组、玩家列表、准备状态、房主开始与结束游戏 |
| 角色 | CoC 风格建卡流程、属性与技能配置、装备和背景信息、完成建卡 |
| 实时通信 | WebSocket 会话绑定、准备、开始游戏、提交行动、房间叙事广播 |
| AI 主持 | 玩家安全上下文、结构化意图解析、确定性规则执行、结果叙事;支持 OpenAI 与千问 3.7 Plus |
| 游戏界面 | 对话区、角色卡、技能、地图、笔记和 D100/D20/D6 本地投骰交互 |
| API SDK | 封装认证、房间、角色和房间 WebSocket;与后端 DTO 对应的类型由 npm run codegen 生成 |
- 默认
HOST_MODEL_PROVIDER=fake,不会访问真实大模型;远程 Host Agent 或 Narrator 失败时当前回合安全中止并允许重试,不会静默回退到 Fake。 - 当前唯一承诺可运行的模组是「追书人」;另外三个示例 JSON 只用于解析与 Schema 回归,不会自动写入运行数据库。
- 技能检定保留
check.request → check.roll → check.result两阶段协议;玩家提交 D100 点数,后端规则引擎权威结算并持久化结果。 - Director、世界知识检索、长期记忆、主动剧情推进、RAG、持久即兴内容和完整重连恢复不在当前阶段。
- 复盘摘要、完整事件记录、语音输入等能力尚未完成。
trpg-frontend (React)
│
▼
trpg-sdk (REST + WebSocket)
│
▼
trpg-backend (FastAPI)
├── /api/v1/* REST API
├── /ws/{roomId} 房间实时通道
├── TurnApplication Host Agent → 两阶段检定 → RuleEngine → Narrator
├── 模型适配器 Fake / OpenAI Responses / Qwen Agents SDK
└── SQL Store 业务数据、模组、Runtime、事件与幂等记录
统一 REST 响应格式如下:
{
"success": true,
"data": {},
"error": null
}WebSocket 使用独立事件信封:客户端发送 { "type", "playerId", "payload" },服务端发送 { "type", "payload" }。
| 层 | 技术 |
|---|---|
| 前端 | React 19、TypeScript 5、Vite 7、Tailwind CSS 3、Zustand 5、React Router 7 |
| SDK | TypeScript、Rollup 4 |
| 后端 | Python 3.12+、FastAPI、Pydantic 2、SQLAlchemy Async、Uvicorn |
| 实时通信 | WebSocket |
| AI 主持 | Host Orchestrator、OpenAI Responses API、阿里云百炼千问 JSON Mode |
| 数据与安全 | SQLite、PostgreSQL 异步驱动、bcrypt |
| 工程质量 | pytest、ruff、ty、GitHub Actions |
TRPG-master/
├── trpg-frontend/ # 移动端 React 应用
├── trpg-sdk/ # 前后端通信 SDK,前端通过本地依赖引用
├── trpg-backend/ # FastAPI 服务、REST API、WebSocket 和测试
├── .github/workflows/ # 三个独立 CI:后端、SDK、前端
└── README.md
- Git
- Node.js 与 npm(版本需支持 Vite 7)
- Python 3.12 或更高版本;仓库的
.python-version当前指定 3.13 - 推荐安装 uv 管理后端环境
git clone https://github.com/1024XEngineer/TRPG-master.git
cd TRPG-master前端通过 file:../trpg-sdk 引用 SDK,因此首次启动前需要先生成 dist。
cd trpg-sdk
npm ci
npm run build
cd ..cd trpg-backend
uv sync --locked
uv run alembic upgrade head # 建表:首次启动、以及之后表结构有变更时都要先跑
uv run uvicorn app.main:app --reload建表由 Alembic 迁移负责(不再由应用启动时自动
create_all)。跳过alembic upgrade head直接启动会因为表不存在、种子数据写入失败而崩溃。如果你之前跑过旧版本、本地已有
trpg-backend/app.db:旧版本靠应用启动时create_all建表、没有 Alembic 迁移历史,直接alembic upgrade head会因为rooms等表已存在而报错。旧版本的业务数据存在内存里(重启即丢),那个app.db里只有空的历史表、没有真实数据,直接删掉重新迁移即可(rm trpg-backend/app.db再alembic upgrade head)。
应用 Seed 只会创建 COC7 规则系统和 wip 状态的追书人目录,不会内嵌简化版
模组内容。执行固定的本地加载命令,将仓库中的追书人
ModuleContent 经过 Validation 后原子写入数据库,并把目录标记为 ready:
cd trpg-backend
uv run python scripts/load_paper_chase.py该命令只读取
agent-collaboration-framework/docs/module-parser/examples/module-content-validation/追书人/module-content-draft.json。
脚本可直接在刚迁移的空数据库运行:缺少 Seed 时会先执行同一套幂等 Seed。重复
执行相同内容会返回 unchanged;同一版本已有不同内容时会拒绝覆盖。
追书人当前发布版本为 1.0.3,使用 content_schema_version=2;原始 1.0.1
归档仍可供已固定版本的房间读取。玩家可见的模组简介、推荐人数和开局页来自发布内容的
presentation 字段,不读取面向叙述 Agent 的 background;升级旧数据库后需要重新执行
上述加载命令,已有固定到旧版本的游戏不会被改写。
后端默认地址:http://127.0.0.1:8000
- 健康检查:http://127.0.0.1:8000/api/v1/health
- Swagger API 文档:http://127.0.0.1:8000/docs
- ReDoc API 文档:http://127.0.0.1:8000/redoc
复制 .env.example 为 .env 后可以覆盖默认配置;不复制也可以使用代码内置的本地开发默认值。
另开一个终端:
cd trpg-frontend
npm ci
npm run dev浏览器打开:http://localhost:9877
默认后端 CORS 配置允许 http://localhost:9877。如果修改前端地址或端口,需要同步调整后端的 CORS_ORIGINS。
| 变量 | 默认值 | 说明 |
|---|---|---|
APP_ENV |
development |
运行环境:development、production 或 test |
DATABASE_URL |
sqlite+aiosqlite:///./app.db |
SQLAlchemy 异步数据库地址 |
ENABLE_DOCS |
true |
是否开放 /docs、/redoc 和 /openapi.json |
LOG_LEVEL |
INFO |
后端日志级别 |
CORS_ORIGINS |
["http://localhost:9877"] |
允许跨域访问的前端来源列表 |
HOST_MODEL_PROVIDER |
fake |
主持模型:fake、openai、qwen 或 deepseek |
OPENAI_API_KEY |
空 | openai 提供商的 API 密钥 |
OPENAI_BASE_URL |
https://api.openai.com/v1 |
OpenAI Responses API 根地址 |
OPENAI_MODEL |
gpt-5.6-luna |
OpenAI 模型名称 |
OPENAI_TIMEOUT_SECONDS |
30 |
OpenAI 请求超时秒数 |
QWEN_API_KEY |
空 | 阿里云百炼 API 密钥 |
QWEN_BASE_URL |
https://dashscope.aliyuncs.com/compatible-mode/v1 |
千问 OpenAI 兼容接口根地址 |
QWEN_MODEL |
qwen3.7-plus |
千问模型名称 |
QWEN_TIMEOUT_SECONDS |
30 |
千问请求超时秒数 |
DEEPSEEK_API_KEY |
空 | DeepSeek 或其他兼容 provider 的 API 密钥 |
DEEPSEEK_BASE_URL |
https://api.deepseek.com |
OpenAI-compatible Chat Completions 根地址 |
DEEPSEEK_MODEL |
deepseek-chat |
DeepSeek 模型名称 |
DEEPSEEK_TIMEOUT_SECONDS |
30 |
DeepSeek 请求超时秒数 |
HOST_AGENT_MAX_TURNS |
6 |
单次 Host Agent 最大模型轮数 |
HOST_AGENT_MAX_TOOL_CALLS |
8 |
单次 Host Agent 最大工具调用数 |
HOST_AGENT_TOOL_TIMEOUT_SECONDS |
5 |
单工具超时秒数 |
HOST_AGENT_TIMEOUT_SECONDS |
30 |
Host Agent 整轮超时秒数 |
OPENING_NARRATION_MODE |
model |
权威开场生成方式:model 或确定性 template |
OPENING_NARRATION_TIMEOUT_SECONDS |
10 |
开场模型生成的独立总超时秒数;失败后使用安全模板 |
RECENT_HISTORY_ENABLED |
true |
是否向 Host/Narrator 提供玩家安全的近期回合 |
RECENT_HISTORY_MAX_TURNS |
6 |
近期历史最多保留的回合数 |
RECENT_HISTORY_MAX_CHARS |
6000 |
近期历史文本总字符预算 |
HOST_MODEL_PROVIDER 决定意图理解和结果叙事使用的模型路径:
| 值 | 请求方式 | 适用场景 |
|---|---|---|
fake |
不发送网络请求 | 默认值;本地开发、自动化测试和无密钥运行 |
openai |
OpenAI Responses API + 严格 JSON Schema | 使用原生支持 text.format=json_schema 的模型 |
qwen |
千问 Chat Completions JSON Mode + 本地 Pydantic 校验 | 阿里云百炼千问 3.7 Plus |
deepseek |
OpenAI-compatible Chat Completions + JSON Mode + 本地 Pydantic 校验 | DeepSeek;兼容同一协议的 provider 可复用 |
无论使用哪种远程模型,模型只负责提出结构化意图或叙事候选;目标、场景、技能和事实引用仍会在应用边界重新校验,最终状态只由规则引擎修改。
-
在阿里云百炼控制台创建 API Key,并取得业务空间的
WorkspaceId。 -
复制本地配置文件:
cd trpg-backend cp .env.example .envPowerShell 可使用:
Copy-Item .env.example .env -
只在本地
.env中填写以下配置:HOST_MODEL_PROVIDER=qwen QWEN_API_KEY=你的百炼_API_Key QWEN_BASE_URL=https://你的WorkspaceId.cn-beijing.maas.aliyuncs.com/compatible-mode/v1 QWEN_MODEL=qwen3.7-plus QWEN_TIMEOUT_SECONDS=30
新加坡地域将地址替换为:
QWEN_BASE_URL=https://你的WorkspaceId.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1
其他地域及最新端点以阿里云百炼 OpenAI 兼容接口文档为准。
.env已被 Git 忽略;不要把真实密钥写入.env.example、README 或提交记录。 -
重启后端使配置生效:
uv run uvicorn app.main:app --reload
健康检查只能确认后端存活,不会调用模型。请进入房间提交一次自然语言行动进行验证。
远程 provider 模式缺少 Key 时后端启动失败;Host Agent 超时、预算耗尽、非法输出或越权候选
会发送玩家安全的 turn.failed,规则引擎不会执行。Narrator 失败不会重跑 Host
Agent、重新掷骰或重复写入状态;使用同一 clientActionId 重试只会复用已提交结果。
复制 trpg-backend/.env.example 为 .env,填写:
HOST_MODEL_PROVIDER=deepseek
DEEPSEEK_API_KEY=你的_API_Key
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_MODEL=deepseek-chat
DEEPSEEK_TIMEOUT_SECONDS=30Host Agent 工具调用和结构化 Narrator 都使用 OpenAI-compatible Chat Completions。 其他厂商如果遵循同一协议,可复用这组配置和 adapter;使用私有协议时应新增独立 provider adapter,而不是把私有字段混入通用请求。
公共 WebSocket 只发送安全进度:turn.started、turn.phase_changed、
tool.started、tool.completed、turn.failed 和 view.updated。内部 call id、
工具参数/结果、Prompt、raw model output、reasoning、异常栈和模组秘密不会进入浏览器。
| 变量 | 默认值 | 说明 |
|---|---|---|
VITE_API_BASE_URL |
http://127.0.0.1:8000/api/v1 |
REST API 根地址;WebSocket 地址由 SDK 自动推导 |
cd trpg-sdk
npm ci
npm run lint
npm run typecheck
npm run build
npm testcd trpg-frontend
npm ci
npm run lint
npm run build # 内部先跑 tsc -b 做类型检查,再用 vite build 打包cd trpg-backend
uv sync --locked
uv run ruff check .
uv run ruff format --check .
uv run ty check
uv run pytesttrpg-sdk/src/generated/dto.ts 里跟后端 DTO 对应的 TS 类型,是从
trpg-backend/app/dto/*.py 的 Pydantic 模型自动生成的,不再手写。改了后端
DTO(REST 请求/响应体,或 app/dto/ws.py 里的 WebSocket 事件 payload)之后,
需要依次跑:
# 1. 后端:把 DTO 导出成 JSON Schema(临时中间产物,不进 git)
cd trpg-backend
uv run python scripts/export_schema.py
# 2. SDK:从 JSON Schema 生成 TS 类型,写入 src/generated/dto.ts
cd ../trpg-sdk
npm run codegen然后把 trpg-sdk/src/generated/dto.ts 的改动跟 DTO 改动一起提交——这个
文件是生成产物但会进 git(跟 dist/ 不同:dist/ 的消费者是机器,这个文件
的消费者是人和 CI,见 issue #75 的决策记录)。忘记重新生成会被 Backend CI 的
codegen-drift job 拦下(见下面「持续集成」)。
.github/workflows/ 下有多个互相独立的 workflow,各自按路径过滤器触发,只有
真正改到对应目录才会跑:
| Workflow | 触发路径 | 检查内容 |
|---|---|---|
trpg-backend-ci.yml(Backend CI) |
trpg-backend/**;另外 trpg-sdk/scripts/generate-types.ts 和 trpg-sdk/src/generated/** 也会触发(见下) |
ruff check、ruff format --check、ty check、pytest;另有 codegen-drift job:重新跑一遍 DTO → JSON Schema → TS 生成管线,用 git diff 确认 trpg-sdk/src/generated/ 跟提交的一致,不一致就报错 |
trpg-sdk-ci.yml(SDK CI) |
trpg-sdk/** |
npm run lint、npm run typecheck、npm run build |
trpg-frontend-ci.yml(Frontend CI) |
trpg-frontend/** |
npm run lint、npm run build |
pr-preview.yml(PR Preview) |
PR 打开、更新、重开、关闭 | 部署或回收 PR 专属预览环境 |
main-preview.yml(Main Preview) |
PR 合并到 main |
更新固定端口的持久预览环境 |
codegen-drift 放在 Backend CI 而不是 SDK CI:它要在"改了 DTO 却忘记重新
生成"的那个 PR 上就亮红灯,而 SDK CI 只在 trpg-sdk/** 变化时触发——一个纯
改后端 DTO 的 PR 根本不会碰 trpg-sdk/**,放在 SDK CI 里等于没测。这也是
Backend CI 的路径过滤器额外加了两条 trpg-sdk/ 路径的原因。
| 成员 | GitHub |
|---|---|
| 高俊周 (GJZ) | @WELT5350 |
| 凌铭辉 (LMH) | @LMH168 |
| 李敏譞 (LMX) | @Ximaohu-LMX |
| 张家豪 (ZJH) | @JoshuaZ16 |
| 黄女珊 (HNS) | @badadal |
| 卢玮晨 (LWC) | @Lyltrum |
- 通过 fork + Pull Request 提交变更,不直接向主仓库主分支提交。
- Commit message 遵循 Conventional Commits。
- 后端 DTO(REST 或 WebSocket)发生变化时,按上面「类型生成(codegen)」的步骤重新生成
trpg-sdk的类型并把生成结果一起提交,不再手动改trpg-sdk/src/types.ts。
1024 XEngineer Camp Season 6