セッション

ADK-Rust におけるセッションは、会話コンテキストの管理を提供し、エージェントが複数のやり取りにわたって状態を維持できるようにします。セッションは、会話履歴(イベント)と、会話全体を通じて保持される任意の状態データを保存します。

概要

セッションは、ユーザーとエージェントの間の 1 回の会話を表します。各セッションは次の特性を持ちます。

  • 一意の識別子を持つ
  • アプリケーション(app_name)とユーザー(user_id)に属する
  • イベントの一覧(会話履歴)を含む
  • 状態データ(キーと値のペア)を保持する
  • 最終更新時刻を追跡する

セッショントレイト

Session トレイトは、セッションオブジェクトのインターフェースを定義します。

use adk_session::{Events, State};
use chrono::{DateTime, Utc};

pub trait Session: Send + Sync {
    /// Unique session identifier
    fn id(&self) -> &str;
    
    /// Application name this session belongs to
    fn app_name(&self) -> &str;
    
    /// User identifier
    fn user_id(&self) -> &str;
    
    /// Access session state
    fn state(&self) -> &dyn State;
    
    /// Access conversation events
    fn events(&self) -> &dyn Events;
    
    /// Last time the session was updated
    fn last_update_time(&self) -> DateTime<Utc>;
}

SessionService トレイト

SessionService トレイトは、セッションを管理するための操作を定義します。

use adk_session::{CreateRequest, GetRequest, ListRequest, DeleteRequest, Event, Session};
use adk_core::Result;
use async_trait::async_trait;

#[async_trait]
pub trait SessionService: Send + Sync {
    /// Create a new session
    async fn create(&self, req: CreateRequest) -> Result<Box<dyn Session>>;
    
    /// Retrieve an existing session
    async fn get(&self, req: GetRequest) -> Result<Box<dyn Session>>;
    
    /// List all sessions for an app/user
    async fn list(&self, req: ListRequest) -> Result<Vec<Box<dyn Session>>>;
    
    /// Delete a session
    async fn delete(&self, req: DeleteRequest) -> Result<()>;
    
    /// Append an event to a session
    async fn append_event(&self, session_id: &str, event: Event) -> Result<()>;
}

リクエストタイプ

CreateRequest

use adk_session::CreateRequest;
use std::collections::HashMap;

let request = CreateRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: None,  // Auto-generate UUID if None
    state: HashMap::new(),  // Initial state
};

GetRequest

use adk_session::GetRequest;

let request = GetRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_abc".to_string(),
    num_recent_events: Some(10),  // Limit events returned
    after: None,  // Filter events after timestamp
};

ListRequest

use adk_session::ListRequest;

let request = ListRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
};

DeleteRequest

use adk_session::DeleteRequest;

let request = DeleteRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_abc".to_string(),
};

SessionService の実装

バックエンドの選択

Rendering architecture…

ADK-Rust は複数のセッションサービス実装を提供します。

実装フィーチャーフラグユースケース
InMemorySessionService(none)開発、テスト、単一インスタンス
SqliteSessionServicesqlite単一ノードの永続化
PostgresSessionServicepostgres本番用リレーショナル永続化
RedisSessionServiceredis低レイテンシのインメモリ永続化
MongoSessionServicemongodbドキュメント指向の永続化
Neo4jSessionServiceneo4jグラフデータベース永続化
FirestoreSessionServicefirestoreGoogle Cloud Firestore
VertexAiSessionServicevertex-sessionVertex AI セッション API

vertex-sessionadk-rust アンブレラクレートによって再エクスポートされます:

[dependencies]
adk-rust = { version = "2.0.0", features = ["vertex-session"] }

VertexAiSessionService

VertexAiSessionService は GA v1 Vertex AI Agent Engine Session API を通じてセッションを永続化します。Application Default Credentials を使用し、 数値の reasoning-engine ID またはその完全なリソース名のいずれかを受け入れます:

use adk_session::{VertexAiSessionConfig, VertexAiSessionService};

let config = VertexAiSessionConfig::new("my-project", "us-central1")
    .with_reasoning_engine("1234567890");
