返回资源广场

Skills 资源 / 技能包

persisting-cross-session-memory

用组合后端把 /memories/ 前缀路由到键值仓库后端,让智能体写下的用户偏好在换一个全新会话后仍能读回,并按命名空间隔离多用户。Use when 智能体换个会话就失忆、要做跨会话的用户偏好或长期事实记忆、需要区分「会话内连续」与「跨会话持久」两条轴、或排查配好了却读不回、抄官方命名空间写法报 AttributeError、进程重启后记忆全没这类问题时。涵盖后端选型、路由配置、两次会话调用、命名空间隔离、持久级别切换;不含单次会话内的大产物卸载(见 offloading-large-tool-output-to-files)。

SKILL.md 技能文档

能力目标

给智能体装上跨会话记忆:约定 /memories/ 前缀下的文件走持久后端、其余路径仍走临时状态,换一个全新会话标识后智能体仍能自发读回并答对;并能按用户隔离记忆、在本地内存实现与数据库实现之间平滑切换。

前置

  • 已能构造智能体、并知道它带 write_file、read_file、ls 这套虚拟文件工具(见 bootstrapping-deepagents-env)。
  • 两个必须先分清的对象:检查点保存器按会话标识存取状态快照、只管同一会话内的连续性;键值仓库独立于任何会话、是跨会话记忆的载体。两者是正交的两条轴,不能互相替代。
  • 不配任何持久化时用的是默认的状态后端,文件存进绑定单次会话的状态通道,换会话就没了。

实操流程

  1. 先复现一次失忆,建立基线。同一个智能体,第一次调用写文件、第二次换一个会话标识再问:

    agent.invoke(
        {"messages": [{"role": "user", "content": "请把这个事实记到 /notes/user.md:……"}]},
        config={"configurable": {"thread_id": "thread-1"}},
    )
    result_2 = agent.invoke(
        {"messages": [{"role": "user", "content": "读 /notes/user.md,告诉我……"}]},
        config={"configurable": {"thread_id": "thread-2"}},
    )
    

    第二次会拿到「文件不存在」,且 result_2["files"] 为空——换会话标识就是换了一块独立状态。

  2. 建一个键值仓库实例,全程复用同一个对象:

    from langgraph.store.memory import InMemoryStore
    store = InMemoryStore()
    
  3. 配组合后端的路由:只让约定前缀走持久后端,其余仍走临时后端:

    from deepagents.backends import CompositeBackend, StateBackend, StoreBackend
    
    backend = CompositeBackend(
        default=StateBackend(),
        routes={
            "/memories/": StoreBackend(namespace=lambda rt: ("demo-user",)),
            "/cache/":    StateBackend(),
        },
    )
    agent = create_deep_agent(model=model, backend=backend, store=store)
    

    兜底后端接管所有没命中前缀的路径;命名空间决定数据存进仓库的哪个隔离区。

  4. 两次会话调用,只换会话标识:

    agent.invoke(
        {"messages": [{"role": "user",
          "content": "请把这个事实写入文件 /memories/user.md,内容就是:……"}]},
        config={"configurable": {"thread_id": "thread-A"}},
    )
    result_2 = agent.invoke(
        {"messages": [{"role": "user",
          "content": "请读取文件 /memories/user.md,告诉我……"}]},
        config={"configurable": {"thread_id": "thread-B"}},
    )
    
  5. 要多用户隔离就把命名空间换成区分用户的值。本地开发用固定元组,上生产再换成从运行时上下文取身份的可调用写法:

    routes={"/memories/": StoreBackend(namespace=lambda rt: ("user-123",))}
    
  6. 要扛住进程重启就换持久化仓库实现,其余代码不动:

    from langgraph.store.postgres import PostgresStore
    store = PostgresStore(conn_string="postgresql://...")
    
  7. 迁到自己的场景只改三处:写入的内容字符串、文件名(保持在约定前缀下)、命名空间取值。架构代码一行不动。

校验回路

  • 第二次会话的回复里包含第一次写入的事实,且这一轮的工具调用记录是 ['read_file']——说明智能体是自发去取文件的,不是被系统硬塞的。这条行为层证据比「文件字节还在仓库里」更硬。
  • 第一次会话打印的 files 键里看不到你写的那个路径,这不矛盾:持久后端的文件进的是键值仓库、不进状态通道,正因如此它才能跨会话存活。
  • 直接查仓库做存储层断言:list(store.search(("demo-user",))),每条的键是剥掉前缀后的路径。
  • 做四档对照确认两条轴正交:不配检查点保存器时同一会话第二次调用就读不到;配上后同一会话读得到、换会话读不到;用组合后端把约定前缀路由到键值仓库后,不配检查点保存器换会话也读得到。
  • 同一个智能体下,写到未命中前缀的路径换会话读不到、写到约定前缀下换会话读得到——路径前缀本身就是持久开关。

