openai-agents-python 沙箱智能体内存(Sandbox Agent Memory):跨运行学习、两阶段记忆生成与多智能体隔离实战指南
2026/9/11 20:20:04 网站建设 项目流程

openai-agents-python 沙箱智能体内存(Sandbox Agent Memory):跨运行学习、两阶段记忆生成与多智能体隔离实战指南

【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python

本文围绕 openai-agents-python 沙箱智能体的Memory()能力展开,系统讲解它如何把一次运行中沉淀的经验(修复的 bug、用户的偏好、任务的失败教训)蒸馏成工作区文件,供后续运行复用,从而降低智能体成本、用户成本与上下文成本。读完本文,你将掌握内存的启用与依赖约束、渐进式公开的读取机制、两阶段内存生成管线、MemoryGenerateConfig/MemoryLayoutConfig的完整配置方法,以及如何通过多轮对话与布局隔离在多智能体场景下正确组织记忆。

说明:本文基于 docs/sandbox/memory.md(含韩文版 docs/ko/sandbox/memory.md、中文版 docs/zh/sandbox/memory.md)撰写,并以仓库源码 src/agents/sandbox/memory/ 与 examples/sandbox/ 作为实现层面的佐证。沙箱智能体目前仍处于 Beta 阶段,API 细节、默认值与支持的能力在正式发布前可能发生变化。

一、什么是沙箱智能体内存:与对话式 Session 记忆的本质区别

在 openai-agents-python 中,内存(memory)让未来的沙箱智能体运行能够从先前的运行中学习。它独立于 SDK 的对话式Session记忆——后者只负责存储消息历史;而沙箱内存把先前运行得到的经验蒸馏(distill)成沙箱工作区中的文件,成为跨运行、可持久复用的"工作记忆"。

从源码结构看,内存能力位于 src/agents/sandbox/capabilities/memory.py,其底层读写、两阶段生成与目录管理分别由 src/agents/sandbox/memory/ 下的manager.pyphase_one.pyphase_two.pystorage.pyprompts.py协作完成。

内存降低的三类成本

  1. 智能体成本(Agent cost):若智能体上次花很长时间才完成某个工作流,下一次运行需要更少的探索,从而降低 token 消耗与完成时间;
  2. 用户成本(User cost):若用户纠正过智能体或表达了偏好,未来运行能记住这些反馈,减少人工干预;
  3. 上下文成本(Context cost):若智能体此前已完成某项任务、用户想在此基础上继续,无需重新查找旧线程或重输全部上下文,任务描述可以更短。

权威示例入口

  • 完整的两运行示例(修复 bug → 生成内存 → 恢复快照 → 后续验证运行使用该内存):examples/sandbox/memory.py;
  • 采用独立内存布局的多轮、多智能体示例:examples/sandbox/memory_multi_agent_multiturn.py。

二、启用内存:Memory()能力与依赖约束

Memory()作为一项能力(capability)添加到SandboxAgent即可启用:

from pathlib import Path import tempfile from agents.sandbox import LocalSnapshotSpec, SandboxAgent from agents.sandbox.capabilities import Filesystem, Memory, Shell agent = SandboxAgent( name="Memory-enabled reviewer", instructions="Inspect the workspace and preserve useful lessons for follow-up runs.", capabilities=[Memory(), Filesystem(), Shell()], ) with tempfile.TemporaryDirectory(prefix="sandbox-memory-example-") as snapshot_dir: sandbox = await client.create( manifest=manifest, snapshot=LocalSnapshotSpec(base_path=Path(snapshot_dir)), )

能力依赖规则(源码可验证)

Memory类的required_capability_types()在 src/agents/sandbox/capabilities/memory.py 中明确定义了依赖关系:

  • 只要启用了读取(readNone),就必须有Shell():注入的摘要不足以回答问题时,智能体需要读取和检索内存文件;
  • 当实时更新(live update)开启时(默认开启),还必须搭配Filesystem():智能体发现陈旧内存或用户要求更新内存时,需要有能力改写memories/MEMORY.md

