万界 · 行者
版本 v1.1.0 · 中文 | English · 架构 · 玩法层 · 对标调研 · 更新日志 · 展示站
AI 负责叙述,引擎负责世界 —— 而且"怎么玩"也能换。 四套世界观、四种玩法循环(自由推进 / 选项推进 / 地点命令 / 声明→判定→后果),同一套规则内核。 世界观无关的文字互动游戏内核:144 个规则函数、39 个 MCP 工具、4 套可直接开玩的设定包、 一块离线可用的只读实况主舞台。同一份存档、同一个种子,永远得到同一个世界。
这是什么
大多数 AI 文字游戏把"世界"交给语言模型即兴发挥:数值会漂移、NPC 会失忆、 同一个选择重开一次结果不同。万界·行者把这件事反过来:
- 引擎算数:命中、伤害、价格、关系、因果、势力消长、谣言扩散,全部由确定性规则函数结算;
- Agent 作文:语言模型只把你看到的 JSON 转写成文学描写,不得编造数值;
- 面板见证:一块只读网页主舞台实时显示真实状态——它不能改对局,因此它显示的每个数字都可核对。
它不是又一个"AI 跑团提示词",而是一层可以被任何 Agent 调用的世界内核: MCP 服务器(stdio)、命令行(14 条命令)、HTTP 只读面板,三条通道共用同一套规则与同一份状态文件。
三十秒上手
pip install -e ".[mcp]" # 仅标准库 + 可选 mcp SDK
把下面这段加进任意 MCP 客户端(Claude Code / Codex / Cursor / opencode):
{ "mcpServers": { "world-walker": { "command": "world-walker-mcp" } } }
然后对 Agent 说一句:
load_setting(path="settings/玄枢界.setting.json")
一局就开好了。接下来每个行动都由工具结算:
你:我走进万石商行,把《引元诀》残页拍在柜台上,问石三娘这东西值多少。
Agent → trade(location="临川城", item="yinyuan_canye", direction="sell", quantity=1)
→ social(actor="player", target="shi_sanniang", event="cooperate")
→ narration_context(...) → publish_prose(...) → tick()
不想装 MCP 也能玩命令行:
python init_game.py # 生成示例世界
python engine.py world_state --state game_state.json
python engine.py combat --state game_state.json --attacker player --defender goblin --action_type heavy
python panel.py --state game_state.json # 打开只读主舞台
界面
全部截图来自真实对局的真实状态,由 scripts/shots.py 在 Edge 中逐项断言后现场拍摄
(来源存档见 docs/demo/,重拍命令见文末)。
四套世界观,同一套引擎
皮肤只是表层。真正随世界改变的是词、阶、钱、地点描写——设定包里写
terms: { level: "身手", gold: "银两", track: "身手阶" },面板上就不会再出现"境界 / 灵石 / 修行轨"。
scripts/verify_settings.py 会逐个世界断言这件事。
主舞台能做什么
响应式、帮助与演示模式:
它和别的 AI 文字游戏有什么不同
对标对象与方法(含证据链接)见 docs/BENCHMARK.md。一句话版本:
我们不做的事:图像生成、多人联机、云端账号体系、插件市场。 这些是取舍不是遗漏,理由写在 ROADMAP.md。
核心机制(都在结算回路上,不是摆设)
1. 世界自己会动
每次 tick 都会推进:
- 势力消长:每个势力带自己的禀赋上限(由名字确定性派生),logistic 增长 + 头部两家 Lotka-Volterra 温和压制 + 挑战者回流。长期有稳定强弱,短期有换手。
- 谣言传播:SIR 一步。业债高、物价异常、相位剧烈时才会播种新谣言。
- 风气漂移:Deffuant 有界信任,每 10 回合结算一次。
- NPC 走动:标注
roam: true的 NPC 会换地点。 - Panarchy 适应性循环:
r → K → Ω → α → r四段状态机,由真实存量(势力均值、关系密度、世界标记、业债、物价偏离)驱动,不是查表。
scripts/verify_world.py 的 W1/W2/W11 断言这四件事真的发生:
60 回合内势力存量变化、长局里四个相位都观测到、MCP tick 工具本身推进世界。
2. 派生指标参与结算
3. 因果债
karma 记的是结构化账本:类型、金额、债主、回合、是否已结清。
未结清的债会压低突破成功率、放大他人敌意,并进入突破时的"天劫强度"。
台账有界:超出 karma_ledger_keep 时合并最旧条目并保留总额(不是截断)。
4. 长局记忆
write_summary 每 20–50 回合写三五句"变了什么"
milestones 突破 / 击杀 / 初次抵达 / 因果升级自动记录
archive 事件超出保留量后追加进 <state>.log.jsonl
recall 按关键词跨归档世代回捞
recap 按字符预算给出「最新摘要 + 大事记 + 最近事件」
回退(snapshot / restore)只切换归档世代文件,绝不删除任何已写下的历史。
5. 可复现
- 开局带
seed; - 每次结算用
sha256(seed, tick, counter)重置随机源,rng_counter随状态落盘; - 世界演化另用
sha256(seed|tick|key)派生随机,不消耗rng_counter; - 只读工具(
status/world_state/read_state)不落盘、不推进计数器。
三条不变量由 verify_cli.py(CLI 通道)、verify_mcp_stdio.py(MCP 通道)、
verify_world.py(世界演化)同时锁定。
MCP 工具一览(39 个)
完整参数与调用约定见 GAME_MASTER_PROTOCOL.md。
玩法层:同一套规则,四种"玩起来不一样"
世界观可换只是第一层。真正决定"这局怎么玩"的是 playbook:
playbook_info 会把当前剧本、进度、失败钟、当前节拍与给宿主的叙事指南一起返回;
scripts/verify_gameplay.py(60 项断言)逐项锁定这些语义。
四套设定包分别演示四种循环:玄幻 freeform / 武侠 ttrpg / 赛博 mud / 悬疑 choice。
事业线:任务只声明目标与压力,不声明过程
设定包的 questlines 是一个 stage DAG:
{
"id": "fog_lighthouse",
"title": "别让灯塔亮起来",
"visibility": {"flag": "无署名信"},
"stages": [
{"id": "letter", "title": "信", "objectives": ["弄清信是谁写的"],
"exit_if": {"visited": "雾港码头"}, "next": ["keeper"]},
{"id": "keeper", "title": "看守", "objectives": ["找到沈伯"],
"exit_if": {"visited": "灯塔"}, "next": ["ledger"]},
{"id": "ledger", "title": "名册", "objectives": ["对上那七个名字"], "next": []}
],
"reward": {"flags": ["灯塔真相"]}
}
- 谓词只用世界状态:
flag/visited/gold_gte/karma_gte/tier_gte/relation_gte/item_gte/counter_gte/stage_done,支持all/any/not组合。 - dormant 不显示:没满足
visibility的事业线不进面板,避免变成待办清单。 exit_if才推进:没有exit_if的一幕永不自动往前跑——它等宿主用quest_advance显式判定。- 失败优先:
fail_if或deadline_tick命中直接判失败。 - 奖励只发一次:完成后反复 tick 不会重复给钱。
- 载入时做 DAG 校验:有环、next 指向不存在的 stage 一律报错。
世界书:与酒馆语义对齐,但是逐回合确定性的
导入 SillyTavern 世界书不再丢语义:
面板「世界书」抽屉显示每条设定的触发次数与 sticky/cooldown 剩余,
用来回答"为什么这一段被触发了"。lore_lookup 工具可以按任意文本预演一次触发。
设定包:换世界不改代码
一个 JSON 就是一个世界(格式说明):
scripts/verify_settings.py 对每个包断言:载入完整、术语覆盖生效且无未声明的外来词、
货币与品牌落盘、地点描写生效、连跑 15 回合后势力与相位都在动。
质量与验证
项目没有 pytest 套件,验证靠 13 个可执行自检脚本 + 1 套变异测试(这是设计选择:
断言必须能被变异测试杀死,而不是只被覆盖率统计到)。
另有两套端到端压测:stress_freeplay.py 500(CI)与 stress_freeplay.py 2000(本机),
全程走真实 MCP stdio。最近一次 500 轮结果:0 意外错误、0 不变量破坏。
推前门禁一条命令:
bash scripts/pre_push_gate.sh # 17 项,与 GitHub Actions 同判据
覆盖率门禁:核心域 ≥70%(当前实测 88%);bandit High/Medium/Low 全 0;pyflakes 0。
项目结构
world-walker/ # 仓库根即 game_engine 包
├── engine.py # CLI(14 条命令,命令表驱动)
├── mcp_server.py # MCP 服务器(39 个工具)
├── state_schema.py # 状态模型 / 原子写 / 世代归档 / 种子
├── panel.py # 只读视图模型 + HTTP/SSE + 行动收件箱
├── panel.html / .css / .js # 主舞台(原生三件套,无构建链)
├── viz_series.py # 长局态势侧写(<state>.viz.jsonl)
├── rules/ # 144 个公开规则函数
│ ├── combat.py economy.py social.py karma.py
│ ├── cognition.py narrative_engine.py progression.py
│ ├── world_dynamics.py world_evolution.py
│ ├── models.py # 16 个命名闭式模型
│ └── social_extended.py cultural_evolution.py systems_feedback.py
├── settings/ # 4 套可直接开玩的设定包
├── scripts/ # 13 个自检 + 变异测试 + 截图/视频/演示存档流水线
├── docs/demo/ # 真实演示存档(截图与视频的数据源)
├── docs/screenshots/ # README 预览(真实渲染 + 逐项断言)
└── assets/echarts.min.js # 离线图表库(Apache-2.0,随包分发)
常见问题
它需要联网吗?
不需要。核心与面板零第三方依赖,图表库本地随包分发。只有 MCP 模式需要装 mcp SDK。
它需要 OpenAI / Claude 的 API Key 吗? 不需要。它不调用任何模型;模型由你的 Agent 提供。引擎只负责"世界怎么变"。
我能用它做商业项目吗? MIT 许可。示例设定包里的人物、势力、地名全部原创,可以自由替换。
为什么不用 pytest?
断言必须经得起"改坏代码看测试红不红"的检验。verify_tests.py 会静态拒绝恒真断言,
verify_mutation.py 则真的把 36 个已知错误逐个注入源码,确认每个都能被测试杀死。
两者都在门禁里。
为什么面板不能直接提交行动?
可以排队,但不直接结算。面板把行动写进 <state>.inbox.jsonl 侧写,Agent 用 pending_actions
取走并照常走引擎。这样"状态只有一个写入源",确定性与快照回退语义不会被破坏。
重新生成演示素材
python scripts/make_demo.py # 4 套世界的真实演示存档 → docs/demo/
python scripts/shots.py # 逐项断言 + 截图 + 录屏 → docs/screenshots/
python scripts/verify_settings.py # 四套设定包全量校验
许可与致谢
MIT。图表库 ECharts 5.5.1(Apache-2.0)。 酒馆生态格式的兼容实现参考了 SillyTavern 社区公开的角色卡与世界书规范; 本项目不复用任何作品的名词与设定。















