記憶対応チャット
ユーザーの好み、過去の出来事、約束、感情的なコンテキストをセッションをまたいですべて記憶する会話エージェントを構築します。このチュートリアルを終える頃には、プログラムによるコンテキストのシーディング、記憶の検索、エージェントがユーザーについて学んだ内容の確認方法を理解できるようになります。
構築するもの
- エージェントが自動的に事実を抽出・保存するストリーミングチャットループ
- 最初の会話前に既存のユーザーデータを注入するプレシーディングフロー
- エージェントが特定のトピックについて何を知っているか確認するメモリ検索API呼び出し
- エージェントのメモリ成長を監査するファクトタイムラインクエリ
メモリの仕組み
チャット中のメモリは完全に自動です。各メッセージ交換後、プラットフォームは:
- 会話内の事実・好み・コミットメント・出来事を特定する抽出パイプラインを実行
- 後継チェーンを使用して既存メモリと重複排除(古い事実は削除されず退職される)
- フルテキスト検索と時間クエリのためにすべてをインデックス化
- 次の会話ではトークンバジェット内で最も関連性の高いメモリを自動取得
ベクターストアの管理・抽出プロンプトの記述・取得ロジックの実装は不要です。プラットフォームがすべて処理します。
1. チャットしてメモリを構築
チャットを開始します。メモリ抽出は応答がストリームされた後に自動的に行われます。あなた側で特別なことは何も必要ありません。
import { Sonzai } from "@sonzai-labs/agents";
const client = new Sonzai({ apiKey: process.env.SONZAI_API_KEY! });
const AGENT_ID = "agent_abc";
const USER_ID = "user_123";
// 最初の会話——エージェントはまだメモリを持っていない
for await (const event of client.agents.chatStream(AGENT_ID, {
userId: USER_ID,
messages: [
{ role: "user", content: "My name is Mia. I'm allergic to peanuts and I love hiking." },
],
})) {
process.stdout.write(event.choices?.[0]?.delta?.content ?? "");
}
// プラットフォームが抽出:name="Mia"、allergy="peanuts"、interest="hiking"
// 2回目の会話——エージェントは上記すべてを想起
for await (const event of client.agents.chatStream(AGENT_ID, {
userId: USER_ID,
messages: [
{ role: "user", content: "What snacks should I bring on my next hike?" },
],
})) {
process.stdout.write(event.choices?.[0]?.delta?.content ?? "");
}
// エージェントはMiaがハイキング好きでピーナッツアレルギーであることを知っている——再自己紹介は不要。メモリはユーザーごとです
ユーザーAの会話から抽出された事実はユーザーBに表示されることはありません。プラットフォームがメモリを正しくスコープするために、すべてのチャット呼び出しで必ず userId(TypeScript)/ user_id(Python)/ UserID(Go)を渡してください。
2. 既存データからメモリをプレシード
ユーザーがシステムに履歴を持っている場合——CRMプロファイル・オンボーディング回答・過去の注文——最初の会話前に注入することで、エージェントがすでに相手を知っているように感じさせます。
// オンボーディング時またはCRMインポート後に一度呼び出す
await client.agents.memory.seed(AGENT_ID, {
userId: USER_ID,
memories: [
{
content: "Mia is a 32-year-old UX designer based in Berlin.",
type: "user_fact",
},
{
content: "Mia subscribed to the Pro plan on 2024-11-03.",
type: "shared_experience",
occurred_at: "2024-11-03T00:00:00Z",
},
{
content: "Mia prefers email over SMS for notifications.",
type: "user_preference",
},
{
content: "Mia mentioned she wants to get into trail running.",
type: "user_goal",
},
],
});サポートされるメモリタイプ:user_fact・user_preference・shared_experience・user_goal・commitment・time_sensitive。
3. エージェントが知っていることを検索
エージェントがトピックについて抽出した内容を直接メモリストアにクエリします。ユーザー向けの「エージェントが何を覚えているか?」機能の構築やデバッグに便利です。
const results = await client.agents.memory.search(AGENT_ID, {
query: "diet restrictions food allergies",
userId: USER_ID,
limit: 10,
});
for (const fact of results.facts) {
console.log(`[${fact.type}] ${fact.content} (confidence: ${fact.confidence})`);
}
// [user_fact] Mia is allergic to peanuts (confidence: 0.97)
// [user_preference] Mia prefers nut-free snacks on hikes (confidence: 0.85)4. メモリツリーをブラウズ
メモリツリーは事実をカテゴリ別に整理する7レベルの階層構造です(/identity/traits・/preferences/interests・/episodes/sessions など)。ノードごとに移動できます。
// トップレベルのノードを取得
const tree = await client.agents.memory.list(AGENT_ID, {
userId: USER_ID,
includeContents: false, // ノードのメタデータのみ、事実テキストなし
});
for (const node of tree.nodes) {
console.log(`${node.path} — ${node.fact_count} facts`);
}
// /identity/traits — 3 facts
// /preferences/interests — 5 facts
// /episodes/sessions — 12 facts
// /temporal — 2 facts
// ノードにドリルダウン
const identityNode = await client.agents.memory.list(AGENT_ID, {
userId: USER_ID,
parentId: "node_identity_traits_id",
includeContents: true, // 事実テキストを含む
});メモリツリーはダッシュボードのエージェント → 対象エージェント → ユーザー → ユーザーを選択 → メモリ → ツリーエクスプローラーでインタラクティブに操作できます。
5. ファクトタイムラインを確認
タイムラインはすべての事実を時系列で表示します——いつ作成・更新・後継されたかを含みます。メモリの成長を監査したり「会話履歴」ビューを構築したりするのに使用します。
const timeline = await client.agents.memory.timeline(AGENT_ID, {
userId: USER_ID,
// オプション:日付範囲を絞り込む
start: "2025-01-01T00:00:00Z",
end: "2025-12-31T23:59:59Z",
});
for (const entry of timeline.entries) {
console.log(
`${new Date(entry.created_at).toLocaleDateString()} — ${entry.type}: ${entry.content}`
);
}6. 抽出された事実を直接一覧表示
管理UIやコンプライアンスエクスポートのために、ツリー階層を通さずにユーザーのすべての生の事実を一覧表示します。factType(TS)/ fact_type(Python/Go)によるフィルタリングをサポートします。
// このユーザーのすべての事実(ページネーション付き)
const facts = await client.agents.memory.listFacts(AGENT_ID, {
userId: USER_ID,
limit: 50,
offset: 0,
factType: "user_preference", // オプションフィルタ
});
console.log(`Total facts: ${facts.total}`);
for (const f of facts.facts) {
console.log(` ${f.content}`);
}GDPR/忘れられる権利
ユーザーのすべてのメモリを削除するには client.agents.memory.reset(agentId, { userId }) を呼び出します。これにより削除された事実が再表示されないようにトゥームストーンレコードが作成されます。データは即時に取得から除外されます。
7. 過去を振り返る(タイムマシン)
タイムマシンを使用すると、過去の任意の時点でエージェントがユーザーについて何を知っていたかを確認できます——エージェントが何かを言った理由のデバッグや、理解がどのように進化したかの監査に便利です。
const snapshot = await client.agents.getTimeMachine(AGENT_ID, {
userId: USER_ID,
at: "2025-03-01T00:00:00Z", // この時点でエージェントは何を知っていたか?
});
console.log("Known facts at 2025-03-01:");
for (const fact of snapshot.facts) {
console.log(` ${fact.content}`);
}後継の仕組み
事実が更新されると、古いレコードは退職(削除ではなく)され、SupersedesID ポインターを持つ新しいレコードが作成されます。タイムマシンはこのチェーンを再生して任意のタイムスタンプ時点の状態を再構築します。
次のステップ
- メモリとコンテキストリファレンスを読んで7レベルの階層全体を理解する
- 自動セッション管理付きのマルチターンチャットセッションを扱う会話をセットアップする
- 感情とムードを探索してエージェントの感情状態がメモリと共にどう進化するかを理解する
- カスタムステートを追加してメモリと並行して構造化アプリケーションデータを保存する