常见陷阱

  • 中途新建了仓库实例:最常见的「明明配对了却读不回」。内存实现的数据就在那一块进程内存里,新建一个等于开了一块空内存。全程复用同一个对象。
  • 抄官方的命名空间写法在本地跑:从运行时上下文取用户身份的写法在本地会报 AttributeError: 'NoneType' object has no attribute 'user',因为本地没有服务端上下文。本地一律用固定元组,生产再换回去。还有一条更早的报错路径:完全脱离图执行去访问命名空间会报「运行时不可用」,两条要分清、别排查错方向。
  • 以为仓库里有目录树:底层是一组扁平的键值对,键是剥掉路由前缀后的路径。ls 能列出子目录是把所有键按分隔符切分临时合成的视图,仓库里并没有目录实体。设计记忆结构时还要知道搜索是先拉全量再本地过滤,同一命名空间下条目越多越重。
  • 写路径时漏掉尾部斜杠:写成不带尾斜杠的前缀本身算精确命中路由根目录,剥前缀后路径变成根。
  • 配了多条重叠前缀不知道谁优先:按前缀长度倒序匹配、最长的先命中。前缀互不重叠时这条规则永远轮不到出场;需要「子目录走更细的后端」时才配重叠前缀。
  • 把跨会话当成永久:内存实现指的是进程内内存,进程重启数据清零。要扛重启换持久化实现。
  • 写错后端参数名:本地磁盘后端的目录参数名是 root_dir,写成别的会报 unexpected keyword argument。
  • 把它和常驻注入型记忆混为一谈:后端持久化是「文件存哪 + 按需读回」,智能体要主动调 read_file 才拿得到;另有一类中间件在每次运行开始前就把约定文件的内容注入系统提示、零工具调用就带着记忆开局。一个可见信号就能区分两者:这一轮有没有 read_file 调用。两者适用场景不同,不是替代关系。

适用范围与前置条件

  • 已能构造智能体、并知道它带 write_file、read_file、ls 这套虚拟文件工具(见 bootstrapping-deepagents-env)。
  • 两个必须先分清的对象:检查点保存器按会话标识存取状态快照、只管同一会话内的连续性;键值仓库独立于任何会话、是跨会话记忆的载体。两者是正交的两条轴,不能互相替代。
  • 不配任何持久化时用的是默认的状态后端,文件存进绑定单次会话的状态通道,换会话就没了。

怎么使用

使用步骤

  1. 先复现一次失忆,建立基线。同一个智能体,第一次调用写文件、第二次换一个会话标识再问:

    agent.invoke(
        {"messages": [{"role": "user", "content": "请把这个事实记到 /notes/user.md:……"}]},
        config={"configurable": {"thread_id": "thread-1"}},
    )
    result_2 = agent.invoke(
        {"messages": [{"role": "user", "content": "读 /notes/user.md,告诉我……"}]},
        config={"configurable": {"thread_id": "thread-2"}},
    )
    

    第二次会拿到「文件不存在」,且 result_2["files"] 为空——换会话标识就是换了一块独立状态。

  2. 建一个键值仓库实例,全程复用同一个对象:

    from langgraph.store.memory import InMemoryStore
    store = InMemoryStore()
    
  3. 配组合后端的路由:只让约定前缀走持久后端,其余仍走临时后端:

    from deepagents.backends import CompositeBackend, StateBackend, StoreBackend
    
    backend = CompositeBackend(
        default=StateBackend(),
        routes={
            "/memories/": StoreBackend(namespace=lambda rt: ("demo-user",)),
            "/cache/":    StateBackend(),
        },
    )
    agent = create_deep_agent(model=model, backend=backend, store=store)
    

    兜底后端接管所有没命中前缀的路径;命名空间决定数据存进仓库的哪个隔离区。

  4. 两次会话调用,只换会话标识:

    agent.invoke(
        {"messages": [{"role": "user",
          "content": "请把这个事实写入文件 /memories/user.md,内容就是:……"}]},
        config={"configurable": {"thread_id": "thread-A"}},
    )
    result_2 = agent.invoke(
        {"messages": [{"role": "user",
          "content": "请读取文件 /memories/user.md,告诉我……"}]},
        config={"configurable": {"thread_id": "thread-B"}},
    )
    
  5. 要多用户隔离就把命名空间换成区分用户的值。本地开发用固定元组,上生产再换成从运行时上下文取身份的可调用写法:

    routes={"/memories/": StoreBackend(namespace=lambda rt: ("user-123",))}
    
  6. 要扛住进程重启就换持久化仓库实现,其余代码不动:

    from langgraph.store.postgres import PostgresStore
    store = PostgresStore(conn_string="postgresql://...")
    
  7. 迁到自己的场景只改三处:写入的内容字符串、文件名(保持在约定前缀下)、命名空间取值。架构代码一行不动。

继续探索

全部资源
Skills 资源 / 技能包

bootstrapping-deepagents-env

在一台干净机器上装好 DeepAgents 运行环境、接入一个 OpenAI 兼容大模型凭证,并跑通第一个 create_deep_agent 工具调用闭环。Use when 需要初始化 DeepAgents 开发环境、系统 Python 版本不达标装不上包、不确定装到了哪个版本、接 DeepSeek 之类国产模型报 ImportError 或 404 这类环境层故障时。涵盖解释器版本核对、虚拟环境置备、主包与提供方包安装、版本核验、凭证注入、最小示例验收;不含 Agent 各项能力的用法(见 tracking-task-progress-with-todos 等能力型 skill)。

Skills 资源 / 技能包

inspecting-agent-graph-and-tools

把一个 create_deep_agent 建出来的智能体拆开看:列出执行图节点、列出实际挂载的工具、捕获框架预装的中间件清单、抓取每轮真正发给模型的工具集。Use when 需要确认某项能力是否真的挂上了、排查「我的工具去哪了 / 这些工具哪来的 / 内置工具到底几个」、验证自定义中间件是否进了图、或要在改配置前后做结构对照时。涵盖图节点自省、工具清单反查、中间件清单捕获、编译期与运行期工具集差异;不含具体能力的用法。

Skills 资源 / 技能包

tracking-task-progress-with-todos

让智能体把多步任务拆成结构化待办清单写进状态,并从调用结果里取出清单、渲染成实时进度、兜底检测「勾完清单却没给答案」的失败形态。Use when 需要给长任务做进度面板、想稳定触发 write_todos、发现规划没被触发、或要把 todos 推给前端 UI 与日志时。涵盖稳定触发写法、取清单的两条路径、三态进度渲染、失败形态检测;不含子任务委派(见 delegating-subtasks-to-subagents)。