实时会话中的记忆
如果一个声音在每次通话之间都会忘记你,它就像一个自助终端。ADK-Rust 让实时 agent 能够记住——在说话之前回想它之前从用户那里学到的内容,并在对话进行中整理新的事实——方法是将一个 MemoryService 接入 IntegratedRealtimeRunner。
有两种协同工作的机制:
- 自动上下文注入 + 轮次存储——集成层会替你完成。
- 由 agent 通过 tools 策划的记忆——agent 使用桥接的知识图谱 tools 决定哪些内容值得保留。
1. 自动注入与存储
附加一个 MemoryService,runner 就会处理往返流程:
let runner = IntegratedRealtimeRunner::builder()
.model(model)
.config(config)
.identity("support", &user_id, &session_id)
.session_service(sessions)
.memory_service(memory.clone())
.integration_config(IntegrationConfig {
inject_memory_context: true, // query memory at connect
max_memory_injection: 10, // cap injected items
store_to_memory: true, // write each completed turn back
persist_transcripts: true,
})
.build()?;
- 在连接时,会为该用户查询 memory,并将结果折叠进 session context,这样 agent 在打招呼时就已经知道相关历史。
- 每一轮,已完成的交互会写回(当
store_to_memory时),因此下一次 session 会在这一次的基础上继续。
任何 MemoryService 都可以——开发时可用内存中的那个,或者下面的知识图谱后端。
2. 知识图谱后端
adk-memory 的 GraphMemoryService 将 memory 存储为一个 双时间知识图谱:实体和关系都会分别跟踪 事件时间(它何时为真)和 摄取时间(系统何时得知它)。这意味着 agent 可以回答“用户当前的计划是什么?”而不会被它曾经是别的内容这一事实干扰——被取代的事实会保留在历史中,而不是覆盖当前状态。
use adk_memory::GraphMemoryService;
use std::sync::Arc;
let kg = Arc::new(GraphMemoryService::new(/* backing store */));
将其作为 memory_service 传入后,上面的自动注入现在就会基于该图谱。
3. 让 agent 策划记忆
最强大的模式:给 agent 提供用于直接写入图谱的 tools,这样它就会有意地记住内容,而不是把每段转录都倾倒进去。adk-tool(feature graph-memory-tools)提供了两个可直接桥接到实时 session 的工具:
| 工具 | 代理使用它做什么 |
|---|---|
RememberTool (remember) | 存储一个显著事实("更偏好电子邮件而不是电话")。 |
RelateTool (relate) | 连接两个实体("Order A-10293 → belongs-to → this user")。 |
通过 .adk_tool(...) 将它们连接起来(见 Tools):
use adk_tool::{RememberTool, RelateTool};
let runner = IntegratedRealtimeRunner::builder()
.model(model).config(config)
.identity("support", &user_id, &session_id)
.memory_service(kg.clone())
.adk_tool(Arc::new(RememberTool::new(kg.clone())))
.adk_tool(Arc::new(RelateTool::new(kg)))
.build()?;
然后指示 agent 使用它们:
When you learn a durable fact about the customer (a preference, an account
detail, a decision), call `remember` to store it. Use `relate` to link orders
or items to the customer. Recall naturally — don't announce that you're saving.
召回是基于 token 的:图会返回与对话最相关的实体/关系,并受 max_memory_injection 约束,因此即使图不断增长,context 也能保持很小。
选择深度
- 无状态演示 → 没有
memory_service。每个 session 都会从头开始。 - 跨 session 的连续性 → 内存中或
GraphMemoryService,并带有自动注入/存储。无需 agent 额外工作。 - 学习型 agent →
GraphMemoryService+remember/relate工具,这样 agent 维护的是一个干净、可查询的用户模型,而不是一堆 transcript。
MIA coaching 示例使用 graph + tools,因此 coach 可以在 sessions 之间记住用户的目标和偏好;参见 Examples。
下一步:构建 web apps →
恢复的 session 中会带入什么
IntegratedRealtimeRunner::connect 会构建一个 context block,并在 provider session 创建之前将其前置到 system instruction 中:
| 章节 | 来源 | 限制 |
|---|---|---|
Earlier in this conversation: | 上一会话的事件 | IntegrationConfig::max_history_injection(默认 20 轮) |
Relevant recalled context: | MemoryService::search | IntegrationConfig::max_memory_injection(默认 10 条目) |
两者都受限制,因为该指令在会话创建时发送一次,并计入模型的上下文。每一轮都会被渲染为 role: text,并在 400 个字符处截断,且没有文本的轮次会被丢弃。将限制设为零会禁用该部分。
IntegratedRealtimeRunner::instruction() 返回会话创建时所使用的内容,因此可以直接断言传递的上下文,而不是推断它:
runner.connect().await?;
let instruction = runner.instruction().await.unwrap_or_default();
assert!(instruction.contains("Relevant recalled context"));
重要: 这段上下文之前已被加载并丢弃 —— 先前的会话被获取到
_session后又丢弃,而且内存分支在一条注释旁记录了“将内存条目注入会话上下文”,该注释说明注入是未来的增强功能。恢复的会话一开始两者都没有,而日志却说相反的话。