内存制品存储位置与复用前提

默认情况下,内存制品存放在沙箱工作区的memories/目录下。要在后续运行中复用它们,必须保留并复用完整的、配置好的内存目录,具体途径有三:保持同一个实时(live)沙箱会话;或从持久化的会话状态(session state)恢复;或从快照(snapshot)恢复。一个全新创建的空沙箱,内存从零开始。

按需裁剪读取与生成

Memory()默认同时启用读取生成。以下两种裁剪方式适用于不同场景:

  • Memory(generate=None):只读取、不生成新内存。适合内部智能体、子智能体、检查器(checker)或一次性工具智能体的运行——这些运行不会产生太多值得沉淀的新信号;
  • Memory(read=None):只生成、不读取。适合需要为后续运行生成内存、但本次运行不希望被既有内存影响的场景。

注意Memorymodel_post_init中会校验"readgenerate至少启用其一",两者都为None会抛出ValueError(见 src/agents/sandbox/capabilities/memory.py)。

三、内存读取:渐进式公开(Progressive Disclosure)与实时更新

内存读取采用渐进式公开策略,避免在每次运行都把全部历史塞进上下文:

  1. 运行开始时,SDK 会把一份精炼摘要memory_summary.md(通常是有用技巧、用户偏好与可用内存的概览)注入到智能体的开发者提示(developer prompt)中。这份摘要足以让智能体判断"先前的工作是否可能与当前任务相关"。
  2. 发现相关时,智能体用当前任务的关键词去检索配置的内存索引——即memories_dir下的MEMORY.md
  3. 仅当任务需要更多细节时,才打开配置的rollout_summaries/目录下对应的历史 rollout 摘要文件。

从实现看,摘要注入由 src/agents/sandbox/capabilities/memory.py 的instructions()完成:它读取memory_summary_path,用truncate_text以 token 策略截断(上限常量_MEMORY_SUMMARY_MAX_TOKENS = 15_000),再通过render_memory_read_prompt拼接读取提示;文件不存在时返回None(不注入),内容为空时不注入。

陈旧内存与 live_update

内存可能过时、与当前环境不一致。SDK 指示智能体只把内存当作参考指引,以当前环境为准。默认情况下内存读取开启了live_update

  • 智能体一旦发现陈旧内存,可以在同一轮运行中更新配置的MEMORY.md
  • 若你希望智能体只读不改(例如对延迟敏感的运行),应关闭实时更新。

提示词层面对这两种模式的约束分别定义在 src/agents/sandbox/memory/prompts.py:只读模式注入"Never update memories. You can only read them.";实时更新模式则要求智能体在检测到冲突时必须同轮完成"核实替换 → 依据当前证据继续任务 → 在本轮结束前编辑MEMORY.md"的完整动作序列。

四、内存生成:两阶段管线与默认工作区布局

一次运行结束后,沙箱运行时会把该运行片段(run segment)追加到对话文件(conversation file)中;累积的对话文件在沙箱会话关闭时统一处理。内存生成分两个阶段:

  1. 阶段一:对话提取(conversation extraction)。内存生成模型(phase-one model)处理一份累积的对话文件,生成对话摘要。系统(system)、开发者(developer)与推理(reasoning)内容会被剔除;对话过长时按上下文窗口截断,保留开头与结尾。同时产出一份"raw memory 提取物"——从对话中抽取的紧凑笔记,供阶段二整合。
  2. 阶段二:布局整合(layout consolidation)。整合智能体(consolidation agent)读取某个内存布局的全部 raw memories,需要更多证据时打开对话摘要,把其中的模式抽取到MEMORY.mdmemory_summary.md

默认工作区布局

