Deep Agents 7. Memory、Store、Summarization 与上下文工程


1. Deep Agents 的上下文来源

1.1 上下文工程解决什么问题

大模型不会因为 Agent 运行了很久就自然拥有记忆。每次模型调用能够使用什么信息,取决于应用这一次究竟发送了哪些消息、System Prompt、Tool Schema,以及 Middleware 对请求做了什么修改。

Deep Agents 所说的“记住”可能来自完全不同的机制:

  • Checkpointer 恢复了同一线程的 messages;
  • MemoryMiddleware 重新读取 AGENTS.md 并加入 System Message;
  • Tool 从 LangGraph Store 查询了用户偏好;
  • Runtime Context 向 Tool 提供了可信的用户身份;
  • Summarization 把旧消息转换成工作摘要;
  • SubAgent 在独立上下文中完成重任务,只向主 Agent 返回最终结果。

这些机制的生命周期、可信度和成本都不同。上下文工程的目标不是让模型“看到尽可能多”,而是让模型在当前步骤看到足够、正确、可追溯的信息,同时控制 Token、延迟和泄露风险。

1.2 信息位置与管理策略是两个维度

Deep Agents 上下文来源与生命周期

上下文不能简单分成并列的“五类”。更准确的方式是使用两个维度。

第一个维度是信息放在哪里、可以存活多久:

位置 典型内容 常见生命周期 是否自动给模型
Model Context System Message、当前消息、Tool Schema 单次模型请求
Runtime Context tenant_id、user_id、连接对象 单次 Agent 运行
Thread State messages、Todo、中断点、线程文件 当前 thread_id 由节点或 Middleware 决定
Store / Backend 用户偏好、Memory 文件、报告和原始历史 由具体实现决定 否,需要加载或读取

第二个维度是怎样控制进入模型请求的内容:

策略 解决的问题
Selection 只选择本次任务需要的指令、Tool 和证据
Offloading 把大 Tool Result 或文件内容移出消息
Summarization 把较旧工作过程压缩成可继续执行的摘要
Isolation 让 SubAgent 在独立上下文处理高噪声任务
Caching 复用稳定 Prompt 前缀,降低 Provider 成本和延迟

Compression 和 Isolation 是管理策略,不是与 State、Store 同级的存储类型。

1.3 一条信息应该放在哪里

可以先判断信息的可信来源和预期生命周期:

信息 推荐位置 原因
当前用户问题 messages 只属于当前交互
已认证租户与用户 ID Runtime Context 不能相信模型生成的身份参数
当前任务 Todo Thread State 需要随线程恢复,不应跨用户共享
项目金额单位 AGENTS.md 每次项目任务都适用
销售分析步骤 Skill 只在相关任务中按需加载
用户报告偏好 Store 或用户级 Memory 文件 需要跨线程复用
大型检索结果 Backend 文件 不应反复占用 Model Context
API Key Secret Manager / Runtime 不能进入 Prompt、Memory、Trace

最危险的错误通常不是“忘记”,而是作用域放错。例如让模型生成 user_id Tool 参数,会给越权读取留下空间;把 API Key 写入 AGENTS.md,则会在每次加载 Memory 时把密钥发送给模型。

1.4 最终模型请求由什么组成

最终请求不是一段固定顺序拼接的 System Prompt。它至少包含四个区域:

请求区域 典型来源
System Message Deep Agents 基础说明、自定义 Prompt、Memory、Skills 指引、Middleware 增补
Messages 用户消息、AI 消息、Tool Call、Tool Result、有效摘要
Tools 内置 Tool、自定义 Tool、MCP Tool 的 Schema
Provider Options Harness Profile、缓存标记和模型专用参数

Tool Schema 是模型请求中的 Tool 定义,不等于 System Message 文本。Middleware 的精确顺序和某段内容最终放在哪个区域,也取决于 Deep Agents、Harness Profile 和模型 Integration 版本。

本文锁定 deepagents==0.6.12,但不把当前源码顺序当作跨版本协议。若业务依赖某段 Prompt 或缓存断点的位置,应拦截真实 ModelRequest 或查看 Trace,而不是根据概念图推测。

1.5 messages、Runtime Context 与 State

