28 KiB
ST-BME
面向 SillyTavern 的图谱记忆扩展。它把聊天过程转成结构化记忆图,再在生成前按场景召回并注入,服务于长对话、角色扮演、持续剧情和世界观管理。
项目概览
ST-BME 的全称是 ST-Bionic-Memory-Ecology。它不是一个独立的后端服务,也不是一个通用数据库,而是一个运行在 SillyTavern 第三方扩展体系内的前端记忆层。
它主要做两件事:
- 写入:从聊天内容中抽取结构化记忆,存入当前聊天对应的知识图谱。
- 读取:在下一次生成前,从图谱中找出与当前输入最相关的记忆,并把它们整理成适合模型理解的上下文片段。
项目目标不是替代大模型的原生上下文能力,而是为长期互动补上一层可累计、可更新、可压缩、可检索的外部记忆系统。
这个项目解决什么问题
在长期 RP 或持续陪伴式对话里,模型通常会遇到几类典型问题:
- 早期剧情、角色状态、地点细节很快被上下文窗口挤掉。
- 模型会记得“有这回事”,但不容易稳定地记住“谁在什么时候做了什么、现在又变成什么状态”。
- 角色状态和地点状态会随剧情变化,旧信息与新信息容易混杂。
- 世界规则、主线目标、前情提要常常需要持续注入,但又不能把所有历史全文塞回 prompt。
ST-BME 的思路是把“聊天历史”变成“图谱化记忆”:
- 事件、角色、地点、规则、主线、概要、反思都以节点表示。
- 节点之间通过关系边连接,形成可扩散的结构。
- 重要、常驻的信息直接进入 Core 注入。
- 与当前用户输入强相关的状态和补充记忆再走召回注入。
项目边界
当前实现很明确地有以下边界:
- 单聊天作用域:每个聊天维护一份独立图谱,图状态挂在当前聊天
chat_metadata下。 - 无独立数据库:没有额外服务端存储层,所有持久化都依赖 SillyTavern 的聊天元数据保存机制。
- LLM 与 Embedding 分离:
- 结构化提取、精确召回、压缩、进化、概要、反思都通过 ST 内部的
sendOpenAIRequest('quiet', ...)调用聊天模型。 - 向量检索依赖单独配置的 OpenAI 兼容 Embedding API。
- 结构化提取、精确召回、压缩、进化、概要、反思都通过 ST 内部的
- 图谱是工程化记忆,不是事实真相库:它依赖 LLM 的结构化输出质量,因此仍然存在抽取偏差、更新遗漏、关系误判等风险。
运行依赖
| 依赖 | 是否必需 | 作用 |
|---|---|---|
| SillyTavern 第三方扩展系统 | 必需 | 提供事件钩子、设置存储、聊天上下文、Prompt 注入接口 |
| 当前可用的聊天模型 | 必需 | 用于提取、精确召回、压缩、进化、概要、反思等所有 LLM 子任务 |
| OpenAI 兼容 Embedding API | 向量检索相关功能必需 | 用于节点 embedding、向量预筛、Mem0 风格近邻对照、记忆进化近邻搜索 |
| 当前聊天元数据 | 必需 | 存储图谱状态、最后处理楼层、最后召回结果 |
系统总览
聊天消息
├─ assistant 回复完成后
│ └─ ST-BME 提取未处理片段
│ ├─ LLM 生成 create/update/delete 操作
│ ├─ 执行图谱写入
│ ├─ 生成缺失 embedding
│ ├─ 可选执行进化 / 概要 / 反思 / 遗忘 / 压缩
│ └─ 保存回 chat_metadata
│
└─ 下次生成前
└─ ST-BME 检索当前图谱
├─ 可见性过滤
├─ 向量预筛
├─ 图扩散
├─ 混合评分
├─ 可选 LLM 精确召回
├─ 场景重构
└─ 格式化为注入文本并送入 prompt
与 SillyTavern 的集成方式
ST-BME 的主入口在 index.js。它不是轮询式工作的,而是绑定在 SillyTavern 的事件生命周期上:
| ST 事件 | 对应逻辑 | 作用 |
|---|---|---|
CHAT_CHANGED |
onChatChanged() |
切换聊天时重新加载该聊天的图谱 |
GENERATION_AFTER_COMMANDS |
runExtraction() |
assistant 回复完成后,处理尚未提取的内容 |
GENERATE_BEFORE_COMBINE_PROMPTS |
runRecall() |
下一轮生成前召回记忆并注入 |
MESSAGE_RECEIVED |
onMessageReceived() |
新消息到达时保存当前图状态 |
这意味着 ST-BME 的运行时机非常清楚:
- 写入发生在回复之后,记录刚刚发生了什么。
- 读取发生在下一次生成之前,决定接下来模型应该看见哪些记忆。
数据存储与持久化
图谱键名固定为 st_bme_graph,存储在当前聊天的 chat_metadata 中。
图状态的核心结构如下:
| 字段 | 含义 |
|---|---|
version |
图数据版本号,当前实现为 v3 |
lastProcessedSeq |
已处理到的聊天楼层索引 |
nodes |
全部节点,包括活跃和归档节点 |
edges |
全部关系边,包括失效边和历史边 |
lastRecallResult |
最近一次召回选中的节点 ID 列表 |
图数据由 graph.js 管理,支持:
- 空图创建
- 节点/边增删改查
- 时序链表维护
- 时序边失效处理
- 版本迁移与兼容反序列化
- 导入导出
节点公共字段
所有节点都会带有一套统一元数据:
| 字段 | 说明 |
|---|---|
id |
UUID |
type |
节点类型 |
level |
压缩层级,原始节点为 0 |
parentId / childIds |
压缩层级父子关系 |
seq |
该节点对应的主楼层索引 |
seqRange |
节点覆盖的楼层范围 |
archived |
是否归档 |
fields |
业务字段主体 |
embedding |
向量表示 |
importance |
重要性,范围 0-10 |
accessCount |
被召回/注入的访问次数 |
lastAccessTime |
最近被访问时间 |
createdTime |
节点创建时间 |
prevId / nextId |
同类型节点的时间链表 |
clusters |
额外标签/聚类信息 |
边公共字段
| 字段 | 说明 |
|---|---|
id |
UUID |
fromId / toId |
边起点和终点 |
relation |
关系类型 |
strength |
边强度,范围 0-1 |
edgeType |
边类型标记,255 表示抑制边 |
createdTime |
创建时间 |
validAt |
生效时间 |
invalidAt |
失效时间 |
expiredAt |
系统标记过期时间 |
其中 contradicts 关系会被映射成抑制边,后续在扩散阶段会传递负能量。
默认 Schema
默认 Schema 定义在 schema.js。它不仅定义了字段,还定义了注入策略、更新策略和压缩策略。
| 类型 | 作用 | alwaysInject |
latestOnly |
压缩 | 说明 |
|---|---|---|---|---|---|
event |
事件、动作、阶段推进 | 是 | 否 | 分层压缩 | 当前实现里属于 Core 常驻注入 |
character |
角色状态快照 | 否 | 是 | 不压缩 | 同名 create 会转成 update |
location |
地点状态快照 | 否 | 是 | 不压缩 | 同名 create 会转成 update |
rule |
规则、约束、世界设定 | 是 | 否 | 不压缩 | 常驻注入 |
thread |
主线或任务线 | 是 | 否 | 分层压缩 | 常驻注入 |
synopsis |
全局前情提要 | 是 | 是 | 不压缩 | 只保留最新一条概要 |
reflection |
高层反思与长期提示 | 否 | 否 | 分层压缩 | 通过召回进入上下文 |
关系类型
默认关系类型包括:
relatedinvolved_inoccurred_atadvancesupdatescontradictsevolvestemporal_update
写入链路详解
写入逻辑主要集中在 index.js 和 extractor.js。
1. 提取触发条件
ST-BME 只统计 assistant 消息 来决定何时提取。
默认策略:
extractEvery = 1时,每 1 条 assistant 回复提取一次。lastProcessedSeq记录的是聊天数组索引,因此它用的是“楼层”语义,而不是消息 ID。
如果开启 enableSmartTrigger,则会在普通频率判断外再做一次轻量触发评分。评分来源包括:
- 命中默认关键词
- 命中自定义正则
- 用户与助手多轮往返
- 感叹号/问号等情绪波动
- 疑似新实体/新地点
当评分达到阈值时,即使未到 extractEvery,也会直接处理所有待处理 assistant 楼层。
2. 提取上下文打包
真正送去提取的不是单条消息,而是一段上下文窗口。
当前实现会:
- 找到本批 assistant 楼层的起止索引
startIdx/endIdx - 从
startIdx - extractContextTurns * 2开始回溯 - 取到
endIdx为止的非系统消息 - 用
#楼层 [role]: content的形式拼成对话文本
这样做的目的,是让 LLM 在提取时看到足够的上下文因果关系,而不是孤立处理单条回复。
3. LLM 结构化提取
提取调用在 llm.js 中统一封装,要求模型返回严格 JSON,核心产物是:
{
"thought": "对这一批对话的理解",
"operations": [
{
"action": "create",
"type": "event",
"fields": {
"summary": "......"
}
}
]
}
当前默认提示词会约束模型:
- 支持的节点类型必须来自当前 Schema
- 每批对话最多创建 1 个事件节点
- 角色/地点优先更新已有同名节点,而不是无脑新建
- 关系类型必须来自允许列表
importance落在 1-10summary应该是抽象摘要,而非原文复制
4. Mem0 风格精确对照
如果开启 enablePreciseConflict 且配置了 Embedding API,则在正式执行操作前,会先对所有 create 操作做近邻对照:
- 对新事实文本生成 embedding。
- 在已有活跃节点中搜最相似近邻。
- 若最高相似度超过阈值,则调用 LLM 判断:
addupdateskip
这一步的目标是降低重复节点、弱冲突节点和应该被视作“状态更新”的伪新增节点。
5. 操作执行语义
create
- 正常创建新节点。
- 若类型是
latestOnly且存在同名旧节点,则自动转为更新旧节点。 - 同批次创建的节点支持通过
ref/targetRef建立链接。
update
更新的处理比普通字段覆盖更复杂,当前实现还会补出一层“可追踪更新语义”:
- 合并字段更新。
- 刷新
seq与seqRange。 - 清空 embedding,等待后续重建。
- 失效掉旧的
updates/temporal_update关系边。 - 若存在
sourceNodeId,则补建新的temporal_update边。 - 根据字段差异自动生成一条新的
event节点,摘要形如field: before -> after。 - 再用
updates边把这个事件挂回被更新节点。
这让“状态变化”不只是覆盖写入,还能留下被检索的更新痕迹。
delete
当前是软删除,不会真的移除节点,只是把节点标记为 archived = true。
6. Embedding 补齐
提取结束后,系统会为所有缺少 embedding 的活跃节点批量生成向量。拼接文本时优先使用:
summarynametitletraitsstateconstraint
如果这些字段都没有,就退化为节点类型名。
7. 提取后增强流程
在一次成功提取后,当前实现还可能继续执行以下步骤:
记忆进化 enableEvolution
新节点写入后,evolution.js 会:
- 为新节点找近邻旧节点。
- 调用 LLM 判断新信息是否改变了对旧记忆的理解。
- 若需要:
- 给新旧节点补链接
- 回溯更新旧节点的
state/summary/core_note - 更新旧节点的
clusters - 记录
_evolutionHistory
全局概要 enableSynopsis
每 synopsisEveryN 次提取后,会基于:
- 事件时间线
- 角色状态
- 主线状态
生成或更新一个 synopsis 节点,用于充当前情提要锚点。
反思条目 enableReflection
每 reflectEveryN 次提取后,会基于:
- 最近事件
- 近期角色状态
- 当前主线
- 已知矛盾边
生成 reflection 节点,并用 evolves 边连接到最近事件。
主动遗忘 enableSleepCycle
compressor.js 中的 sleepCycle() 会按保留价值归档低价值节点。当前保留价值大致由以下因素决定:
- 重要性
- 新近性
- 访问频率
规则、概要、主线和高重要性节点默认不会被遗忘。
层级压缩
对支持分层压缩的类型,系统会:
- 从最低层级开始扫描
- 只压缩超过阈值且不属于“最近保留叶子”的旧节点
- 以
fanIn为批次调用 LLM 总结 - 新建更高层级节点
- 把被压缩子节点归档,并建立
parentId/childIds
当前默认支持分层压缩的类型是:
eventthreadreflection
读取链路详解
读取逻辑主要集中在 retriever.js 和 injector.js。
1. 活跃节点筛选
读取开始前会先获取当前图中的活跃节点,并过滤掉:
archived = true的节点seqRange不完整的异常节点
如果启用了 enableVisibility,还会根据节点 fields.visibility 进行认知边界过滤:
- 支持数组形式
- 支持逗号分隔字符串
- 支持
*通配 - 当前视角默认取
context.name2
2. 自适应检索策略
检索策略会根据活跃节点规模自动调整:
| 活跃节点数 | 检索策略 |
|---|---|
< 20 |
不做向量预筛,所有节点参与评分,可选直接走 LLM 精确召回 |
20 - 200 |
向量预筛 + 图扩散 + 混合评分,默认不走 LLM 精确召回 |
> 200 |
向量预筛 + 图扩散 + 混合评分 + LLM 精确召回 |
这套阈值定义在 retriever.js 的 STRATEGY_THRESHOLDS 中。
3. 向量预筛
如果图规模达到阈值且配置了 Embedding API,会:
- 对当前用户输入生成 query embedding。
- 对已有节点 embedding 做暴力余弦相似度检索。
- 取 Top-K 作为候选。
当前实现明确采用暴力搜索,而不是 HNSW/ANN,因为它假设 ST 使用场景通常是中小图规模。
4. 实体锚点与交叉检索
系统会额外从用户输入中做一层简单实体锚定:
- 如果消息里直接出现了某个节点的
name或title - 就把它视为一个高能量种子
如果开启 enableCrossRecall,则会进一步:
- 沿着这些实体节点的有效边展开
- 找到相邻的
event节点 - 把它们也作为附加扩散种子
这一步更偏向“场景联想”,而不是单纯语义相似。
5. 图扩散
diffusion.js 实现了一个轻量版 PEDSA 扩散引擎:
- 从种子节点出发传播能量
- 每步乘衰减因子
- 只保留 Top-K 活跃节点
- 抑制边会传递负能量
- 能量值会被钳位到固定区间
当前默认配置:
- 最多 2 步扩散
- 衰减系数 0.6
- 每步最多保留 100 个活跃节点
6. 混合评分
混合评分公式定义在 dynamics.js:
FinalScore = (GraphScore * alpha + VectorScore * beta + ImportanceNorm * gamma) * TimeDecay
默认权重为:
graphWeight = 0.6vectorWeight = 0.3importanceWeight = 0.1
时间衰减采用对数衰减,而不是快速指数衰减,目的是让久远但重要的记忆不要掉得太快。
7. LLM 精确召回
在小图或大图场景下,如果开启 recallEnableLLM,系统会:
- 先把候选节点按混合得分排好。
- 取前 30 个以内节点作为候选池。
- 把最近对话、用户最新输入、候选节点字段摘要一起喂给 LLM。
- 让 LLM 输出最终选中的节点 ID 列表。
如果 LLM 召回失败,则回退到纯评分排序结果。
8. 场景重构
在得到初始召回节点后,系统不会立刻结束,而是还会做一次“场景补全”:
- 若命中的是
event,会补入与该事件直接相关的角色、地点、主线、反思节点,以及时间上最邻近的事件。 - 若命中的是
character/location,会先找其关联事件,再围绕这些事件继续补场景。
这一步的目标,是避免只召回一个孤立节点,尽量把一个能被模型理解的局部情境一起带回来。
9. 概率触发回忆
如果开启 enableProbRecall,系统还会从未选中的高重要性节点里抽少量候选,并按概率追加进结果。这更像是“偶发闪回”,用于给长期剧情增加一点远程记忆回流。
10. 访问强化
被最终选中的节点会执行访问强化:
accessCount + 1importance + 0.1- 更新时间
lastAccessTime
这使得经常被召回、反复证明有用的节点,后续更容易继续存活和命中。
注入策略
注入文本由 injector.js 生成,格式是 Markdown 表格,主要分为两部分:
1. Core 常驻注入
凡是 Schema 中 alwaysInject = true 的类型,都会直接进入 Core:
eventrulethreadsynopsis
这意味着当前默认设计并不是“所有东西都走检索”,而是:
- 叙事主干直接常驻
- 状态与补充记忆按需召回
这是当前实现最值得注意的一个架构选择。
2. Recalled 召回注入
非 alwaysInject 且被选中的节点会进入召回区,并按桶组织:
- 当前状态记忆
- 情景事件记忆
- 反思与长期锚点
- 规则与约束
- 其他关联记忆
在默认 Schema 下,召回区最常见的其实是:
characterlocationreflection
因为事件、规则、主线、概要默认都属于 Core。
3. Token 估算
注入完成后,系统会做一个粗略 token 估算,便于观察注入体积。当前估算规则大致是:
- 2 个中文字符约等于 1 token
- 4 个英文字符约等于 1 token
一个完整运行示例
下面用一个简化示例说明从聊天到图谱、再到召回的大致闭环:
- 用户说:“我们先去钟楼看看,之前失踪案很可能和那里有关。”
- 助手回复了一段剧情,描述角色艾琳进入钟楼,发现地下暗门。
- 这轮回复结束后,提取器可能产出:
- 一个
event:艾琳在钟楼发现地下入口 - 一个
location:钟楼,状态为存在隐藏入口 - 一个
thread:失踪案调查,状态推进
- 一个
- 如果图中本来就有“钟楼”地点节点,则该地点不会重复创建,而会变成更新。
- 新节点生成后,系统补 embedding,并可能触发:
- 记忆进化:修正旧事件对钟楼的理解
- 全局概要:更新前情提要
- 下一轮用户问:“地下入口会不会和之前失踪的人有关?”
- 召回阶段会:
- 命中“地下入口”“失踪”等语义相关节点
- 把钟楼、相关事件、最近主线等一起拉回
- 再用注入表格告诉模型当前关键情境
- 模型在生成时,就不只是看当前一句话,而是能同时看到:
- 最近核心事件
- 当前地点/角色状态
- 当前主线和概要
功能清单与成熟度
已实现主链路
| 功能 | 当前状态 | 说明 |
|---|---|---|
| 聊天级图谱持久化 | 已实现 | 图谱跟随当前聊天保存与切换 |
| LLM 结构化提取 | 已实现 | 支持 create/update/delete |
| 节点 embedding 生成 | 已实现 | 依赖外部 Embedding API |
| 向量预筛 | 已实现 | 余弦相似度暴力检索 |
| 图扩散排序 | 已实现 | PEDSA 风格轻量扩散 |
| 混合评分 | 已实现 | 图分、向量分、重要性、时间衰减 |
| LLM 精确召回 | 已实现 | 小图/大图场景触发 |
| 场景重构 | 已实现 | 围绕事件和实体补上下文 |
| 层级压缩 | 已实现 | 事件/主线/反思支持 |
| 记忆进化 | 已实现 | 基于近邻与 LLM 回溯更新 |
| 全局概要 | 已实现 | 周期生成 synopsis |
| 反思条目 | 已实现 | 周期生成 reflection |
| 主动遗忘 | 已实现 | 按保留价值归档 |
| 导入/导出 | 已实现 | 导出时去掉 embedding |
实验性能力
| 功能 | 当前状态 | 备注 |
|---|---|---|
| 精确对照(Mem0 风格) | 实验性 | 对不同剧情密度的收益仍需更多验证 |
| 认知边界过滤 | 实验性 | 依赖节点 visibility 字段质量 |
| 交叉检索 | 实验性 | 更像场景增强,不一定总是增益 |
| 概率触发回忆 | 实验性 | 可能提升“闪回感”,也可能增加噪声 |
| 反思节点召回策略 | 实验性 | 当前以结构就绪为主,策略仍可细化 |
已有实现但未完全打通的预留项
下面这些字段或配置已经出现在代码中,但当前还不应在 README 中当作完整能力宣传:
| 项 | 当前情况 |
|---|---|
nodeTypeSchema |
设置层支持,但当前没有现成 UI 做 Schema 编辑 |
extractPrompt |
设置层支持,但当前没有现成 UI 暴露自定义提取提示词 |
injectPosition / injectRole |
默认设置存在,但实际注入调用当前只使用 injectDepth |
evoConsolidateEvery |
设置项存在,但当前没有真正的“进化后整理”执行逻辑 |
forceUpdate |
Schema 元数据存在,但当前运行期没有用它强制产出节点 |
配置说明
设置面板定义在 settings.html,逻辑绑定在 index.js。
基础与召回配置
| 配置项 | 默认值 | 作用 |
|---|---|---|
enabled |
false |
总开关 |
extractEvery |
1 |
每 N 条 assistant 回复提取一次 |
extractContextTurns |
2 |
提取时往前带多少轮上下文 |
recallEnabled |
true |
是否启用生成前记忆注入 |
recallTopK |
15 |
评分后的候选上限 |
recallMaxNodes |
8 |
LLM 精确召回最多选多少节点 |
recallEnableLLM |
true |
是否启用 LLM 精确召回 |
injectDepth |
4 |
注入深度 |
混合评分权重
| 配置项 | 默认值 | 说明 |
|---|---|---|
graphWeight |
0.6 |
图扩散得分权重 |
vectorWeight |
0.3 |
向量相似度权重 |
importanceWeight |
0.1 |
节点重要性权重 |
v2 增强功能配置
| 配置项 | 默认值 | 说明 |
|---|---|---|
enableEvolution |
true |
开启记忆进化 |
evoNeighborCount |
5 |
进化近邻搜索数量 |
enablePreciseConflict |
true |
开启精确对照 |
conflictThreshold |
0.85 |
触发精确对照的相似度阈值 |
enableSynopsis |
true |
开启全局概要 |
synopsisEveryN |
5 |
每 N 次提取更新概要 |
enableVisibility |
false |
开启认知边界过滤 |
enableCrossRecall |
false |
开启交叉检索 |
enableSmartTrigger |
false |
开启轻量触发提取 |
triggerPatterns |
"" |
自定义关键词或正则 |
smartTriggerThreshold |
2 |
智能触发阈值 |
enableSleepCycle |
false |
开启主动遗忘 |
forgetThreshold |
0.5 |
节点保留价值阈值 |
sleepEveryN |
10 |
每 N 次提取执行一次遗忘 |
enableProbRecall |
false |
开启概率回忆 |
probRecallChance |
0.15 |
概率回忆触发概率 |
enableReflection |
false |
开启反思条目 |
reflectEveryN |
10 |
每 N 次提取生成反思 |
Embedding 配置
| 配置项 | 默认值 | 说明 |
|---|---|---|
embeddingApiUrl |
"" |
OpenAI 兼容 API 基地址 |
embeddingApiKey |
"" |
API Key |
embeddingModel |
text-embedding-3-small |
embedding 模型名 |
推荐使用方式
起步建议
如果你是第一次用这个扩展,建议先用最保守的组合:
- 开启
enabled - 保持
extractEvery = 1 - 开启
recallEnabled - 开启
recallEnableLLM - 开启
enableEvolution - 开启
enableSynopsis - 暂时关闭
enableVisibility、enableCrossRecall、enableProbRecall enableReflection可以先关闭,等剧情稳定后再打开
成本敏感场景
如果更在意 API 成本,可以尝试:
extractEvery = 2或3- 关闭
recallEnableLLM - 提高
synopsisEveryN - 关闭
enableReflection - 仅保留 Embedding 相关能力
长剧情 / 高连续性场景
如果是重剧情、重状态变化的 RP:
- 保留
enableEvolution - 保留
enableSynopsis - 在确认节点
visibility字段可控后,再测试enableVisibility - 对多地点、多人物切换频繁的剧情,可逐步开启
enableCrossRecall
操作面板
当前 UI 已经提供以下手动操作:
| 按钮 | 作用 |
|---|---|
| 查看图谱 | 显示活跃/归档节点数、边数、类型分布、最后处理楼层 |
| 查看注入 | 直接查看最近一次生成前的注入文本 |
| 重建图谱 | 清空当前聊天图谱,下次生成重新抽取 |
| 手动压缩 | 对当前图谱执行压缩 |
| 导出 | 导出图谱 JSON,不包含 embedding |
| 导入 | 导入图谱 JSON,导入后会清空所有 embedding |
| 测试连接 | 测试 Embedding API 是否可用 |
目录与模块职责
| 文件 | 作用 |
|---|---|
| manifest.json | 扩展清单 |
| index.js | 扩展入口、事件绑定、设置管理、总流程调度 |
| settings.html | 设置面板 UI |
| style.css | 扩展样式 |
| graph.js | 图数据结构、时序边、序列化、导入导出 |
| schema.js | 默认 Schema 与关系类型定义 |
| extractor.js | 写入路径、精确对照、概要、反思 |
| retriever.js | 读取路径、图扩散、混合评分、精确召回 |
| injector.js | 注入文本组织与格式化 |
| embedding.js | Embedding API 调用、向量相似度检索 |
| llm.js | LLM 请求与 JSON 解析封装 |
| diffusion.js | PEDSA 风格扩散引擎 |
| dynamics.js | 时间衰减、访问强化、混合评分 |
| compressor.js | 层级压缩与主动遗忘 |
| evolution.js | 记忆进化引擎 |
| tests/ | 当前已有的轻量本地测试 |
测试与验证
当前仓库内已有的测试比较轻量,主要覆盖部分核心逻辑:
node tests/smart-trigger.mjs
node tests/graph-retrieval.mjs
node tests/injector-format.mjs
它们分别验证:
- 智能触发评分逻辑
- 时序边过滤与图扩散基础行为
- 注入文本格式化流程
当前尚未覆盖的重点包括:
- 真实 LLM 提取质量
- 真实 Embedding API 行为
- 完整的 ST 生命周期集成
- 大图规模下的性能与稳定性
- 导入导出后的重建与回归
已知限制
截至当前代码实现,建议明确接受以下限制:
- 这是一个聊天内图谱,不是跨聊天统一记忆库。
- 导入图谱后,所有节点 embedding 会被清空;当前没有单独的“全量重建 embedding”按钮,向量能力需要后续写入或额外处理来逐步恢复。
- LLM 子任务很多,结构化输出质量会直接影响图谱质量。
- 当前没有内建图谱可视化界面,调试主要依赖统计信息、日志和注入文本。
- 默认
event/rule/thread/synopsis都是 Core 常驻注入,项目当前更偏向“主干常驻 + 状态召回”,而不是纯检索式记忆架构。 - 实验性功能已经接入主流程,但仍缺少更系统的 benchmark 和回归验证。
设计来源与参考
ST-BME 不是这些项目的直接移植,而是结合 SillyTavern 扩展场景做的工程化整合。当前设计大致受以下项目启发:
| 参考项目 | 启发点 |
|---|---|
A-MEM |
记忆进化、基于近邻的回溯修正 |
EM-LLM |
惊奇度触发、段落边界与提取时机 |
Graphiti |
时序边、关系有效性和图建模思路 |
Mem0 |
新旧记忆对照、增量更新决策 |
RoleRAG |
认知边界过滤 |
AriGraph |
沿图边展开的交叉检索 |
MemoRAG |
全局概要作为长期锚点 |
SleepGate |
主动遗忘与保留价值评估 |
Reflexion |
反思条目方向 |
PeroCore |
图扩散、记忆动力学、向量检索策略 |
当前版本
- 扩展版本:
0.1.0 - 清单文件:
manifest.json
许可证
本项目采用 AFPL License,详见 LICENSE。