SDK v0.11: Sliding Window Extraction, Procedural Memory & Smarter Recall
neuromem team
SDK v0.11 是自 v0.8 以来最大的一次版本更新。这个版本围绕三个方向做了深度改进:提取质量(滑动窗口提取)、记忆类型(程序性记忆)和召回精度(保留分数 + 双时间线 + 自动去重)。5 个核心特性,+2,400 行代码变更,全部向下兼容。
5
新特性
+2,400 行
代码变更
0.11.4
版本号
100%
向下兼容
滑动窗口提取 (Sliding Window Extraction)
这是 v0.11 最核心的改进。
问题很直观:当用户发送"好的"、"嗯"、"收到"这样的短消息时,逐条送入 LLM 提取几乎不可能产出有意义的记忆。LLM 拿到一条孤立的"好的",什么上下文都没有,自然提取不出任何事实或事件。更糟糕的是,这些无效的 LLM 调用还在消耗 token 预算。
解决方案是 extraction_mode='window'。开启后,SDK 不再逐条提取,而是将消息缓冲到累积字符数达到 window_char_threshold(默认 500 字符)时再统一提取。缓冲区内的所有消息作为完整上下文一起送入 LLM,提取质量显著提升。
滑动窗口提取流程
用户发送短消息
内容 < 500 字符
消息进入缓冲区
等待更多上下文积累
累积达到阈值
≥ 500 字符或会话结束
合并提取
完整上下文 → 更准确的 facts/episodes
使用方式非常简单,只需要在初始化时指定两个参数:
async with NeuroMemory(
...,
extraction_mode="window", # 开启滑动窗口
window_char_threshold=500, # 累积到 500 字符再提取
) as nm:
await nm.ingest(user_id="alice", role="user", content="好的")
# → 缓冲,不触发提取
await nm.ingest(user_id="alice", role="assistant", content="还有什么需要帮忙的吗?")
# → 缓冲,继续等待
await nm.ingest(user_id="alice", role="user", content="对了,我下周要去北京出差,帮我查一下天气")
# → 累积达到阈值,三条消息一起提取
效果对比一目了然:
逐条提取 vs 窗口提取
| 场景 | 逐条提取 | 窗口提取 |
|---|---|---|
| "好的" + "收到" + "下周去北京出差" | 提取 0 条有效记忆 | 提取 1 条: "下周计划去北京出差" |
| 三轮闲聊后提到工作变动 | 仅提取最后一条 | 完整上下文,关联前后信息 |
| LLM 调用次数(10 条短消息) | 10 次 | 2-3 次 |
窗口提取不仅提升了质量,还显著降低了 LLM 调用次数——对于以短消息为主的对话场景,调用量可以减少 70-80%。
程序性记忆 (Procedural Memory)
v0.11 新增了第五种记忆类型:程序性记忆(Procedural)。
之前的四种类型——事实(Fact)、事件(Episode)、特征(Trait)、文档(Document)——覆盖了"用户是什么样的人"和"发生过什么事",但缺少对"用户怎么做事"的建模。当用户描述"每天早上先看邮件,然后开站会,再 review PR",这是一个多步骤的工作流程,不应该被拆成三条孤立的事实。
程序性记忆专门捕获这类多步骤流程。提取时 LLM 会识别对话中的流程描述,生成结构化的 procedure_steps,每个步骤包含顺序、描述和可选的条件。
记忆类型分布(新增 Procedural)
五种记忆类型各司其职
程序性记忆还支持按 Space 级别的召回排除开关。有些 Agent(比如纯问答型)可能不需要在 recall 结果中看到工作流步骤,可以在 Space 设置中关闭 procedural 类型的召回。
保留分数 (Retention Score)
recall 的打分逻辑新增了一个因子:保留分数(Retention Score)。
核心思路是:被反复访问的记忆更可能是重要的,应该在排序中获得加分。同时,刚存入不久的新记忆不应该因为访问次数少就被埋没。
计算公式:
retention_boost = log2(1 + access_count) * 0.05,上限 +0.25- 7 天新记忆保护:创建 7 天内的记忆,retention_boost 最低为 0.02
保留分数随访问次数变化
access_count → retention_boost (上限 0.25)
这个设计参考了认知科学中的间隔重复效应:你越频繁地回忆一件事,它在记忆中就越牢固。retention_boost 的对数增长曲线确保前几次访问带来的增益最大,后续趋于平缓,不会无限放大高频记忆的优势。
双时间线查询 (Dual Timeline)
recall 现在支持两套时间线过滤参数:
created_after/created_before:按记忆的创建时间过滤event_after/event_before:按事件的实际发生时间过滤
两套时间线参数
| 参数 | 过滤依据 | 使用场景 |
|---|---|---|
| created_after / created_before | 记忆创建时间 | 最近一周存入的所有记忆 |
| event_after / event_before | 事件发生时间 | 上个月发生的所有事件 |
为什么需要两套?因为记忆的创建时间和事件的发生时间经常不一致。用户可能周一开了一个重要会议,但直到周三才在对话中提到。如果只按 created_at 查询,用"上周一的会议"这种自然语言查询就无法正确定位。
event_after / event_before 解决了这个问题。它们过滤的是记忆中记录的事件时间戳(event_date 字段),让时间相关的查询更加精准。
自动去重 (Memory Deduplication)
长期运行的 Agent 不可避免地会积累重复记忆。用户反复提到"我住在北京",每次都会创建一条新的事实记忆。v0.11 新增了两个 API 来解决这个问题:
find_duplicates():检测重复记忆。使用 content_hash 精确匹配和向量相似度近似匹配两种策略merge_duplicates():合并重复记忆。保留最新版本的内容,合并 access_count,删除旧条目
去重可以手动调用,也可以在 digest 流程中自动触发。合并策略遵循"保留最新、累加权重"的原则,确保不丢失任何有价值的访问统计信息。
总结
SDK v0.11 的五个特性分别解决了记忆系统中的五个实际痛点:
- 滑动窗口提取 — 短消息不再产生垃圾记忆,LLM 调用量减少 70%+
- 程序性记忆 — 捕获"怎么做",补全记忆类型的最后一块拼图
- 保留分数 — 高频访问的记忆排序更靠前,新记忆受 7 天保护
- 双时间线 — 按事件发生时间查询,而不仅仅是记录时间
- 自动去重 — 清理重复记忆,保持记忆库整洁
所有特性均向下兼容,升级到 v0.11 不需要修改任何现有代码。新特性通过参数开启,不影响默认行为。
完整的 API 变更请参考 SDK Changelog。