用原生记忆让 Agent 跨轮次、跨会话延续知识,或由应用接管记忆的生成与消费。
记忆让 Agent 把一次会话中获得的知识保留下来,供后续会话使用。项目的构建命令、测试目录结构、代码约定这类信息,无需在每次会话中重新探索。
Qoder Agent SDK 的记忆由生成与消费两部分组成,可分别配置:
语言可用性:TypeScript、Python、Java。
本页的能力由 TypeScript 的
native 模式下可配置的仅有两个作用域开关和结果回调。存储位置、生成提示词、加载预算等由运行时掌握,并随版本持续优化。Java 的
原生记忆有两个作用域,默认均启用。
可在单次 Query 中关闭其中一个:
native 模式要求至少启用一个作用域,同时关闭两个会抛出异常:
Python 在连接 CLI 之前以
空对象是显式的关闭开关,等同于不传
每个 root 对应一个记忆 Agent 可访问的目录:
Java 用
生成默认在每轮结束后触发。通过
回调接收本轮的
显式传入
文件按数组顺序注入,
Python 的字段为
记忆在后台运行,其结果应显式暴露给应用,不宜默认成功。有两种获取方式。
一是注册回调,
二是从消息流中读取,对应 subtype 为
Java 的结果回调返回
消费会上报
查询对象提供两个方法,用于处理后台生成带来的时序问题。
记忆选项需与运行时协商确定,因此应读回实际生效的配置,而非依赖传入值:
| 组成 | 作用 | 何时运行 |
|---|---|---|
| 生成(Generation) | 将 Agent 获得的知识写入记忆文件 | 一轮结束后,由后台记忆 Agent 执行 |
| 消费(Consumption) | 将记忆文件加载进 Agent 的上下文 | 会话初始化时,以及显式刷新时 |
memory 选项、Python 的 QoderAgentOptions.memory、Java 的 SessionOptions.Builder.memory(MemoryOptions),以及各自的运行时方法提供:TypeScript 和 Java 为 flushMemory() / refreshMemory(),Python 为 flush_memory() / refresh_memory()。Python 的配置字段使用 snake_case(如 user_scope、on_result),回调输入、结果和初始化信息中的字段保留 camelCase(如 turnIndex、writtenFiles)。
记忆仅支持本地 CLI runtime,不支持 experimental Cloud Agent runtime:TypeScript 和 Python 在启用 Cloud Agent 时传入 memory,会在连接前抛错;启用 Cloud Agent 时调用 flushMemory() / refreshMemory()(Python 为 flush_memory() / refresh_memory())也会报错。
启用原生记忆
mode: 'native'(TypeScript)/ {"mode": "native"}(Python)/ MemoryOptions.nativeBuilder()(Java)将全部行为交由运行时的默认策略处理,建议作为起点使用:由运行时判断哪些内容值得记录、存放在何处、何时加载。
MemoryOptions Builder 另有 drainTimeoutMs(long),限定显式 flush 和正常关闭时等待记忆处理的时长,默认 60000 毫秒。
选择记忆作用域
原生记忆有两个作用域,默认均启用。
| 作用域 | 根目录 id | 存放内容 |
|---|---|---|
| 用户级 | user | 跟随使用者、跨项目通用的知识 |
| 项目级 | project | 仅属于当前工作目录的知识 |
TypeError 拒绝,报错为 native memory requires at least one enabled scope; use memory={} to disable。Java 在 MemoryOptions 的 build() 时抛出 IllegalArgumentException,不会启动 CLI。
关闭记忆
空对象是显式的关闭开关,等同于不传 memory,不会向运行时下发任何记忆配置。Python 传 memory={} 或省略 memory。Java 用 MemoryOptions.disabled(),它也是默认值;memory(null) 抛出 NullPointerException。
覆盖生成与消费
mode: 'custom'(TypeScript)/ {"mode": "custom"}(Python)/ MemoryOptions.customBuilder()(Java)仅覆盖显式给出的部分,其余沿用运行时默认行为。当记忆的存放位置或记录标准需要由应用决定时,使用该模式。
控制写入什么
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
id | string | — | 稳定标识,用于提示词、初始化和生成结果 |
path | string | — | 运行 QoderCN CLI 的机器上的目录路径 |
access | 'read' | 'read-write' | 'read-write' | 记忆 Agent 是否可写入该 root |
indexFile | string | — | root 内的相对索引路径。若所有文件均为内容文件则省略 |
MemoryRoot.builder(id, path) 创建 root,access 取 MemoryRoot.Access.READ 或 READ_WRITE(默认)。indexFile 必须是 root 内的相对路径,绝对路径或含 .. 的路径会抛出 IllegalArgumentException;roots 不能为空,id 不能重复。
Python 的 root 字段为 id、path、access、index_file。index_file 必须位于 root 内,roots 不能为空,id 不能重复,不符合时以 TypeError 拒绝。
按轮决定是否写入
生成默认在每轮结束后触发。通过 shouldGenerate,应用可以否决单次生成,例如跳过无价值的轮次、按租户控制预算,或在只读评审场景中不写入记忆。
prompt 与 response,以及 sessionId、cwd 和从 1 开始的 turnIndex。返回 run: false(Java 为 MemoryGenerationGateResult.skip(reason))时不启动后台 Agent,改为产生一条 skipped 结果。
Java 的回调返回 CompletionStage<MemoryGenerationGateResult>,在客户端的 callbackExecutor 上执行;第二个参数是取消令牌,CLI 取消或回调超时后生效,回调应检查它并停止不再需要的工作。onGateError 对应 OnGateError.SKIP / REPORT_FAILED。
Python 的闸门 should_generate(turn, context) 可以是同步或异步函数。turn 是本轮输入,字段保留 camelCase(如 turnIndex);CLI 取消请求时,context["signal"](asyncio.Event)被设置。返回值必须是带布尔值 run 的 dict,否则向 CLI 报告错误。对应的配置字段为 should_generate、timeout_ms、on_gate_error。
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled | boolean | true | 是否启用轮次结束生成 |
shouldGenerate | 回调 | — | 应用侧闸门。省略时仅使用运行时内置的保护规则 |
timeoutMs | number | 10000 | 闸门回调的最长耗时 |
onGateError | 'skip' | 'report_failed' | 'skip' | 回调报错或超时的上报方式 |
控制加载什么
显式传入 files 会在本次 Query 中替换原生自动记忆,静态指令仍照常加载。
id 作为注入内容的小节名。
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled | boolean | true | 是否加载记忆 |
files | MemoryConsumptionFile[] | — | 显式文件列表,会替换原生自动记忆 |
maxTokens | number | — | 所有显式文件共享的 token 预算 |
overflow | 'truncate' | 'fail_query' | 'truncate' | 内容超出 maxTokens 时的处理方式 |
failureMode | 'best_effort' | 'fail_query' | 'best_effort' | 读取失败时的处理方式。仅标记 required 的文件会导致 Query 失败 |
files、max_tokens、overflow、failure_mode,取值与上表相同。
Java 的 overflow 和 failureMode 分别取 MemoryConsumptionOptions.Overflow 和 MemoryConsumptionOptions.FailureMode 的枚举值。files 不能为空,id 不能重复。
查看记忆的执行结果
记忆在后台运行,其结果应显式暴露给应用,不宜默认成功。有两种获取方式。
一是注册回调,native 和 custom 模式均支持:
memory_generation 和 memory_consumption 的 system 消息。Python 中是 MemoryGenerationMessage 和 MemoryConsumptionMessage(SystemMessage 的子类),结果在 .result 中。Java 中是 MemoryGenerationMessage 和 MemoryConsumptionMessage,用 result() 取得类型化结果:
CompletionStage<Void>,在 callbackExecutor 上执行。回调抛出异常不会影响消息流中的结果消息,也不会终止会话。
Python 的结果回调可以是同步或异步函数;回调抛出的异常只记录到日志,不影响消息流。
生成会上报以下五种结果之一:
| 状态 | 含义 |
|---|---|
saved | 所有待写文件均写入成功 |
partial | 至少一个文件写入成功、一个失败,需检查 failedFiles |
no_change | 已执行,但未发现需要记录的新内容 |
skipped | 未执行,例如闸门返回了 run: false |
failed | 已执行,但没有任何文件写入成功 |
success、partial 或 failed,并给出每个文件的状态:loaded、missing、failed 或 truncated。missing 不属于错误,首次运行时记忆文件可能尚未创建。
Java 用枚举表示这些状态:MemoryGenerationResult.Status、MemoryConsumptionResult.Status 和 MemoryConsumptionResult.FileStatus,取值为上表的大写形式(如 SAVED、NO_CHANGE)。CLI 返回无法识别的状态时为 UNKNOWN,原始值可从 wireStatus() 读取。
在运行时控制记忆
查询对象提供两个方法,用于处理后台生成带来的时序问题。
flushMemory() 在 CI 和测试场景中尤为重要:若进程在收到最终 result 消息后立即退出,可能中断正在进行的写入。
Java 的 QoderQuery 和 QoderSession 都提供这两个方法,它们会自行等待初始化完成:
flushMemory()等待 CLI 已触发的记忆处理结束,上限为drainTimeoutMs。完成不代表每个文件都写入成功,仍需检查生成结果。refreshMemory()按现有加载配置重新读取记忆文件,不改变配置,超时使用controlRequestTimeoutMs。- 会话没有启用 Memory 时,两个方法都以
UnsupportedCliCapabilityException失败,不发送请求。 - 一次性查询在最终回答后会等待已触发的记忆处理,期间继续投递 Memory 消息,处理完成或超时后才结束消息流。收到
ResultMessage不代表消息流已经结束,应读到流结束。
QoderSDKClient 上提供 flush_memory() 和 refresh_memory():
- 并发调用
flush_memory()共用同一次请求。 - 未使用
daemon选项时,单次query()在结束输入前自动 flush,客户端在disconnect()或退出async with时自动 flush。自动 flush 失败只记录日志,关闭照常进行;需要确认成功时显式调用flush_memory()。 - 使用
daemon选项时,如果 CLI 声明了daemon_async_session_teardown_v1,单次query()结束和客户端关闭时都不自动 flush,关闭完成不代表记忆已经写完。关闭后要立即校验记忆文件或启动下一个会话时,改用QoderSDKClient,在关闭前调用await client.flush_memory()并检查生成结果。 - CLI 没有为 SDK 启用记忆时,两个方法抛出
RuntimeError,不发送请求。
校验实际生效的配置
记忆选项需与运行时协商确定,因此应读回实际生效的配置,而非依赖传入值:
memory 省略或设为 {} 时,init.memory 为 undefined。Python 从 get_server_info() 返回值的 memory 读取,字段保留 camelCase。Java 未启用 Memory 时 memory() 返回 Optional.empty()。
Java 显式启用 Memory 后,CLI 必须在初始化时确认已为 SDK 启用记忆,SDK 才发送首条输入;CLI 未确认时,初始化以 UnsupportedCliCapabilityException 失败。