let service = VertexAiSessionService::new_with_adc(config)?;

識別の分離

公開セッション ID は常に CreateRequest に渡された ID、または ID が渡されない場合にローカルで 生成された UUID です。バックエンドは完全な (app_name, user_id, session_id) タプルから Vertex リソース ID を導出し、バージョン付きの識別子マーカーをセッション状態に永続化します。

プロパティ動作
論理 IDcreate、get、list、append、delete 全体で保持される
リモート ID完全な識別情報から導出される決定論的な 63 文字の adk1-… ID
識別子マーカー保護された状態として保存され、公開セッション状態から削除される
共有推論エンジン同じ論理 ID を持つセッションは、アプリとユーザーごとに分離されたままになる
予約済み状態create state と event state の差分では識別子マーカーを設定できない

バックエンドは、すべての ID スコープ付き操作の前に、マーカーと計算された resource ID の両方を検証します。計算された resource にマーカーが存在しない、または一致しない場合は、別のテナントの session ではなく、破損として扱われます。

旧 session の移行

Python ADK および pre-v2 の session は、logical ID を直接使用し、identity marker を省略する場合があります。互換性は、reasoning-engine の parent をどのように選択するかに依存します:

設定未マーキングのセッションの動作
固定されていない reasoning engineapp_name が先頭ゼロなしの正規化された非ゼロの数値 reasoning-engine ID である場合にのみ有効
固定された reasoning engineデフォルトでは無効
固定エンジンと allow_unmarked_sessions_for_app("app")そのアプリに対してのみ有効
use adk_session::{VertexAiSessionConfig, VertexAiSessionService};

let config = VertexAiSessionConfig::new("my-project", "us-central1")
    .with_reasoning_engine("1234567890");
let service = VertexAiSessionService::new_with_adc(config)?
    .allow_unmarked_sessions_for_app("legacy-app");

重要: マークのない session は app の所有権を証明しません。固定エンジン互換オプションは、指定された app がその reasoning engine 内の legacy sessions を排他的に所有している場合にのみ有効にしてください。厳密な user_id の一致は引き続き適用されます。マーク付きの direct resources、予約済みの adk1- namespace、そして computed/direct の曖昧性は fail closed します。

legacy の append_event(session_id, event) API でも、ちょうど 1 つの cached app/user scope が必要です。まず create、get、または list を呼び出すか、append_event_for_identity() を直接使用してください。cache は bounded なので、長時間稼働する process は、scope が evict された後に古い session を再度 get するか list する必要があります。

エンドポイントと event の忠実性

場所デフォルトエンドポイント
globalhttps://aiplatform.googleapis.com
ushttps://aiplatform.us.rep.googleapis.com
euhttps://aiplatform.eu.rep.googleapis.com
リージョナルロケーションhttps://LOCATION-aiplatform.googleapis.com

カスタムエンドポイントは、Google の認可ヘッダーと完全なセッション/イベントペイロードを受け取るため、信頼されたオリジンでなければなりません。コンストラクションは、エンドポイントが userinfo、パス、クエリ、またはフラグメントを含まない HTTPS オリジンでない限り失敗します。テスト用にはループバック HTTP が許可されます。リダイレクトは無効です。

本番環境の境界

境界デフォルト
Vertex user_id最大 128 Unicode スカラー値
接続の期限10 秒
Credential-header の期限30 seconds
HTTP request の期限120 seconds
長時間実行オペレーションのポーリング120 seconds
エンコードされた request bodycreate/append request ごとに 64 MiB
デコードされた応答本文1 回の応答あたりおよび 1 回のページ分割リスト操作全体で 64 MiB
ページネーション経過時間完全なリスト操作で 120 秒
ページトークン64 KiB
JSON と Vertex Struct のネスト64 レベル

