这次我们来看一个和 LLM Agent 长期记忆相关的架构方案Hierarchical Graph Memory for LLM Agents with Path-level Localization and Rewrite。一句话概括它解决的是 Agent “记不住、找不准、改不动”的问题当对话变长、任务变复杂Agent 需要从大量历史记忆里快速找到完整决策路径并且能在用户纠正或目标变化后有效更新记忆而不是把旧信息越攒越乱。这套机制最核心的特点有三个第一记忆不是平铺的向量片段而是分层的图结构第二检索定位的基本单位不是单个节点而是“路径”也就是从入口节点到目标节点的一条完整引用链第三记忆支持重写Agent 可以根据新反馈修正已经存下来的路径信息。如果你正在做 RAG 增强、多轮对话系统、Autonomous Agent 的工程落地或者在做 Agent 记忆方向的论文复现这篇内容建议直接收藏。本文会从问题背景入手拆解层级图记忆的结构设计、路径级定位原理、记忆重写机制然后给出可参考的实现伪代码、接口设计、实验验证方向和排错清单。文章不涉及具体硬件部署重点讲清楚这套记忆机制的设计思路和落地路径。1. 核心能力速览维度说明技术类型LLM Agent 长期记忆管理机制核心机制分层图记忆Hierarchical Graph Memory检索方式路径级定位Path-level Localization更新方式记忆重写Rewrite解决核心问题长对话记忆混乱、多跳关系无法定位、历史记忆无法修正适用对象LLM Agent、Autonomous Agent、多轮对话系统、Agentic RAG模型依赖不绑定特定模型但需要 LLM 具备一定信息抽取和路径生成能力硬件需求机制本身主要消耗内存和检索服务资源具体取决于底座模型部署方式实现组件向量检索、图结构存储、LLM 抽取与重写、路径索引评估方向检索命中率、路径完整度、多跳任务成功率、重写后稳定性、Token 成本需要说明一点这个标题是典型的论文式命名具体实验数据、开源仓库地址、参数配置不能凭空给出。下面讲的所有内容都是基于标题和机制名称能够直接推导的方法论以及这类架构在 Agent 工程中的通用实现思路。2. 为什么 Agent 需要层级图记忆2.1 长期记忆的三个痛点先看第一个痛点上下文窗口有上限。不管模型是 128K 还是 200K 上下文把整个会话历史全部塞进去都不现实Token 成本高、噪音大而且真正关键的早期决策信息会被稀释。第二个痛点是平面向量检索丢失路径信息。常规做法是把历史记忆切块、做 embedding、存进向量库需要时召回 Top-K 片段。这种结构只能回答“哪个片段和当前问题相似”回答不了“这个片段是在哪条决策链上出现的”“它前面是什么、后面是什么”。对单轮问答够用但对 Agent 这种需要连续执行多个步骤的场景远远不够。第三个痛点是记忆不能自我更新。已经写入记忆的信息如果被用户纠正或者被后续证明是错误路径普通方案只能重新追加一条新记录旧错误路径还会在后续检索中被召回。结果就是同一个问题Agent 每次给出的历史依据可能彼此冲突。2.2 为什么是“图”而不是“文档”把记忆组织成图本质上是把“一段文字”变成“一组节点和边”。节点可以表示实体、事件、决策步骤、用户偏好边可以表示先后顺序、因果关系、引用关系、相似关系。图结构天然适合表达多跳路径。例如 Agent 经历过这样一个任务链用户提出“帮我调研三家云服务商的定价”。Agent 调用了官网价格抓取工具。抓取结果不完整又调用文档解析工具补全。最后生成了对比表格。用户随后纠正“不需要包含带宽费用只看计算实例”。这里面每一步都是一个节点步骤之间的先后关系就是有向边。如果是平铺向量库第 5 条纠正信息很可能被记成一条独立的文本和前面 4 步没有任何关联。但在图结构里这条纠正信息可以反向指向第 2 步和第 3 步的“定价数据采集路径”下次再遇到类似任务Agent 能直接避开“采集带宽费用”这个重复动作。2.3 层级结构解决“规模”问题如果只有一张巨大的图检索时照样会迷路。层级图记忆的做法是把图分成多个抽象层次全局层存放 Agent 的长期目标、角色设定、稳定偏好。这一层结构稀疏变化缓慢。场景层存放某个任务场景或某段会话的核心结论。比如“这次调研的最终结论是选 A 厂商”。这一层是连接全局和事件细节的中间层。事件/路径层存放每一次具体动作、每一步决策、每一条工具调用链。这一层结构密集变化频繁。检索时先从场景层判断“当前属于哪类任务”再通过场景层定位到相关事件层子图最后在子图内做路径展开。这样检索范围被逐层缩小多跳路径的候选集不会爆炸。从实现角度理解层级结构类似于给记忆加了“目录索引”。全局层是书的目录场景层是章节名事件层是具体段落。平面向量库的问题是只有段落没有目录而层级图记忆等于把目录和段落一起存下来检索时可以按层级逐级索引。3. 层级图记忆的结构设计3.1 节点定义在一个典型的层级图记忆系统中节点通常包含以下字段字段说明node_id全局唯一节点 IDlevel层级标识global / scenario / eventcontent节点对应的文本内容或结构化信息embedding节点内容的向量表示用于语义召回metadata时间戳、来源会话 ID、关联任务 ID、置信度等parent_id父节点 ID用于层级向上聚合children_ids子节点 ID 列表节点是记忆的最小信息单元。Event 层节点要尽量细粒度比如一次工具调用的输入输出、一个用户反馈、一个中间结论。不要把一个完整会话压成一个节点否则路径内部的中间步骤会全部丢失。3.2 边定义边定义了节点之间的关联方式常见的有时序边nextA 动作之后执行 B 动作。因果边causesB 结果由 A 导致。引用边references本路径引用了某个事实节点。版本边replaces新路径取代旧路径用于重写后的历史追溯。归属边belongs_to事件节点归属到场景节点。边本身也可以带权重或置信度例如用户明确纠正过的路径权重应当高于未经验证的自动生成路径。检索时可以对权重做归一化优先展开高置信度边。3.3 层级之间的动态聚合层级不是静态不变的。当大量事件节点反复出现在一条路径上系统可以把它们聚合为一个“场景节点”并将其中的关键结论提升到场景层。这个操作可以定期执行也可以由 LLM 在写入记忆时顺便完成。举个例子Agent 连续三天都在处理“用户对某 SaaS 产品的工单答疑”每天产生几十个事件节点。聚合后场景层会形成一个“该产品常见问题清单”节点全局层会形成“用户偏好直接给出排障命令”的偏好节点。这样后续同类任务可以直接命中场景层不需要每次从事件层反推。4. 路径级定位从“找片段”到“找路径”4.1 什么是路径级定位普通 RAG 检索返回的是若干文本片段路径级定位返回的是一整条从入口到出口的节点链。两者的差异非常明显。假设 Agent 被问到“上次我们是怎么解决数据同步失败问题的”普通 RAG 可能只召回“检查了防火墙策略”这一句话但路径级定位会返回入口用户报告数据同步失败 → 步骤1检查数据源连接状态 → 步骤2检查防火墙策略发现端口未放行 → 步骤3调用配置工具放行端口 → 步骤4重跑同步任务验证成功 出口问题已解决这条路径包含了完整上下文Agent 可以直接复用步骤而不是根据一句话猜上下文。4.2 定位过程拆解路径级定位可以拆成三个步骤第一步语义入口召回。先用当前问题生成 embedding在节点库中做向量相似度检索召回多个候选入口节点。入口节点就是路径的起点或路径上某一个关键节点。第二步路径展开。从候选入口节点出发沿有向边向前向后遍历扩展出若干条完整或部分路径。这里的“边”可以按路径权重剪枝也可以设置最大跳数比如限制单条路径最多 8 个节点避免路径爆炸。第三步路径筛选与排序。将候选路径的文字序列拼接起来交给 LLM 或轻量排序模型打分选中最符合当前问题意图的路径。排序标准包括语义相关性、路径完整性、路径置信度、路径新鲜度。4.3 伪代码路径检索这里给出一段通用实现伪代码不绑定具体项目。实际接入时需要按你的 Agent 框架替换图存储和向量库接口。def retrieve_path(query_embedding, top_k_entries10, max_hops6, max_paths3): # 1. 语义入口召回 candidate_nodes vector_store.search(query_embedding, top_ktop_k_entries) # 2. 从候选节点出发做双向路径展开 candidate_paths [] for node in candidate_nodes: paths graph.expand_path( start_nodenode.node_id, directionboth, max_hopsmax_hops, edge_filterlambda e: e.weight 0.3 ) candidate_paths.extend(paths) # 3. 对路径做去重和打分 deduped deduplicate(candidate_paths) scored score_paths_with_llm(deduped, query_embedding) # 4. 返回 Top-N 路径 return sort_by_score(scored)[:max_paths]这段代码的关键点在于向量检索只负责“入口”真正的召回质量取决于路径展开和路径打分。这样即使入口节点不是最优相似片段只要它落在正确的子图上也能通过图结构把完整路径拉出来。4.4 多跳与路径完整度路径级定位最大的价值在于“多跳”。普通向量检索本质上是一阶匹配也就是问题向量和文档片段向量直接比对。而 Agent 场景里的问题往往是多阶的比如“上次确认了 A 依赖 B而 B 又依赖 C最后我们用 C 的配置解决了问题”这种关系无法在平铺片段中体现。图结构的边允许系统做二跳、三跳甚至更多跳的扩展。跳数越多可用信息越丰富但也会带来两个问题一是检索耗时会上升二是路径可能漂移到不相关的子图。工程上的折中方案是默认跳数 4 到 6给边设置“跨场景”禁止遍历标记避免检索跑到其他任务领域路径扩展超过上限时停止只保留高置信度节点的扩展结果。5. 记忆重写机制5.1 为什么需要重写如果记忆只能写入、不能修改Agent 的长期行为一定会被历史错误绑架。典型的重写触发场景包括用户明确纠正“我刚才说的不对应该是 B 而不是 A。”任务执行失败Agent 发现历史路径存在逻辑漏洞。目标变化原路径不再适用于新目标。新信息与旧记忆冲突比如产品接口版本升级导致旧操作步骤失效。重写的目标不是把旧记录删掉而是生成一条“替代路径”让后续检索优先命中新路径同时保留旧路径作为审计和回滚依据。5.2 重写过程的四个阶段重写过程可以拆成四个阶段触发检测。系统需要判断“当前反馈是否需要对旧路径做修改”。可以通过规则触发比如用户消息包含“不对”“纠正”“改成”等词也可以通过 LLM 判断让模型比较当前反馈与已检索路径是否冲突。旧路径定位。用路径级定位找到需要修改的旧路径。这里不能只定位到单个节点因为修改单点可能会导致整条路径逻辑断裂。比如用户纠正的是“检查顺序应该先看网络再看数据库”如果只改一个节点前后步骤可能无法衔接。新路径生成。将旧路径完整文本、当前会话上下文、用户反馈一起送入 LLM生成修订后的路径。生成时要求模型输出结构化结果包含保留节点、修改节点、新增节点、删除节点以及修改原因。写回与版本化。新路径写入图存储并与旧路径建立replaces关系。旧路径标记为“已过期”但不过期节点仍然可以被其他路径引用。检索排序时新路径权重提高旧路径权重降低但不立刻删除。5.3 伪代码路径重写def rewrite_path(old_path_id, user_feedback, session_context): # 1. 从图库读取旧路径 old_path graph.get_path(old_path_id) # 2. 让 LLM 生成新路径结构 new_path_result llm.generate_path( old_pathold_path.to_text(), feedbackuser_feedback, contextsession_context ) # 3. 校验和落图 new_path graph.create_path( nodesparse_nodes(new_path_result), edgesparse_edges(new_path_result), sourcerewrite ) # 4. 建立版本关系 graph.create_edge( from_nodenew_path.end_node, to_nodeold_path.start_node, edge_typereplaces, weight0.95 ) # 5. 可选的全局偏好同步 if new_path_result.has_global_preference: graph.update_global_preference( contentnew_path_result.preference_text ) return new_path.path_id5.4 重写的风险控制重写不是越多越好。如果每条反馈都触发大规模重写记忆库会频繁变动检索结果也会变得不稳定。常见的风险控制策略包括版本保留每条路径保留历史版本新版本明显更差时可以回滚。人工确认对影响全局偏好或核心业务规则的重写加入人工审核队列。修改范围限制一次重写尽量只修改必要的节点子集不要重建整条路径。延迟生效新路径先进入候选池经过多次成功复用后再提升为高置信度路径。6. 整体工作流程现在把前面的机制串起来看一个完整案例。假设用户和 Agent 进行了一场多轮调研对话流程如下会话开始系统从全局层读取用户偏好和历史结论作为 Agent 的初始化知识。任务执行过程中Agent 每完成一个步骤系统将该步骤作为事件节点写入图中并建立与前后步骤的边。任务产生关键结论系统将结论节点聚合到场景层更新当前会话场景。后续用户提出相关问题系统先做路径级定位找回完整决策路径。用户纠正了某个结论系统触发重写机制生成替代路径并标记旧路径过期。再次遇到同类问题检索优先命中新路径历史错误不再干扰后续决策。从工程视角看这六个步骤对应一套 Memory API 的核心方法初始化、添加节点、聚合层级、路径检索、路径重写、路径淘汰。无论底层用 Neo4j、NetworkX 还是自研内存图接口语义都基本一致。7. 实现思路与技术要点7.1 技术选型组件可选方案用途LLMGPT 系列、Claude、通义千问、DeepSeek 或任何可调用的 API信息抽取、路径打分、重写生成向量库FAISS、Milvus、Qdrant、pgvector节点语义检索图存储Neo4j、NetworkX、Memgraph、自研邻接表路径存储与遍历缓存Redis高频路径缓存减少重复检索如果只是原型验证NetworkX FAISS 就够用不需要引入重型图数据库。生产环境如果路径规模大、并发高建议用 Neo4j 或支持图遍历的专用存储。7.2 数据结构设计示例from dataclasses import dataclass, field from typing import List dataclass class MemoryNode: node_id: str level: str # global / scenario / event content: str embedding: List[float] metadata: dict field(default_factorydict) dataclass class MemoryEdge: edge_id: str from_node: str to_node: str edge_type: str # next / causes / references / replaces / belongs_to weight: float 1.0 metadata: dict field(default_factorydict) dataclass class MemoryPath: path_id: str node_ids: List[str] # 按顺序排列的节点列表 confidence: float # 路径置信度 version: int 1 replaced_by: str None这里需要注意MemoryPath不是图里的实体节点而是一个视图。路径由一串节点和边组成存储时可以单独维护路径索引表避免每次检索都重新遍历图。7.3 接口设计参考如果要把这套记忆能力封装成服务可以参考下面的接口语义。注意这是通用设计不是某个开源项目的既定 API。POST /api/v1/memory/node { content: 用户不希望对比带宽费用, level: event, session_id: session_123, parent_id: scenario_456, metadata: { timestamp: 2025-01-01T12:00:00Z } }POST /api/v1/memory/path/retrieve { query: 上次调研的最终结论是什么, max_paths: 3, max_hops: 6 }POST /api/v1/memory/path/rewrite { path_id: path_789, feedback: 用户更正应该选 B 厂商而不是 A 厂商, session_context: 当前会话上下文摘要 }接口设计的关键是检索和重写都接受“查询文本”或“反馈文本”而不是要求上游系统预先构造图查询语句。这样做的好处是Agent 主流程不需要关心记忆内部结构只要把自然语言传给 Memory 服务即可。7.4 LLM 抽取的稳定性处理这套机制非常依赖 LLM 的结构化抽取能力。如果模型把节点内容抽取得忽长忽短或者生成的路径 JSON 格式非法整个记忆质量都会下降。工程上建议增加一层后处理强制让 LLM 输出 JSON并在 Prompt 中给出字段 schema 示例。解析失败时重试一次重试仍失败则降级为纯文本记忆不阻塞主流程。对节点内容长度设置上下限比如 20 到 500 字避免节点粒度过粗或过细。对重写后的路径做一致性校验确认没有出现悬空节点引用。8. 实验验证与效果评估方向标题没有提供公开实验数据所以下面给出的是评估方向而不是结果。如果你在做论文复现或工程验证可以从四个维度设计实验。8.1 检索效果维度对比“普通向量库 Top-K 检索”与“路径级定位”的效果。评估指标包括路径命中率召回的路径是否包含当前问题真正需要的完整链条。路径完整度路径中节点缺失比例少了中间步骤视为不完整。多跳准确率涉及二跳、三跳关系的查询Agent 最终是否给出正确结果。建议在多跳问答类数据集上做验证同时准备一批“需要引用历史路径”的 Agent 任务。8.2 记忆更新维度重点验证重写机制是否有效。设计实验时可以模拟这些场景用户纠正历史结论观察再次检索时是否优先返回新路径。旧路径与新路径同时存在时排序是否正确。连续多次重写后路径版本链是否清晰检索是否稳定。8.3 成本与延迟维度图记忆的代价在于多跳遍历和 LLM 路径打分。需要记录单次路径检索的平均延迟和 P95 延迟单次路径检索的 Token 消耗图存储的增长速率尤其是节点和边的数量随时间的变化重写操作的触发频率和平均耗时。这些数据直接决定机制能不能上生产。如果路径打分需要调用大模型单次检索延迟会明显高于纯向量检索建议把路径打分做成轻量模型或只在候选路径数量较多时触发。8.4 消融实验如果写论文建议做消融去掉层级结构只保留单层图、去掉路径级定位只做节点召回、去掉重写机制只追加不修改。这样能验证每个模块的贡献度。从机制设计看路径级定位对多跳任务的影响应该最明显层级结构对大规模记忆下的延迟影响最明显重写对长期任务稳定性影响最明显。9. 优点、缺点与适用场景9.1 优点保留完整决策链Agent 可以“理解”历史操作而不是只翻到一句话。层级结构能支撑大规模记忆检索范围可控。路径级定位天然适合多跳任务。重写机制让记忆具备演化能力适合长周期 Agent。9.2 缺点实现复杂度高需要同时维护向量库、图存储、路径索引。依赖 LLM 抽取质量模型不稳定会影响整条链路。多跳遍历和图增长可能带来性能问题。重写机制需要额外设计版本控制否则存在记忆漂移风险。9.3 适用场景场景是否推荐原因企业级长期助理 Agent推荐需要跨天跨周复用历史经验自动化工作流编排推荐工作流本质就是路径执行历史客服知识库中等如果问题高度重复平面 RAG 可能够用单轮短对话不推荐引入图记忆的收益低、成本高低延迟高并发在线问答不推荐路径打分和 LLM 抽取会显著增加延迟10. 常见问题与排查方法问题现象可能原因排查方式解决方案检索到的路径与问题明显不相关向量入口召回不准确召回范围太小检查入口节点相似度分数扩大top_k_entries或增加入口召回候选路径缺失中间步骤边权重剪枝过严跳数上限太低查看被剪枝边的权重降低剪枝阈值适当提高max_hops重写后记忆混乱没有版本控制新旧路径未建立替换关系检查路径replaced_by字段统一走版本化写入流程图节点数量增长过快事件节点写入过于频繁没有聚合统计节点增长速率增加定期聚合任务将事件节点合并到场景层路径检索延迟高路径展开候选集太大或 LLM 打分过于频繁查看路径展开数量和打分次数先做规则过滤再打分设置候选路径上限LLM 抽取的节点格式不稳定Prompt 缺少 schema 示例检查模型输出日志增加 schema 示例和 JSON 校验重试机制11. 最佳实践与后续方向如果你准备在自己的 Agent 项目里引入这套机制建议按下面的顺序落地。第一步先做一个最小可用版本把历史会话改成“事件节点 顺序边”然后用 NetworkX 存内存图用 FAISS 做节点向量检索。先不要引入重写机制只验证路径级定位是否能提升多跳问答的准确率。第二步把重写功能加上但限定触发条件。先只处理用户显式纠正的信息不做主动冲突检测。这样可以把重写的风险控制在小范围内不会因为算法误判把正确的历史路径冲掉。第三步增加层级聚合。当事件节点数量超过阈值后定期调用 LLM 把同类事件聚合为场景节点再把关键结论提升到全局层。这一步能显著降低检索延迟也能改善长尾记忆的命中率。最后如果效果稳定可以把记忆模块从主 Agent 进程中拆成独立服务通过 API 暴露节点写入、路径检索、路径重写三个能力。这样上层 Agent 框架不关心记忆内部结构后续扩展也方便。后续值得探索的方向包括让图记忆主动参与 Agent 规划而不只是在被检索时才起作用引入记忆压缩机制自动淘汰低价值节点以及在多 Agent 协作场景中共享图记忆让多个 Agent 共同维护一张经验图。关于这套机制最值得先验证的就是路径级定位能否解决普通向量检索的“只找到片段、找不到链路”问题。最容易踩的坑是 LLM 抽取不稳定和图增长失控所以第一步一定要加格式校验和版本控制。如果你正在做 Agent 长期记忆方面的选型这套思路可以作为对比方案重点参考。