記憶対応チャット
ユーザーの好み、過去の出来事、約束、感情的なコンテキストをセッションをまたいで すべて記憶する会話エージェントを構築します。このチュートリアルを終える頃には、 プログラムによるコンテキストのシーディング、記憶の検索、エージェントがユーザーについて 学んだ内容の確認方法を理解できるようになります。
構築するもの
- → エージェントがファクトを自動的に抽出・保存するストリーミングチャットループ
- → 最初の会話の前に既存のユーザーデータを注入するプリシーディングフロー
- → エージェントが特定のトピックについて知っていることを見つける記憶検索APIコール
- → 時間経過に伴うエージェントの記憶成長を監査するファクトタイムラインクエリ
記憶の仕組み
記憶はチャット中に完全に自動で処理されます。各メッセージ交換後、プラットフォームは:
- 会話内のファクト、嗜好、約束、イベントを特定する抽出パイプラインを実行
- 置換チェーン(supersession chain)を使用して既存の記憶に対して重複を排除(古いファクトは削除ではなく退役)
- 全文検索および時系列クエリのためにすべてをインデックス化
- 次の会話時に、トークン予算内で最も関連性の高い記憶を自動的に取得
ベクトルストアの管理、抽出プロンプトの記述、検索ロジックの実装は一切不要です。プラットフォームがすべてを処理します。
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";
// First conversation — agent has no memory yet
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 ?? "");
}
// Platform extracts: name="Mia", allergy="peanuts", interest="hiking"
// Second conversation — agent recalls all of the above
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 ?? "");
}
// Agent knows Mia loves hiking and is allergic to peanuts — no re-intro needed.記憶はユーザーごと
ユーザーAの会話から抽出されたファクトは、ユーザーBには表示されません。 プラットフォームが記憶を正しくスコープするよう、すべてのチャットコールで必ずuserId(またはuser_id / UserID)を渡してください。
2. 既存データから記憶をプリシーディング
ユーザーがシステム内に履歴を持っている場合 -- CRMプロフィール、オンボーディング回答、 過去の注文など -- 最初の会話前に注入することで、エージェントが既にユーザーを知っているかのように振る舞えます。
// Call once during onboarding or after CRM import
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など)。ノードごとに辿ることができます。
// Get top-level nodes
const tree = await client.agents.memory.list(AGENT_ID, {
userId: USER_ID,
includeContents: false, // just node metadata, no fact text
});
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
// Drill into a node
const identityNode = await client.agents.memory.list(AGENT_ID, {
userId: USER_ID,
parentId: "node_identity_traits_id",
includeContents: true, // include fact text
});記憶ツリーはダッシュボードの Agents → あなたのエージェント → Users → ユーザーを選択 → Memory → Tree Explorer でインタラクティブに確認できます。
5. ファクトタイムラインの確認
タイムラインは、すべてのファクトを時系列順に表示します -- 作成、更新、置換された時点がわかります。記憶の成長を監査したり、「会話履歴」ビューを構築するのに使用します。
const timeline = await client.agents.memory.timeline(AGENT_ID, {
userId: USER_ID,
// Optional: narrow to a date range
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やコンプライアンスエクスポートのために、ツリー階層を経由せずにユーザーのすべての生ファクトを一覧表示します。カテゴリによるフィルタリングをサポートしています。
// All facts for this user (paginated)
const facts = await client.agents.memory.listFacts(AGENT_ID, {
userId: USER_ID,
limit: 50,
offset: 0,
category: "user_preference", // optional filter
});
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", // what did the agent know at this moment?
});
console.log("Known facts at 2025-03-01:");
for (const fact of snapshot.facts) {
console.log(` ${fact.content}`);
}置換(supersession)の仕組み:ファクトが 更新されると、古いレコードは退役(削除ではなく)され、SupersedesIDポインターを持つ 新しいレコードが作成されます。タイムマシンはこのチェーンを再生して、任意のタイムスタンプでの 状態を再構築します。