messages 是模型推理闭环的主要工作上下文,包含用户消息、模型回答、Tool Call 和 Tool Result。它顺序敏感、会持续增长,也可能在模型请求层被摘要或裁剪,因此不适合作为永久档案库。

Runtime Context 保存本次运行的可信元数据:

class UserContext(TypedDict):
    """Identity supplied by the trusted application, not by the model."""

    tenant_id: str
    user_id: str

调用者从认证结果构造 Context:

result = agent.invoke(
    {"messages": [{"role": "user", "content": "读取我的报告偏好"}]},
    context={"tenant_id": authenticated_tenant, "user_id": authenticated_user},
    config={"configurable": {"thread_id": authorized_thread_id}},
)

context 回答“当前调用者是谁”,thread_id 回答“恢复哪个线程”。两者都应由服务端生成或校验,不能直接采信聊天文本。

State 是 LangGraph 节点之间共享的线程工作状态。Deep Agents 的 State 除 messages 外,还可能包含 Todo、线程文件和 Middleware 私有字段。State 适合保存正在进行的工作,不适合复制所有跨线程用户资料。

2. AGENTS.md Memory

2.1 MemoryMiddleware 做了什么

Deep Agents 的 memory= 接收一组文件路径。MemoryMiddleware 通过 Backend 读取这些文件,将结果保存到私有 memory_contents 状态,并在模型调用前把内容追加到 System Message。

MEMORY_SOURCES = ["/AGENTS.md", "/memory/project/AGENTS.md"]

return create_deep_agent(
    model=model,
    backend=FilesystemBackend(root_dir=ROOT, virtual_mode=True),
    memory=MEMORY_SOURCES,
)

这里的 /AGENTS.md 是 Backend 虚拟路径。使用 FilesystemBackend(root_dir=ROOT, virtual_mode=True) 时,它映射到示例目录中的 AGENTS.md,不是宿主机根目录。

memory= 负责“加载哪些文件”;能否跨线程、跨进程或跨机器保存,由承载这些路径的 Backend 决定。名字叫 Memory 并不会自动获得持久性。

2.2 可维护的 AGENTS.md

本文示例把 Memory 写成可验证的项目契约:

# 数据分析项目记忆

## 稳定约定

- 所有金额以人民币元为单位,展示时保留两位小数。
- 报告必须列出数据口径、日期范围和输入文件路径。
- 计算结论优先引用确定性脚本输出,不允许心算替代脚本。
- 无法验证的数据必须标记为“待确认”,不得补造数值。

## 交付习惯

- 先给结论,再给证据和计算过程。

“回答专业一些”无法稳定测试;“金额保留两位小数”“报告必须写日期范围”可以进入测试和审核清单。几十页 API 文档、一次性任务参数和完整聊天记录不应常驻 Memory。

2.3 多份 Memory、缓存与更新

多份 Memory 按声明顺序全部加载和展示,不使用 Skill 的同名覆盖规则。在本文锁定版本中:

  • 找不到某个文件时跳过该路径;
  • 其他 Backend 下载错误会抛出异常;
  • HTML 注释在格式化 Prompt 时删除;
  • State 已包含 memory_contents 时,本次 State 不重复加载。

HTML 注释删除是 deepagents==0.6.12 的实现和本文契约测试结果,不是任意 AGENTS.md 工具都必须遵守的通用规范。

同一线程恢复的 State 可能已经缓存 Memory。若文件变更必须立即生效,应设计显式刷新、使用 Runtime 动态 Prompt,或让 Tool 按需读取 Store;不要假设每次模型调用都会重新下载文件。

2.4 用真实 Agent 检查最终请求

业务代码不应该直接调用 MemoryMiddleware.before_agent。本文使用确定性 Recording Model 构造完整 Agent,拦截模型实际收到的消息:

def test_real_agent_injects_memory_into_actual_model_request() -> None:
    model = RecordingChatModel()
    agent = build_agent(store=InMemoryStore(), model=model)

    agent.invoke(
        {"messages": [{"role": "user", "content": "检查项目约定"}]},
        config={"configurable": {"thread_id": "p07-memory-prompt"}},
        context={"tenant_id": "tenant-a", "user_id": "alice"},
    )
    system_prompt = str(model.received_messages[-1][0].content)

    assert "数据分析项目记忆" in system_prompt
    assert "当前项目补充约定" in system_prompt
    assert "只供维护者阅读" not in system_prompt