with_max_request_bytes() は送信ボディのバジェットを変更し、 with_max_response_bytes() は両方の受信ボディのバジェットを変更し、 with_pagination_timeout() はリスト全体の期限を変更します。高いボディ上限は デフォルトの割り当て保護を弱めます。これらの上限は、 並行呼び出し全体に対してではなく、各 request、response、または list operation ごとに独立して適用されます。 これらはバイト単位のバジェットであり、JSON のパースと base64 デコード後の プロセス全体のメモリ上限ではありません。 with_credentials() は失敗する可能性があります。これは endpoint を検証し、制限付きで redirect 無効の HTTP client を構築するためです。

recent-event の上限と timestamp の下限は events.list に押し込まれます。 backend は recent-event の上限が満たされるまで descending ページのみを要求し、その後 ascending の時系列順に戻します。num_recent_events = 0 は event-list endpoint を呼び出しません。

create、delete、append の HTTP 408/5xx、transport/body、malformed 2xx、 または無効な successful-result の失敗は、service が mutation を commit した後に発生する可能性があります。これらは session.vertex.{create,delete,append}_outcome_ambiguousretry.should_retry = false 付きで返します。create または delete が operation name を返した後は、 transport/status の失敗や malformed または無効な successful responses を poll する際に 同じ再試行不可の曖昧性 contract を使い、operation name を含めます。 手動で再試行する前に、対象の session、operation、または event list を確認してください。 終端の done: true operation error は、その代わりに既知の失敗結果です。 それは session.vertex.operation_failed とカテゴリ由来の retry hint を保持します。

HTTP 4xx の直接的な rejection のうち 408 以外は、引き続き確定的です。429 は 再試行可能です。operation が受理された後の poll の失敗は、429 を含め、すべて 曖昧で再試行不可です。backend は内部で throttle も retry もしません。 アプリケーションレベルの concurrency と retry policy を見積もるには、現在の Vertex AI quotas を使用してください。

無視された GA v1 canary は、Application Default Credentials が設定された既存の reasoning engine に対してのみ実行してください。 これは 1 つの session と 1 つの代表的な event を create および delete します。engine を create または delete することはありません。

ADK_VERTEX_LIVE_TEST=1 \
GOOGLE_CLOUD_PROJECT=PROJECT_ID \
GOOGLE_CLOUD_LOCATION=LOCATION \
GOOGLE_CLOUD_REASONING_ENGINE_ID=REASONING_ENGINE_ID \
cargo test -p adk-session --features vertex-session \
  --test session_contract_vertex test_vertex_live_ga_v1_canary \
  -- --ignored --exact --nocapture

Canonical Vertex content、Google ADK rawEvent fields、および任意の rawEvent Struct 値は、静かなデータ損失なしに round-trip します。任意の Struct 値は opaque のままです。reappend は元のすべての key と value を保持し、予約済みの _adkRust envelope を追加するため、その envelope を削除すると元の Struct が得られます。既存の malformed な _adkRust key は fail closed します。Google ADK projection は、Struct に空でない string id、numeric timestamp、および string invocationIdauthor がある場合でも best-effort です。 互換性のない content、actions、metadata、または optional fields は canonical SessionEvent を拒否しません。

GA v1 FunctionCallFunctionResponse の wire messages には id がありません。 ID を持つ ADK function parts と、空または非 canonical な Base64 thought-signature bytes は、損失のない private rawEvent path を使用します。Part.mediaResolution は、 その他すべてが有効な canonical part である場合に、bounded な JSON object を受け入れます。トップレベルの inlineData/fileData displayName values は、展開済みの GA wire から受け入れられます。ADK projection はこれらの provider-only fields を省きますが、canonical な sidecar は reappend をまたいでそれらを保持します。private-envelope validation は、integer または Struct-normalized な 1.0 schema versions を受け入れ、省略された proto3 の default scalar fields を空の private 値と同等とみなし、安全な integer/double-normalized な Vertex Struct 値を意味的に比較し、正確な private scalar presence を復元します。

InMemorySessionService

session をメモリ内に保存します。開発、テスト、単一インスタンスのデプロイに最適です。

