mcpField
H
helix-memory-system

helix-memory-system

github.com/helixdbAgent 工作流4269 次安装4 天前更新
代码仓库

关于 helix-memory-system

面向构建 Helix Agent 记忆系统的架构技能,涵盖按租户隔离的图关系、向量相似度和 BM25 检索。它覆盖记忆从摄取、生成、去重和版本管理到遗忘及混合召回的生命周期,并明确区分 Helix 能力与应用侧工作。

主要功能

  1. 01设计按租户隔离的记忆模型
  2. 02结合图关系与向量、BM25 检索
  3. 03规划记忆去重、更新和版本管理
  4. 04支持分类、画像维护与遗忘
  5. 05区分数据库能力和应用侧任务

适用场景

  1. 01设计 Agent 长期记忆数据模型
  2. 02构建语义、关键词与图混合召回
  3. 03实现记忆纠正、过期和删除
  4. 04规划文档分块与来源追踪

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/helixdb/skills --skill helix-memory-system

安装后在 Claude Code 中输入/helix-memory-system确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

EXAMPLES.md
EXAMPLES.rust.md
REFERENCE.md

详细介绍

Helix 记忆系统

在 Helix 上构建持久化的多租户 Agent 记忆平台,在一个数据库中整合图关系、向量相似度和 BM25 全文检索。本技能涵盖完整的记忆生命周期:原始上下文摄取、提取、记忆生成、去重、更新/版本管理、删除/遗忘、分类、档案维护和混合检索。

Helix 是存储和检索引擎。完整的记忆产品还需要应用工作进程来处理提取、分块、嵌入、关系分类、重排序、连接器同步和档案摘要。

适用场景

在以下任务中使用本技能:

  • 设计 Agent 记忆、长期记忆、用户档案、文档/分块 RAG 或“记住用户告诉我的内容”功能的数据模型
  • 编写用于创建、去重、强化、整合、版本管理、纠正、过期、遗忘、分类或检索记忆的查询
  • 判断给定的记忆操作应使用哪种 Helix 功能(属性索引、图边、向量索引、BM25 文本索引)
  • 构建融合语义 + 关键词 + 图关系 + 档案上下文的混合记忆召回
  • 实现高级记忆组件,例如源文档、分块、连接器、提取出的事实、持续演变的档案、关系感知召回和遗忘

不要将本技能用于通用查询语法问题。有关构建器/方法的详细信息,请参阅 helix-query-typescript(默认 DSL)、helix-query-rust 或 helix-query-json-dynamic。本技能假设你已了解这些内容,重点介绍基于 Helix 构建记忆架构。

第一步

  1. 检查目标代码库中现有的标签、边、属性、索引和路由风格。如果已有内容,请沿用其准确的大小写形式。
  2. 默认使用 TypeScript v3 DSL(@helix-db/[email protected]),以便应用将查询生成逻辑放在服务代码附近。仅当运行环境为 Rust,或团队明确使用 Rust 查询时,才使用 EXAMPLES.rust.md。
  3. 在建模之前,先确定租户边界。规范的租户属性是 tenant_id,因为 Helix 当前要求租户分区文本索引使用此名称。为每个租户所有的节点和边添加 tenant_id。
  4. 将记忆可见性边界与租户边界分开确定。在大多数应用中,tenant_id 用于划分索引,而 userId、containerId、projectId 或应用 ACL 决定哪些记忆可以被召回。默认示例使用 userId 作为第二级作用域。
  5. 在发明标签之前,先沿用以下规范模型。调整名称,而不是模型结构。
  6. 确认嵌入的生成方式。生产环境和可基准测试的记忆系统默认使用 OpenAI text-embedding-3-small:1536 个维度,以 F32 数组存储。应用在客户端计算嵌入,并传入数值数组(param.array(param.f32()))。Helix 不会在动态查询路径上嵌入文本;当前 DSL 中没有 Embed()/SearchV。每个向量索引都应固定嵌入模型和维度。确定性哈希嵌入仅适用于本地 UI 演示或冒烟测试,不适用于质量基准测试。
  7. 确定 Helix 之外的应用工作进程:提取器、分块器、嵌入器、记忆写入器、关系分类器、衰减/过期清理器、档案摘要器、可选的查询改写器、可选的重排序器,以及连接器同步任务。

一览记忆模型