inspect_context.py 仍直接调用 Hook 和私有格式化函数,但它被明确限定为 0.6.12 内部契约审计,不是推荐业务用法。

2.5 Memory 的信任边界

Memory 内容最终位于 System Message,但“当前用户要求优先于过期 Memory”来自 Deep Agents Memory 指引,而不是消息协议自动替应用解决冲突。

Memory 可能过期、错误或被不可信进程修改。应用应遵循:

  1. 平台安全策略不能被 Memory 覆盖;
  2. 当前请求与 Memory 冲突时,结合来源、时效和业务规则判断;
  3. Tool 和代码库验证得到的证据优先于未经验证的 Memory 事实;
  4. 高风险决策使用程序规则或 Tool 再验证,不能只依赖模型遵循 Prompt。

2.6 Memory、Skill、Tool、State 与 Store

Memory、Skill、Tool、State 与 Store 的边界

机制 解决的问题 如何进入模型 持久性
Memory 长期遵守什么 配置后加载到 System Message 由 Backend 决定
Skill 某类任务怎样完成 元数据常驻,正文按需读取 由 Skill Source 决定
Tool Agent 可以执行什么 Schema 提供能力,调用时运行 由 Tool 后端决定
State 当前线程进行到哪里 节点和 Middleware 按需使用 由 Checkpointer 决定
Store 哪些数据跨线程保存 Tool、应用或 StoreBackend 读取 由 Store 实现决定

从通用 Agent Memory 理论看,可以近似映射:Memory 文件和 Store 常承载语义记忆,Skills 承载程序性记忆,Checkpoint 历史可以成为情景记忆的数据基础。这是概念性映射,不是 Deep Agents 的固定组件定义。

3. Checkpointer 与线程记忆

3.1 Checkpointer 保存什么

Checkpointer 在图执行过程中保存 State 快照,用于同一 thread_id 的多轮连续性、中断恢复、历史状态查看和 Time Travel。它的作用域是线程,不能替代跨线程 Store。

本文使用确定性图验证恢复,不依赖模型是否“听话”:

class CounterState(TypedDict):
    value: Annotated[int, operator.add]


def increment(_: CounterState) -> CounterState:
    return {"value": 1}


def build_counter_graph():
    builder = StateGraph(CounterState)
    builder.add_node("increment", increment)
    builder.add_edge(START, "increment")
    builder.add_edge("increment", END)
    return builder.compile(checkpointer=InMemorySaver())

同一线程调用两次,再切换线程:

1
2
1

第二次 thread-a 恢复了已有 State,而 thread-b 从独立状态开始。

3.2 thread_id 是受保护的资源标识

生产系统需要保存 tenant_id、user_id 与 thread_id 的归属关系。恢复线程前必须鉴权,不能把用户提供的任意字符串直接传给 Checkpointer。

仅把租户 ID 拼进 thread_id 不是权限控制。服务端仍需验证调用者是否拥有该线程,并在删除用户数据时同步清理 Checkpoint、Backend 文件、Store 和检索索引。

3.3 本地 Saver 与生产 Checkpointer

普通 Python 本地示例应显式传入 InMemorySaver()。它只适合开发测试:进程退出即丢失,多实例之间也不共享。

生产环境应使用与 LangGraph 版本和部署方式匹配的持久 Checkpointer,例如对应版本的 PostgreSQL Integration,并评估连接池、保留周期、State 增长、加密备份和并发运行冲突。Agent Server 托管环境可能由服务端提供持久化,不应在每个请求中重新创建内存 Saver。

4. Store 与跨线程长期记忆

4.1 Namespace 是数据隔离边界

Store 保存应用定义的键值数据,不依赖某一个线程的消息:

store.put(namespace, key, value)
store.get(namespace, key)
store.search(namespace)
store.delete(namespace, key)

本文使用可信 Runtime Context 生成 Namespace:

def memory_namespace(context: UserContext) -> tuple[str, str, str, str]:
    tenant_id = context["tenant_id"].strip()
    user_id = context["user_id"].strip()
    if not tenant_id or not user_id:
        raise ValueError("tenant_id and user_id must not be empty")
    return ("tenants", tenant_id, "users", user_id)

