Skip to content

Repository files navigation

TRPG RAG

把任意 TRPG 规则书故事模组(PDF)转换成可检索、可问答的 RAG 知识库,为 GM 主持 / 规则问答 / 剧情推进 / LLM 集成提供支持。

  • 规则书:陈述性知识(规则条目),支持多规则书独立索引、混合检索、规则问答。
  • 模组:叙事性 + 指令性内容,按「场景树 + 多级披露」结构化,支持分阶段防剧透检索GM 准备标注

两者共用底层检索引擎,提供统一接口(Python API / CLI / HTTP / MCP / Web)。

功能特性

规则书(陈述性知识 · 规则问答)

  • 解析:书签优先;缺失时检测器链自动探测(L1 书签 → L2 编号模式 → L3 字号分档 → L4 兜底),并过滤噪声/目录/重复页
  • 分块:按规则条目切分,建立父子文档关系(父=章节,子=小节),支撑上下文补全
  • 混合检索:BM25 关键词 + FAISS 向量双路召回,RRF 融合,Reranker 精排;按查询类型(rule/judge/quote/narrative)动态调权;可选术语索引强信号
  • 多规则书:每本独立向量库,自由切换/联合,检索带缓存
  • 评测:Recall@k / MRR / 分类别统计,QueryCase 驱动(benchmarks/test_set.json

模组(叙事性 · 分阶段防剧透检索)

  • 解析:检测器链(L1 书签 → L2 目录页 → L3 排版字号 → L4 LLM 语义兜底),不依赖特定标题格式,适配各种模组书写风格;同页多场景按 (page, y) 切分
  • 结构化:场景树(scene/location/sequence)+ 多级披露(disclosure_levels);自动区分剧情阶段(线性推进,如 导入/第X场/地点N/结局)与背景阶段(资料性,如 角色/背景/NPC/附录)
  • 分阶段防剧透:检索层硬约束——调用方传 visible_filter(玩家当前阶段 + 已满足条件),未解锁 chunk 在 BM25/FAISS 检索前被物理剔除;背景块始终可见;GM 不传 filter 则全量(主持人全知)
  • GM 标注:准备阶段逐个 chunk 决定是否暴露给玩家(expose_to_player → 视角)+ 设置多级披露解锁条件(conditions);支持 LLM 辅助标注;标注独立持久化,构建时应用
  • 脚本工具:分块查阅(inspect_module.py)、交互标注(annotate_module.py)、RAG 测试(test_module_rag.py,覆盖解析/防剧透/多级披露/GM全量/真实检索,benchmarks/module_test_*.json

通用

  • 统一接口:Python API / CLI / HTTP REST / MCP Server / Web 前端(规则书与模组共用一套接口层)
  • 隔离存储:规则书/模组索引(faiss_index/rulebooks vs modules)、许可声明(LICENSES/rulebooks vs modules)各自独立

版权与许可

  • 代码:本项目的 RAG 引擎(core/service/interfaces/app/ 等)为独立创作,采用 MIT 许可,见 LICENSE
  • 示例规则书data/rulebooks/ 下的规则书均为粉丝同人作品,采用 CC BY-NC-SA 4.0 许可,禁止商业用途
  • 示例模组data/modules/ 下的模组为粉丝同人作品,许可见 LICENSES/modules/
    • 《多索雷斯狂欢节pro》(作者:信仰の殉道),采用 CC BY-NC-ND 4.0(署名-非商业性使用-禁止演绎),禁止商业用途、禁止修改发布,详见 LICENSES/modules/DUOSUOLESI.md
    • 《黑水溪》(Blackwater Creek,作者:Scott Dorward,译者:埃兰迪斯),依据原作者声明允许转载 / 制作 / 修改发布禁止商业用途,详见 LICENSES/modules/HEISHUIXI.md
  • 其他规则书/模组:您自行准备的内容(如 COC7、忍神等)版权归原作者所有,请勿上传到公开仓库。仅使用有明确开放许可的内容作为示例数据。

声明

本项目为个人学习项目(非商业用途)。AI 辅助编码、模型调用与免责条款详见 AI-DECLARATION.md

项目结构

TRPG-RAG/
├── config.py            # 配置(分块/嵌入/检索/Reranker/术语 + ContentType)
├── requirements.txt     # 依赖
├── LICENSE              # 代码开源许可证
├── AI-DECLARATION.md    # AI 辅助编码声明
├── LICENSES/            # 示例数据许可声明
│   ├── rulebooks/       #   规则书许可(ARKNIGHTS/XINGYUTAILA)
│   └── modules/         #   模组许可(DUOSUOLESI/HEISHUIXI)
├── core/                # 核心逻辑
│   ├── pdf_loader.py    #   规则书解析(书签/自动探测)
│   ├── chunker.py       #   规则书分块(父子文档)
│   ├── indexer.py       #   FAISS + BM25 索引
│   ├── retriever.py     #   混合检索 + RRF + 可见性过滤
│   ├── reranker.py      #   Reranker 精排
│   ├── terms.py         #   术语索引
│   ├── evaluate.py      #   评测工具
│   ├── module_model.py  #   模组数据模型(场景树/多级披露/可见性过滤)
│   ├── module_parser.py #   模组解析(检测器链:书签/目录/排版/LLM)
│   ├── module_chunker.py#   模组分块入库
│   └── module_annotator.py # 模组 GM 标注接口
├── service/             # 服务层
│   ├── index_manager.py #   规则书索引管理
│   ├── retrieve_service.py # 规则书检索服务
│   └── module_service.py  #   模组统一业务层(构建/检索/标注)
├── interfaces/          # 接口(CLI/HTTP/MCP)
├── app/                 # Streamlit Web 前端demo
├── benchmarks/          # 测试集(test_set + module_test_*)
├── script/              # 测试/查阅/标注脚本
├── data/                # 素材根目录(规则书/模组)
│   ├── rulebooks/       #   规则书 PDF(示例为 CC 许可的明日方舟TRPG/行于泰拉)
│   ├── modules/         #   模组(示例为多索雷斯狂欢节pro/黑水溪)
│   └── _annotations/    #   GM 标注结果持久化
└── faiss_index/         # 索引根目录(生成的向量库)
    ├── rulebooks/       #   规则书索引
    └── modules/         #   模组索引(多索雷斯/黑水溪)

安装

# 1. 克隆仓库
git clone https://github.com/ghostdoglzd/TRPG-RAG.git
cd TRPG-RAG

# 2. 创建虚拟环境
uv venv .venv --python 3.11
uv pip install -r requirements.txt

# 3. 配置 API key(复制 .env.example 为 .env 并填入)
cp .env.example .env

.env 需要以下 key(参考 .env.example):

  • EMBEDDING_API_KEY:用于 embedding(必需)
  • LLM_API_KEY:用于 LLM 生成回答 / GM 裁决 / LLM 辅助标注(必需)
  • EMBEDDING_BASE_URL / EMBEDDING_MODEL:可选,自定义 embedding 服务地址与模型
  • LLM_BASE_URL / LLM_MODEL:可选,自定义 LLM 服务地址与模型
  • RERANKER_*:可选,启用 Reranker 精排时配置

注:本项目开发、测试使用的API如下

模型类型 所用API
embedding bge-m3(硅基流动API)
LLM deepseek
reranker bge-reranker-v2-m3(硅基流动API)

若无 LLM_API_KEY,规则书检索(BM25/向量)仍可工作,但 LLM 生成回答、GM 裁决、模组 LLM 标注等依赖 LLM 的功能不可用。

规则书使用

1. 快速开始

# 1. 构建规则书索引(示例使用随仓库提供的明日方舟TRPG)
python -m interfaces.cli build data/rulebooks/明日方舟TRPG规则书v0.36.pdf --book 明日方舟TRPG

# 2. 检索
python -m interfaces.cli retrieve "如何创建干员" --book 明日方舟TRPG

# 3.(可选)启动 Web 前端或 HTTP/MCP 服务,见下文

2. CLI 命令

python -m interfaces.cli <命令> [参数]

list                                  # 列出所有已建索引的规则书
build <pdf路径> --book <书名>          # 构建规则书索引(同步)
delete --book <书名>                  # 删除某规则书索引
info --book <书名>                    # 查看某规则书索引信息
retrieve "<查询>" --book <书名> [--topk 5]  # 混合检索
tasks                                 # 查看后台构建任务

3. Python API

# 构建索引
from service.index_manager import IndexManager
from config import DEFAULT_CONFIG
mgr = IndexManager(DEFAULT_CONFIG)
mgr.build_index_sync("data/rulebooks/明日方舟TRPG规则书v0.36.pdf", "明日方舟TRPG")

# 检索
from service.retrieve_service import RetrieveService
svc = RetrieveService(DEFAULT_CONFIG)
results = svc.retrieve_dicts("如何创建干员", book_name="明日方舟TRPG", top_k=5)
# 每条结果含 text / page / chapter / section / score

4. Web 前端

# Windows
run_app.bat
# 或(跨平台)
.venv/Scripts/python.exe -m streamlit run app/Home.py
#
streamlit run app/Home.py

然后,浏览器访问 http://localhost:8501

Web 页面包含:

  • 📚 总览:规则书 + 模组概览
  • 📤 上传构建:规则书上传构建
  • 🔍 检索问答:规则书检索问答
  • 🎭 模组 RAG:模组分阶段防剧透检索 + GM 标注 + 场景结构
  • 📊 评测:规则库检索评测

5. HTTP API

python -m interfaces.api   # 127.0.0.1:8000
方法 路径 说明 请求参数 响应
GET /indexes 列出所有已建索引 - {"books": [...]}
GET /indexes/{book_name} 查询单本规则书索引信息 路径参数 book_name 索引信息对象
POST /indexes/build 构建索引(异步) Body: {"file_path": "...", "book_name": "..."} {"task_id": "...", "status": "pending"}
GET /indexes/tasks/{task_id} 查询构建任务进度 路径参数 task_id {"status": "...", "progress": ...}
DELETE /indexes/{book_name} 删除索引 路径参数 book_name {"deleted": "..."}
GET /retrieve 混合检索 Query: query(必填)、booktop_k(默认5)、mode(默认 rule) {"query": "...", "book": "...", "results": [...]}
GET /health 健康检查 - {"status": "ok", "books": [...]}

mode 取值:rule / judge / quote / narrative

# 规则书检索
curl "http://127.0.0.1:8000/retrieve?query=如何创建干员&book=明日方舟TRPG&top_k=5"

# 构建规则书索引(异步,返回 task_id 后轮询任务进度)
curl -X POST http://127.0.0.1:8000/indexes/build \
  -H "Content-Type: application/json" \
  -d '{"file_path": "data/rulebooks/明日方舟TRPG规则书v0.36.pdf", "book_name": "明日方舟TRPG"}'
curl "http://127.0.0.1:8000/indexes/tasks/<task_id>"

6. MCP Server

python -m interfaces.mcp_server   # stdio 传输

启动后会在后台预热:预加载所有已建索引的检索器 + 预热 embedding(在线 API, 首次调用约 6~7s)。预热不阻塞握手,首次检索无需再承担冷启动成本。 embedding 调用带超时(config.embed.timeout,默认 15s),网络慢时不会无限阻塞。

工具名 说明 参数
rule_list_indexes 列出所有已建索引的规则书 -
rule_build_index 上传规则书构建索引(返回 task_id) file_pathbook_name
rule_build_status 查询索引构建任务进度 task_id
rule_book_info 查询某本规则书的信息 book_name
rule_delete_index 删除索引 book_name
rule_retrieve 混合检索(GM 主持 / 规则问答核心工具) querybook(可选)、top_k(默认5)、mode(默认 rule)
rule_quote 精确查规则原文(含出处) querybook(可选)、top_k(默认3)

MCP 客户端接入示例(以 Claude Desktop 为例)

在客户端配置中注册本 MCP 服务:

{
  "mcpServers": {
    "trpg-rag": {
      "command": "d:/TRPG-RAG/.venv/Scripts/python.exe",
      "args": ["-m", "interfaces.mcp_server"],
      "cwd": "d:/TRPG-RAG"
    }
  }
}

接入后,LLM 客户端即可调用 rule_*(规则书检索)与 module_*(模组分阶段防剧透检索 / GM 标注)工具。

模组使用

模组与规则书的信息本质不同(叙事性 + 指令性 + 顺序性),因此模组 RAG 有独立的数据模型、解析链与使用方式。本段结构对齐「规则书使用」,覆盖 CLI / Python API / Web / HTTP / MCP / 评测。

1. 核心概念

  • 剧情阶段(plot_stages):线性推进(如 导入→第一场→...→结局),用于分阶段防剧透。解析器自动识别含顺序信号(第X场/地点N/导入/结局)的标题。
  • 背景阶段(background_stages):资料性章节(角色/背景/设定/NPC/附录),游戏开始前注入,始终可见、不参与阶段过滤。
  • 分阶段防剧透:模组信息是时间线性的,玩家不能提前看到后续剧情。通过检索层硬约束实现——调用方传入 visible_filter(玩家当前阶段 + 已满足条件),未解锁 chunk 在 BM25/FAISS 检索前就被物理剔除;GM 不传 filter 则全量(主持人全知)。本项目不维护运行时游戏状态,只按给定 filter 过滤。
  • GM 标注:准备阶段决定每个 chunk 是否可暴露给玩家 + 设置解锁条件。

2. 脚本 / CLI 工具

# 构建模组索引
python -c "
from config import DEFAULT_CONFIG
from service.module_service import ModuleService
svc = ModuleService(DEFAULT_CONFIG)
svc.build_index('data/modules/黑水溪/[COC模组翻译]黑水溪-Blackwater Creek.pdf', '黑水溪')
"

# 分块查阅(检查分块是否符合预期)
python script/inspect_module.py list                 # 场景树概览
python script/inspect_module.py scene S-01           # 查看某场景
python script/inspect_module.py grep 关键词          # 全文搜索

# GM 标注(准备阶段决定每个 chunk 是否可暴露给玩家)
python script/annotate_module.py list        # 列出待标注块
python script/annotate_module.py             # 交互式逐块审阅决策

# 模组 RAG 测试(A 解析 / B 防剧透 / C 多级披露 / D GM全量 / E 真实检索)
python script/test_module_rag.py            # 离线测试
python script/test_module_rag.py build      # 建索引 + 完整测试

测试数据在 benchmarks/module_test_*.json(纯查询数据集),脚本顶部配置 PDF_PATH/BOOK_NAME/TEST_SET 切换模组。

3. Python API

from service.module_service import ModuleService
from config import DEFAULT_CONFIG
svc = ModuleService(DEFAULT_CONFIG)

# 构建索引(可选 annotation_path 应用 GM 标注)
svc.build_index("data/modules/黑水溪/[COC模组翻译]黑水溪-Blackwater Creek.pdf",
                "黑水溪", annotation_path="data/_annotations/黑水溪.json")

# 查看场景树与剧情/背景阶段
structure = svc.get_structure("黑水溪")
# -> {"plot_stages": [...], "background_stages": [...], "scenes": [...]}

# GM 全量检索(主持人全知,含未来剧情)
res = svc.retrieve_gm("贾维农场有什么线索", "黑水溪", top_k=5)

# 玩家分阶段检索(玩家当前到「地点2」,未解锁内容被物理过滤)
res = svc.retrieve("贾维农场有什么线索", "黑水溪", top_k=5,
                   stage="地点2", stage_order=structure["plot_stages"])

4. GM 准备标注

准备阶段,GM(或 LLM 扮演的 GM)可审阅每个信息块,决定其曝光范围与解锁条件:

# 列出待标注 chunk(含视角/条件/文本)
chunks = svc.list_chunks("黑水溪")

# 决策:某块不可暴露给玩家(设为主持人专用)
svc.decide_chunk("黑水溪", chunks["chunks"][0]["id"], expose_to_player=False)

# 决策:某块需满足「侦查成功」条件才可披露
svc.decide_chunk("黑水溪", chunks["chunks"][1]["id"],
                 conditions=[{"desc": "侦查成功", "type": "check", "skill": "侦查"}])

# 构建索引:标注文件默认自动查找 data/_annotations/{book_name}.json,
# 无需显式传 annotation_path(未找到时返回 annotation.warn 告警,防剧透未启用)
svc.build_index(pdf_path, book_name)
# -> result.annotation = {loaded, applied, total, warn, annotation_path}

# 方式B(推荐):已构建索引后,标注 → 就地更新(不重新向量化,即时生效)
# 原理:向量只由 chunk 的 text 生成,与 meta 无关;过滤只读 meta。
# 因此标注(audience/conditions)改动无需重建/重 embedding,直接 patch 即可。
svc.apply_annotations("黑水溪")
# -> {"status": "ok", "applied": n, "total": m, ...}
# 标注文件默认取 data/_annotations/{book_name}.json(decide_chunk 落盘位置);
# 也可显式指定:svc.apply_annotations("黑水溪", annotation_path="...")

# 把某 chunk 的自动抽取条件采纳为正式生效条件(3.1:让条件披露机制跑通)
svc.adopt_auto_conditions("黑水溪", "SCENES-01_C01_L1")

# 分阶段检索:stage_order 自动注入,无需调用方传——
# 传 stage 时自动读取 plot_stages,「截至当前阶段」语义自动生效
res = svc.retrieve("贾维农场有什么线索", "黑水溪", top_k=5, stage="地点2")
# -> res.stage_order 为实际使用的剧情阶段顺序

5. Web 前端

「🎭 模组 RAG」页面提供:

  • 分阶段检索:选择检索视角(GM 全量 / 玩家分阶段)→ 玩家视角下再选「玩家当前到达的剧情阶段」,未解锁阶段内容会被物理过滤
  • GM 标注:审阅每个信息块(含完整内容)并决定是否暴露给玩家、设置解锁条件
  • 场景结构:场景树 / 剧情阶段 / 背景阶段可视化

6. HTTP API

方法 路径 说明 请求参数 响应
GET /modules/indexes 列出所有已建模组索引 - {"modules": [...]}
GET /modules/indexes/{book_name} 查询单模组索引信息 路径参数 book_name 索引信息对象
GET /modules/indexes/{book_name}/structure 获取模组场景树与剧情阶段 路径参数 book_name {"plot_stages": [...], "scenes": [...]}
POST /modules/indexes/build 构建模组索引(同步,可带标注) Body: {"file_path", "book_name", "annotation_path"?} {"status": "done"/"failed", "result": {...}}
DELETE /modules/indexes/{book_name} 删除模组索引 路径参数 book_name {"deleted": "..."}
GET /modules/retrieve 模组分阶段检索(防剧透) Query: querybookstage(玩家当前阶段)、scene_idaudiencemet_conditionstop_k {"player_limited": bool, "results": [...]}
GET /modules/retrieve_gm GM 全量检索(主持人全知) Query: querybooktop_k {"player_limited": false, "results": [...]}
GET /modules/annotate/chunks 列出待标注 chunk(暴露视角/条件) Query: bookscene_id? {"chunks": [...]}
POST /modules/annotate/decide GM 决策 chunk 曝光范围/条件 Body: {"book_name", "chunk_id", "expose_to_player", "audience"?, "conditions"?} 决策结果
POST /modules/annotate/apply 把标注就地更新到已构建索引(不重新向量化) Body: {"book_name", "annotation_path"?} {"status": "ok", "applied": n, "total": m}
POST /modules/annotate/adopt 把某 chunk 的自动条件采纳为正式生效条件 Query: bookchunk_id {"chunk_id", "adopted": true, "conditions": [...]}
# 模组 GM 全量检索(主持人全知)
curl "http://127.0.0.1:8000/modules/retrieve_gm?query=贾维农场有什么线索&book=黑水溪&top_k=5"

# 模组分阶段检索(玩家到「地点2」,未解锁内容被过滤)
curl "http://127.0.0.1:8000/modules/retrieve?query=贾维农场有什么线索&book=黑水溪&stage=地点2&stage_order=引入调查员,地点1,地点2,地点3,地点4,地点5,地点6,地点7,地点8,地点9,结局"

# 列出模组待标注 chunk
curl "http://127.0.0.1:8000/modules/annotate/chunks?book=黑水溪"

# GM 决策某 chunk 是否可暴露给玩家
curl -X POST http://127.0.0.1:8000/modules/annotate/decide \
  -H "Content-Type: application/json" \
  -d '{"book_name": "黑水溪", "chunk_id": "SCENES-04_C02", "expose_to_player": false}'

模组检索的核心:传 stage 表示「玩家当前只推进到该阶段」,未解锁阶段的块被物理过滤(防剧透);不传 stage 则为 GM 全量(主持人全知)。

7. MCP Server

同一 MCP 服务(python -m interfaces.mcp_server)暴露模组工具:

工具名 说明 参数
module_list_indexes 列出所有已建模组索引 -
module_build_index 构建模组索引(同步,可带标注) file_pathbook_nameannotation_path?
module_delete_index 删除模组索引 book_name
module_structure 获取模组场景树与剧情阶段 book_name
module_retrieve 分阶段检索(防剧透) querybookstage?、scene_id?、audience?、met_conditions?、top_k
module_retrieve_gm GM 全量检索(主持人全知) querybooktop_k
module_list_chunks 列出待标注 chunk(暴露视角/条件) bookscene_id?
module_decide_chunk GM 决策 chunk 曝光范围/条件 book_namechunk_idexpose_to_playeraudience?、conditions_json?

8. 模组评测

模组 RAG 测试程序验证「解析 / 防剧透 / 多级披露 / GM 全量 / 真实检索」,输出 PASS/FAIL 报告:

python script/test_module_rag.py            # 离线测试(A-D)
python script/test_module_rag.py build      # 建索引 + 完整测试(含 E 真实检索)

测试数据在 benchmarks/module_test_*.json,脚本顶部配置 PDF_PATH/BOOK_NAME/PLOT_STAGES/TEST_SET 切换模组。

7. 评测

评测工具基于 core/evaluate(提供 Recall@k / MRR / 分类别统计)。script/evaluate.py 读取 benchmarks/test_set.json 测试数据,评测指定规则书的检索效果。

# 评测已建索引的规则书
python script/evaluate.py

# 若向量库不存在,先构建再评测
python script/evaluate.py build

适配其他规则书:修改 script/evaluate.py 顶部的 PDF_PATH / BOOK_NAME / TEST_SET 常量,并编辑 benchmarks/test_set.json 中的查询和期望命中值。

实测效果(《明日方舟TRPG》,benchmarks/test_set.json 84 条查询,Recall@5):

规则书 测试集 Recall@5 MRR
明日方舟TRPG 84 条(6 个类别) 100% 0.979

分类别命中率:创建 10/10、能力 10/10、判定 6/6、战斗 41/41、源石 8/8、装备 9/9(均 100%)。

测试数据由随仓库提供的《明日方舟 TRPG》(CC BY-NC-SA 4.0 许可)构建并评测,可直接复现。

技术栈

PyMuPDF · LangChain · FAISS · rank-bm25 · 硅基流动(bge-m3) · DeepSeek · FastAPI · MCP SDK · Streamlit · pydantic

常见问题

Q1:没有 LLM_API_KEY,能用吗? 可以。规则书的 BM25/向量检索、索引构建、评测不依赖 LLM。只有「LLM 生成回答」「GM 裁决」「模组 LLM 辅助标注」需要 LLM。

Q2:如何切换要测试/构建的模组?

  • 测试:修改 script/test_module_rag.py 顶部 PDF_PATH/BOOK_NAME/PLOT_STAGES/TEST_SET,或改用 benchmarks/module_test_*.json 数据集
  • 构建:svc.build_index(pdf_path, book_name) 或 HTTP /modules/indexes/build

Q3:模组检索时如何做到不剧透? 检索时传入 stage(玩家当前到达的剧情阶段)即可。VisibilityFilter 会在 BM25/FAISS 检索前把未解锁阶段的 chunk 物理剔除,不会进入 LLM 上下文。GM 不传 stage 则全量可见(主持人全知)。

Q4:模组的「背景」和「剧情阶段」如何区分? 解析器自动识别:含顺序信号(第X场/地点N/导入/结局)的为剧情阶段(线性、防剧透);资料性章节(角色/背景/设定/NPC/附录)为背景阶段,游戏开始前注入、始终可见。

Q5:分块结果不符合预期怎么办?script/inspect_module.py 查阅分块详情(场景树/单个场景/全文搜索/带条件块),定位问题后可调整 module_parser.py 的分块逻辑。

About

TRPG-RAG 是一个为 TRPG 规则书和模组打造的 RAG 知识库系统,支持规则问答、模组分阶段防剧透检索、GM 准备标注,并提供统一接口(CLI/HTTP/MCP/Web)。

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages