@lsby/ai-memory
v0.2.2
Published
A PGlite-backed TypeScript library for OpenAI-compatible agents with persistent, layered memory.
Downloads
130
Maintainers
Readme
AI Memory
@lsby/ai-memory 是一个带长期记忆的 TypeScript 智能体库,使用 PGlite / pgvector 在本地保存记忆。它让智能体能够跨会话地记住、遗忘和重组经验,而不是每次对话从零开始。
当前版本为
0.x,API 可能在小版本间发生变化。Node.js 需为 20 或更高版本。
安装
pnpm add @lsby/ai-memory openai zod快速上手
import OpenAI from 'openai'
import { z } from 'zod'
import { 带记忆的智能体, 记忆等级 } from '@lsby/ai-memory'
// 1. 创建智能体实例
let 客户端 = new OpenAI({ apiKey: process.env['OPENAI_API_KEY'] })
let 智能体实例 = new 带记忆的智能体({
存储: { 模式: '文件', 路径: './data/agent-memory' },
向量模型: { 类型: '无' },
})
try {
// 2. 注入初始记忆
await 智能体实例.批量注入记忆([
{
内容: '用户偏好简洁的中文回答。',
关键词: ['用户', '偏好'],
标签: ['偏好'],
评分: 80,
等级: 记忆等级.一级,
创建时间: new Date(),
},
])
// 3. 发起对话,AI 会自动检索智能体的相关记忆
let 回答 = await 智能体实例.对话({
命令: '用一句话说明你记住了什么。',
预期结果Schema: z.object({ 回答: z.string() }),
预期结果描述: '包含回答文本的对象',
openai客户端: 客户端,
模型名称: 'gpt-4.1-mini',
回调: async () => {},
})
console.log(回答.结果)
} finally {
// 4. 使用完毕后销毁实例,释放数据库连接
await 智能体实例.销毁()
}会话与模型缓存
对话() 由实例管理完整模型上下文。任何已经发送给模型的消息都会保持内容和顺序不变,后续请求只在末尾追加消息,以便提供商复用前缀缓存。零级/一级记忆状态仅在实际变化时追加一个新的完整版本;旧版本不会被删除或改写,新版本显式覆盖旧状态的语义。
let 完整模型历史 = 智能体实例.读取对话历史()
let 界面展示历史 = 智能体实例.读取可见对话历史()
// 恢复会话时必须使用完整模型历史,不能使用过滤后的界面历史。
智能体实例.载入对话历史(完整模型历史)需要由应用自行管理历史时,使用 执行回合()。推演() 与 私有反思() 可以读取当前会话,但不会写回会话主线。
字符与缓存审计
内核会为每次模型请求生成一条 模型请求审计 事件。这里统计的是 Unicode 字符,不是模型 token:中文、英文字母和单个 Unicode 码点都按 1 个字符计算。发出字符数取模型实际收到的 messages 与 tools 语义载荷序列化结果,返回字符数取提供商实际返回的文本与工具调用;内核为兼容非函数调用提供商而补全的响应前缀不会计入返回字符数。
缓存状态不声称代表提供商的真实缓存命中。内核仅按会话生命周期判定:新实例或 重置对话() 后的第一次模型请求为 新建对话,同一会话的后续请求为 复用已有对话。完整状态会保存会话标识,因此导入后继续请求也会判定为复用。
let 记录列表 = 智能体实例.读取模型审计记录()
let 汇总 = 智能体实例.读取模型审计汇总()
let 会话标识 = 智能体实例.读取会话标识()
智能体实例.清空模型审计记录()审计记录默认只存在于当前实例内;应用层需要持久化时,可以在回调中接收 模型请求审计 事件并写入自己的审计存储。清空记录不会改变当前会话的新建或复用状态。
应用管理接口
内核只保存记忆本身及其自然运行数据,不加入审核状态、所有者等应用治理字段。角色管理、权限、人工确认和版本发布状态应由应用层维护。可用的内核管理接口包括:
添加记忆()、批量注入记忆()、更新记忆()、删除记忆()查询记忆()、查找记忆()、查询记忆关联()、查询记忆提交()、回退最近记忆提交()查询动态工具()及动态工具的注册、查看、更新、删除、调用接口保存快照()/载入快照():记忆、关联图和动态工具导出完整状态()/导入完整状态():上述快照加完整模型会话历史
所有写操作都会经过短事务和同路径串行队列;模型请求与普通工具执行不会占用数据库长事务。多个文件存储实例传入的等价相对/绝对路径会归一化为同一资源。
架构概览
┌──────────────────────────────────────────────────────┐
│ 应用层 │
│ 对话 · 推演 · 反思 · 动态工具 · 定时任务 │
├──────────────────────────────────────────────────────┤
│ 带记忆的智能体 │
│ ┌──────────┐ ┌──────────┐ ┌───────────────────┐ │
│ │ 记忆管理 │ │ 关联图谱 │ │ 动态工具 (QuickJS) │ │
│ │ 零/一/二级 │ │ 关键词重叠 │ │ Schema校验 + 沙箱 │ │
│ │ 遗忘曲线 │ │ 向量相似度 │ │ 5秒超时隔离 │ │
│ └──────────┘ └──────────┘ └───────────────────┘ │
├──────────────────────────────────────────────────────┤
│ 智能体 (基类) │
│ 工具调用 · 结构化输出 · 流式请求 · REPL │
├──────────────────────────────────────────────────────┤
│ PGlite + pgvector (嵌入式) │
│ 事务串行化 · 文件锁 · 引用计数共享 │
└──────────────────────────────────────────────────────┘为什么需要记忆
大多数智能体框架的工作方式是:收到请求 -> 读取上下文窗口 -> 生成回复。这种模式有几个根本限制:
| | 传统智能体 | 带记忆的智能体 | | -------------- | ------------------------------------- | -------------------------------------------- | | 跨会话记忆 | 会话结束后一切归零,下次对话无法继续 | 记忆持久化存储,跨会话保留 | | 上下文窗口 | 受 token 上限约束,只能截断或滑动窗口 | 按需召回相关记忆,不受窗口大小限制 | | 信息检索 | 全量塞入上下文,或依赖外部 RAG | 内置关键词 + 向量混合检索,沿关联图扩展 | | 信息淘汰 | 简单截断最早的消息 | 模拟人类遗忘曲线:高价值、常用的记忆保留更久 | | 经验积累 | 无法从历史交互中学习 | 通过反思和重组,将经验沉淀为结构化记忆 | | 个性化 | 每次都需要重新告知偏好 | 自动记住用户偏好、关键事实和历史决策 |
一个具体的例子
假设你在做一个编程助手:
传统方式:用户第 1 次说"我喜欢函数式风格",AI 在当前会话中记住了;第 2 次对话,AI 完全忘记这件事,又写出命令式代码。你只能每次都在 system prompt 里手动维护用户偏好。
本库的方式:用户第 1 次说"我喜欢函数式风格",这条信息被写入智能体的一级记忆(高优先级)。之后的每次对话,智能体会自动召回这条记忆,始终生成函数式风格的代码。即使中间隔了几百轮对话,这条记忆也不会丢失。而且随着使用,这条记忆会被频繁命中,保留信号越来越强,不会被遗忘。
核心设计思路
本库不是简单地把聊天记录存进数据库再检索出来(那只是"外挂记忆")。它的记忆系统模仿人类记忆的几个关键特征:
- 分层存储:核心事实(零级)、即时信息(一级)、长期经验(二级),不同层级有不同的保留策略。
- 关联网络:记忆之间通过关键词重叠和向量相似度自动建立关联,形成一张知识图谱。
- 主动遗忘:不是所有信息都值得保留。长期不使用、低评分的记忆会自然衰减并被淘汰。
- 自主反思:智能体可以在空闲时回顾已有记忆,发现新的联系,将洞见写回记忆网络。
- 使用越多越强:被频繁召回的记忆会获得更高的保留权重,形成正反馈循环。
记忆系统
记忆分层
在本系统中,记忆被分为三个层级,对应不同的重要性和生命周期:
| 等级 | 类比 | 设计意图 | | ---- | -------- | ------------------------------------------------------------------------------------------------ | | 零级 | 核心记忆 | 最重要的、需要始终保留的信息,例如人设、身份定义、基本规则。这是智能体的"自我认知",不会被遗忘 | | 一级 | 工作记忆 | 当前任务的即时上下文。就像你正在做一道数学题时脑子里暂存的中间结果,容量有限,溢出时会降级到二级 | | 二级 | 永久记忆 | 大量的历史经验和长期积累。容量大但会随新信息冲刷而逐步淘汰不活跃的部分 |
这种分层解决的核心矛盾是:智能体需要保留足够多的经验信息,但又不能让无关信息淹没真正重要的内容。 核心记忆(零级)保证人设和身份始终在线;工作记忆(一级)服务于当前任务,容量可控;永久记忆(二级)承载大量经验但允许自然淘汰。
记忆关联
记忆不是孤立存储的。每写入一条新记忆,系统会自动检查它和已有记忆的关系,并建立带权重的关联边:
- 关键词重叠:两条记忆共享相同关键词,说明它们在讨论相关话题。共享的关键词越多,关联越强。
- 向量相似度:即使用词不同,语义接近的记忆也能被关联起来。比如"用户喜欢简洁的代码"和"不要写冗余的注释"在语义上是相关的,向量距离会反映这一点。
这些关联形成一张记忆关联图——节点是记忆,边是关联强度。这张图不仅是数据结构,更是智能体的"知识图谱"。
记忆召回
AI 在对话中会使用回忆延伸机制来检索智能体的记忆。这不是简单的关键词搜索,而是模拟人类"越想越多"的联想过程:
第一步:直接检索
对当前输入做分词,用关键词匹配和向量相似度做混合排序,找到最相关的记忆。这相当于"直觉反应"——听到一个问题后,脑子里立刻浮现的相关信息。
第二步:关联扩展
扩展阶段则不是直接搜索出更多记忆,而是沿关联图返回相关的关键词。AI 拿到这些关键词线索后,由它自己决定是否要用这些关键词发起下一轮检索。这相当于"联想"——脑海中浮现出几个相关的线索和念头,你可以决定要不要顺着想下去。如果 AI 判断某个线索对解决当前问题具有价值,它会主动调用记忆检索工具,将选定的线索作为参数发起下一轮检索。
涌现的魔法就发生在这里:整个过程不是机械死板的预编程检索,而是 AI 在智能体记忆网络中的自主探索。AI 甚至可能从一个偶然的线索出发,最终找到开发者都未曾预料到的深层关联。
举一个例子:
- 直接检索:用户提问 "帮我写一个登录弹窗组件"。系统直接命中了记忆 A(
项目前端组件使用的是 Vue 3 + TailwindCSS 方案),同时关联图谱延伸返回了相关线索词:[暗黑模式, 主题色, 圆角规范]。 - 产生第一轮联想:AI 看到线索词后推理:“用户要做新组件,线索中出现了‘暗黑模式’与‘主题色’——这个弹窗是不是也需要默认适配项目的暗黑主题?”。
- 第一轮延伸检索:AI 主动调用记忆检索工具,以
暗黑模式 主题色为线索发起检索,调出了深层记忆 B(项目所有弹窗与卡片均需支持 dark: 样式,且主色调固定为 emerald-500)。同时,该记忆又延伸返回了新的线索词:[表单校验, Zod 规范, 错误提示样式]。 - 第二轮联想与检索:AI 顺藤摸瓜继续推理:“登录表单需要做输入校验,线索里提到了‘Zod 规范’,应该查一下项目的表单验证方式”。AI 再次发起检索,调出了记忆 C(
项目表单统一采用 Vee-Validate + Zod 做校验,并在字段下方显示统一的红色轻量提示)。 - 给出结果:AI 一步到位生成了完全符合预期的登录弹窗——不仅采用了 Vue 3 + TailwindCSS,还默认适配了暗黑模式与 emerald-500 主题色,并且预置了标准的 Zod 校验逻辑。
正反馈机制
每次被命中的记忆都会更新访问次数和最后访问位置。经常被证明有用的记忆会积累更强的保留信号,在遗忘清理中获得更高的保留优先级。这意味着记忆系统会"学会"哪些信息是真正有价值的——用得越多,记得越牢。
记忆遗忘
遗忘不是 bug,而是记忆系统的核心机制。没有遗忘,记忆会无限膨胀,检索质量会持续下降。
本库使用信息冲刷模型来模拟自然遗忘:
- 每当新记忆写入,所有旧记忆都会受到一次"冲刷"。长期未被访问的记忆衰减最快。
- 每条记忆有一个保留指数,由评分、关联强度和访问频率综合决定。保留指数越高,越能抵抗遗忘。
- 一级记忆容量有限,溢出时低评分的记忆会降级到二级。
- 二级记忆中保留指数过低的记忆会被自然淘汰。
- 零级记忆(核心记忆)豁免冲刷,永远不会被遗忘。
这意味着:重要的、常用的、和其他记忆紧密关联的信息会留下来;琐碎的、过时的、孤立的信息会逐渐消失。 这和人类的遗忘规律是一致的。
动态工具系统
传统智能体只能使用开发者预先定义好的工具。本库更进一步:AI 可以在运行时自己编写新工具,保存到智能体中,之后随时调用。
这意味着智能体不再局限于"出厂配置"。当 AI 在对话中发现某个计算逻辑会被反复用到时,可以自己编写一个工具函数,注册为智能体的动态工具。下次遇到类似问题时,AI 会自动检索并调用该工具,而不是每次都从头推理。
举一个例子:AI 在多次对话中反复被要求做时区转换。AI 可以自己编写一个时区转换工具,注册保存到智能体中。之后再遇到时区相关的问题,AI 会直接调用这个工具,而不是每次都手动计算。
智能体通过使用经验积累出越来越丰富的动态工具库,驱动它的 AI 在交互中发挥出不断增长的能力。
安全方面,每个动态工具都运行在独立的 QuickJS 沙箱中,没有文件系统、网络和 require 访问权限,执行有 5 秒超时限制。参数和返回值都会经过 JSON Schema 校验。
⚠️ 安全提示:这不是操作系统级安全沙箱。动态工具只应来自可信来源;生产环境仍建议使用进程或容器隔离,并限制资源和调用权限。