tenant-a/alice、tenant-a/bob 和 tenant-b/alice 会进入三个不同 Namespace。Namespace 隔离仍需与服务端认证、数据库权限和审计配合,不能单独构成完整授权系统。

生产可选择与锁定 LangGraph 版本兼容的官方或生态 Store Integration,例如 PostgreSQL、Redis 或 MongoDB。安装方式、事务、TTL 和查询能力应查阅相应版本文档。

4.2 Runtime Context 不进入 Tool Schema

偏好 Tool 只允许模型提供业务值,身份由 Runtime 注入:

@tool
def save_user_preference(
    preference: str,
    runtime: ToolRuntime[UserContext],
) -> str:
    """Save a stable report preference for the authenticated user."""

    if runtime.context is None or runtime.store is None:
        raise ValueError("context and store are required")
    put_preference(runtime.store, runtime.context, preference)
    return "preference saved"

测试将 Tool 转成 OpenAI Tool Schema 后,参数中只有 preference,没有 tenant_id、user_id 和 runtime。模型不能通过生成参数选择另一个 Namespace。

4.3 文件型 Memory 与结构化 Store 记忆

AGENTS.md 文件 Memory 与结构化 Store 记忆

本文存在两种“长期记忆”,必须明确区分:

方式 数据模型 读取方式 是否自动进入 Prompt
AGENTS.md 文件 Memory Backend 文件路径与内容 MemoryMiddleware 配置 memory= 后会
结构化 Store 记忆 Namespace + Key + Value Tool 或应用代码 不会

两者可以共享同一个底层 LangGraph Store,但不会自动同步。save_user_preference 写入的结构化 Key 不会自行变成 AGENTS.md;只有 Agent 主动调用读取 Tool,或应用把值整合进 Memory 文件 / 动态 Prompt,模型才能使用它。

4.4 完整 Agent 配置与依赖注入

def build_agent(*, store: BaseStore | None = None, model=None):
    """Construct the agent without making a model request."""

    resolved_store = store if store is not None else InMemoryStore()
    resolved_model = model if model is not None else build_model()

    return create_deep_agent(
        model=resolved_model,
        tools=[save_user_preference, get_user_preference],
        backend=FilesystemBackend(root_dir=ROOT, virtual_mode=True),
        memory=MEMORY_SOURCES,
        context_schema=UserContext,
        checkpointer=InMemorySaver(),
        store=resolved_store,
    )

不能写成 store or InMemoryStore()。依赖对象是否存在应判断 is not None;否则实现了 boollen 的空 Store 可能被静默替换,跨线程数据会写进另一个实例。本文增加了一个返回 False 的 Store 子类,通过真实 Tool Call 验证传入实例没有被替换。

4.5 CompositeBackend 让 Memory 文件跨线程

文件型 Memory 要跨线程保存,可以把 /memories/ 路由到 StoreBackend,工作区仍留在 State:

def build_file_memory_backend() -> CompositeBackend:
    return CompositeBackend(
        default=StateBackend(),
        routes={
            "/memories/": StoreBackend(
                namespace=lambda runtime: file_memory_namespace(runtime.context),
            )
        },
    )

测试分别用 Alice 和 Bob 的 Runtime Context 调用真实 Agent。只在 Alice Namespace 中写入的 /memories/AGENTS.md 进入 Alice 的 System Message,不会进入 Bob 的请求。

Checkpointer、Store 与 Backend 的作用域

图中的关系可以概括为:

  • thread_id 选择 Checkpointer 中的线程 State;
  • tenant_id + user_id 选择 Store Namespace;
  • Backend 决定虚拟文件路径最终落到 State、Store、主机或 Sandbox;
  • memory= 只选择由 Backend 读取的文件路径。

5. Offloading、Summarization 与 Isolation

5.1 Tool Result Offloading

长任务最先膨胀的往往不是聊天轮次,而是大型 Tool Result、日志和文件内容。Deep Agents 文件系统中间件会把大结果写入 Backend,在消息中留下路径与预览,模型后续再用 read_file 或 grep 读取片段。

版本说明:本节的阈值、预览行数、历史路径和 Overflow 恢复行为均基于 deepagents==0.6.12 及本文锁定依赖。升级 Deep Agents、LangChain 或模型 Integration 后必须重新执行契约测试。

