docs: add developer docs scaffold + architecture section

This commit is contained in:
youzini
2026-05-31 16:58:56 +00:00
parent fc288bd29f
commit ac6a1a295a
5 changed files with 389 additions and 0 deletions

49
docs/README.md Normal file
View File

@@ -0,0 +1,49 @@
# ST-BME 开发者文档
这里是 ST-Bionic-Memory-EcologyST-BME的**开发者/架构文档**。面向想理解内部原理、算法、或参与维护的人。
普通用户的安装、面板、配置、排障说明在仓库根目录的 [`README.md`](../README.md),本目录不重复。
## 文档地图
### architecture/ — 架构与控制平面
跨文件的结构、数据路径、不变量。这些内容变化慢,是理解"为什么这样组织"的入口。
- [`overview.md`](architecture/overview.md) — 子系统地图 + 写入/读取/安全三条数据路径
- [`control-plane.md`](architecture/control-plane.md) — 身份解析、持久化状态机、必须维持的不变量
- [`storage-and-formats.md`](architecture/storage-and-formats.md) — 存储分层、快照契约、向前兼容纪律
- [`server-integration.md`](architecture/server-integration.md) — 三档 Authority 集成、自动降级/升级、能力探测
### algorithms/ — 算法原理
核心算法"怎么算的":参数、公式、阈值。基于真实代码编写,并标注关键文件位置。
- [`retrieval.md`](algorithms/retrieval.md) — 三层混合检索:向量预筛 + 图扩散 + LLM 精排 + 多意图/DPP/残差
- [`extraction.md`](algorithms/extraction.md) — LLM 提取管线:消息 → 结构化 → 图谱写入 → 时序边
- [`diffusion-and-dynamics.md`](algorithms/diffusion-and-dynamics.md) — 图扩散PEDSA+ 混合评分 + 访问强化/衰减
- [`consolidation-and-compression.md`](algorithms/consolidation-and-compression.md) — 记忆整合/去重 + 压缩遗忘 + 分层总结
- [`vector-and-embedding.md`](algorithms/vector-and-embedding.md) — 向量空间身份 + 批量 embedding + 维度门禁
### features/ — 功能解析
每个面向用户的功能"做什么、怎么用、边界在哪"。
- [`memory-model.md`](features/memory-model.md) — 节点类型、关系类型、主客观分层、故事时间线
- [`recall-cards.md`](features/recall-cards.md) — 持久召回卡片
- [`history-safety.md`](features/history-safety.md) — 历史变动恢复、渲染限制保护、Restore Lock
- [`hide-and-render.md`](features/hide-and-render.md) — 隐藏旧楼层与渲染限制
- [`ena-planner.md`](features/ena-planner.md) — ENA Planner
- [`native-acceleration.md`](features/native-acceleration.md) — Native/WASM 灰度加速
### contributing/ — 参与维护
怎么开发、怎么测、必须遵守的约定。
- [`development.md`](contributing/development.md) — 构建、测试、检查命令;分支工作流
- [`testing.md`](contributing/testing.md) — 测试分类、ratchet 防线、依赖注入守卫
- [`conventions.md`](contributing/conventions.md) — 注入式控制器模式、必须保持的不变量
## 维护原则(重要)
文档最大的敌人是腐烂。本目录遵守三条防腐铁律:
1. **离代码越近,腐烂越慢。** 单个函数的 API 细节留在模块头注释里(改代码自然会改它),不抄进这里。本目录只写"跨文件的算法原理、不变量、功能行为"。
2. **不写一改就过期的内容。** 避免"某函数第几行做什么"这种描述;算法文档引用文件位置时,描述的是"哪个算法在哪个文件",而非逐行。
3. **改了行为就更新对应文档。** 算法参数、不变量、功能边界发生变化时,更新这里;纯重构(不改行为)通常不需要动文档。

View File