核心标签:Tenant、User、UserProfile、SourceDocument、Chunk、Memory、Category、Entity、Session,以及可选的 Connector 和 IngestionJob。

核心边:OWNS(Tenant/User→Memory)、HAS_PROFILE(User→UserProfile)、HAS_CHUNK(SourceDocument→Chunk)、EXTRACTED_FROM(Memory→Chunk 或 SourceDocument)、IN_CATEGORY(Memory→Category)、MENTIONS(Memory→Entity)、UPDATES(新 Memory→旧 Memory)、EXTENDS(Memory→Memory 增补)、DERIVES(推断出的 Memory→支持它的 Memory)、RELATES_TO(Memory→Memory 关联)、DERIVED_FROM(Memory→Session),以及可选的 PARENT_OF(Category→Category)。

快速且安全的字段:

  • 在每个由租户拥有的节点和边上设置 tenant_id,并在用作锚点时为其建立相等性索引
  • 在用户或容器专属的记忆、源文档和分块上设置 userId 或等效的作用域键;只有明确设计为共享的记录才应覆盖整个租户
  • 稳定 ID,例如 memoryId、documentId、chunkId、categoryKey、entityKey、sessionId 和 profileId
  • 使用 Memory.isLatest、validFrom、validTo、expiresAt 和 deletedAt 筛选记录生命周期
  • 当记忆涉及有日期的事件或事实时,可选用现实世界时间字段,例如 observedAt、eventStartAt、eventEndAt、temporalText 和 timezone
  • 在 Memory.embedding/Memory.content 上建立按租户分区的向量/文本索引,也可选择在 Chunk.embedding/Chunk.content 上建立;所有索引均按 tenant_id 分区

完整规范、类型和索引初始化见 REFERENCE.md。

模态选择规则

根据要回答的问题选择机制,并有意组合使用:

需求使用原因
租户隔离(tenant_id)、精确身份、生命周期标记(deletedAt、expiresAt、validTo、isLatest)、排序/筛选(createdAt、salience)属性 + 相等性/范围索引缩小锚点范围并安全筛选。租户作用域不可妥协。
分类、实体、来源、配置文件所有权、更新/增补/推导、关联簇、分类体系图边这些都是关系;应遍历并聚合。
去重、释义召回、相似记忆、相似分块向量搜索语义相似度;能够容忍措辞变化。
精确名称、ID、罕见词元、命令、文件路径、产品术语BM25 文本搜索嵌入会模糊精确词元;BM25 能保留这些词元。
模型应始终了解的广泛用户上下文UserProfile 节点 + 汇总器工作进程避免为稳定的身份/偏好/近期关注点执行多次搜索。
原始文档和引文SourceDocument + Chunk 节点记忆事实不能替代基于来源的 RAG。

经验法则:绝不要将记忆系统简化为仅使用向量。 向量会遗漏精确名称,也不具备所有权、时效性、矛盾、来源、配置文件状态或分类的概念。

始终使用 tenantValue = tenant_id 限定向量/BM25 搜索范围。租户作用域是必要条件,但不总是充分条件:默认的用户记忆召回应通过 userId 或应用中等效的容器/ACL 进行筛选,除非记录明确在整个租户范围内共享。每条召回路径都必须筛除已忘记/过时的记录:deletedAt IsNull、isLatest = true、validTo IsNull,且 expiresAt 不存在或在未来。应在执行限定遍历范围的向量或 BM25 搜索之前构建这些候选项,以确保语义召回和关键词召回都严格遵守可见性成员资格。如果某条路径无法表达完整的可见性规则,应在应用代码中强制执行其余规则。

产品层

Helix 提供图谱和搜索原语。一个完整的智能记忆系统还需要:

层职责
摄取 API接收文本、聊天记录、文件、URL、连接器事件和直接记忆写入。
提取器将 PDF、文档、HTML、图像/OCR、音频/视频转录文本、代码和结构化数据转换为文本。
分块器按语义章节、消息轮次、文档标题、代码 AST 边界或转录片段拆分原始上下文。
嵌入工作器在写入 Helix 之前,为记忆和分块生成 text-embedding-3-small 1536 维 F32 嵌入,除非应用已明确统一使用其他模型。
记忆生成器使用当前轮次及近期上下文、活跃实体、召回的记忆和当前日期,从对话/文档中提取以实体为中心的原子候选事实。
关系分类器判定每个候选项属于 UPDATES、EXTENDS、DERIVES、重复项,还是独立项。
个人资料摘要器根据最新记忆维护 UserProfile.staticSummary 和 dynamicSummary。
遗忘任务执行过期、衰减、个人资料过时和连接器删除清理任务。
检索服务改写查询,对记忆/分块执行向量搜索和 BM25,融合结果、重新排序、扩展图谱,并附带引用打包上下文。
评估衡量召回质量、过时记忆抑制、租户隔离、延迟和令牌效率。