在该版本中,超过约 20,000 tokens 的大型 Tool Result 会被卸载,替换内容包含路径和前 10 行预览。这些数值是当前实现默认值,不是跨版本协议。

Offloading 把“每次请求重复发送的文本”变成“按地址读取的文件”,但要求 Backend 在后续步骤仍可访问、路径按用户隔离、文件生命周期明确。

5.2 Summarization 生命周期

Deep Agents 长上下文压缩、State 与历史文件

create_deep_agent 默认装配 Deep Agents Summarization Middleware。达到阈值后,当前实现会:

  1. 先尝试截断较旧消息中的大型 Tool 参数;
  2. 选择较旧有效消息作为摘要输入;
  3. 把被逐出的原始历史追加到 /conversation_history/{thread_id}.md;
  4. 生成包含任务意图、产物、完成工作和下一步的摘要;
  5. 让当前及后续模型请求使用“摘要 + 近期消息”;
  6. 用私有 _summarization_event 保存 cutoff、摘要消息和历史路径。

历史文件写入失败时,摘要仍可能继续,但被逐出上下文的细节将失去 Backend 恢复路径,因此必须监控相关 Warning。

5.3 Deep Agents 0.6.12 不会重写原始 messages

这里必须区分两个实现:

实现 摘要后对 State 的处理
Deep Agents 0.6.12 Summarization _summarization_event 记录有效视图,原始 state[“messages”] 保持完整
LangChain 基础 SummarizationMiddleware 使用 RemoveMessage 重写 State,由摘要替换旧消息

Deep Agents 当前 Summarization API Reference 明确称其为 Non-mutating message state。本文契约测试构造三条原始消息和一个摘要事件,确认:

raw state messages: [old question, old answer, recent question]
effective messages: [summary, recent question]

因此,在本文锁定版本中可以区分四份数据:

  • 模型本次看到的“摘要 + 近期消息”;
  • 当前 State 中仍保留的原始 messages;
  • State 私有 _summarization_event 中的 cutoff、摘要与路径;
  • Backend 中追加保存的原始会话日志。

Checkpoint 是否保留每个历史版本、保存多久,仍由 Checkpointer 实现和保留策略决定。不要把 Deep Agents 的非破坏实现泛化到所有 LangChain Summarization Middleware。

5.4 Profile、阈值与 Overflow 恢复

以下代码调用内部默认计算函数,只用于版本审计,不应成为业务逻辑依赖:

def summarization_policy(max_input_tokens: int | None) -> dict[str, object]:
    model = ChatOpenAI(model="offline-inspection", api_key="test-key")
    if max_input_tokens is not None:
        model.profile = {"max_input_tokens": max_input_tokens}
    return dict(compute_summarization_defaults(model))

0.6.12 实测结果:

Profile Trigger Keep Tool 参数策略
有 max_input_tokens 窗口的 85% 近期 10% 85% / 10%
无 Profile 170,000 tokens 6 条消息 20 条 / 20 条

32K 或 64K 模型若没有正确 Profile,170K 回退显然太晚。自定义模型 Integration 应提供真实 max_input_tokens,或显式设置更保守的摘要策略。

如果 Provider 抛出框架识别的标准 ContextOverflowError,0.6.12 会进入摘要路径并用有效摘要重试。若 Provider 使用自定义异常、单条消息本身不可裁剪,或 System Prompt 与 Tool Schema 已经超限,回退仍可能失败。

5.5 Streaming 只过滤 UI,不删除 Trace

官方 0.6.12 示例使用 LangGraph v2 messages 事件,并通过 lc_source == “summarization” 识别摘要 Token。前端代码还应防御事件类型和数据形状变化:

def visible_message_chunks(events):
    for event in events:
        if not isinstance(event, dict) or event.get("type") != "messages":
            continue
        data = event.get("data")
        if not isinstance(data, (tuple, list)) or len(data) != 2:
            continue
        token, metadata = data
        if not isinstance(token, BaseMessage) or not isinstance(metadata, dict):
            continue
        if metadata.get("lc_source") == "summarization":
            continue
        yield token, metadata

