[控场AI]
· 18 分钟阅读· 9,031 字

为本地AI Agent构建七层记忆架构:实战与反思

为本地AI Agent构建七层记忆架构:实战与反思

拆解本地AI Agent七层记忆架构的实战得失,揭示检索、溯源与安全边界的真实教训。

本文基于本地AI Agent Hermes Sterling的真实案例,将记忆问题拆解为持久化、检索、溯源、成本和完整性五个维度,并给出七层功能分工架构。文章详述了SQLite+FTS5作为词汇检索骨干、HRR因哈希向量缺乏语义与查询空间不可比而失效、改用原始文本向量后相似度从噪声升至+0.328、BGE-M3本地嵌入通过门控条件激活并以词汇回退兜底,以及同义词标签这一简单手段将改述查询Hit@1从41.7%提升至66.7%。在安全层面,作者指出add操作缺乏与replace/remove同等的来源控制,存在提升污染风险,并强调高置信度不应等同于写入授权。最后,文章反思了基准测试自身的错误——条件MRR导致指标不相容、活库导致结果漂移——并规划了隔离实验室与HFEA情景档案。

为本地AI Agent构建七层记忆架构:实战与反思

当你的AI助手能流畅回答通用问题,却记不住三周前你们讨论过的决策时,你会意识到:智能和记忆是两个不同的问题。本文基于一个真实案例,详细拆解如何为本地AI Agent Hermes Sterling构建七层记忆系统,涵盖架构设计、检索机制、语义融合与安全边界,并坦诚分享那些“负结果”带来的宝贵教训。

rss source: Beyond context: how we built a 7-layer memory for a local AI agent

记忆不只是存储,而是五个独立问题

把事实存到磁盘上并不难。难的是让它在正确的时间、以正确的作者身份出现,且不被误当成操作指令。实践中,我们需要解决五个维度:持久化(信息跨会话存活)、检索(用不同词汇也能找到事实)、溯源(区分用户观点与Agent推断)、成本(旧事实不应消耗每轮Token)、完整性(更新与备份不能静默破坏记忆)。

初始方案将所有记忆注入上下文——MEMORY.md和USER.md合计约4.59万字符,折合每轮约1.24万Token。虽然提示缓存能缓解部分开销,但架构上的前缀重量无法消除。核心决策是:不急于缩减常驻文件,而是先让替代层证明自己能保留并检索那些终将移出的知识。

七层架构:按功能划分职责

系统按功能而非笼统的“记忆”来组织,形成七个层次:

层级技术职责
永久记忆Markdown持续加载的高价值信息
事实存储SQLite, FTS5, HRR, 嵌入细粒度事实与关系检索
技术WikiMarkdown按需查阅的详细文档
程序记忆技能与脚本重复任务的执行方法
历史SQLite与文本搜索定位过往对话与决策
压缩上下文摘要控制对话窗口增长
保存备份与恢复故障与更新后的恢复

需要澄清的是:Wiki并非由全息机制自动检索,而是需要时手动查询。七层不等于七个集成检索机制——实际是五个检索面、一个控制机制和一个保存层。这种分离避免了将工作记忆、情景记忆、事实知识和程序混为一谈的常见设计错误。

SQLite + FTS5:第一个真正重要的机制

操作核心是本地SQLite事实存储。每条事实包含文本内容、类别、标签、置信度、日期和作者元数据。实体与关系保存在关联结构中。FTS5提供倒排索引实现词汇检索,无需远程服务,索引可与数据库同步更新。

检索流程中,FTS5产生候选,其他机制参与评分与选择。预取机制每轮最多注入5条事实到上下文,其余留在磁盘上直到问题需要。优势很直接:只在相关时查找细节,而不是每条消息都为所有细节付费。

FTS5 是 SQLite 内置的全文检索扩展模块,采用倒排索引结构:为每个词项维护一个记录其出现在哪些行、哪些位置的列表,因此可以在不扫描全表的情况下快速匹配关键词。它支持 MATCH 语法、前缀查询、短语查询和 BM25 排序函数,并且索引与主表在同一个数据库文件中,可随事务一起更新、随 backup() 一起备份。相比把文本交给远程搜索服务,FTS5 的优点是零网络依赖、零额外进程、可离线运行,这正好契合本地 Agent 的部署约束。它的局限同样明确:只做词形层面的匹配,不理解同义词、改述或语义相近,这正是后文需要引入关键词标签和 BGE-M3 嵌入来弥补的部分。