不要暗示 Helix 会自动执行提取、分块、嵌入、关系分类、个人资料生成、连接器同步、重新排序或 TTL。这些都属于应用职责,除非用户使用的是提供这些功能的托管服务。

记忆生命周期

每个步骤都链接到 EXAMPLES.md(TypeScript)和 EXAMPLES.rust.md(Rust)中的完整示例。

1. 摄取与生成
  1. 将原始上下文作为 SourceDocument、对话/会话、直接记忆写入或连接器更新接收。
  2. 如果输入尚未是原子记忆,则在应用侧进行提取和分块。
  3. 默认在应用侧使用 OpenAI text-embedding-3-small 为每个候选记忆/分块生成嵌入。存储/传递长度为 1536 的 F32 向量。
  4. 提取原子、完整且独立的候选记忆。优先采用以实体为中心的事实:使用“Alex 偏好上午开会”,而非“偏好上午开会”。
  5. 对候选项分类:fact、preference、episode、procedure 或应用特定的等效类型。
  6. 写入前进行去重。相似度阈值无法作为批处理条件,因此语义去重应采用先读后写,精确重复项则使用幂等 upsert。
  7. 写入包含 tenant_id、memoryId、content、embedding、kind、salience、isLatest: true 和生命周期时间戳的 Memory;关联所有权和溯源边。
  8. 立即分类并关联实体。
上下文提取规则

不要孤立地从最新用户消息中提取信息。提取工作器应接收:

  • 当前用户消息
  • 上一条助手消息,因为它通常会定义简短回答的含义
  • 有界的近期对话窗口
  • 回忆出的活跃记忆和活跃实体
  • 用于解析相对时间短语的当前日期/时间
  • 记忆范围(tenant_id 加上 userId、containerId、projectId 或 ACL 上下文)

在决定是否存储记忆之前,先解析代词、省略表达和简短的后续回答。如果助手提出了一个会产生记忆的后续问题,而用户简短作答,则将回答转换为完整、自包含的记忆。

提取器的输出应具有足够的结构,以便确定性地写入:shouldStore、自包含的 content、kind、confidence、salience、entities、source 指针、scope、可选的时间字段,以及关系判定(new、duplicate、EXTENDS、UPDATES 或 DERIVES)。不要让单个向量距离阈值决定是否更新;应通过向量检索和 BM25 检索候选项,并在应用代码中判定是完全重复、更新还是扩展。

示例:

Existing memory: User is planning a trip to Japan with Maya.
Assistant: When are you going?
User: next April
Extract: User is planning a trip to Japan with Maya next April.
Relationship: EXTENDS the existing Japan trip memory; MENTIONS Maya and Japan.

Assistant: What do you want to do there?
User: mostly food, temples, and trains
Extract: User wants their Japan trip with Maya to focus on food, temples, and trains.
Relationship: EXTENDS the existing Japan trip memory; categorise as travel/preferences.