这段适配器经过合成 v2 事件测试。它只控制 UI 展示,原始事件仍应保留在 Trace 中,用于统计摘要成本和评估摘要质量。升级 LangGraph 后应使用真实 Stream 事件重新验证结构。

5.6 SubAgent Isolation 是推荐模式,不是输出契约

SubAgent 的中间 Tool Call 和消息通常隔离在独立上下文,主 Agent 接收 SubAgent 的最终结果。最终结果可能是长文本、短摘要、错误信息、结构化 JSON 或文件路径,取决于 SubAgent Prompt、任务描述、Tool 和模型行为。

为了控制主上下文成本,可以要求 SubAgent 返回精炼结论,把大型产物写入共享 Backend 后只返回路径。这是推荐设计,不是框架强制契约。只有主、子 Agent 共享可访问的 Backend,并且 SubAgent 实际按约定写入文件时,路径才对主 Agent 有意义。

6. Prompt Cache 与 Token 预算

6.1 Prompt Cache 只优化成本

Prompt Cache 适合稳定且重复的 Prompt 前缀,可以降低 Provider 重复处理相同输入的费用和延迟。它不提供线程恢复、跨线程 Memory、权限控制和数据正确性。

create_deep_agent(cache=…) 的 cache 是 LangGraph 节点缓存接口,也不是模型 Provider Prompt Cache。两者缓存对象、生命周期和命中规则不同。

6.2 Provider 支持要分三层确认

在本文锁定版本中,create_deep_agent 会装配 Anthropic Prompt Caching Middleware,并在安装相应 AWS Integration 时尝试装配 Bedrock 缓存 Middleware。MemoryMiddleware(add_cache_control=True) 只会对原生 ChatAnthropic 的 Memory 边界添加标记,Bedrock 和 Vertex 包装器不满足该类型检查。

是否真正命中缓存必须分三层观察:

  1. Deep Agents 是否添加 Middleware 或 cache_control;
  2. LangChain 模型 Integration 是否把标记转换成 Provider 请求;
  3. Provider 是否支持、命中并返回 cache read / cache creation Token 指标。

对不受支持模型,当前 Middleware 配置采用 unsupported_model_behavior=”ignore”,但升级后的行为仍应通过 Trace 验证,不能根据模型厂商名称推断。

6.3 提高命中率但不牺牲正确性

  • 稳定内容放前面,动态内容放后面;
  • System Prompt 前缀不加入当前时间和随机 ID;
  • Tool Schema 顺序和描述保持稳定;
  • Memory 只保留真正稳定的规则;
  • Memory 更新后允许缓存自然失效;
  • 使用 Provider Usage 指标计算命中率。

缓存命中率高但上下文已经过期,不是成功的上下文工程。

6.4 Token 预算来自 Trace,不来自固定比例

可以分别观测基础 Prompt、Memory、Tool Schema、Skills 元数据、近期消息、Tool Result 和输出安全余量。不同模型、任务与 Provider 的 Tokenizer 和缓存计费不同,不应复制一套通用百分比。

如果冷启动请求已经占用窗口的大部分空间,后续再好的摘要策略也难以支撑长任务。应从第一轮真实 Trace 开始测量,并监控 Offloading、主动摘要、Overflow 回退和 Cache Token。

7. 数据治理、安全与测试

7.1 长期记忆需要完整生命周期

生产 Memory 至少应支持:

  • 查看:用户知道系统保存了什么;
  • 更正:新事实可以替换旧事实;
  • 删除:清理 Store、Checkpoint、文件、索引和备份策略覆盖的数据;
  • 来源:区分用户声明、系统推断和人工发布;
  • 时间:记录创建、更新和可选过期时间;
  • 审批:组织策略默认只读,变更经过审核;
  • 冲突:并发写入使用版本检查或合并,不静默覆盖。

后台 Consolidation 可以把零散会话提炼为短记忆,但提炼结果仍要经过规则检查,不能把模型推测直接升级为长期事实。

7.2 并发写入与安全边界

两个线程可能同时修改同一偏好。结构化 Store 可以使用版本字段与乐观锁、每个偏好独立 Key、写前重读或事件日志;高价值共享 Memory 应进入审批队列。纯 Markdown 易于人工维护,但不擅长高频并发写入。