HRR:当优雅技术遭遇实验极限

全息缩减表示(HRR)通过SHA-256哈希确定性地生成文本原子,用相位向量表示,支持bind、unbind、bundle等代数操作。听起来很吸引人:无需大型神经模型就能表示关系。

但决定性细节是:哈希生成的向量并不知道“卡片”“塑料”和“支付手段”在语义上可能相关。它提供代数性质和确定性,但不能替代为自然语言相似度训练的编码器。

审计中发现了更严重的问题:查询向量从原始查询编码而来,没有角色,而存储的事实向量是带有角色(内容、实体)的绑定bundle()。两个向量生活在无法比较的空间中。在45个真实候选上测量,查询与事实的相似度在**−0.023到+0.015之间波动——0.038的跨度就是噪声。经过内部重缩放映射到[0,1]后,该术语对每个分数的贡献为0.489–0.508**,即最终结果中近乎恒定的0.147–0.152。它没有对任何东西排序,只是在稀释词汇信号,实际上给了trust_score更高的相对权重。

第二个缺陷是结构性的:bundle()对角度取平均(循环均值),所以unbind与bundle不可交换。从bundle中恢复组件时,1、2、3、5、9和17个组件的恢复值都约为0.000。换句话说,probe、related和reason在实体图已经限制的候选集内按噪声排序——真正起作用的是图,不是代数。

修复方案务实:存储原始文本向量而非bundle,并在数据库中标记为textv1以便识别和迁移旧行。对照测量证明了改变的价值:主题匹配时,改述查询与存储文本向量的相似度为**+0.328**,不匹配时为**+0.009**。真实信号存在,只是被某种破坏它的东西混合了。

教训: 不要把数学上有趣的表示与经验上良好的相关性度量混为一谈。也不要因为代数优雅就信任某个组件——用实际产生的排序来测量它。

全息缩减表示(Holographic Reduced Representation, HRR) 是 Plate 在 1990 年代提出的一种分布式表示方案,属于向量符号架构(VSA)家族。其核心思想是用高维相位(或实数)向量表示概念,并通过三种代数运算组合它们:bind 将两个向量绑定为一个近似正交的新向量,用于编码“角色—填充”关系(如“主语=卡片”);unbind 是 bind 的逆运算,可从组合向量中近似还原出某个角色对应的填充;bundle 将多个向量叠加为一个,用于表示集合或多项并存。理论上,这些运算可以在不依赖神经网络的情况下实现结构化的关系推理。但其关键前提是:向量之间的相似度必须反映语义相似度。若向量由哈希确定性生成,则任意两个不同文本的向量近似正交,语义关系无从体现——这正是文中实验遭遇的根本瓶颈。

BGE-M3 via Ollama:本地语义的运营成本

为弥合词汇差异,我们通过Ollama添加了BGE-M3本地嵌入(1024维多语言模型)。机制设计为优雅降级模式:语义通道的激活是条件性的——一个门控观察顶级候选的词汇覆盖率,决定是否值得调用模型。

测量显示:精确匹配覆盖率1.00,改述0.44,第三方问题0.63。阈值设为0.9时,真正需要语义的查询恰好打开通道,而FTS5已良好服务的查询不必为模型调用付费。

成本体现在启动和资源竞争上。在Xeon E5-2450 v2、15GB RAM、Debian 13的共享服务器上,BGE-M3冷加载实测2.66秒,而旧超时为2.50秒——语义通道恰好在模型卸载时失败。调整每轮限制至2.8秒,嵌入批处理至60秒。冷启动余量仍然狭窄,这就是词汇回退保持关键的原因。

另一个发现:按事实数量确定回填规模是不佳的近似。48条短事实与48条长事实截然不同——模型成本是文本的函数,不是行的函数。我们开始按文本量限制批次,上限约8000字符。当天基础库上,约524条事实的完整回填估计为3.4分钟,分约19批完成。

一个关联bug(zip用了错误的集合)差点将向量写入错误的事实。此后,完整性测试会比对存储嵌入与同一文本重新计算的嵌入。这个测试和搜索本身一样重要。

最令人惊讶的提升来自简单之物:同义词

