セッション
ADK-Rust のセッションは会話コンテキストの管理を提供し、エージェントが複数のやり取りにわたって状態を維持できるようにします。セッションには会話履歴(イベント)と、会話全体を通じて保持される任意の状態データが保存されます。
概要
セッションは、ユーザーとエージェント間の単一の会話を表します。各セッションには、次の特徴があります。
- 一意の識別子を持つ
- アプリケーション(
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 の実装
バックエンドの選択
ADK-Rust は、複数のセッションサービス実装を提供します。
| 実装 | 機能フラグ | 用途 |
|---|---|---|
InMemorySessionService | (なし) | 開発、テスト、単一インスタンス |
SqliteSessionService | sqlite | 単一ノードの永続化 |
PostgresSessionService | postgres | 本番環境向けリレーショナル永続化 |
RedisSessionService | redis | 低レイテンシのインメモリ永続化 |
MongoSessionService | mongodb | ドキュメント指向永続化 |
Neo4jSessionService | neo4j | グラフデータベース永続化 |
FirestoreSessionService | firestore | Google Cloud Firestore |
VertexAiSessionService | vertex-session | Vertex AI セッション API |
vertex-session は adk-rust アンブレラクレートによっても転送されます。
[dependencies]
adk-rust = { version = "2.1.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)?;
デプロイされた Vertex AI Agent Engine コンテナ内では、プラットフォームが設定を環境変数として提供し、VertexAiSessionConfig::from_env()
がそれらを読み取ります。
| 変数 | 意味 |
|---|---|
GOOGLE_CLOUD_PROJECT | Google Cloud プロジェクト ID |
GOOGLE_CLOUD_LOCATION | GCP リージョン |
GOOGLE_CLOUD_AGENT_ENGINE_ID | 素の数値エージェントエンジン ID(完全なリソース名ではありません) |
use adk_session::{VertexAiSessionConfig, VertexAiSessionService};
let config = VertexAiSessionConfig::from_env()?;
let service = VertexAiSessionService::new_with_adc(config)?;
空または未設定の変数がある場合は、それぞれの変数名を示す無効な入力エラーが生成されます。
セッションの有効期限
ttl と expireTime は Session.expiration oneof メンバー
(google/cloud/aiplatform/v1beta1/session.proto)です。設定ではセッション作成時に最大で
1 つだけ送信されます。ttl は入力専用で、JSON の期間
文字列(例:"86400s")としてシリアライズされ、最小値は 24 時間です。
expireTime は RFC 3339 タイムスタンプです。両方のメンバーを設定した場合、
または 24 時間未満の TTL を設定した場合、サービスの構築に失敗します。
use adk_session::VertexAiSessionConfig;
use std::time::Duration;
let config = VertexAiSessionConfig::new("my-project", "us-central1")
.with_reasoning_engine("1234567890")
.with_ttl(Duration::from_secs(86_400)); // sent as "86400s"
ID の分離
公開セッション ID は常に CreateRequest に指定された ID です。ID が指定されていない場合は、
ローカルで生成された UUID が使用されます。バックエンドは完全な
(app_name, user_id, session_id) タプルから Vertex リソース ID を導出し、バージョン付きの ID マーカーを
セッション状態に永続化します。
| プロパティ | 動作 |
|---|---|
| 論理 ID | 作成、取得、一覧表示、追加、削除の間で保持される |
| リモート ID | 完全な ID から導出される決定論的な 63 文字の adk1-… ID |
| ID マーカー | 保護された状態として保存され、公開セッション状態から削除される |
| 共有推論エンジン | 同じ論理 ID のセッションは、アプリとユーザーごとに分離されたままになる |
| 予約済み状態 | 状態およびイベント状態の差分では ID マーカーを設定できない |
バックエンドは、アイデンティティのスコープが設定された操作を実行する前に、マーカーと計算されたリソース ID の両方を検証します。計算されたリソースにマーカーがない場合や一致しない場合は、別のテナントのセッションとしてではなく、破損として扱われます。
レガシーセッションの移行
Python ADK および v2 より前のセッションでは、論理 ID を直接使用し、アイデンティティマーカーを省略する場合があります。互換性は、reasoning-engine の親がどのように選択されるかによって決まります。
| 設定 | 未指定セッションの動作 |
|---|---|
| 固定の推論エンジンなし | app_name が先頭にゼロを付けない正規のゼロ以外の数値による推論エンジン ID の場合にのみ有効 |
| 固定の推論エンジン | デフォルトでは無効 |
固定エンジンと 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");
重要: マークのないセッションは、アプリの所有権を証明するものではありません。指定されたアプリがその推論エンジン内のレガシーセッションを排他的に所有している場合にのみ、固定エンジン互換オプションを有効にしてください。正確な
user_idの一致も引き続き適用されます。マーク付きダイレクトリソース、予約済みのadk1-名前空間、および計算済み/ダイレクトの曖昧性は、安全側に倒して失敗します。
レガシー append_event(session_id, event) API には、キャッシュされたアプリ/ユーザースコープが正確に1つ存在することも必要です。まず create、get、または list を呼び出すか、append_event_for_identity() を直接使用してください。キャッシュには上限があるため、長時間実行されるプロセスでは、スコープから追い出された古いセッションを再度 get または list する必要があります。
エンドポイントとイベントの忠実性
| 場所 | デフォルトエンドポイント |
|---|---|
global | https://aiplatform.googleapis.com |
us | https://aiplatform.us.rep.googleapis.com |
eu | https://aiplatform.eu.rep.googleapis.com |
| 地域の所在地 | https://LOCATION-aiplatform.googleapis.com |
カスタムエンドポイントは、Google の認証ヘッダーと完全なセッション/イベントペイロードを受信するため、信頼できるオリジンでなければなりません。エンドポイントが、userinfo、パス、クエリ、フラグメントを含まない HTTPS オリジンでない限り、構築に失敗します。テストではループバック HTTP が許可されます。リダイレクトは無効です。
本番環境の制約
| 境界 | デフォルト |
|---|---|
Vertex user_id | Unicode スカラー値で最大 128 個 |
| 接続期限 | 10 秒 |
| 認証情報ヘッダーの期限 | 30 秒 |
| HTTP リクエストの期限 | 120 秒 |
| 長時間実行オペレーションのポーリング | 120 秒 |
| エンコードされたリクエスト本文 | 作成/追加リクエストあたり 64 MiB |
| デコードされたレスポンス本文 | 各レスポンスおよび1回のページ分割されたリスト操作全体で合計64 MiB |
| ページ分割の経過時間 | リスト操作全体で120秒 |
| ページトークン | 64 KiB |
| JSONとVertex Structのネスト | 64レベル |
with_max_request_bytes()は送信本文の予算を変更し、
with_max_response_bytes()は受信本文の両方の予算を変更し、with_pagination_timeout()はリスト全体の期限を変更します。本文の上限を高くすると、
デフォルトの割り当て保護が弱まります。これらの上限は、同時実行される呼び出し全体に対してではなく、
各リクエスト、レスポンス、またはリスト操作に対して個別に適用されます。これらはバイト単位の予算であり、
JSONの解析とbase64デコード後におけるプロセス全体のメモリ上限ではありません。
with_credentials()は、エンドポイントを検証し、上限付きでリダイレクトを無効にしたHTTPクライアントを構築するため、
失敗する可能性があります。
最近のイベント数の上限とタイムスタンプの下限はevents.listに渡されます。
バックエンドは、最近のイベント数の上限を満たすまで降順のページを要求し、その後、昇順の時系列に戻します。num_recent_events = 0
はイベントリストのエンドポイントを呼び出しません。
作成、削除、追加のHTTP 408/5xx、転送/本文、不正な2xx、
または無効な成功結果のエラーは、サービスが変更を確定した後に発生する可能性があります。これらは
session.vertex.{create,delete,append}_outcome_ambiguousと
retry.should_retry = falseを返します。作成または削除が操作名を返した後は、
ポーリング中の転送/ステータスエラー、および不正または無効な成功レスポンスには、
同じ再試行不能の不確実性に関する契約が適用され、操作名も含まれます。
手動で再試行する前に、対象のセッション、操作、またはイベントリストを確認してください。
終端のdone: true操作エラーは、既知の失敗結果として扱われます。その場合は
session.vertex.operation_failedと、そのカテゴリから導出された再試行ヒントが保持されます。
HTTP 4xx 以外の 408 に対する直接的な拒否は引き続き確定的です。429 は再試行可能です。操作が受け付けられた後のポーリング失敗は、429 を含め、すべて不明確な状態であり、再試行できません。バックエンドは内部でスロットリングや再試行を行わないため、アプリケーションレベルの同時実行数と再試行ポリシーの設定には、現在の Vertex AI
クォータ
を使用してください。
無視されている GA v1 カナリアテストは、Application Default Credentials が構成された既存の推論エンジンに対してのみ実行してください。これは 1 つのセッションと 1 つの代表的なイベントを作成および削除しますが、エンジンを作成または削除することはありません。
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
正規の Vertex コンテンツ、Google ADK rawEvent フィールド、および任意の rawEvent Struct 値は、サイレントなデータ損失なしに往復変換されます。任意の Struct 値は不透明なままです。再付加によって元のすべてのキーと値が保持され、予約済みの _adkRust エンベロープが追加されるため、そのエンベロープを削除すると元の Struct が得られます。既存の不正な _adkRust キーがある場合は、安全側に失敗します。Struct に空でない文字列 id、数値 timestamp、および文字列 invocationId と author が含まれている場合でも、Google ADK への投影はベストエフォートです。互換性のないコンテンツ、アクション、メタデータ、またはオプションフィールドによって、正規の SessionEvent が拒否されることはありません。
GA v1 FunctionCall および FunctionResponse のワイヤメッセージには id がありません。
ID を含む ADK の関数部分、および空または非カノニカルな Base64 思考署名
バイト列には、ロスレスなプライベート rawEvent パスが使用されます。Part.mediaResolution は、
その他の点では有効なカノニカル部分であれば、任意の部分に境界付きの
JSON オブジェクトを受け入れます。トップレベルの
inlineData/fileData displayName 値は、デプロイ済みの GA
ワイヤから受け入れられます。ADK の射影ではこれらのプロバイダー専用フィールドが省略されますが、カノニカル
サイドカーでは再付加後も保持されます。プライベートエンベロープの検証では、整数または Struct に正規化された 1.0 スキーマバージョンを受け入れ、省略された proto3
デフォルトのスカラー型フィールドを空のプライベート値と同等として扱い、安全な整数/double に正規化された Vertex Struct の値を意味的に比較し、プライベートスカラーの存在を正確に復元します。
InMemorySessionService
セッションをメモリ内に保存します。開発、テスト、単一インスタンスのデプロイに適しています。
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
セッションを SQLite データベースに保存します。永続性が必要な開発および単一ノードのデプロイに適しています。
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(())
}
注記:
SqliteSessionServiceにはsqlite機能フラグが必要です:adk-session = { version = "2.1.0", features = ["sqlite"] }
PostgresSessionService
セッションを 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(())
}
注記:
postgres機能フラグが必要です:adk-session = { version = "2.1.0", features = ["postgres"] }
MongoSessionService
セッションを MongoDB に保存します。スタンドアロンとレプリカセットの両方のデプロイを標準でサポートします。
起動時に、MongoSessionService::new()はhelloコマンドを発行して、接続されたMongoDBインスタンスがレプリカセットの一部かどうかを自動検出します。レプリカセット(またはシャーディングクラスタ)が検出された場合、すべての複数ドキュメント書き込みでアトミック性を確保するためにトランザクションを使用します。スタンドアロンデプロイメントでは、トランザクションを使用せずに操作を順番に実行します。upsertベースの書き込みはべき等であるため、部分的な障害が発生しても状態を復旧できます。
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(())
}
注記:
mongodb機能フラグが必要です。adk-session = { version = "2.1.0", features = ["mongodb"] }
MongoDBのデプロイメントモード
| デプロイメント | トランザクション | 動作 |
|---|---|---|
| スタンドアロン | いいえ | 順次アップサート、べき等な書き込み |
| レプリカセット | はい | 複数ドキュメントの ACID トランザクション |
| シャードクラスタ | はい | 複数ドキュメント ACID トランザクション |
retryWrites=false 接続文字列パラメーターは、スタンドアロンデプロイでは不要になりました。サービスがこれを透過的に処理します。
MongoDB コレクション
MongoSessionService は4つのコレクションを使用します。
sessions— セッションレベルの状態を含むセッションドキュメントevents—session_idによって関連付けられたイベントドキュメントapp_states—app_nameをキーとするアプリケーションレベルの状態user_states—(app_name, user_id)をキーとするユーザーレベルの状態
Neo4jSessionService
Neo4j にセッションをグラフノードとして保存します。セッション、イベント、状態階層間の関係は、グラフエッジとしてモデル化されます。
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(())
}
注記:
neo4j機能フラグが必要です:adk-session = { version = "2.1.0", features = ["neo4j"] }
RedisSessionService
Redis にセッションを保存します。低レイテンシーで高スループットのデプロイに適しています。
use adk_session::{RedisSessionService, RedisSessionConfig};
let config = RedisSessionConfig::new("redis://localhost:6379");
let session_service = RedisSessionService::new(config).await?;
注記:
redis機能フラグが必要です:adk-session = { version = "2.1.0", features = ["redis"] }
スキーママイグレーション
すべてのデータベースベースのセッションサービス(SQLite、PostgreSQL、MongoDB、Neo4j)には、バージョン管理された前方のみのマイグレーションシステムが含まれています。マイグレーションは、_schema_migrations レジストリテーブルで追跡されます。
暗号化されたセッション
任意の SessionService を EncryptedSession でラップすると、AES-256-GCM を使用して保存時のセッション状態を暗号化できます。encrypted-session 機能フラグが必要です。
adk-session = { version = "2.1.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?;
状態は JSON にシリアライズされ、ランダムな96ビットの nonce で暗号化され、内部サービスに [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();
マイグレーションの実行
任意のデータベースバックエンドサービスを構築した後に 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}");
ベースラインの検出
移行システムが追加される前に作成された既存のデータベースがある場合、migrate() は既存のテーブルを検出し、適用済みとして登録します。これにより、破壊的な再作成を回避し、段階的な導入が可能になります。
移行の保証
- 前進のみ: 移行は順番に適用され、ロールバックされることはありません
- 冪等性:
migrate()を複数回実行しても安全です - チェックサム付き: 改ざんを検出するため、各移行は SHA-256 チェックサムとともに記録されます
- アトミック: PostgreSQL の移行では、同時実行を防ぐためにアドバイザリロックを使用します
セッションのライフサイクル
1. 作成
セッションは 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. イベントの追加
会話の進行に応じて、イベントをセッションに追加します。通常は Runner が処理しますが、手動で行うこともできます。
use adk_session::Event;
let event = Event::new("invocation_123");
service.append_event(session.id(), event).await?;
4. 一覧表示
ユーザーのすべてのセッションを一覧表示します。
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. 削除
不要になったセッションを削除します。
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 でのセッションの使用
エージェントの実行時、セッションは通常 Runner によって管理されます。Runner は次の処理を行います。
- セッションを作成または取得する
- セッションコンテキストをエージェントに渡す
- 会話の進行に応じてイベントを追加する
- エージェントのアクションに基づいてセッション状態を更新する
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-3.7-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(())
}
型付きセッション識別子
セッション操作では、AdkIdentity — AppName、UserId、SessionIdから成る型付き複合体 — を使用して、セッションを曖昧さなく指定します。これにより、パラメーターの順序に関するバグが排除され、マルチテナントの分離が保証されます。
型付きセッション操作
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?;
すべてのセッションバックエンド(メモリ内、SQLite、PostgreSQL、Redis、MongoDB、Firestore、Neo4j、Vertex)は、型付き ID パスをサポートします。従来の append_event(&str, ...) メソッドも後方互換性のため引き続き利用できます。
新しいコードでは、append_event_for_identity() およびその他の型付き ID ヘルパーを優先してください。従来の append_event(&str, ...) パスは移行目的のみに維持されており、内部の呼び出し元が完全に AdkIdentity に移行した後、将来的に非推奨となる予定の最初の従来型 ID API です。
マルチテナントの安全性
型付き ID を使用すると、同じ生の session_id を共有していても、app_name または user_id が異なる 2 つのセッションは常に正しく指定されます。
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);
セッションからの ID の読み取り
Session trait は、既存のセッションから ID を抽出するための型付きヘルパーを提供します。
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 つの ID 層(認証、セッション、実行)について詳しくは、コア型 — ID を参照してください。
イベント
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;
}
セッションからイベントにアクセスします。
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(())
}
関連項目
- 状態管理 - プレフィックスを使用したセッション状態の管理
- コンテキストの圧縮 - LLM コンテキストサイズの削減
- イベント - イベントの構造とアクション
- Runner - セッションを使用したエージェントの実行