NeverEndingQuest 静态分析:模块设计与工作原理
对象:MoonlightByte/NeverEndingQuest · 方法:纯静态分析
一、总体架构
项目为纯 Python 单进程引擎(另含 web/frontend React 前端与 Node 构建脚本),无独立数据库、无 ORM,状态以 JSON 文件持久化。分层如下(ASCII):
┌─ 入口/界面层:main.py(终端) · run_web.py · web/web_interface.py(Flask+SocketIO) · React(/play/)
├─ 游戏逻辑层:core/ai/action_handler.py(命令分发) · core/managers/*(各子系统管理器)
├─ 上下文/压缩层:core/ai/{conversation_utils,chunked_compression,ultra_compressor,cumulative_summary,adv_summary}.py
├─ AI 模型层:core/ai/api_client.py(多供应商路由) · model_config.py(逐调用点模型矩阵) · core/generators/*
└─ 数据层:modules/ · schemas/ · data/(SRD) · utils/file_operations.py(原子写)
主要数据流(主循环,main.py:5551 main_game_loop):
用户输入 → action_predictor(路由) → get_ai_response(LLM) → validate_ai_response(校验)
→ process_action(action_handler 命令模式) → [Manager] → 原子持久化(file_operations)
→ 位置/模块转换触发压缩 → 保存会话历史
Manager 模式(ARCHITECTURE.md:241)贯穿子系统:CampaignManager、CombatManager、StorageManager、LocationManager、LevelUpManager、StatusManager;战斗等复杂子系统以「信号返回」(needs_post_combat_narration、enter_levelup_mode 等,action_handler.py:1638/2196)交还主循环(ARCHITECTURE.md:494-528)。
二、核心工作流(最重要链路)
链路 A:单回合「输入 → 推演 → 判定 → 状态更新」
- 输入:
main_game_loop循环内input()取玩家指令(main.py:6177),循环顶部先做truncate_dm_notes、remove_duplicate_messages、位置转换压缩(main.py:6052-6080)。 - 动作预测路由:
utils/action_predictor.py:141predict_actions_required用 mini 模型二分类该回合是否需要结构化 JSON 动作,失败/畸形一律保守回退全模型(action_predictor.py:184-224)。 - 推演:
main.py:5121get_ai_response(经capture_and_fanout→core/ai/api_client.py:280create_completion路由到 OpenAI/Gemini/本地)。 - 校验:
main.py:1782validate_ai_response与core/validation/dm_response_validator.py等对 LLM 输出做规则/语义校验,不合规重试。 - 状态更新:
core/ai/action_handler.py:1374process_action按命令模式分发(updateCharacterInfo/transitionLocation/createEncounter/updatePlot/updateWorldTime 等,action_handler.py:34-40),落到 Manager 后原子落盘。 - 压缩:位置转换时
main.py:2321check_and_process_location_transitions;模块转换时campaign_manager归档+生成 living summary。
链路 B:模组生成(story-first 流水线)
module_builder.py(编排器) → story_first/pipeline.py(崩溃安全 7 阶段:outline→area_binding→plot_derivation→location_fill→npc_repair→candidate_hardening→creature_compile,pipeline.py:65-73),每阶段经 jsonschema 校验+语义纠错(core/generators/story_first/validators.py),模型能力不足时降级到兼容生成器(module_builder.py:91-101)。产物写入 modules/<name>/{areas,encounters,media,module_plot.json,...} 并生成 _BU.json 复位备份(module_builder.py:2034-2074)。
三、模块逐一分析
1. 多供应商 AI 抽象(core/ai/api_client.py + model_config.py + utils/capture/multi_model_capture.py)
- 职责:把 OpenAI/Gemini/LM Studio 归一为 OpenAI 响应形状,隔离 provider 差异。
- 关键函数:
create_completion(api_client.py:280,薄路由层);_openai_completion:403;_gemini_completion:456(复用utils/capture/gemini_caller.py做消息转换与 thinking 判断);_NormalizedResponse:97保证choices[0].message.content/usage统一;_enforce_provider_constraints:370硬约束(gpt-5-mini 禁 temperature、Gemini 忽略 temperature)。 - 工作原理:
model_config.py维护 67+ 个「逐调用点」配置 dict(ACTION_PRED_GPT5MINI_LOW:201、COMBAT_VALID_GPT54_NONE:376等),PROVIDER_MODELS:1193按 provider 切换 full/mini 默认模型,set_provider:1251、get_provider:1274(延迟读取防快照失效)、get_model_for_callsite:1286按 task_id 覆盖;capture_and_fanout(multi_model_capture.py)主调用同步返回、其余变体后台线程 fan-out 用于测评/采集。 - 设计亮点:provider 中立错误(
ProviderCallError/ProviderEmptyResponse);JSON-Schema→Gemini response_schema 自动转换convert_to_gemini_schema:10;Local/Custom 端点动态读user_settings.json(utils/openai_client.py:30-40)。
2. Token 压缩系统(多套并存)
- 并行会话压缩:
utils/compression/conversation_compressor_parallel.py(760 行,ThreadPoolExecutor + MD5 缓存,README 宣称 76-82%/消息)。 - 分块压缩:
core/ai/chunked_compression.py:102chunked_compression,阈值/块大小chunked_compression_config.py:11-12为COMPRESSION_TRIGGER=12, CHUNK_SIZE=6(README 旧文写 8,实际代码 6);按「最近 chronicle 之后」的 location summary 计数,用LocationSummarizer生成 AI 编年史替换消息区间(chunked_compression.py:157-204)。 - 符号化压缩:
core/ai/ultra_compressor.py:70UltraCompressor把叙述转@C/@L/@S/@I/@R + EVT[...]索引格式(ultra_compressor.py:14-24),动作动词映射符号(ACTION_SYMBOLS:74),spaCy 可选、缺省回退正则(ultra_compressor.py:35-40)。 - 角色卡压缩:
core/ai/character_sheet_compressor.py(5e 角色 JSON → dense key-value,声称 88% 缩减,character_sheet_compressor.py:6-12)。 - 战斗压缩:
core/ai/combat_compressor.py与core/ai/combat_compression_engine.py(1480 行,输出@T=CS/v2纯文本标签,response_format=None退出 JSON 模式)。 - 提示词压缩:
prompts/system_prompt_compressed.txt、prompts/combat/combat_validation_prompt_compressed.txt等 @TAG 机器语言(README 称 101K→8K chars)。 - 累计摘要:
core/ai/cumulative_summary.py(858) 与core/ai/adv_summary.py(1075) 生成/维护模块 living summary。 - 断裂遗留:
core/ai/conversation_compression.py:13-14导入dynamic_compressor、compressor_spec_location、block_location_compressor,三者在仓库中未读到(glob 无命中),该文件为死代码。
3. 记忆系统(core/memories/)
- 职责:伙伴 NPC 的长期记忆与关系演化。
- 关键函数:
companion_memory.py:20CompanionMemoryManager(编排);ActionParser解析行为;MemoryCrystallizer(阈值 0.35)结晶为CoreMemory;GravitationalRetrieval按「重力」检索。 - 情绪记忆:
emotional_vectors.py:25EmotionalVector5 维情绪(trust/power/intimacy/fear/respect)带边界钳制(emotional_vectors.py:11-23);行为特征向量 5 维(protector_vs_exploiter 等,companion_memory.py:63-71)。 - 压缩存储:
data/companion_memories_compressed/_schema_spec_v2.json70-85% 压缩,键缩写(n/cm/es/bm/ti…)与动作码表(_schema_spec_v2.json:44-85)。 - 世界级记忆:
campaign_manager.py的 living summary(_generate_module_summary:3007)+ 会话归档modules/campaign_archives/+ 跨模块上下文注入inject_campaign_context:4015、get_accumulated_summaries_context:3972。
4. SRD 规则集成(数据文件,非硬编码)
- 数据文件:
data/spell_repository.json(约 300+ 法术,每条含source:"SRD 5.2.1"、version:"5.2.1"、compactGuidance)、data/srd_common_rules.json、data/srd_spell_roll_contracts.json、data/bestiary/{monster,npc}_compendium.json。 - 查询层:
core/ai/srd_reference.py:46SRDReferenceIndex版本化精确/别名查找(resolve:135、reference:139),校验 key 与名称归一一致、source/version 必须为 SRD 5.2.1(srd_reference.py:74-89);srd_roll_contracts.py提供掷骰契约。 - 战斗运算落在 Python:
core/combat/resolver.py确定性裁决(resolve_intent:328、resolve_adjudicated:559、apply_resolution:1338、check_invariants:1734)。 - 预掷骰:
core/combat/rolls.py:17PersistedPrerollSource由 Pythonrandom预生成并持久化(ensure_agentic_roll_reserve:186),LLM 只能消耗不能捏造。
5. Module Toolkit / 模组解析
- 编排:
core/generators/module_builder.py(编排器) 调module_generator.py(worker,负责区域连接/ID)、area_generator/npc_builder/monster_builder/plot_generator/location_generator(CLAUDE.md 强调「改 bug 只改 worker 不改编排器」)。 - story-first:
core/generators/story_first/契约(contracts.pyStorySeed/AcceptedOutline/...)、编译器(compilers.py)、执行门(execution.py)、阶段(stages/*)、模块医生(module_doctor.py)。 - 集成与安全:
core/generators/module_stitcher.py(4939 行) 扫描modules/、ID 冲突消解、文件安全(禁可执行/10MB 上限)、AI 内容安全审查、schema 校验(80% 阈值,module_stitcher.py:34-39)。 - NPC 校验上下文:
core/ai/build_npc_context.py:123动态扫描所有区域产出@NPC_VALIDATION_DATA防幻觉。
6. 战斗子系统(core/combat/ + core/managers/combat_manager.py)
- 职责:回合制战斗的确定性裁决与 agentic 意图选择。
- 关键函数:
combat_manager.py(4924 行)回合调度;pipeline.py:229resolve_claimed_window;resolver.py:169validate_intent、328resolve_intent(已知招式)、559resolve_adjudicated(自由裁决)、1338apply_resolution、1734check_invariants。 - 工作原理:LLM 每回合产出「战术意图」(T096,
model_config.py:255_AGENTIC_COMBAT_INTENT_SCHEMA)与「叙述」(T097),Python 负责算术/排序/校验/恢复,事件可 crash-replay。 - 设计亮点:效果 tick(
core/effects/model.py:16TICK_TRIGGERS)与时钟窗口(pipeline.py:151)把持续效果精确结算到回合。
7. 战役/模块转换子系统(core/managers/campaign_manager.py)
- 职责:hub-and-spoke 战役编排、模块完成/迁移、living summary。
- 关键函数:
CampaignManager:1326;refresh_modules:1361;publish_party_module_transition:1683;stage_module_completion_intent:1945;complete_module:2375/_complete_module_once:2477;_generate_module_summary:3007;detect_module_transition:3935;get_accumulated_summaries_context:3972。 - 工作原理:模块完成走「分期意图 + 收据 + 崩溃恢复」(
_recover_module_work_locked:970、_recover_campaign_completion_transaction_locked:856),归档会话、生成摘要、切换可用模块。 - 设计亮点:模块完成/迁移以文件锁与收据保证恰好一次(exactly-once)。
8. 存储/住房子系统(core/managers/storage_manager.py)
- 职责:自然语言存储("I store my gold in a chest")与位置绑定容器。
- 关键函数:
storage_manager.py(670 行) 备份/回滚(storage_manager.py:24-29);storage_processor.py交易处理;storage_action_schema.json约束存储动作输出(T049)。 - 设计亮点:所有操作走原子备份/回滚,跨会话/跨模块持久。
9. 效果/时间系统(core/effects/)
- 职责:临时效果(buff/debuff/条件)的声明式契约与生命周期。
- 关键函数:
model.pycanonical_stat:34、effect_identity:56;clock.py幻想历法(12 自造月名clock.py:11-24)与时长换算fixed_duration_seconds:36;projection.py/lifecycle.py/outbox.py负责效果投影/到期/一次性通知。 - 设计亮点:
EFFECTS_PIPELINE_VERSION=2(model.py:11),一次性通知 + 迁移(effects_migration.py)。
10. Web 界面(web/web_interface.py + web/frontend/)
- 职责:Flask REST + SocketIO 实时同步,React(
/play/)与 legacy(/)双前端。 - 关键点:媒体 3 级回退(当前模块 media → 全模块搜索 → 静态回退,CLAUDE.md);
_BU.json区域扫描(web_interface.py:2201/4519/4828);凭证经 Settings 面板 + OS 凭证存储(model_config.py:1306-1405)。
四、数据模型与持久化
- Schema 驱动:
schemas/15 个 JSON Schema(char/locationfile/module/party/encounter/plot/map/mon/room/plan/storage_action/journal…),内容可无代码扩展(ARCHITECTURE.md:425)。 - 模块结构:
modules/<name>/{areas,characters,monsters,encounters,media,module_plot.json,party_tracker.json,<name>_module.json}(README.md:656)。 - plot 模型:
module_plot.json含plotTitle/mainObjective/plotPoints[](每点有id/title/description/location/nextPoints/status/sideQuests[],见module_plot_BU.json:1-27)。 - map 模型:
map_schema.json含rooms[].{id,name,connections,coordinates,directions,tags,purpose,dangerLevel}与layout2D 网格(map_schema.json:30-100)。 - 原子写:
utils/file_operations.py(备份→写入→校验→回滚)。 - 复位种子:
_BU.json为「洁净复位种子」,首启/重置由utils/startup_wizard.py:151-157、utils/reset_campaign.py:131-140复制为 live 文件。 - Git 策略:
.gitignore明确忽略实时状态文件(module_plot.json、areas/*.json、party_tracker.json、characters/、campaign.json、world_registry.json、campaign_archives|summaries),仅提交_BU.json+媒体(.gitignore:111-124,396-407)。 - 凭证:
model_config.py:1306-1405OS 凭证存储优先、失败回退 owner-only 的user_settings.json(密钥 0o600 权限,_save_user_settings:1325-1336)。
五、与 Z.R.I.C 功能对照表
| Z.R.I.C 功能 | 本项目实现 | 位置 | 备注 |
|---|---|---|---|
| 剧本解析 | story-first 7 阶段 + jsonschema 校验 | story_first/pipeline.py:65、contracts.py |
有语义纠错与降级 |
| 推演分支 | plotPoints/nextPoints/sideQuests 结构 + LLM 推演 | modules/*/module_plot_BU.json:4-27、updates/plot_update.py |
分支由 AI 依上下文决定,非确定性脚本 |
| 自动判定 | 战斗确定性裁决 + 效果 tick | core/combat/resolver.py:328/559、core/effects/* |
代码持有算术,LLM 只选意图 |
| 世界状态 | world_registry.json / campaign.json / party_tracker.json | campaign_manager.py、README.md |
跨模块后果由 living summary 承载 |
| NPC 情绪记忆 | 5 维情绪向量 + 行为特征 + 结晶/重力检索 | core/memories/emotional_vectors.py:25、companion_memory.py |
压缩存储 _schema_spec_v2.json |
| 地图 | map_schema(rooms/connections/coordinates/layout 2D 网格) | schemas/map_schema.json、modules/*/map_*_BU.json |
位置 ID 用 A/B/AA 字母前缀 |
| 触发器 | 效果 tick 触发器(start/end of turn/round);故事级显式触发器未读到 | core/effects/model.py:16 |
无独立「剧情触发器」脚本系统 |
| 时间线 | 幻想历法 + 会话按序归档 + visitCount/首末次日期 | core/effects/clock.py、campaign_manager.py |
无显式 timeline.json |
| 记忆系统 | 会话压缩 + living summary + chronicle + 伙伴记忆 | chunked_compression.py、cumulative_summary.py、core/memories/* |
多层「压缩即记忆」 |
| 本地模型 | LM Studio/OpenAI 兼容端点 provider | api_client.py、model_config.py:1426、utils/openai_client.py |
默认 http://localhost:1234/v1 |
| 许可 | Fair Source License 1.0(5 年后转 Apache-2.0)+ SRD CC-BY 4.0 | LICENSE、LICENSE-SRD、README.md:218-236 |
双许可 |
六、设计评价
优点(附代码依据)
- 多供应商与逐调用点模型矩阵隔离良好:provider 差异集中在
api_client.py+model_config.py,调用点只传命名 config dict,get_provider()延迟读取避免快照失效(model_config.py:1274-1283)。 - 数据完整性工程化程度高:原子写(
file_operations.py)+_BU.json复位种子 + 战斗「预掷池」防 LLM 篡改骰子(rolls.py:186)+ 模块完成/迁移有崩溃恢复与锁(campaign_manager.py大量_recover_*/transaction lock)。 - 压缩体系层次分明:从角色卡/提示词/战斗叙述/会话分块到 living summary,且「代码持算术、LLM 持意图」把不确定性收敛到可验证边界(
resolver.py、action_predictor.py:184保守回退)。 - 契约驱动、可测性强:大量 task_id(T013/T040/T082…)与 callsite 注册(
register_callsite),并配有针对性的 pytest(.gitignore白名单中 20+ 测试文件)。
缺陷(附代码依据)
- 遗留代码与缺失依赖并存:
conversation_compression.py:13-14导入的三模块在仓库中未读到(glob 无命中),该文件实为断裂/死代码;dm_wrapper.py:11-15、enhanced_dm_wrapper.py:10-13自标 DEPRECATED。 - 单一巨型模块:
main.py7427 行、module_stitcher.py4939 行、campaign_manager.py3804 行、action_handler.py3672 行,职责过度集中、耦合高,main.py混入大量校验/压缩/归档细节。 - 文档与实现不一致:README 宣称分块压缩「8 transitions」而
chunked_compression_config.py:11-12实际COMPRESSION_TRIGGER=12, CHUNK_SIZE=6;README「70-90% 成本削减」缺乏仓库内可复现基准(telemetry 数据被 gitignore)。 - 状态分散于文件系统:以 JSON 文件为唯一真相源、靠路径约定与锁串行化,无 schema 迁移版本管理(effects 迁移另设
effects_migration.py补丁),并发/跨模块一致性维护成本高。
七、结论
NeverEndingQuest 是一个「AI 驱动 DM + JSON 文件持久化」的单体 Python 引擎,工程重心不在规则引擎而在上下文治理:用压缩(会话分块、@TAG 符号化、living summary、伙伴情绪记忆)对抗 LLM 上下文窗口,用逐调用点多模型矩阵控制成本,用「代码持算术、LLM 持叙事」的 agentic 战斗与预掷骰保证规则可验证。SRD 5.2.1 规则以数据文件(data/spell_repository.json 等)+ 查询层集成而非硬编码,战斗/效果由 Python 确定性裁决。架构上优点(原子写、崩溃恢复、provider 隔离、契约可测)与缺点(巨型文件、遗留断链代码、文档与实现漂移、文件态一致性)并存,符合其 75★、v0.3.5 Alpha 的成熟度定位。