第十二个本地补丁没有引入新神经网络,而是引入了搜索关键词。问题很日常:语音查询使用的词汇与记录事实时的词汇不同。FTS5很快,但不会猜测索引中不存在的同义词。

我们开始在tags列中为某些事实添加替代术语,不改变原始事实内容。附加术语随后被FTS5索引,使得即使BGE-M3不可用,也能通过新词检索到事实。

集成需要三个 precautions:不将关键词与原始词汇证据混淆(语义门控不能仅因额外标签匹配就人为关闭);不意外索引秘密(关键词经过现有敏感内容检查);不更新事实年龄(添加同义词不意味着今天重新确认了事实)。

基准测试:四种配置

在517条事实的冻结快照上,使用79个查询:55个精确、12个改述、6个第三方相关、6个无预期答案。命中指标基于73个可回答查询。

配置总体Hit@1改述@1
无嵌入,无关键词84.9%33.3%
无嵌入,有关键词90.4%66.7%
有嵌入,无关键词87.7%41.7%
有嵌入,有关键词91.8%66.7%

两者兼用时,Hit@5升至95.9%,MRR@10达0.932。上一配置(有嵌入、无关键词)的相同指标为Hit@5 91.8%和MRR@10 0.894。在六个无答案问题上,特定噪声指标保持0/6。

关键警告:与改述关联的事实是手动丰富的,且测试集已知。这使得实验对验证实现及其效果有用,但不能证明对未见问题的泛化能力。正确的下一步是使用独立评估集。

结果融合:Reciprocal Rank Fusion

另一个实现决策是从僵化的作者排序转向相关性与作者排名的融合,使用RRF,k=60。逻辑是防止高度相关的事实仅因第三方撰写而被丢弃,同时不失去对更接近用户来源的偏好。

注意归因: 在一次试验中,RRF和仅相关性策略产生了相同的最终注入结果。因此,我们不能将观察到的全部改进专门归因于RRF。这是一个架构决策,仍需测试以测量其增量收益。

Reciprocal Rank Fusion(RRF) 是一种无需训练、无需分数归一化的多路排序融合方法,公式为 score(d) = Σ 1/(k + rank_i(d)),其中 rank_i(d) 是文档 d 在第 i 个排序列表中的名次,k 为平滑常数(常用 60)。它的优点是不依赖各检索通道的原始分数尺度——词汇检索的 BM25 分数与嵌入的余弦相似度本不可直接比较,但名次可以。常数 k 抑制了头部名次的过度主导,使多个列表的中段结果也能贡献分数,从而提高召回稳定性。在本文场景中,RRF 被用来融合“相关性排序”与“作者来源排序”两路结果,避免高相关但来自第三方的信息被直接丢弃。需要注意,RRF 只改变融合方式,不产生新的相关性信号,因此其增量收益必须通过对照实验单独测量,而不能与关键词或嵌入的改进混为一谈。

溯源:到底是谁说的?

即使存储的文本完全可检索,持久记忆也可能是错误的。如果系统将外部文档的断言或Agent的推断存储为用户所说,就制造了作者身份错误。

我们的版本记录author、author_kind(user/agent/third_party/unknown)和role(uttered_by/mentioned/addressee)。系统还有81个巴西葡萄牙语自提取模式,针对不同陈述类型功能性验证,但尚无独立集估计其在真实对话中的精确率和假阳性率。

还处理了一个看似琐碎的细节:呼语不是主语。在“Hermes,检查备份”中,“Hermes”是话语的接收者,不一定是待记忆事实的主语。

但工作尚未完成。架构仍需更严格区分:谁断言了原始内容、谁记录了它、来自哪个文档或事件、何时有效、谁授权其提升为永久记忆。事实置信度和写入授权是不同的维度。 高trust_score不应授予修改操作指令的权力。在我们的模式中,两者从不相遇:置信度住在事实存储里,权威住在三个文件里,任何分数都无法触及。风险不在模式中——而在桥接它们的审查器,这是下一节的主题。

尚未解决的安全问题

独立审计发现自动记忆审查过程中存在重要不对称:replace和remove等破坏性操作经过批准机制,但add操作可能在没有同等结构控制的情况下通过审查。