workspace/ ├── sessions/ │ └── <rollout-id>.jsonl └── memories/ ├── memory_summary.md ├── MEMORY.md ├── raw_memories.md (intermediate) ├── phase_two_selection.json (intermediate) ├── raw_memories/ (intermediate) │ └── <rollout-id>.md ├── rollout_summaries/ │ └── <rollout-id>_<slug>.md └── skills/

该布局由SandboxMemoryStorage.ensure_layout()在 src/agents/sandbox/memory/storage.py 中实际创建:它会并行mkdirsessions_dirmemories_dirraw_memories/rollout_summaries/skills/,并确保MEMORY.mdmemory_summary.md两个空文件存在。

使用MemoryGenerateConfig配置内存生成

from agents.sandbox import MemoryGenerateConfig from agents.sandbox.capabilities import Memory memory = Memory( generate=MemoryGenerateConfig( max_raw_memories_for_consolidation=128, extra_prompt="Pay extra attention to what made the customer more satisfied or annoyed", ), )

MemoryGenerateConfig的全部字段定义在 src/agents/sandbox/config.py,各参数含义与默认值如下:

参数默认值说明
max_raw_memories_for_consolidation256阶段二整合时最多考虑的最近raw memories 数量;必须在(0, 4096]区间内,否则抛ValueError
phase_one_model"gpt-5.4-mini"阶段一单 rollout 提取使用的模型
phase_one_model_settingsModelSettings(reasoning=Reasoning(effort="medium"))阶段一模型设置,接受ModelSettings实例或其字段字典
phase_two_model"gpt-5.5"阶段二内存整合使用的模型
phase_two_model_settingsModelSettings(reasoning=Reasoning(effort="medium"))阶段二模型设置
extra_promptNone追加到提取与整合提示中的开发者自定义指引

extra_prompt的正确用法

extra_prompt告诉内存生成器:对你的用例而言,哪些信号最重要。例如对 GTM 智能体而言,客户与公司细节是关键。源码 src/agents/sandbox/memory/prompts.py 会把extra_prompt包裹进DEVELOPER-SPECIFIC EXTRA GUIDANCE区块,插入到阶段一提取提示与阶段二整合提示中。官方建议(见 src/agents/sandbox/config.py 与 examples/sandbox/memory.py 注释)保持简短:最好几条聚焦的要点,总长度远低于约 5k tokens——阶段一模型本就要在同一上下文窗口中处理大量内置提示与被截断的对话,过大的附加提示会挤占真正需要总结的证据空间。

遗忘机制(Forgetting)

当最近的 raw memories 数量超过max_raw_memories_for_consolidation时,阶段二只保留最新对话中的内存,移除更旧的。"最新"以对话最后一次更新的时间(updated_at)为准。这一遗忘机制确保内存始终反映最新的环境。实现上,storage.py 的build_phase_two_input_selection()会扫描raw_memories/目录,按updated_at排序(无时间戳的条目排最后),截取前 N 条作为本次整合输入,并将淘汰列表写入phase_two_selection.json

五、多轮对话:让多次Runner.run汇聚成一条内存对话

多轮沙箱对话的正确姿势是:使用常规 SDKSession+ 同一个实时沙箱会话

from agents import Runner, SQLiteSession from agents.run import RunConfig from agents.sandbox import SandboxRunConfig conversation_session = SQLiteSession("gtm-q2-pipeline-review") sandbox = await client.create(manifest=agent.default_manifest) async with sandbox: run_config = RunConfig( sandbox=SandboxRunConfig(session=sandbox), workflow_name="GTM memory example", ) await Runner.run( agent, "Analyze data/leads.csv and identify one promising GTM segment.", session=conversation_session, run_config=run_config, ) await Runner.run( agent, "Using that analysis, write a short outreach hypothesis.", session=conversation_session, run_config=run_config, )