User later: actually we're going in May instead
Extract: User is planning a trip to Japan with Maya in May.
Relationship: UPDATES the previous next-April timing memory and invalidates the older version.
2. 更新与版本控制
  • **访问时强化:**递增 accessCount、lastAccessedAt,并在限定范围内提高 salience。
  • **更新/更正:**创建一条新记忆,关联 new -UPDATES-> old,将旧记忆的 isLatest 设为 false 并设置 validTo;如果希望它不再出现在常规召回中,还可以设置 deletedAt。
  • **扩展:**当新事实丰富了旧事实但没有取代它时,关联 new -EXTENDS-> existing。
  • **推导:**为推断出的事实添加指向支持它的记忆的 DERIVES 边,并通过置信度元数据将其标记为推断内容。
  • 如果 content 发生变化,应重新生成嵌入,并在同一次写入中更新 embedding。内容与向量绝不能不一致。
  • 将生命周期有效性(validFrom、validTo、deletedAt)与现实世界事件时间(eventStartAt、eventEndAt、temporalText)分开。因事实发生变化而更新记忆时,即使新旧事实都指向未来或过去的日期,也应使旧记录失效。
  • **写入时等待持久化。**更新/版本控制采用先读后写,并且常常会在多个会话中并发运行,因此 HTTP 409 写入冲突的可能性更高。写入时等待持久化以减少冲突(TypeScript 中使用 .shouldAwaitDurability(true),Rust 中使用 .should_await_durability(true),Go 中使用 helix.AwaitDurability(true))。应根据稳定的 transaction_conflict 代码识别真实冲突(远程调用时对应 HTTP 409),绝不要通过解析诊断消息来判断。重新加载权威记忆状态后再重建变更;只有在操作具备幂等性或其他方面安全时,才进行有界退避重试。请参阅对应语言的 skill 和 ../../docs/error-handling.md。
3. 删除 / 遗忘

Helix 没有原生 TTL 或衰减机制。遗忘通过应用运行的显式写入查询实现。

  • 软删除(首选):设置 deletedAt = Expr.datetime(),并在读取时过滤掉该记录。可撤销且便于审计。
  • **版本失效:**当某条记忆被取代时,将 isLatest 设为 false,并将 validTo 设为 Expr.datetime()。
  • **过期清理:**隐藏或硬删除 expiresAt < now 的记忆。
  • **衰减清理:**隐藏较弱、过时且很少访问的情景记忆。
  • **硬删除:**仅在策略要求物理删除时使用 drop()。drop() 会删除节点及其关联边;对于对多重图敏感的路径,请使用 dropEdgeById 进行精确的边清理。
4. 分类与实体链接
  • 将显示分类存储为 Category 节点,并按 tenant_id 隔离;使用唯一的 categoryKey,例如 ${tenant_id}:${normalisedName}。
  • 将实体存储为 Entity 节点,并按 tenant_id 隔离;使用唯一的 entityKey,例如 ${tenant_id}:${normalisedName}。
  • 如果需要按标签/实体遍历、聚合或回忆,优先使用边,而不是数组。
  • 对于无需扩展图谱的显示/审计字段,使用嵌套对象元数据。将经常用于筛选的字段放在顶层;如果需要按标签/实体遍历、聚合或回忆,则优先使用边。
5. 资料维护
  • 为每个用户/容器维护一个 UserProfile,包含 profileId、tenant_id、userId、staticSummary、dynamicSummary 和 updatedAt。
  • 静态资料:身份、稳定偏好、长期背景。
  • 动态资料:当前项目、近期上下文、临时目标、未解决的任务。
  • 在写入和删除记忆后异步更新资料;确保资料生成过程足够确定,以便进行测试。
检索

运行多个召回路径,并在应用端融合:

  1. 获取 UserProfile,用于始终提供的上下文。
  2. 对当前 Memory 节点运行向量检索和 BM25,并按租户、用户/容器范围及新鲜度进行筛选。
  3. 可选:对 Chunk 节点运行向量检索和 BM25,以进行基于源材料的 RAG 并提供引用;除非文档明确为共享文档,否则采用相同的所有者/范围策略。
  4. 在应用端使用 RRF 融合,然后根据显著性、近期程度、关系类型和可选的交叉编码器分数重新排序。
  5. 通过 MENTIONS、IN_CATEGORY、EXTENDS、UPDATES 和 RELATES_TO 扩展排名靠前的记忆,并限制深度和租户范围。
  6. 整理上下文时不包含嵌入;如有来源/引用元数据,则一并包含。

反模式

