メモリの概念
メモリの semantic-store 側は、いくつかの小さな型から構成されています。 この4つを理解すれば、残りのセクションも追えます。
MemoryEntry
単一のメモリレコード: いくつかのコンテンツ、誰が作成したか、そしていつか。
use adk_memory::MemoryEntry;
use adk_core::Content;
use chrono::Utc;
let entry = MemoryEntry {
content: Content::new("user").with_text("I prefer dark mode"),
author: "user".to_string(),
timestamp: Utc::now(),
};
エントリは保存の単位であり、想起の単位でもあります — search は、クエリに最も関連するエントリを返します。
MemoryService trait
すべての backend は1つの trait を実装します。2つの method が必須で、残りには backend がオーバーライドできる default 実装があります:
#[async_trait]
pub trait MemoryService: Send + Sync {
// Required
async fn add_session(&self, app: &str, user: &str, session: &str,
entries: Vec<MemoryEntry>) -> Result<()>;
async fn search(&self, req: SearchRequest) -> Result<SearchResponse>;
// Optional (default: "not implemented")
async fn delete_user(&self, app: &str, user: &str) -> Result<()>; // GDPR
async fn delete_session(&self, app: &str, user: &str, session: &str) -> Result<()>;
async fn add_entry(&self, app: &str, user: &str, entry: MemoryEntry) -> Result<()>;
async fn delete_entries(&self, app: &str, user: &str, query: &str) -> Result<u64>;
// …plus a health check
}
add_sessionは、完了した会話に含まれるエントリを取り込みます。memory が接続されていると、Runner がこれを代わりに呼び出します。searchは想起です。backend は query を keyword、embedding similarity、または graph token-scoring としてそれぞれ異なる方法で解釈しますが、契約は同じです。
SearchRequest / SearchResponse
use adk_memory::SearchRequest;
let req = SearchRequest {
app_name: "support".into(),
user_id: "alice".into(),
query: "contact preference".into(),
..Default::default() // top_k, project scoping, etc.
};
let resp = memory.search(req).await?; // resp.entries: Vec<MemoryEntry>
Session state と memory
これらは別の tool です — 混同しないでください:
| セッション状態 | メモリ | |
|---|---|---|
| ライフタイム | 1つの会話 | 複数の会話にまたがる |
| API | adk-session (SessionService, state map) | adk-memory (MemoryService) |
| 保持 | ライブのトランスクリプト + スクラッチ状態 | 後で思い出す価値のある永続的な事実 |
| 読み取り | 常にコンテキスト内 | 必要時に、search_memory 経由で |
典型的な流れは、会話がセッション状態に存在し、終了時(または各 ターンごと)に、重要な部分がメモリへ書き込まれ、次のセッションが コンテキストを復元するためにメモリを検索します。Sessions & State を参照してください。
分離: アプリ、ユーザー、プロジェクト
メモリは常に(app_name, user_id)でキー付けされるため、ユーザー同士が互いの
メモリを見ることはありません。ユーザー内で第3のレベル、つまりプロジェクトをスコープできます:
- グローバルエントリ (
project_id = None) — すべてのコンテキストで表示されます。 - プロジェクトエントリ (
project_id = Some(id)) — そのプロジェクト内でのみ表示されます。 - プロジェクト検索 はグローバル + 一致するプロジェクトエントリを返します。グローバル 検索 はグローバルエントリのみを返します。
use adk_memory::{MemoryServiceAdapter, InMemoryMemoryService};
use std::sync::Arc;
let service = Arc::new(InMemoryMemoryService::new());
// store within a project
service.add_session_to_project("app", "user", "sess", "acme-project", entries).await?;
// an adapter scoped to that project
let adapter = MemoryServiceAdapter::new(service, "app", "user")
.with_project_id("acme-project");
プロジェクトを使って、たとえばユーザーの仕事用アシスタントと個人用アシスタントがメモリを共有しないようにしつつ、真にグローバルな事実は共有できます。
すべてのバックエンドがプロジェクトスコープを実装しているわけではない
プロジェクト分離はバックエンドの機能であり、trait が保証できるものではありません。 依存する前に確認してください:
if service.supports_project_scoping() {
service.add_entry_to_project("app", "user", "acme-project", entry).await?;
} else {
// Decide explicitly: refuse, or store globally on purpose.
}
| バックエンド | プロジェクトのスコープ設定 |
|---|---|
InMemoryMemoryService | はい |
SqliteMemoryService | はい |
PostgresMemoryService | はい |
RedisMemoryService | はい |
MongoMemoryService | はい |
Neo4jMemoryService | はい |
GraphMemoryService | いいえ — プロジェクトのメソッドはエラーを返します |
プロジェクトをサポートしないバックエンドは、黙ってグローバルに書き込んだり削除したりするのではなく、呼び出しを失敗させます。書き込みのスコープを広げると、あるプロジェクト向けのデータが同じアプリとユーザー配下のすべてに見えてしまい、削除のスコープを広げると、指定されたプロジェクト外のエントリまで削除してしまいます。どちらも、呼び出し側が戻り値から検出できるものではありません。MemoryServiceAdapter::supports_project_scoping
は、そのバックエンドが何をサポートしているかを報告します。
GDPR による消去
delete_user(app, user) は、すべてのプロジェクトにわたるユーザーのすべての記憶(エントリと
埋め込み)を削除します — いわば right-to-erasure の基本操作です。永続的に保存するバックエンドはこれを実装します。アカウント削除の経路から呼び出してください。
memory.delete_user("support", "alice").await?;
次へ: Backends →