两次运行传入同一个 SDK 对话会话(session=conversation_session),因此共享同一个session.session_id,两次运行会追加到同一条内存对话文件。这不同于沙箱(sandbox)本身——沙箱只标识实时工作区,不会被用作内存对话 ID。由于阶段一在沙箱会话关闭时才审视累积的对话,内存得以从整段交流中提取,而不是把两次孤立轮次分开处理。

内存对话 ID 的解析顺序

若希望多个Runner.run(...)调用合并为一条内存对话,请在多次调用间传递一个稳定标识符。内存把一次运行关联到对话时,按以下顺序解析:

  1. 传入Runner.run(...)conversation_id
  2. 传入 SDKSession(如SQLiteSession)时的session.session_id
  3. 以上都没有时,使用RunConfig.group_id
  4. 仍无稳定标识符时,为每次运行生成独立的 per-run ID。

从管理器实现看(src/agents/sandbox/memory/manager.py),每次运行的结果会被序列化并以<rollout-id>.jsonl的形式追加写入sessions_dir;rollout ID 必须匹配^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$的文件安全格式(manager.py)。

六、多智能体内存隔离:用MemoryLayoutConfig而非智能体名

内存隔离的依据是MemoryLayoutConfig,而不是智能体名称

  • 拥有相同布局 + 相同内存对话 ID的智能体,共享一条内存对话与一份整合后的内存;
  • 布局不同的智能体,即使共用同一个沙箱工作区,也会各自维护独立的 rollout 文件、raw memories、MEMORY.mdmemory_summary.md

当多个智能体共享一个沙箱、但内存必须互不干扰时,为它们配置不同布局:

from agents import SQLiteSession from agents.sandbox import MemoryLayoutConfig, SandboxAgent from agents.sandbox.capabilities import Filesystem, Memory, Shell gtm_agent = SandboxAgent( name="GTM reviewer", instructions="Analyze GTM workspace data and write concise recommendations.", capabilities=[ Memory( layout=MemoryLayoutConfig( memories_dir="memories/gtm", sessions_dir="sessions/gtm", ) ), Filesystem(), Shell(), ], ) engineering_agent = SandboxAgent( name="Engineering reviewer", instructions="Inspect engineering workspaces and summarize fixes and risks.", capabilities=[ Memory( layout=MemoryLayoutConfig( memories_dir="memories/engineering", sessions_dir="sessions/engineering", ) ), Filesystem(), Shell(), ], ) gtm_session = SQLiteSession("gtm-q2-pipeline-review") engineering_session = SQLiteSession("eng-invoice-test-fix")

MemoryLayoutConfig只有两个字段(src/agents/sandbox/config.py):

字段默认值说明
memories_dir"memories"整合后内存文件所在目录
sessions_dir"sessions"每个 rollout 的 JSONL 制品所在目录

路径校验同样在 src/agents/sandbox/capabilities/memory.py:memories_dir/sessions_dir必须是相对于沙箱工作区根目录的相对路径——绝对路径、包含..逃逸、或空路径都会抛ValueError

这样配置后,GTM 分析不会被整合进工程 bug 修复的内存中,反之亦然。布局级的管理器复用逻辑见 src/agents/sandbox/memory/manager.py:同一沙箱会话内,相同(memories_dir, sessions_dir)的生成管理器会被复用;若同一memories_dirsessions_dir已被占用但配置不同,会抛出UserError提示改用不同目录或相同布局。

七、源码级实现解析:从运行结束到内存落盘

