ワーキングメモリ
conversation history と semantic recall はエージェントが会話内容を記憶するのに役立ちますが、ワーキングメモリは、やり取りをまたいでユーザーに関する情報を持続的に保持するための仕組みです。
エージェントの「今考えていること」やメモ帳のようなものだと考えてください—ユーザーやタスクに関して常に参照できる重要な情報です。人が会話中に相手の名前や好み、重要なポイントを自然に覚えているのと似ています。
これは、常に関連し、常にエージェントが利用できるべき進行中の状態を維持するのに役立ちます。
ワーキングメモリは2つのスコープで保持できます:
- スレッドスコープ(デフォルト): メモリは会話スレッドごとに分離されます
- リソーススコープ: 同一ユーザーのすべての会話スレッドをまたいでメモリが保持されます
重要: スコープを切り替えると、もう一方のスコープのメモリはエージェントから見えません。スレッドスコープのメモリはリソーススコープのメモリと完全に分離されています。
クイックスタート
ワーキングメモリ付きのエージェントをセットアップする最小例は次のとおりです:
import { Agent } from "@mastra/core/agent";
import { Memory } from "@mastra/memory";
import { openai } from "@ai-sdk/openai";
// ワーキングメモリを有効にしたエージェントを作成
const agent = new Agent({
name: "PersonalAssistant",
instructions: "You are a helpful personal assistant.",
model: openai("gpt-4o"),
memory: new Memory({
options: {
workingMemory: {
enabled: true,
},
},
}),
});
仕組み
Working memory は、エージェントが時間とともに更新し、継続的に有用な情報を保持できる Markdown テキストのブロックです。
メモリの永続スコープ
ワーキングメモリには2つのスコープがあり、会話間でのメモリの持続方法を選択できます。
スレッドスコープのメモリ(デフォルト)
デフォルトでは、ワーキングメモリは個々の会話スレッド単位でスコープされます。各スレッドは独立したメモリを保持します。
const memory = new Memory({
storage,
options: {
workingMemory: {
enabled: true,
scope: 'thread', // Default - memory is isolated per thread
template: `# User Profile
- **Name**:
- **Interests**:
- **Current Goal**:
`,
},
},
});
ユースケース:
- 別々のトピックに関する個別の会話
- 一時的またはセッション固有の情報
- 各スレッドでワーキングメモリは必要だが、スレッド同士が短命かつ無関係なワークフロー
リソーススコープのメモリ
リソーススコープのメモリは、同一ユーザー(resourceId)についてすべての会話スレッドをまたいで保持され、永続的なユーザーメモリを実現します。
const memory = new Memory({
storage,
options: {
workingMemory: {
enabled: true,
scope: 'resource', // Memory persists across all user threads
template: `# User Profile
- **Name**:
- **Location**:
- **Interests**:
- **Preferences**:
- **Long-term Goals**:
`,
},
},
});
ユースケース:
- ユーザーの嗜好を記憶するパーソナルアシスタント
- 顧客コンテキストを維持するカスタマーサービスボット
- 学習者の進捗を追跡する教育アプリ
Agentとの併用
リソーススコープのメモリを使用する場合は、必ず resourceId
パラメータを渡してください。
// Resource-scoped memory requires resourceId
const response = await agent.generate("Hello!", {
threadId: "conversation-123",
resourceId: "user-alice-456" // Same user across different threads
});
ストレージアダプターのサポート
リソース単位のワーキングメモリには、mastra_resources
テーブルに対応した特定のストレージアダプターが必要です。
✅ 対応しているストレージアダプター
- LibSQL (
@mastra/libsql
) - PostgreSQL (
@mastra/pg
) - Upstash (
@mastra/upstash
)
カスタムテンプレート
テンプレートは、エージェントがワーキングメモリで追跡・更新すべき情報の指針となります。テンプレートが指定されていない場合はデフォルトが使用されますが、通常はエージェントの具体的なユースケースに合わせ、最も重要な情報を確実に記憶できるようカスタムテンプレートを定義することをおすすめします。
以下はカスタムテンプレートの例です。この例では、ユーザーが該当情報を含むメッセージを送信したタイミングで、エージェントはユーザーの名前、所在地、タイムゾーンなどを保存します:
const memory = new Memory({
options: {
workingMemory: {
enabled: true,
template: `
# User Profile
## Personal Info
- Name:
- Location:
- Timezone:
## Preferences
- Communication Style: [e.g., Formal, Casual]
- Project Goal:
- Key Deadlines:
- [Deadline 1]: [Date]
- [Deadline 2]: [Date]
## Session State
- Last Task Discussed:
- Open Questions:
- [Question 1]
- [Question 2]
`,
},
},
});
効果的なテンプレートの設計
適切に構造化されたテンプレートは、エージェントが情報を解釈・更新しやすくします。テンプレートは、アシスタントに常に最新の状態で保ってほしい「簡易フォーム」として扱いましょう。
- 短く要点を押さえたラベル。 段落や非常に長い見出しは避けます。ラベルは簡潔に(例:
## Personal Info
や- Name:
)して、更新を読みやすくし、切り捨てを避けましょう。 - 大文字・小文字の統一。 大文字小文字が不一致(
Timezone:
とtimezone:
)だと更新が乱雑になります。見出しや箇条書きのラベルは、Title Case か小文字のいずれかに統一してください。 - プレースホルダーテキストはシンプルに。
[e.g., Formal]
や[Date]
のようなヒントを使って、LLM が適切な箇所を正しく埋められるようにします。 - 非常に長い値は省略。 短い形式で足りる場合は、正式名称の全文ではなく、
- Name: [First name or nickname]
や- Address (short):
のようなガイダンスを含めて簡略化しましょう。 - 更新ルールは
instructions
に記載。 エージェントのinstructions
フィールドで、テンプレートの各箇所をいつ・どのように入力・クリアするかを明示的に指示できます。
代替テンプレートスタイル
必要な項目が少ない場合は、より短い単一ブロックを使用します:
const basicMemory = new Memory({
options: {
workingMemory: {
enabled: true,
template: `User Facts:\n- Name:\n- Favorite Color:\n- Current Topic:`,
},
},
});
より叙述的なスタイルを好む場合は、主要な事実を短い段落形式で保存することもできます:
const paragraphMemory = new Memory({
options: {
workingMemory: {
enabled: true,
template: `Important Details:\n\nKeep a short paragraph capturing the user's important facts (name, main goal, current task).`,
},
},
});
構造化ワーキングメモリ
ワーキングメモリは、Markdown テンプレートの代わりに構造化スキーマを使って定義することもできます。これにより、追跡すべきフィールドと型を Zod のスキーマで正確に指定できます。スキーマを使用すると、エージェントはスキーマに一致する JSON オブジェクトとしてワーキングメモリを参照・更新します。
重要: template
と schema
はどちらか一方のみを指定し、両方を同時に指定しないでください。
例: スキーマベースのワーキングメモリ
import { z } from 'zod';
import { Memory } from '@mastra/memory';
const userProfileSchema = z.object({
name: z.string().optional(),
location: z.string().optional(),
timezone: z.string().optional(),
preferences: z.object({
communicationStyle: z.string().optional(),
projectGoal: z.string().optional(),
deadlines: z.array(z.string()).optional(),
}).optional(),
});
const memory = new Memory({
options: {
workingMemory: {
enabled: true,
schema: userProfileSchema,
// template: ... (do not set)
},
},
});
スキーマが指定されている場合、エージェントはワーキングメモリを JSON オブジェクトとして受け取り、次のようになります:
{
"name": "Sam",
"location": "Berlin",
"timezone": "CET",
"preferences": {
"communicationStyle": "Formal",
"projectGoal": "Launch MVP",
"deadlines": ["2025-07-01"]
}
}
Template と Schema の選び方
- エージェントに、ユーザープロファイルやスクラッチパッドのような自由形式のテキストブロックとしてメモリを保持させたい場合は、template(Markdown)を使用します。
- 検証可能で、JSONとしてプログラムからアクセスできる構造化かつ型安全なデータが必要な場合は、schemaを使用します。
- 同時に有効にできるモードは1つだけです。
template
とschema
を同時に設定することはできません。
例: マルチステップの保持
以下は、短いやり取りの中で User Profile
テンプレートがどのように更新されるかの簡略図です:
# User Profile
## Personal Info
- Name:
- Location:
- Timezone:
--- ユーザーが「私の名前は **Sam** で、**Berlin** 出身です」と言った後 ---
# User Profile
- Name: Sam
- Location: Berlin
- Timezone:
--- ユーザーが「ちなみに、普段は **CET** です」と付け加えた後 ---
# User Profile
- Name: Sam
- Location: Berlin
- Timezone: CET
エージェントは、作業メモリに保存されているため、以降の応答で Sam
や Berlin
を改めて尋ねることなく参照できます。
期待どおりにエージェントが作業メモリを適切に更新しない場合は、エージェントの instructions
設定に、このテンプレートをどのように・いつ使用するかについてのシステム指示を追加してください。
初期ワーキングメモリの設定
エージェントは通常 updateWorkingMemory
ツールでワーキングメモリを更新しますが、スレッドの作成時や更新時に初期ワーキングメモリをプログラムから設定することもできます。これは、ユーザーの名前や嗜好など、毎回のリクエストで渡さずにエージェントが参照できるようにしておきたいユーザーデータをあらかじめ投入しておくのに便利です。
スレッドのメタデータ経由でワーキングメモリを設定する
スレッドを作成する際、メタデータの workingMemory
キーで初期ワーキングメモリを指定できます:
// 初期ワーキングメモリを持つスレッドを作成
const thread = await memory.createThread({
threadId: "thread-123",
resourceId: "user-456",
title: "Medical Consultation",
metadata: {
workingMemory: `# Patient Profile
- Name: John Doe
- Blood Type: O+
- Allergies: Penicillin
- Current Medications: None
- Medical History: Hypertension (controlled)
`
}
});
// エージェントは以降のすべてのメッセージでこの情報にアクセスできます
await agent.generate("What's my blood type?", {
threadId: thread.id,
resourceId: "user-456"
});
// Response: "Your blood type is O+."
ワーキングメモリをプログラムから更新する
既存のスレッドのワーキングメモリを更新することもできます:
// ワーキングメモリを追加/変更するためにスレッドのメタデータを更新
await memory.updateThread({
id: "thread-123",
title: thread.title,
metadata: {
...thread.metadata,
workingMemory: `# Patient Profile
- Name: John Doe
- Blood Type: O+
- Allergies: Penicillin, Ibuprofen // Updated
- Current Medications: Lisinopril 10mg daily // Added
- Medical History: Hypertension (controlled)
`
}
});
直接メモリを更新する
または、updateWorkingMemory
メソッドを直接使用します:
await memory.updateWorkingMemory({
threadId: "thread-123",
resourceId: "user-456", // リソーススコープのメモリに必須
workingMemory: "Updated memory content..."
});
例
- 基本的なワーキングメモリ
- テンプレート付きワーキングメモリ
- スキーマ付きワーキングメモリ
- リソース単位のワーキングメモリ - リソーススコープのメモリ永続化を示す完全な例