use adk_session::{InMemorySessionService, SessionService, CreateRequest};
use std::collections::HashMap;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Create the service
    let session_service = InMemorySessionService::new();
    
    // Create a session
    let session = session_service.create(CreateRequest {
        app_name: "my_app".to_string(),
        user_id: "user_123".to_string(),
        session_id: None,
        state: HashMap::new(),
    }).await?;
    
    println!("Session ID: {}", session.id());
    println!("App: {}", session.app_name());
    println!("User: {}", session.user_id());
    
    Ok(())
}

SqliteSessionService

session を SQLite database に保存します。永続化が必要な開発および単一ノードのデプロイに適しています。

use adk_session::{SqliteSessionService, SessionService, CreateRequest};
use std::collections::HashMap;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Connect to database
    let session_service = SqliteSessionService::new("sqlite:sessions.db").await?;
    
    // Run migrations to create tables
    session_service.migrate().await?;
    
    // Create a session
    let session = session_service.create(CreateRequest {
        app_name: "my_app".to_string(),
        user_id: "user_123".to_string(),
        session_id: None,
        state: HashMap::new(),
    }).await?;
    
    println!("Session persisted: {}", session.id());
    
    Ok(())
}

Note: SqliteSessionService には sqlite feature flag が必要です:

adk-session = { version = "2.0.0", features = ["sqlite"] }

PostgresSessionService

session を PostgreSQL に保存します。本番のマルチノードデプロイに適しています。

use adk_session::{PostgresSessionService, SessionService, CreateRequest};
use std::collections::HashMap;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let session_service = PostgresSessionService::new(
        "postgres://user:pass@localhost:5432/mydb"
    ).await?;
    
    session_service.migrate().await?;
    
    let session = session_service.create(CreateRequest {
        app_name: "my_app".to_string(),
        user_id: "user_123".to_string(),
        session_id: None,
        state: HashMap::new(),
    }).await?;
    
    println!("Session persisted: {}", session.id());
    Ok(())
}

Note: postgres feature flag が必要です:

adk-session = { version = "2.0.0", features = ["postgres"] }

MongoSessionService

session を MongoDB に保存します。スタンドアロンとレプリカセットの両方のデプロイをそのままサポートします。

起動時に、MongoSessionService::new() は、接続された MongoDB インスタンスがレプリカセットの一部かどうかを、hello command を発行して自動検出します。レプリカセット(またはシャードクラスタ)が検出されると、すべての multi-document write は原子性のために transaction を使用します。スタンドアロンのデプロイでは、operation は transaction なしで順次実行されます。upsert ベースの write は冪等であるため、部分的な失敗があっても state は回復可能なままです。

use adk_session::{MongoSessionService, SessionService, CreateRequest};
use std::collections::HashMap;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Works with both standalone and replica-set MongoDB
    let session_service = MongoSessionService::new(
        "mongodb://user:pass@localhost:27017",
        "my_sessions_db",
    ).await?;

    // Run migrations (creates indexes)
    session_service.migrate().await?;

    // Check deployment mode
    if session_service.supports_transactions() {
        println!("Replica set detected — transactions enabled");
    } else {
        println!("Standalone mode — sequential writes");
    }

    let session = session_service.create(CreateRequest {
        app_name: "my_app".to_string(),
        user_id: "user_123".to_string(),
        session_id: None,
        state: HashMap::new(),
    }).await?;

    println!("Session persisted: {}", session.id());
    Ok(())
}

Note: mongodb feature flag が必要です:

adk-session = { version = "2.0.0", features = ["mongodb"] }

MongoDB deployment modes

デプロイトランザクション動作
スタンドアロンいいえシーケンシャルな upsert、冪等な書き込み
レプリカセットはいマルチドキュメント ACID トランザクション
シャーディングクラスタはいマルチドキュメント ACID トランザクション

retryWrites=false の接続文字列パラメータは、スタンドアロン展開ではもはや不要です。サービスがこれを透過的に処理します。

MongoDB コレクション

MongoSessionService は 4 つのコレクションを使用します:

  • sessions — session-level state を持つ session documents
  • eventssession_id で関連付けられた event documents
  • app_statesapp_name をキーとする application-level state
  • user_states(app_name, user_id) をキーとする user-level state