结合 src/agents/sandbox/memory/ 目录,可以把"运行 → 内存"的完整链路串起来:

  1. 运行结果入队:每次Runner.run结束后,SandboxMemoryGenerationManager.enqueue_result()把结果序列化为 rollout payload,追加写入sessions/<rollout-id>.jsonl(manager.py);
  2. 会话关闭触发处理:沙箱会话关闭时触发flush()(该管理器在初始化时通过session.register_pre_stop_hook(self.flush)注册了 pre-stop 钩子,见 manager.py),把所有 rollout 文件排入队列并等待 worker 处理完毕;
  3. 阶段一逐 rollout 提取:worker 为每个 rollout 渲染阶段一提示(phase_one.py 会把 terminal 元数据整理成 JSON,并用TruncationPolicy.tokens(150_000)截断 rollout 内容、在截断时插入明显的省略标记),调用 phase-one 模型产出rollout_slugrollout_summaryraw_memory,三者皆为空则跳过(validate_rollout_artifacts);随后写入memories/raw_memories/<rollout-id>.mdmemories/rollout_summaries/<rollout-id>_<slug>.md
  4. 阶段二整合:所有 rollout 处理完后,_run_phase_two()基于max_raw_memories_for_consolidation构建输入选择、重建聚合文件raw_memories.md,然后调用阶段二整合智能体(max_turns=500,见 phase_two.py)把模式写入MEMORY.mdmemory_summary.md,最后持久化phase_two_selection.json

这些行为均有测试覆盖,例如 tests/sandbox/test_memory.py(约 1950 行)对 rollout 文件命名、阶段一提示渲染、内存生成管理器注册与布局冲突等进行了系统验证。

八、完整两运行实战:修复 bug → 生成内存 → 快照恢复后复用

examples/sandbox/memory.py 演示了内存的端到端价值,核心流程如下:

  • 构建工作区清单Manifest中放入一个带 bug 的src/acme_metrics/report.pyformat_invoice_total把税率当加数直接相加)、pyproject.toml、README 与一个测试;
  • 构建带内存的智能体SandboxAgentcapabilities=[Memory(), Filesystem(), Shell()],其中Memory()同时启用读写与实时更新;
  • 第 1 次运行:提示词为"检查工作区并修复src/acme_metrics/report.py中的发票总额 bug",在async with sandbox:块内执行,RunConfig(sandbox=SandboxRunConfig(session=sandbox), workflow_name=...);会话退出时后台生成内存制品;
  • 快照恢复resumed_sandbox = await client.resume(sandbox.state),用同一个本地快照目录开启新沙箱会话——确保第 2 次运行依赖的是落盘的内存而非进程内状态;
  • 第 2 次运行:提示词为"为之前修复的 bug 添加回归测试",智能体通过读取第 1 次运行沉淀的内存(bug 原因、修复方式)直接完成任务;
  • 打印内存树:脚本末尾输出sessions/memories/MEMORY.mdmemory_summary.mdraw_memories.mdraw_memories/rollout_summaries/的实际生成情况,便于直观核对默认布局。

该示例的注释还给出调优建议:read.live_update=False可省去运行中修复陈旧内存的耗时,但陈旧内存会累积到下次整合;generate.extra_prompt应保持简短。运行方式:python examples/sandbox/memory.py --model <model>(默认模型gpt-5.6-sol,实际以你的环境可用模型为准)。

九、注意事项与适用前提

  • Beta 功能:沙箱智能体处于 Beta 阶段,API 细节、默认值与支持能力在正式发布前可能调整;
  • 依赖完整性:启用读取时必须配置Shell();开启实时更新(默认)时还必须配置Filesystem(),否则能力校验无法通过;
  • 内存复用前提:只有保持同一 live 沙箱会话,或从持久化会话状态/快照恢复,才能复用已生成的内存目录;全新空沙箱内存为空;
  • 路径安全memories_dirsessions_dir必须是工作区内的相对路径,禁止绝对路径与..逃逸;
  • 模型与 token 约束:阶段一有 150k token 的 rollout 截断上限(保留头尾、插入省略标记),extra_prompt建议控制在约 5k tokens 以内,避免挤占对话证据的上下文空间;
  • 记忆的时效性:内存只是指引而非真理,智能体被明确要求以当前工作区证据为准;live_update与阶段二的遗忘机制共同保证内存持续跟随最新环境。

【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询