请勿:

  • 在新的动态/TS/Rust DSL 工作中使用已弃用的 .hx 方言(Embed()、SearchV、SearchBM25、AddV)
  • 将 userId 用作文本索引的租户属性;对于按租户分区的文本/向量索引,请使用 tenant_id
  • 认为仅凭 tenant_id 就能为组织/团队租户建立安全的召回边界;请通过 userId、containerId、项目 ACL 或明确的共享记忆标记进行筛选
  • 只为 Memory 附加 tenant_id;每个归租户所有的节点和边都需要它
  • 在未同时检查 tenant_id 的情况下,通过 memoryId 修改、删除、分类或强化记忆
  • 因为召回只检查了 deletedAt,就返回已被取代、遗忘或过期的记忆
  • 将生命周期时间戳(validTo、deletedAt)与现实世界的事件日期混用;对于涉及旅行、截止日期、预约或历史事实的记忆,请使用单独的时间字段
  • 构建仅支持向量的存储,并将其称为记忆
  • 在生产环境的召回或基准测试声明中使用玩具哈希嵌入;除非应用有更好的标准模型,否则默认使用 text-embedding-3-small
  • 仅凭向量阈值决定去重/更新/扩展;应结合精确检查、BM25 候选项、向量候选项,以及应用/LLM 判定
  • 只从最新的用户消息中提取记忆,因而遗漏“明年四月”或“主要是美食、寺庙和火车”等上下文相关的后续信息
  • 在解析上下文前,因为简短的后续回答缺乏独立完整性而将其丢弃
  • 写入没有所有者或范围字段的用户专属分块/文档,之后又在整个租户范围内召回
  • 期望 Helix 自动提取文件、分块文档、生成嵌入、分类更新、构建资料、重新排序、同步连接器或运行 TTL 作业
  • 在 out/in/both 步骤之后读取 $distance 或 $score;搜索后应立即投影排名元数据
  • 试图将相似度阈值去重表达为 BatchCondition;它只能检查变量是否为空/其大小
  • 更新 content 后不重新生成嵌入
  • 在 API 响应中返回 embedding 数组,除非明确要求
  • 在多租户记忆应用中按显示名称将 Category 或 Entity 设为全局节点

验证清单

完成前:

  • 确认 readBatch() 与 writeBatch() 使用正确
  • 每个租户所有的节点和边都有 tenant_id
  • 向量索引/文本索引使用 tenant_property = "tenant_id",且搜索传入 tenantValue = tenant_id
  • 向量检索和 BM25 检索在遍历范围限定的搜索之前,先构建用户/ACL 和生命周期候选项
  • 每次记忆读取都筛选 tenant_id、用户/容器可见性、deletedAt IsNull、当前/最新状态和有效期
  • 每条写入路径都接受并按 tenant_id 筛选
  • 用于 upsert 的 ID 要么全局唯一,要么包含租户标识(categoryKey、entityKey 等)
  • 特定于用户/容器的文档和分块都携带检索使用的相同所有者/范围字段,或通过应用策略明确标记为共享
  • 生命周期有效性字段不与事件时间字段混用;带日期的事实使用 observedAt、eventStartAt、eventEndAt、temporalText 或应用中的等效字段
  • 嵌入模型为 openai:text-embedding-3-small,且每个向量都是 1536 维 F32,除非应用明确统一采用其他固定模型/维度
  • 内容编辑与重新生成嵌入在同一次写入中完成
  • 生成过程进行语义去重,且完全相同的重复内容具有幂等性
  • 在决定存储什么之前,抽取过程会查看上一轮助手对话、近期对话窗口、检索到的有效记忆/实体以及当前日期
  • 抽取过程会输出结构化的关系/范围/来源/时间决策,以便进行确定性测试
  • 如果功能承诺提供引用或对原始上下文执行 RAG,则必须存在源文档/分块
  • 如果功能承诺始终提供个性化体验,则必须存在用户资料更新任务
  • 评估涵盖租户隔离、用户/容器隔离、过时记忆抑制、上下文追问抽取、精确令牌检索、时间更正、删除、资料重建、延迟和令牌预算
  • 时间戳采用统一约定;此技能使用通过 Expr.datetime() 和 param.dateTime() 设置的类型化 DateTime
  • 除非明确要求,否则投影输出不包含 embedding
  • 最多一个值的响应字段允许为 null;集合/折叠/变更的空值保留为 [],标量 0 或 false 保持为标量
  • 标签/边/属性与现有代码库的大小写格式一致

参考文件

  • REFERENCE.md — 完整的数据模型规范、租户规则、索引、模态速查表、嵌入指南、融合/重排公式,以及 TypeScript ↔ Rust API 映射。
  • EXAMPLES.md — 生命周期场景的 @helix-db/[email protected] TypeScript 代码片段。默认使用。
  • EXAMPLES.rust.md — 使用 Rust DSL 编写的相同场景。
  • 相邻技能:helix-query-typescript、helix-query-rust、helix-query-json-dynamic、helix-query-optimize。

评论

支持 Markdown