Neo4jSessionService

Neo4j では sessions をグラフノードとして保存します。sessions、events、state tiers 間の関係はグラフエッジとしてモデル化されます。

use adk_session::{Neo4jSessionService, SessionService, CreateRequest};
use std::collections::HashMap;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let session_service = Neo4jSessionService::new(
        "bolt://localhost:7687",
        "neo4j",
        "password",
    ).await?;

    session_service.migrate().await?;

    let session = session_service.create(CreateRequest {
        app_name: "my_app".to_string(),
        user_id: "user_123".to_string(),
        session_id: None,
        state: HashMap::new(),
    }).await?;

    println!("Session persisted: {}", session.id());
    Ok(())
}

Note: neo4j feature flag が必要です:

adk-session = { version = "2.0.0", features = ["neo4j"] }

RedisSessionService

Redis に sessions を保存します。低レイテンシ、高スループットの展開に最適です。

use adk_session::{RedisSessionService, RedisSessionConfig};

let config = RedisSessionConfig::new("redis://localhost:6379");
let session_service = RedisSessionService::new(config).await?;

Note: redis feature flag が必要です:

adk-session = { version = "2.0.0", features = ["redis"] }

スキーマ移行

データベースを利用するすべての session service(SQLite、PostgreSQL、MongoDB、Neo4j)には、バージョン管理された forward-only の migration system が含まれます。Migrations は _schema_migrations registry table に記録されます。

暗号化されたセッション

任意の SessionServiceEncryptedSession でラップして、AES-256-GCM を使用して session state を保存時に暗号化します。encrypted-session feature flag が必要です。

adk-session = { version = "2.0.0", features = ["encrypted-session"] }

基本的な使用法

use adk_session::{EncryptedSession, EncryptionKey, InMemorySessionService};

// Generate a random 256-bit key
let key = EncryptionKey::generate();

// Or load from environment variable (base64-encoded)
// let key = EncryptionKey::from_env("SESSION_ENCRYPTION_KEY")?;

// Wrap any session service
let inner = InMemorySessionService::new();
let service = EncryptedSession::new(inner, key, vec![]);

// Use exactly like any other SessionService — encryption is transparent
let session = service.create(CreateRequest { /* ... */ }).await?;

State は JSON にシリアライズされ、ランダムな 96-bit nonce で暗号化され、inner service には [nonce || ciphertext] として保存されます。復号は読み取り時に透過的に行われます。

鍵のローテーション

古い鍵で暗号化されたデータの読み取りをサポートするために、前の鍵を渡します:

let new_key = EncryptionKey::generate();
let old_key = EncryptionKey::from_env("OLD_SESSION_KEY")?;

let service = EncryptedSession::new(inner, new_key, vec![old_key]);

読み取り時には、まず現在の鍵が試されます。復号に失敗した場合は、各前の鍵が順番に試されます。前の鍵で成功すると、データは自動的に現在の鍵で再暗号化されます。

鍵の管理

// Generate random key
let key = EncryptionKey::generate();

// Load from base64-encoded environment variable
let key = EncryptionKey::from_env("MY_KEY")?;

// Create from raw 32 bytes
let key = EncryptionKey::from_bytes(&[0u8; 32])?;

// Access raw bytes (e.g., for storing securely)
let bytes: &[u8; 32] = key.as_bytes();

マイグレーションの実行

データベースを利用する service を構築した後に migrate() を呼び出します。これは冪等であり、起動のたびに呼び出しても安全です:

use adk_session::SqliteSessionService;

let service = SqliteSessionService::new("sqlite:sessions.db").await?;
service.migrate().await?;

スキーマバージョンの確認

let version = service.schema_version().await?;
println!("Current schema version: {version}");

ベースライン検出

migration system が追加される前に作成された既存の database がある場合、migrate() は既存の tables を検出して、すでに適用済みとして登録します。これにより破壊的な再作成を回避し、段階的な導入が可能になります。

