リアルタイムセッションにおけるメモリ

呼び出しのたびにあなたのことを忘れる音声は、キオスクのように感じられます。ADK-Rust はリアルタイムの agent が 記憶する ことを可能にします。つまり、話し始める前にユーザーについて学んだことを想起し、会話の途中で新しい事実を取り込むことができます。これは、MemoryServiceIntegratedRealtimeRunner に接続することで実現します。

連携する仕組みは 2 つあります。

  1. 自動コンテキスト注入 + ターン保存 — 統合レイヤーがこれを自動で行います。
  2. ツールによる agent 管理メモリ — agent は、ブリッジされた knowledge-graph 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 は関連する履歴をすでに知った状態で相手に挨拶します。
  • 各 turn ごと、完了したやり取りが書き戻されます(store_to_memory の場合)、 そのため次の session はこの session の内容を引き継ぎます。

どの MemoryService でも使えます。開発用の in-memory のものでも、下記の knowledge-graph backend でも構いません。

2. knowledge-graph backend

adk-memoryGraphMemoryService は、memory を bi-temporal knowledge graph として保存します。エンティティと関係はそれぞれ、event time(それが真実だった時点)と ingestion time(システムがそれを学習した時点)で追跡されます。つまり agent は、「ユーザーの 現在の plan は何か?」という質問に対して、以前は別の内容だったという事実に惑わされずに答えられます。古い事実は現在を上書きせず、履歴として残ります。

use adk_memory::GraphMemoryService;
use std::sync::Arc;

let kg = Arc::new(GraphMemoryService::new(/* backing store */));

それを memory_service として渡すと、上記の自動注入が graph を参照するようになります。

3. agent に memory を管理させる

最も強力なパターンは、agent に graph 自体へ書き込むための tools を与えることです。そうすれば、すべての transcript を無差別に保存するのではなく、意図的に記憶できます。adk-tool (feature graph-memory-tools) は、リアルタイム session に直接つながる 2 つを提供します:

ツールエージェントがそれを使って行うこと
RememberTool (remember)重要な事実を保存する("メールを電話より好む")。
RelateTool (relate)2つのエンティティを関連付ける("Order A-10293 → 所属する → このユーザー")。

.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()?;

次に、エージェントにそれらを使うよう指示します:

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.

Recall は トークンベース です。グラフは会話に最も関連するエンティティ/リレーションシップを返し、その範囲は max_memory_injection で制限されるため、グラフが成長してもコンテキストは小さく保たれます。

深さの選び方

  • ステートレスなデモmemory_service はありません。各セッションは新しく開始します。
  • セッションをまたぐ継続性 → メモリ内または GraphMemoryService を使い、自動的な注入/保存を行います。エージェント側の手間はゼロです。
  • 学習するエージェントGraphMemoryService + remember/relate ツールを使い、エージェントがユーザーの会話ログの山ではなく、きれいで問い合わせ可能なモデルを整理します。

MIA のコーチング例では、グラフ + ツールを使うことで、コーチがセッション間でユーザーの目標と好みを覚えています。Examples を参照してください。

次へ: Web アプリの構築 →

再開されたセッションに引き継がれるもの

IntegratedRealtimeRunner::connect は 1 つのコンテキストブロックを作成し、プロバイダーのセッションが作成される前にそれをシステム指示の先頭に追加します:

セクションソース上限
Earlier in this conversation:前回セッションのイベントIntegrationConfig::max_history_injection (デフォルト20ターン)
Relevant recalled context:MemoryService::searchIntegrationConfig::max_memory_injection (デフォルト10エントリ)

どちらも制限されます。というのも、指示はセッション作成時に一度だけ送信され、モデルのコンテキストに対してカウントされるからです。各ターンは role: text としてレンダリングされ、400文字で切り詰められ、テキストのないターンは除外されます。制限を 0 に設定すると、そのセクションは無効になります。

IntegratedRealtimeRunner::instruction() は、セッションが作成されたときの内容を返すため、引き継がれたコンテキストは推測ではなく検証できます。

runner.connect().await?;
let instruction = runner.instruction().await.unwrap_or_default();
assert!(instruction.contains("Relevant recalled context"));

重要: このコンテキストは以前に読み込まれて破棄されていました — 以前のセッションは _session に取得されて破棄され、メモリ分岐は「メモリエントリをセッションコンテキストに注入しています」とログに出力していましたが、注入は将来の拡張であると記したコメントもありました。再開されたセッションはどちらも含まない状態で始まりましたが、ログはそうではないことを示していました。