@@ -0,0 +1,101 @@
# 控制平面:身份、持久化、不变量
这是 ST-BME 最关键的一块。过去反复出现的 bug——"提取卡住"、"未进入聊天"、"reroll 乱召回"、"一致性审计永远说有漂移"——几乎全部源于这块的状态管理。本文档记录其结构和**必须维持的不变量**。
## 根本问题(历史背景)
早期实现里,**身份、持久化确认、加载状态、图谱可写性、向量脏标记、召回复用,全都从多个异步事件路径读写同一份模块级可变状态**`currentGraph``graphPersistenceState`、pending 标志、commit marker
没有单一事实源,也没有把"做决定"和"写入"分开。每个修复都是在这些接缝上打补丁,于是每修一个就冒出下一个。
解决方向:把控制平面抽成**纯逻辑/注入式模块**,让整类 bug 在结构上不可能发生。
## 身份解析
聊天身份是一切持久化的主键。问题从来不是"chatId 这个键选错了",而是"有好几个来源都自称是当前身份,还互相偷偷顶替"。
身份核心在 `runtime/identity-resolver.js`,把身份明确拆成**四条独立通道**,绝不互相顶替:
| 通道 | 含义 | 唯一来源 |
| --- | --- | --- |
| **active identity** | 当前宿主活动聊天 | 只来自宿主上下文context integrity / hostChatId 的已知别名 / hostChatId |
| **graph-owner identity** | 图谱自带的所属身份 | 图谱 meta只用于校验/恢复 |
| **queued identity** | 排队持久化的身份 | 持久化队列状态,只用于校验 |
| **marker identity** | commit marker 的身份 | commit marker只用于校验/恢复 |
**核心不变量:**
> active identity 只能来自宿主上下文。graph-owner / queued / marker 身份只能用于校验和恢复,**绝不能"偷偷"变成当前聊天**。
这正是"未进入聊天"那类 bug 的根:旧代码用一个"优先级抽奖"函数接受十几个竞争来源,结果某个非活动身份被当成了活动聊天。现在 `resolveActiveIdentity` / `resolveGraphOwnerIdentity` / `resolveQueuedIdentity` / `resolveMarkerIdentity` 是分开的入口,不给聚合入口诱导污染。
> 身份是每次操作解析一次、显式传递的,不是从全局随用随取。
## 持久化确认状态机
持久化确认逻辑收敛在 `sync/persistence-reducer.js`,是**纯函数**:无 IO、无图谱变更、无 UI 副作用。
它把"这批记忆到底存好了没"变成 `(身份, 存储层 tier, 版本 revision, 证据)` 的纯函数。核心不变量写死进 reducer而不是到处打补丁
```
已确认版本 >= 排队版本
且 同一身份
且 是规范 tiercanonicalauthority-sql / indexeddb / opfs / luker-chat-state
⟹ pendingPersist 必须为 false
```
**派生不变量:**
> recovery-only tier`shadow` / `metadata-full` / `runtime-recovery` 等)永远不能推进确认状态。它们只用于灾难恢复,不能被当作"数据已安全落地"的证据。
> 当 `lastAcceptedRevision >= max(batchRevision, queuedPersistRevision)` 且排队聊天与当前聊天一致时,陈旧的 `pendingPersist` 标志自动清除。
这条解决了"SQL 已确认 rev=2、但 pendingPersist 赖着不走、把提取一直卡死"的 bug。reducer 让"陈旧 pending 卡住提取"在结构上不可能发生。
历史上的语义修复Phase 2 引入不变量、Phase 5 把调用点改为显式事件)都保留在该文件头注释里。
## 图谱可写性门禁
`sync/graph-mutation-gate.js` 决定"现在能不能改图谱",避免在加载中/恢复中/未进入聊天时误写。
关键判定(注入式 impl
- `ensureGraphMutationReady` — 操作前的总门禁
- `getGraphMutationBlockReason` — 给用户的暂停原因文案
- `assertRecoveryChatStillActive` — 异步恢复过程中,校验聊天没被切走(切走则抛 abort
- `getGraphPersistenceLiveState` — 把内部状态投影成面板/调试可读形态
## 向量门禁与 reroll 边界
- `vector/vector-gate.js` — 向量准备/修复前置门禁,决定 skip / repair / blocked / sync。
- `runtime/reroll-transaction-boundary.js` — reroll 召回复用事务边界。
**reroll 不变量:**
> reroll 助手楼层时,若上方用户楼层未变且存在可复用的持久召回记录,则跳过预召回历史恢复和新向量检索;但被 reroll 的助手楼层的**图谱回滚必须保留**(走既有 `onReroll` 路径)。
换句话说:召回注入可以复用,但图谱状态该回滚还得回滚。两者不能混为一谈——这是"reroll 乱召回"修复的核心。
## 副本一致性模型
Authority 场景下有三处存储,它们**不是平级的版本副本**
| 存储 | 角色 |
| --- | --- |
| Authority SQL | **规范主源**canonical primary |
| Blob checkpoint | 备份副本backup replica |
| Trivium | 搜索副本search replica |
**不变量:**
> 只有 Authority SQL 有可靠的图谱版本。当 SQL rev > Blob/Trivium rev 时,状态是"副本待同步"**不是**"数据漂移"。SQL 领先时不建议从 checkpoint 恢复(那会用旧数据覆盖新数据)。
> checkpoint 生成时,若 SQL 是主存储层,必须以 Authority SQL 快照为源SQL 导出失败/为空时checkpoint 生成失败(`authority-sql-checkpoint-source-empty`),绝不回退到可能陈旧的运行时图谱。
> 副本同步动作checkpoint 写入、Trivium/向量同步)相互独立执行,一个失败不阻塞其余。
## 依赖注入接缝
控制平面模块通过一个 `runtime` 对象拿到所有依赖,由 `index.js``create*Runtime()` builder 提供。这有个隐患:模块"期望"的 `runtime.X` 必须全部被 builder 提供,否则运行时(尤其 fallback 路径)才炸。
`tests/runtime-deps-completeness.mjs` 守住这条线。详见 [`../contributing/conventions.md`](../contributing/conventions.md) 和 [`../contributing/testing.md`](../contributing/testing.md)。

View File

@@ -0,0 +1,87 @@
# 架构总览
ST-BME 是一个 SillyTavern 第三方前端扩展:在聊天进行时,把对话提炼成一张**记忆图谱**(节点 + 关系),并在生成前把相关记忆召回、注入回提示词。
`manifest.json` 指向 `index.js`(主入口/编排层)和 `style.css`
## 子系统地图
```
index.js 主入口:事件钩子、设置管理、流程调度、依赖注入组合根
├── graph/ 记忆图谱数据结构、节点/关系 schema、快照、认知/区域/时间线状态
├── maintenance/ 写入链路:提取、整合、压缩、分层总结、智能触发
├── retrieval/ 读取链路:混合检索、图扩散、评分、增强、注入格式化
├── vector/ 向量索引、直连 embedding、向量空间身份、维度门禁
├── sync/ 持久化与同步IndexedDB/OPFS 存储、快照契约、控制平面模块
├── prompting/ 提示词构建、任务预设、正则/世界书/EJS 任务模式
├── llm/ LLM 请求封装
├── runtime/ 运行时状态、设置默认值、身份解析、注入式控制器/工厂
├── host/ SillyTavern 宿主适配(事件绑定、上下文、原生渲染)
├── ui/ 面板、图谱渲染、消息级 UI、召回卡片
├── ena-planner/ ENA Planner独立规划子系统
└── native/ Native/WASM 加速灰度fail-open 回退 JS
```
`index.js` 在这次重构后正在收敛成**组合根composition root**:它持有少数模块级运行时状态,并通过 `create*Runtime()` 把依赖显式注入给抽出的控制器模块。详见 [`control-plane.md`](control-plane.md) 和 [`../contributing/conventions.md`](../contributing/conventions.md)。
## 三条数据路径
ST-BME 的运行可以归纳为三条相对独立的链路。
### 写入链路(对话 → 记忆图谱)
助手回复落地后触发,把新对话提炼进图谱。
```
助手消息落层
→ 自动提取计划(够不够触发?智能触发?)
→ 构建结构化提取输入(过滤 think/analysis 等)
→ LLM 提取 → 规范化操作create/update/delete/link
→ 写入图谱节点与关系(含时序边)
→ 后处理:整合去重 → 分层总结 → 反思 → 睡眠遗忘 → 压缩
→ 向量同步(为新节点生成 embedding
→ 持久化到耐久存储层
```
算法细节见 [`../algorithms/extraction.md`](../algorithms/extraction.md) 和 [`../algorithms/consolidation-and-compression.md`](../algorithms/consolidation-and-compression.md)。
### 读取链路(用户输入 → 注入提示词)
生成前触发,把相关记忆召回并注入。
```
解析召回输入override / 发送意图 / 聊天尾部用户楼层)
→ 可复用的持久召回记录?命中则跳过新检索
→ 向量预筛(多意图拆分 + 多查询)
→ 图扩散PEDSA 扩散激活)
→ 混合评分(图 + 向量 + 词法 + 重要度 × 时间衰减)
→ 认知边界过滤 + 可选 DPP 多样性 + 可选残差召回
→ 可选 LLM 精排
→ 访问强化 + 可选概率召回
→ 注入格式化(按 POV/区域分桶成表格)
```
算法细节见 [`../algorithms/retrieval.md`](../algorithms/retrieval.md) 和 [`../algorithms/diffusion-and-dynamics.md`](../algorithms/diffusion-and-dynamics.md)。
### 安全链路(保护已有记忆不被误删/误覆盖)
横跨写入和读取,确保宿主的各种异常状态(只渲染最近 N 条、reroll、切换聊天、历史被编辑不会误清空或覆盖记忆图谱。
```
历史变动检测 → 必要时历史恢复replay 或全量重建)
渲染切片识别 → 暂停破坏性恢复
Restore Lock → 恢复期间阻断图谱变更
持久化身份校验 → 防止把别的聊天身份当成当前聊天
```
细节见 [`control-plane.md`](control-plane.md) 和 [`../features/history-safety.md`](../features/history-safety.md)。
## 控制平面 vs 数据平面
这次重构的核心理念是把**"做决定"(控制平面)和"执行副作用"(数据平面)分开**
- **控制平面**身份解析、持久化确认状态机、图谱可写性门禁、向量门禁、reroll 边界。这些是纯逻辑/策略,已抽成可独立测试的注入式模块。
- **数据平面**:实际的 IndexedDB/OPFS/Authority/Luker 读写。仍在编排层,由控制平面的决定驱动。
这条分界是过去大量 bug陈旧 pending、未进入聊天、reroll 乱召回、一致性漂移)的修复基础。详见 [`control-plane.md`](control-plane.md)。

View File

@@ -0,0 +1,74 @@
# 服务器集成Authority / st-doa
ST-BME 可以独立运行(纯前端),也可以在检测到 st-doa/Authority 服务器时自动升级到更稳定的数据平面操作。本文档说明这套自动集成。
## 核心原则
1. **BME 独立运行是第一公民。** 没有 st-doa 时BME 必须完整可用,优雅降级,不报错。纯前端模式显示"纯前端模式",不是错误状态。
2. **第三方自定义 URL embedding 是主流路径。** OpenAI 兼容 `/v1/embeddings`、one-api、new-api、litellm、vLLM、llama.cpp、Ollama 桥接等都是一等公民。embedding 默认在客户端执行(`embeddingExecution` 默认 `client`)。
3. **集成是自动的,不需要用户配置。** 能力探测驱动的升级/降级,没有面向用户的"服务器模式"开关。既要优雅降级(无 st-doa也要优雅升级有 st-doa全程零用户干预。
> Authority 是**增强层**,不是硬依赖。它不生成 embedding——只通过 Trivium 存储/搜索向量,并要求调用方提供向量。
## 三档集成模式
| 模式 | 含义 | embedding | 存储 |
| --- | --- | --- | --- |
| **Mode A** | 纯前端(基线) | 前端生成 | 浏览器本地IndexedDB/OPFS |
| **Mode B** | Authority 存储增强 | 前端仍生成 | Authority 负责可靠 SQL/Trivium 持久化与诊断 |
| **Mode C** | 可选服务器端 embedding | 服务器端(**仅 opt-in** | Authority |
Mode A 必须完全稳定它是基线。Mode C 是可选的、必须显式开启。
## 自动升级状态机
ST-BME 维护一个派生的 `authorityUpgradeState`,由能力探测驱动:
```
standalone → probing → shadow → candidate → enhanced → degraded
```
- **standalone**:没有 Authority纯前端模式。
- **probing**:检测到可能的 Authority正在探测能力。
- **shadow / candidate**:逐步确认 Authority 各项能力。
- **enhanced**Authority 各项能力就绪,使用增强数据平面。
- **degraded**Authority 部分能力不可用,自动回退到仍可用的层。
面板显示当前自动模式,但不提供手动切换开关。
## 能力探测
ST-BME 从 Authority 的 `/probe` 和 session 响应里读取 BME 专属能力标志:
- `bmeVectorManifestReady`
- `bmeVectorApplyReady`
- `bmeVectorApplyJobsReady`
- `bmeServerEmbeddingProbeReady`
- `bmeCandidateSearchReady`
- `bme.protocolVersion`
旧版/无 Authority 时这些默认全 false自动走 fallback。这保证了对老 Authority 探针的向后兼容。
## 任务类型协商
Authority 的后台 job 系统只支持特定 job 类型。ST-BME 不能盲目提交。
> `shouldUseAuthorityJobs` 校验具体 job 类型是否在服务器声明的 registry 中(从 `jobs.builtinTypes` / `jobs.registry.jobTypes` / `core.health.jobRegistrySummary.jobTypes` 读取)。服务器没声明支持的 job 类型不提交,直接走直连同步。
> 空的 `supportedJobTypes: []` 表示"服务器明确不支持任何 job";缺失/默认则表示"未知",保持对旧探针的向后兼容。
## 向量应用端点
`POST /bme/vector-apply`:客户端生成向量后,由 Authority 执行 Trivium 批量 upsert + link。
> Authority 不碰 embedding API key。它只接收客户端生成好的向量。
> 该端点按批校验 `vectorSpaceId` / `observedDim` 一致性,拒绝混合维度,返回带类型的校验错误。
向量空间身份和维度门禁的算法见 [`../algorithms/vector-and-embedding.md`](../algorithms/vector-and-embedding.md)。
## Authority SQL 图谱存储选择
> Authority SQL 图谱持久化由 `storagePrimaryReady`SQL + session + permission门控**不是** `serverPrimaryReady`SQL + Trivium + Jobs + Blob
这意味着 Blob/Jobs/Trivium 降级不会禁用 SQL 持久化——SQL 图谱持久化只需要 SQL + 会话 + 权限。这条避免了"某个增强能力不可用就连主存储都不写"的过度联动。

View File

@@ -0,0 +1,78 @@
# 存储分层与数据格式
ST-BME 的图谱数据可能存在多种位置,取决于宿主环境和服务器能力。本文档说明分层策略、快照契约,以及保证"以后改格式不用大迁移"的向前兼容纪律。
## 存储分层
| 层 | 用途 | 说明 |
| --- | --- | --- |
| **Authority SQL** | 规范主源 | 有 st-doa/Authority 时的权威存储;唯一有可靠图谱版本 |
| **Luker chat-state** | 宿主当前聊天主存储 | Luker 宿主下作为当前聊天状态的主写入目标 |
| **IndexedDB** | 浏览器本地主存储 | 普通 SillyTavern 下的默认本地存储 |
| **OPFS** | 浏览器本地存储(替代) | Origin Private File System sidecar |
| **Blob checkpoint** | 备份副本 | Authority 场景的备份,非主源 |
| **Trivium** | 搜索副本 | 向量搜索存储,非主源 |
| **metadata-full / shadow / runtime-recovery** | 仅恢复用 | 灾难兜底,**永远不能**推进持久化确认状态 |
存储层的选择是能力探测驱动的不需要用户手动配置。Authority 是增强层,缺席时优雅降级。详见 [`server-integration.md`](server-integration.md)。
**关键设计:** Luker 宿主下,浏览器全图镜像默认关闭(`cacheStorageTier = none`),避免把大图谱重复写进 IndexedDB/OPFS。只有用户显式"重建本地缓存"才写浏览器缓存。
## 快照契约
耐久快照的顶层结构被**冻结**为固定的六个键(实现见 `sync/graph-snapshot-schema.js`
```
{
schemaVersion, // 顶层快照布局版本
meta, // 图谱元信息(含 meta.schemaVersion 等)
nodes,
edges,
tombstones,
state // lastProcessedFloor / extractionCount 等运行状态
}
```
> **不变量:顶层这六个键永不增减。** 所有未来演进都放进 `meta` / `state` / 各记录字段里——这些层级已经容错(保留未知字段)。
## 向前兼容纪律
这是保证"ST-BME 以后不需要再做 v4/v5 大迁移"的核心机制。它**不是**一个信封框架或预留字段,而是一条解析纪律:
### 1. 宽容解析(保留未知字段)
> 读取方遇到不认识的字段,必须保留、不报错、不丢弃。
具体现状:
- 节点 / 边 / tombstone 记录:整对象克隆,未知字段天然保留。
- `meta`:展开保留,已有 `meta.schemaVersion`
- 顶层:冻结为六键 + 保留未知顶层字段。
原理:如果读取代码遇到不认识的字段就崩或就丢,那么**任何**格式改动都会逼出一次迁移;如果遇到不认识的字段就忽略并原样保留,那么以后所有改动都是**加法**,老版本读新数据照样不崩,永远不需要换命名空间、不需要大搬家。这是 protobuf 这类协议几十年验证过的做法。
### 2. 只加不减
> 新字段一律可选,永不删字段、永不改已有字段的含义。
这样新版写的数据老版仍能读,反之亦然。
### 3. 就地升级upgrade-on-read
实现见 `sync/graph-snapshot-upgrade.js`,接入真实加载路径 `buildGraphFromSnapshot`
> 读到旧 `schemaVersion` 时,在内存里逐级升级一格再用,下次保存时顺手写成新版。单调、幂等、读到比当前更新的数据绝不向下改写。
当前快照布局版本是第一版,升级链为空,但框架和铁律已立住——以后改格式只是"加一个升级步骤",不是搬家。
### 关于 Luker sidecar
Luker checkpoint 存的是完整序列化图谱(`serializeGraph`),节点/边的未知字段被保留——所以图谱正文通过 Luker 是容错的。sidecar 上的信封元数据manifest 统计、checkpoint 元信息)用白名单规范化是**有意为之**:那些是可重算的运行指标,不是图谱本身,丢了能重建。
## 图谱内容版本 vs 快照布局版本
注意区分两个版本号:
- **`GRAPH_VERSION`**`graph/` 内):图谱**内容结构**的版本,有自己的 v2→v9 迁移链,管节点/关系语义的演进。
- **`schemaVersion`**(快照顶层):耐久快照**布局**的版本,管"存进磁盘的信封形状"的演进。
两者独立。本文档的向前兼容纪律针对的是后者(快照布局)。