マイグレーションの保証

  • Forward-only: Migrations は順番に適用され、ロールバックされることはありません
  • Idempotent: migrate() を複数回実行しても安全です
  • Checksummed: 各 migration は SHA-256 checksum で追跡され、改ざんを検出します
  • Atomic: PostgreSQL migrations は同時実行を防ぐために advisory locks を使用します

セッションのライフサイクル

1. 作成

Sessions は CreateRequest で作成されます。session_id が提供されない場合、UUID が自動的に生成されます。

use adk_session::{InMemorySessionService, SessionService, CreateRequest};
use std::collections::HashMap;

let service = InMemorySessionService::new();

// Create with auto-generated ID
let session = service.create(CreateRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: None,
    state: HashMap::new(),
}).await?;

// Create with specific ID
let session = service.create(CreateRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: Some("my-custom-id".to_string()),
    state: HashMap::new(),
}).await?;

2. 取得

セッションを識別子で取得します:

use adk_session::GetRequest;

let session = service.get(GetRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_abc".to_string(),
    num_recent_events: None,
    after: None,
}).await?;

println!("Retrieved session: {}", session.id());
println!("Events: {}", session.events().len());

3. イベントの追加

会話の進行に応じて、events は sessions に追加されます。通常は Runner が処理しますが、手動でも実行できます:

use adk_session::Event;

let event = Event::new("invocation_123");
service.append_event(session.id(), event).await?;

4. 一覧表示

ユーザーのすべての sessions を一覧表示します:

use adk_session::ListRequest;

let sessions = service.list(ListRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
}).await?;

for session in sessions {
    println!("Session: {} (updated: {})", 
        session.id(), 
        session.last_update_time()
    );
}

5. 削除

不要になったら session を削除します:

use adk_session::DeleteRequest;

service.delete(DeleteRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_abc".to_string(),
}).await?;

Runner での Sessions の使用

Agents を実行するとき、sessions は通常 Runner によって管理されます。Runner は次の処理を行います:

  1. session を作成または取得する
  2. session context を agent に渡す
  3. 会話の進行に応じて events を追加する
  4. agent のアクションに基づいて session state を更新する
use adk_rust::prelude::*;
use adk_rust::{SessionId, UserId};
use adk_runner::{Runner, RunnerConfig};
use adk_session::InMemorySessionService;
use std::sync::Arc;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    dotenvy::dotenv().ok();
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.5-flash")?);

    let agent = LlmAgentBuilder::new("assistant")
        .model(model)
        .instruction("You are a helpful assistant.")
        .build()?;

    let session_service = Arc::new(InMemorySessionService::new());

    // Create runner with session service
    let runner = Runner::new(RunnerConfig {
        app_name: "my_app".to_string(),
        agent: Arc::new(agent),
        session_service,
        artifact_service: None,
        memory_service: None,
        run_config: None,
    })?;

    // Run with user and session IDs
    let user_content = Content::new("user").with_text("Hello!");
    let stream = runner.run(
        UserId::new("user_123")?,
        SessionId::new("session_abc")?,
        user_content,
    ).await?;

    Ok(())
}

型付き Session ID

Session 操作では AdkIdentityAppNameUserIdSessionId の型付き composite — を使用して、sessions を曖昧さなく指定します。これにより parameter ordering のバグを排除し、マルチテナント分離を保証します。

型付き Session 操作

SessionService は、既存の文字列ベースの API と並んで型付きメソッドを提供します:

use adk_core::{AdkIdentity, AppName, SessionId, UserId};
use adk_session::{AppendEventRequest, Event, SessionService};

let identity = AdkIdentity::new(
    AppName::try_from("my_app")?,
    UserId::try_from("user_123")?,
    SessionId::try_from("session_abc")?,
);

// Typed append — uses the full (app, user, session) triple
let event = Event::new("inv_001");
service.append_event_for_identity(AppendEventRequest {
    identity: identity.clone(),
    event,
}).await?;

// Typed get and delete
let session = service.get_for_identity(&identity).await?;
service.delete_for_identity(&identity).await?;