风险 防护方式
跨租户读取 Runtime 身份、Namespace、服务端鉴权三层隔离
Memory Prompt Injection 把文件视为可验证的数据,不赋予无限信任
密钥泄露 密钥仅在 Runtime / Secret Manager,禁止进入 Memory 和 Trace
过度记忆 数据最小化、用途限制、TTL 和删除接口
错误事实长期传播 来源、更新时间、置信度和人工纠正
摘要历史泄露 Backend 加密、路径权限、Trace 脱敏

7.3 可复现版本矩阵

本文离线验收环境:

项目 版本
Python 3.12.11
deepagents 0.6.12
langchain-core 1.4.9
langgraph 1.2.9
langgraph-checkpoint 4.1.1
langchain-openai 1.3.5
操作系统 macOS 15.7.3 arm64

运行命令:

cd source/_posts/deepagent/examples

source .venv_deepagent/bin/activate
python -m compileall -q p07_memory_context
python -m ruff check p07_memory_context
pytest -o addopts='' p07_memory_context -q
PYTHONPATH=. python p07_memory_context/inspect_context.py
PYTHONPATH=. python p07_memory_context/checkpoint_demo.py

运行结果:

All checks passed!
.................                                                        [100%]
17 passed

loaded sources: ['/AGENTS.md', '/memory/project/AGENTS.md']
profile policy: {'trigger': ('fraction', 0.85), 'keep': ('fraction', 0.1), ...}
fallback policy: {'trigger': ('tokens', 170000), 'keep': ('messages', 6), ...}

1
2
1

测试覆盖 Memory 文件契约、真实 Agent Prompt 注入、HTML 注释、State 缓存、缺失文件、两个摘要 Profile、Deep Agents 非破坏消息 State、v2 Streaming 防御过滤、Checkpointer、Namespace、StoreBackend 文件 Memory、Tool Schema、假值 Store 注入和完整 Agent 构造。

7.4 真实模型跨 Thread Store 验证

下面是保存的在线结果。模型通过第三方 OpenAI-compatible Gateway 调用;qwen3.7-plus 是该端点返回的模型 ID / 网关别名,不代表所有 Qwen Provider 都支持同名模型。

项目
Endpoint 类型 第三方 OpenAI-compatible Gateway
模型 ID / 别名 qwen3.7-plus
Deep Agents 0.6.12
LangChain OpenAI 1.3.5
Store 同一进程、同一实例的 InMemoryStore
生产持久化 未验证
{
  "scene": "p07_cross_thread_memory",
  "status": "passed",
  "model": "qwen3.7-plus",
  "latency_ms": 16088,
  "save_tool": {"name": "save_user_preference", "preference": "报告先给结论"},
  "load_tool": {"name": "get_user_preference", "result": "报告先给结论"},
  "store_namespace": ["tenants", "tenant-live", "users", "alice"],
  "threads": ["p07-save", "p07-load"]
}

该结果证明:两个不同 thread_id 在同一进程、同一 Store 实例和相同 Namespace 下可以共享结构化偏好;模型确实分别调用保存和读取 Tool。它没有证明进程重启、多实例共享、数据库事务、TTL 或并发更新。

StoreBackend 隔离、假值 Store 和摘要 State 语义由 17 项离线契约测试验证,不依赖在线模型结果。

8. 总结

Deep Agents 的 Memory 不是一个变量。Model Context 决定本次模型看到什么;Runtime Context 提供可信身份和依赖;State 与 Checkpointer 恢复线程工作;Store 保存跨线程数据;Backend 决定虚拟文件的实际生命周期;AGENTS.md 把总是相关的文件内容注入 System Message。

文件型 Memory 与结构化 Store 可以共享底层存储,但不会自动同步。长任务还需要 Offloading、Summarization 和 SubAgent Isolation 控制上下文增长;Prompt Cache 只优化支持它的 Provider 成本,不能替代持久化和权限。

本文锁定版本的 Deep Agents Summarization 通过 _summarization_event 构造有效消息视图,保留原始 State messages;这与 LangChain 基础摘要中间件的 State 重写行为不同。任何依赖精确阈值、事件格式、缓存标记或 Middleware 顺序的系统,都应在升级后重新执行契约测试。

可靠的上下文工程,最终是让每条信息拥有清晰的来源、可信度、作用域、生命周期、压缩方式和删除路径。


文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
  目录