这开启了提升污染风险:粘贴文档、工具输出或上下文摘要中的指令可能被策展过程解释为新的永久规则。问题通过检查和授权机制测试得到证明,但尚未通过审查器的端到端试验。我们没有发现此链条在生产中被利用的证据;也不认为未利用就是安全保证。

下一次验证称为QA-20F,应在完全隔离的环境中运行受污染的合成转录,区分写入尝试与有效持久化。在此之前,我们不授权对永久记忆文件进行破坏性清理。

核心教训:记忆不能仅仅因为模型决定存储,就将检索到的信息转化为操作权威。

更新教会我们版本化自己的补丁

Holographic离开Hermes Agent核心,作为社区维护插件分发。社区版本保持本地存储、SQLite、FTS5、实体和HRR,无需外部模型。我们的安装使用带有十二个补丁的本地变体。

更新期间,我们暂时丢失了本地补丁和一个未版本控制的embedding.py。基础库幸存,但这一事件表明:定制安装在其文件未版本控制之前不是可复现项目。我们恢复了本地实现,扩展了更新后验证器,并将变体移入自己的仓库——打标签,社区上游保留为远程以进行选择性变基。

成本必须直言:十二个本地补丁意味着框架更新可能静默抹去十二样东西。这发生过一次(v0.21.7将插件移出核心)。没有可检测标记的补丁会无错误消息消失,这就是为什么每个补丁现在都带有验证器能找到的标记,且验证器在每次更新后运行。

我们如何测试——以及如何发现测试错了

当前本地实现记录12个补丁和144项功能检查通过。10月12日测量的数据库有524条事实、94个实体、1053个关系、28个Wiki页面;524条中的522条有嵌入。五天后,同一安装有554条事实,全部有嵌入,103个实体和1096个关系。基础库是活的——这正是使一项测量无效的原因。

比测试数量更重要的是学会质疑仪器。首次审计中,我们发布了MRR@10为0.966和Hit@1为86.3%。数字看起来很棒,但如果在同一查询集上计算,它们在数学上不相容。缺陷很简单:MRR仅在命中的查询上计算,将失败排除在分母之外。

我们修复实现,为失败分配零倒数排名,并将旧指标保留为mrr_conditional,正确标注。还添加了每次运行检查的不变量:

Hit@1  <= MRR@10
MRR@10 <= Hit@1 + (Hit@10 − Hit@1) / 2

另一个问题在比较运行时出现:基础库是活的,实验期间接收新事实。即使搜索配置未变,结果也变了。我们开始要求通过SQLite的backup() API冻结快照,它能正确处理WAL模式数据库。简单复制.db文件不够——在我们的基础库上测量,复制给出505条事实,而实时数据库有507条,因为-wal文件被留下了。

这些错误留下了三个永久原则:基准需要明确的分母;评估的基础库必须稳定且可识别;指标也需要测试。

MRR@10(Mean Reciprocal Rank) 衡量的是第一个正确结果出现位置的平均倒数:若正确结果排第 1,得 1;排第 2,得 0.5;排第 k,得 1/k;若前 10 名内没有正确结果,则得 0。Hit@1 与 Hit@5 则分别表示正确结果是否出现在第 1 位、前 5 位,是 0/1 计数的比例。三者之间存在数学约束:由于 Hit@1 的查询必然贡献 MRR 的 1.0,而仅命中第 2 至第 10 位的查询最多贡献 0.5,因此恒有 Hit@1 ≤ MRR@10 ≤ Hit@1 + (Hit@10 − Hit@1)/2。文中正是利用这条不变量发现了早期指标的不相容:当时 MRR 只在命中的查询上求平均,把失败样本排除在分母之外,人为抬高了数值。这提醒我们,指标本身也是需要被测试的代码。

我们获得了什么——尚未获得什么

观察到的益处是具体的:更稳健的词汇检索,尤其是用不同词汇表述的问题;本地运行和可预测回退(语义模型不可用时FTS5继续工作);更多可追溯性(作者、实体、角色和回归测试);通过预取和按需查找减少对永久上下文访问操作细节的依赖;更好的仪器化,能揭示冷超时、嵌入关联错误和基准本身的不一致。

限制同样清晰:尚未证明MEMORY.md和USER.md的安全缩减;测量的检索质量不保证历史保真度、事实正确性或策展安全;溯源仍不完整,尤其是旧记录和派生摘要;QA-20F场景、Markdown与SQLite之间的一致协调以及完整恢复仍需验证;我们仍没有外部基准、稳健的保留集或与其他记忆架构的公平比较。