すべての session backend(in-memory、SQLite、PostgreSQL、Redis、MongoDB、Firestore、Neo4j、Vertex)は、型付き identity path をサポートします。従来の append_event(&str, ...) メソッドは、後方互換性のために引き続き利用できます。

新しいコードでは、append_event_for_identity() とその他の型付き identity helper を推奨します。従来の append_event(&str, ...) path は migration のためにのみ保持されており、内部呼び出し元が完全に AdkIdentity に移行した後に将来的な廃止を予定している、最初の従来型 identity API です。

マルチテナントの安全性

型付き identity を使うと、同じ raw session_id を共有しつつ、app_name または user_id が異なる 2 つの session は、常に正しくアドレス指定されます:

let tenant_a = AdkIdentity::new(
    AppName::try_from("app-a")?,
    UserId::try_from("alice")?,
    SessionId::try_from("shared-session-id")?,
);

let tenant_b = AdkIdentity::new(
    AppName::try_from("app-b")?,
    UserId::try_from("bob")?,
    SessionId::try_from("shared-session-id")?,
);

// These address completely different sessions
assert_ne!(tenant_a, tenant_b);

Sessions から Identity を読み取る

Session trait は、既存の session から identity を抽出するための型付き helper を提供します:

let session = service.get(get_request).await?;

// Get the full session identity
let identity = session.try_identity()?;

// Or individual typed fields
let app = session.try_app_name()?;
let user = session.try_user_id()?;
let sid = session.try_session_id()?;

3 つの identity layer(auth、session、execution)について詳しくは、Core Types — Identity を参照してください。

Events

Events trait は、会話履歴へのアクセスを提供します:

pub trait Events: Send + Sync {
    /// Get all events
    fn all(&self) -> Vec<Event>;
    
    /// Get number of events
    fn len(&self) -> usize;
    
    /// Get event at index
    fn at(&self, index: usize) -> Option<&Event>;
    
    /// Check if empty
    fn is_empty(&self) -> bool;
}

session から events にアクセスします:

let events = session.events();
println!("Total events: {}", events.len());

for event in events.all() {
    println!("Event {} by {} at {}", 
        event.id, 
        event.author, 
        event.timestamp
    );
}

完全な例

use adk_session::{
    InMemorySessionService, SessionService, 
    CreateRequest, GetRequest, ListRequest, DeleteRequest,
    Event, KEY_PREFIX_USER,
};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let service = InMemorySessionService::new();
    
    // Create session with initial state
    let mut initial_state = HashMap::new();
    initial_state.insert(format!("{}name", KEY_PREFIX_USER), json!("Alice"));
    initial_state.insert("topic".to_string(), json!("Getting started"));
    
    let session = service.create(CreateRequest {
        app_name: "demo".to_string(),
        user_id: "alice".to_string(),
        session_id: None,
        state: initial_state,
    }).await?;
    
    println!("Created session: {}", session.id());
    
    // Check state
    let state = session.state();
    println!("User name: {:?}", state.get("user:name"));
    println!("Topic: {:?}", state.get("topic"));
    
    // Append an event
    let event = Event::new("inv_001");
    service.append_event(session.id(), event).await?;
    
    // Retrieve session with events
    let session = service.get(GetRequest {
        app_name: "demo".to_string(),
        user_id: "alice".to_string(),
        session_id: session.id().to_string(),
        num_recent_events: None,
        after: None,
    }).await?;
    
    println!("Events: {}", session.events().len());
    
    // List all sessions
    let sessions = service.list(ListRequest {
        app_name: "demo".to_string(),
        user_id: "alice".to_string(),
    }).await?;
    
    println!("Total sessions: {}", sessions.len());
    
    // Delete session
    service.delete(DeleteRequest {
        app_name: "demo".to_string(),
        user_id: "alice".to_string(),
        session_id: session.id().to_string(),
    }).await?;
    
    println!("Session deleted");
    
    Ok(())
}

Previous: ← MCP Tools | Next: State Management →

セッション - ADK-Rust ドキュメント | ADK-Rust