使用 Hindsight 为 LangGraph 智能体构建持久化记忆:工具、节点与记忆注入完整实战指南
【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight
本文以hindsight-langgraph集成包为主线,讲解如何为 LangGraph(以及普通 LangChain)应用接入 Hindsight 的长期记忆能力,让智能体在多次运行之间记住用户的偏好与项目事实。读完本文,你将掌握三种接线方式(工具、记忆节点、记忆指令注入)的完整写法,理解 bank 的作用域策略与动态解析机制,并学会如何验证记忆真正跨会话生效。
快速结论
- 安装
hindsight-langgraph集成包;- 指向 Hindsight Cloud 或本地 Hindsight API;
- 用一个稳定的 bank ID 把记忆接入 LangGraph 运行时;
- 先存储一条偏好或项目事实,再开启一次全新运行;
- 确认 recall 能自动带回之前的上下文。
为什么这种接线方式有效
LangGraph 本身就有清晰的执行图(StateGraph+ 边),记忆的放置位置因此非常明确:recall 节点运行在 LLM 节点之前,负责把相关记忆注入上下文;retain 节点运行在 LLM 节点之后,负责把本轮对话沉淀为记忆。同时,LangGraph 的RunnableConfig天然支持运行时配置传递,这让“每次运行动态解析 bank ID”成为可能——无需在图中硬编码任何用户标识符,就能实现按用户隔离的持久记忆。
从源码看,create_recall_node与create_retain_node返回的都是兼容StateGraph的异步节点函数(见 hindsight-integrations/langgraph/hindsight_langgraph/nodes.py 与 create_retain_node 定义),可以直接通过builder.add_node(...)注册进图。
前置条件
- 一个可运行的 LangGraph 或 LangChain 应用;
- Python 3.10+,并已安装
hindsight-langgraph; - 一个稳定的用户或线程标识符,用于映射到 Hindsight bank;
- 一个可用的 Hindsight 后端:Hindsight Cloud 账户,或自托管的 Hindsight 实例(本地 API 默认端口
8888)。
关于依赖版本,pyproject.toml 明确了langchain-core>=0.3.0、hindsight-client>=0.4.0;节点模式额外要求langgraph>=0.3.0(需通过pip install hindsight-langgraph[langgraph]安装)。
第一步:安装集成包
pip install hindsight-langgraph如果使用节点模式(需要在StateGraph中插入 recall/retain 节点),请一并安装 langgraph 依赖:
pip install hindsight-langgraph[langgraph]从源码结构看,hindsight_langgraph包对 langgraph 采用惰性导入:create_recall_node/create_retain_node只在真正被访问时才导入,若此时未安装 langgraph 会抛出明确的提示错误(见 hindsight-integrations/langgraph/hindsight_langgraph/init.py)。因此纯 LangChain 场景可以只装基础包,无需引入 langgraph。
第二步:连接 LangGraph 与 Hindsight
连接的核心是一个Hindsight客户端实例:
from hindsight_client import Hindsight client = Hindsight(base_url="http://localhost:8888")- 使用 Hindsight Cloud 时,将
base_url设为https://api.hindsight.vectorize.io,并在客户端配置中传入你的 API token。 - 使用自托管实例时,指向本地地址(如
http://localhost:8888)。
客户端解析的底层逻辑
集成包内部通过resolve_client统一解析客户端(见 hindsight-integrations/langgraph/hindsight_langgraph/_client.py),优先级如下:
- 显式传入的
client参数; hindsight_api_url/api_key参数;- 之前调用过
configure()时保存的全局配置; - 默认 URL
https://api.hindsight.vectorize.io+ 环境变量HINDSIGHT_API_KEY(见 config.py)。
也就是说,即使完全不调用configure(),只要设置了HINDSIGHT_API_KEY环境变量,create_hindsight_tools(bank_id="user-123")也能直接工作。API key 在构造阶段是可选的——缺失时只在真正发起调用时才报错。
全局配置 configure()
需要统一设置连接参数和默认行为时,可以调用configure()(见 hindsight-integrations/langgraph/hindsight_langgraph/config.py):
from hindsight_langgraph import configure configure( api_key="your-api-key", # 或设置 HINDSIGHT_API_KEY 环境变量 budget="mid", tags=["source:langgraph"], )自托管实例的全局配置:
configure( hindsight_api_url="http://localhost:8888", )也可以在任意工厂函数上直接传hindsight_api_url进行单点覆盖:
tools = create_hindsight_tools(bank_id="user-123", hindsight_api_url="http://localhost:8888")所有工厂函数都接受client、hindsight_api_url、api_key三个参数用于覆盖全局配置。核心参数含义如下:
| 参数 | 说明 | 默认值 |
|---|---|---|
hindsight_api_url | Hindsight API 地址 | https://api.hindsight.vectorize.io |
api_key | API key(或HINDSIGHT_API_KEY环境变量) | None |
budget | recall 预算等级:low/mid/high | mid |
max_tokens | recall 结果的最大 token 数 | 4096 |
tags | 应用于 retain 存储操作的标签 | None |
recall_tags | 用于过滤 recall 结果的标签 | None |
recall_tags_match | 标签匹配模式:any/all/any_strict/all_strict | any |
第三步:把记忆接入运行时(三种模式)
官方集成包提供了三种接线方式,本文档主推前两种,第三种适合纯 LangChain 链路(完整说明见 hindsight-integrations/langgraph/README.md):
- 工具(Tools)——把 retain / recall / reflect 暴露为 LangChain
@tool,由智能体自主决定何时读写记忆,同时兼容 LangChain 与 LangGraph; - 记忆节点(Nodes)——在图中 LLM 节点前后自动插入 recall / retain 节点,无需智能体主动调用;
- 记忆指令(Memory Instructions)——预取记忆并拼进系统提示词,适用于任何 LangChain 模型,无需构图。
模式一:工具调用(Tools)
create_hindsight_tools()会返回一组 LangChain 工具实例,可直接绑定到模型或挂进ToolNode(实现见 hindsight-integrations/langgraph/hindsight_langgraph/tools.py):
from hindsight_langgraph import create_hindsight_tools from langchain_openai import ChatOpenAI from langgraph.prebuilt import create_react_agent # 设置 HINDSIGHT_API_KEY 环境变量即可完成鉴权 tools = create_hindsight_tools(bank_id="user-123") agent = create_react_agent( ChatOpenAI(model="gpt-4o"), tools=tools, ) result = await agent.ainvoke( {"messages": [{"role": "user", "content": "Remember that I prefer dark mode"}]} )默认返回三个工具:hindsight_retain(存储信息)、hindsight_recall(检索相关记忆,返回编号列表)、hindsight_reflect(基于记忆综合生成有推理的回答)。若需要裁剪,可分别用include_retain/include_recall/include_reflect开关控制;仓库中的单元测试验证了“默认三个工具”以及“只保留 retain 时仅返回一个工具”等行为(见 hindsight-integrations/langgraph/tests/test_tools.py)。
动态 bank ID 与工具:如果智能体只构建一次、却要服务多个用户,就省略静态bank_id,改为在每次请求时从config["configurable"]中解析:
tools = create_hindsight_tools(bank_id_from_config="user_id") agent = create_react_agent(ChatOpenAI(model="gpt-4o"), tools=tools) result = await agent.ainvoke( {"messages": [{"role": "user", "content": "Remember that I prefer dark mode"}]}, config={"configurable": {"user_id": "user-456"}}, )传入bank_id="user-123"则会把所有工具调用固定到该 bank,且优先级高于bank_id_from_config。
模式二:记忆节点(Nodes)
在图中 LLM 节点前后插入自动化的 recall 与 retain 节点:
from hindsight_client import Hindsight from hindsight_langgraph import create_recall_node, create_retain_node from langgraph.graph import StateGraph, MessagesState, START, END client = Hindsight(base_url="http://localhost:8888") recall = create_recall_node(client=client, bank_id_from_config="user_id") retain = create_retain_node(client=client, bank_id_from_config="user_id") builder = StateGraph(MessagesState) builder.add_node("recall", recall) builder.add_node("agent", agent_node) # 你的 LLM 节点 builder.add_node("retain", retain) builder.add_edge(START, "recall") builder.add_edge("recall", "agent") builder.add_edge("agent", "retain") builder.add_edge("retain", END) graph = builder.compile()recall 节点的工作方式(见 nodes.py):从state["messages"]中取出最新一条HumanMessage作为查询,调用arecall,将命中的记忆格式化成一个SystemMessage(内容为Relevant memories about this user:开头的编号列表)追加回messages。max_results控制注入条数上限,tags/tags_match/recall_types/recall_include_entities均可透传。相关行为在 test_nodes.py 中有完整覆盖。
retain 节点的工作方式(见 nodes.py):默认只取最新一条HumanMessage文本内容存入记忆(retain_human=True),可通过retain_ai=True同时存储 AI 回复。ToolMessage/FunctionMessage会被有意跳过,避免把工具调用协议噪音存进记忆;tags、metadata、document_id可附加到存储操作上。
关于消息顺序的注意点:默认把记忆SystemMessage追加进messages时,由于MessagesState的add_messagesreducer 是追加语义,该消息会出现在已有消息之后而非最前。如果 LLM 提供商要求系统消息置顶,建议用output_key把记忆文本写入独立的 state 字段,再在 agent 节点手动拼进系统提示词:
from typing import Optional from langgraph.graph import MessagesState class AgentState(MessagesState): memory_context: Optional[str] = None recall = create_recall_node( client=client, bank_id="user-123", output_key="memory_context" ) # 在 agent 节点中读取 state["memory_context"],拼接到系统提示词头部模式三:记忆指令(Memory Instructions)
不想构图时,可以用memory_instructions预取记忆并注入系统提示词,任何 LangChain 模型都适用(见 tools.py):
from hindsight_langgraph import memory_instructions from langchain_openai import ChatOpenAI get_instructions = memory_instructions( bank_id="user-123", base_instructions="You are a helpful assistant.", ) # 每次调用都会重新拉取记忆,保持最新 instructions = await get_instructions() response = await ChatOpenAI(model="gpt-4o").ainvoke([ {"role": "system", "content": instructions}, {"role": "user", "content": "What do you know about me?"}, ])与 recall/retain 节点“失败即抛HindsightError”不同,memory_instructions面向提示词构建路径:当 Hindsight 调用失败(如网络异常)时,它会记录日志并原样返回base_instructions,让 LLM 调用继续执行,实现优雅降级。
关于 BaseStore 适配器
指南中还提到了HindsightStore适配器,用于需要 LangGraph 原生存储语义(BaseStore)的场景。需要说明的是:当前仓库的hindsight-langgraph包(hindsight-integrations/langgraph)中,源码结构确认提供的三种现成模式是工具、节点与记忆指令;如果你需要 BaseStore 原生模式,请以对应版本的包说明与集成文档为准。工具、节点、BaseStore 三者的选择原则是:要智能体自主控制记忆调用选工具,要自动注入与自动存储选节点,要 LangGraph 原生存储语义选 BaseStore。
第四步:选择正确的 bank 策略
只要拥有稳定的用户或租户键,就应该从RunnableConfig动态解析 bank ID,让记忆在多次图运行之间始终挂到正确的人身上:
# 动态解析:每次运行从 config 中读取 recall = create_recall_node(bank_id_from_config="user_id") retain = create_retain_node(bank_id_from_config="user_id") result = await graph.ainvoke( {"messages": [{"role": "user", "content": "hello"}]}, config={"configurable": {"user_id": "user-456"}}, )解析逻辑位于 nodes.py(工具侧见 tools.py 的 _resolve_bank_id):静态bank_id优先;否则从config["configurable"][bank_id_from_config]读取;都拿不到时,recall/retain 节点会记录告警并跳过本次记忆操作,而工具会抛出HindsightError。
作用域建议:
- 给团队内部单一助手使用时,共享 bank 可行;
- 但绝大多数生产图应当按用户、租户或线程作用域隔离记忆——按用户隔离是最安全默认值,需要更强隔离时再叠加租户或线程上下文。
第五步:验证记忆是否真正生效
按以下步骤做一次端到端验证:
- 用测试用户运行一次图,存储一条偏好或项目事实;
- 用相同的
user_id(放在configurable中)再次调用图; - 提出一个依赖之前事实的问题,确认 recall 能把上下文带回来;
- 换一个不同的
user_id重复同样的测试,确认记忆按用户隔离。
如果第二次运行能回答出第一次运行留下的细节,说明配置成功。如果不能,按顺序排查:打开调试日志、核对解析到的 bank ID、确认 retain 调用确实执行完毕(仓库中节点失败会抛HindsightError,错误信息包含Recall node failed/Retain node failed,见 test_nodes.py)。
仓库还提供了更高层的验证:test_graph_flow.py与test_e2e.py覆盖完整的图流程与端到端集成(见 hindsight-integrations/langgraph/tests),可作为你本地验证的参考模板。
常见错误
- 在纯 LangChain 中绑定了工具,却忘记运行工具执行循环——工具模式在 LangChain 下同样可用,但工具调用需要你自己驱动;
- 第二次运行使用了不同的运行时键——
bank_id_from_config读取到的键不同,会静默创建一个全新 bank,导致记忆“丢失”; - 所有用户共用一个 bank——当应用真正需要按用户隔离记忆时,共享 bank 会造成串记忆。
FAQ
应该用工具、节点还是 BaseStore?
想由智能体自主控制记忆调用,用工具;想在图中自动 recall / retain,用节点;想要 LangGraph 原生存储模式,用BaseStore。
纯 LangChain 也能用吗?
可以。工具模式在 LangChain 中同样可用,但你需要自己处理工具执行循环。
应该怎样划分 bank 作用域?
按用户隔离是最安全的默认值。当应用需要更强隔离时,再叠加租户或线程上下文。
下一步
- 需要托管记忆后端,从 Hindsight Cloud 开始;
- 阅读完整集成说明 hindsight-integrations/langgraph/README.md;
- 查阅 Python 客户端与 recall / retain API 的实现细节 hindsight-clients/python/README.md 与 hindsight_client.py(
recall见 L504、retain见 L346、reflect见 L590); - 参考测试用例 hindsight-integrations/langgraph/tests/test_nodes.py 与 hindsight-integrations/langgraph/tests/test_tools.py,加深对节点与工具行为边界的理解;
- 若使用纯 LangChain 场景,优先尝试
memory_instructions模式,它无需 langgraph 依赖即可完成记忆注入。
【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考