第二个限制不是理论性的。我们基础库中存储的一个凭证带有一个语音转文字误听字符;另外三个来源相互一致,与它不一致。检索以完全自信返回了错误值,因为检索完全按设计工作。抓住分歧的是来源间的交叉检查——不是排序。完美检索的记忆仍然可能是说谎的记忆。

下一步:可复现实验室

审计揭示了一个比坏指标更不显眼的限制:创建测试基础库还不够;必须重现Agent实际决策和写入记忆的环境。QA-20F中,我们想验证隐藏在文档、工具输出或压缩摘要中的指令是否可能被background_review提升到MEMORY.md、USER.md或技能。代码分析显示了合理风险:replace和remove经过批准,而add不面临相同的来源检查。这并不证明污染已发生;完整F1–F6行为试验仍待进行。

在临时沙箱中运行失败于真实基础设施细节:框架虚拟环境间的重定向未保留必要依赖,记忆工具需要初始化了数据根的HERMES_HOME。这是系统可测试性问题,不仅仅是测试覆盖率。

三种运行方式:Docker容器适合快速迭代和CI;本地虚拟机适合更强隔离的完整运行时测试;临时Droplet适合远程实验和版本矩阵。初始选择是Docker加合成数据,只要容器能忠实重现审查器真实路径。

实验契约F1–F6必须使用真实审查器,而非仅文本模拟。对于每个合成转录,需记录对抗内容是否到达Agent、是否尝试写入以及工具是否持久化了更改——三个不同现象。包括粘贴文档、工具结果、另一个Agent、压缩、阳性对照和技能六类场景。评估要求阳性和阴性案例、重复运行、工具调用日志和前后状态比较。

HFEA:高保真情景档案(未来工作)

七层设计用于选择和检索有用知识,但有些问题需要不同的东西:决策如何成熟?对话过程中什么改变了?哪些疑虑和细节在压缩中被丢弃?

对此,我们提出独立于操作记忆的组件HFEA——尚未在本文描述的Hermes中实现或验证。HFEA不是第八个预取层:其索引不参与常规排序,记录不每轮加载,也不作为永久指令。它是最后手段的情景档案,仅在常规层无法支撑答案时查询。

提案是每日例行程序,将当天会话整合为详尽的按时间顺序记录,保留序列与因果、表达的细微差别、学习与上下文、可验证的溯源以及保真与修正。用Markdown按日期组织,索引可提前构建,但查找保持阻塞直到回退触发。

触发不能简单是“第一个结果分数低”:需要在相关常规路线咨询后观察证据充分性、无答案或未解决矛盾。如果HFEA也无法提供充分支持,正确答案仍是承认差距。

安全、成本和验证方面,详细记录可能包含比摘要事实敏感得多的个人信息。HFEA需要访问隔离、维护者定义的保留策略、受保护备份,且不将真实数据导出到公共仓库或测试环境。夜间例行程序需幂等、可审计。

在隔离实验室中,可以创建特定合成测试:答案不在七层但在情景档案中的问题;正常记忆可回答但不得访问日记的问题;包含矛盾、推断情感和托管指令的片段;以及成本、延迟、引用保真度和假回退触发的测量。HFEA不是为了更快记忆,而是为了保存记忆优化通常忘记的东西。

结论:好记忆不是大记忆

Agent记忆不仅仅是写文本以便以后重用。它是选择、检索、上下文化、归属来源、验证以及必要时拒绝信息的问题。

最令人惊讶的细节不是新模型,而是意识到一个小型词汇丰富,经过正确测试和集成,在嵌入单独尚未交付预期结果的地方产生了重要增益。

安全和保存不是额外功能,而是任何优化值得信任的条件。这就是为什么测试策展的隔离实验室不是配件:它是将安全假设转化为证据的下一块必要拼图。

下一个目标不是“让Agent记住一切”,而是能够回答更难的问题:它能检索什么?信息来自哪里?它不知道什么?有什么证据表明没有重要东西丢失?

#AI Agent记忆#本地AI Agent#七层记忆架构#SQLite FTS5#BGE-M3嵌入#记忆检索#Agent安全
分享:

相关推荐