Working Memory
会話履歴とセマンティック・リコールがエージェントが会話を記憶するのに役立つ一方で、ワーキングメモリはエージェントがインタラクション間でユーザーに関する永続的な情報を維持することを可能にします。
これをエージェントのアクティブな思考やメモ帳として考えてください - ユーザーやタスクについて利用可能に保持している重要な情報です。これは、人が会話中に自然に誰かの名前、好み、または重要な詳細を覚えている方法と似ています。
これは、常に関連性があり、エージェントが常に利用できるべき継続的な状態を維持するのに役立ちます。
ワーキングメモリは2つの異なるスコープで永続化できます:
- Thread-scoped(デフォルト):メモリは会話スレッドごとに分離されます
- Resource-scoped:メモリは同じユーザーのすべての会話スレッド間で永続化されます
重要: スコープ間の切り替えは、エージェントが他のスコープからのメモリを見ることができないことを意味します - thread-scopedメモリはresource-scopedメモリから完全に分離されています。
クイックスタート
以下は、ワーキングメモリを設定したエージェントの最小限の例です:
import { Agent } from "@mastra/core/agent";
import { Memory } from "@mastra/memory";
import { openai } from "@ai-sdk/openai";
// Create agent with working memory enabled
const agent = new Agent({
name: "PersonalAssistant",
instructions: "You are a helpful personal assistant.",
model: openai("gpt-4o"),
memory: new Memory({
options: {
workingMemory: {
enabled: true,
},
},
}),
});
仕組み
ワーキングメモリは、エージェントが継続的に関連する情報を保存するために時間の経過とともに更新できる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**:
`,
},
},
});
使用例:
- ユーザーの好みを記憶するパーソナルアシスタント
- 顧客のコンテキストを維持するカスタマーサービスボット
- 学生の進捗を追跡する教育アプリケーション
Agentsでの使用
リソーススコープメモリを使用する場合は、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:
)は、更新を乱雑にする可能性があります。見出しや箇条書きのラベルには、タイトルケースまたは小文字を一貫して使用してください。 - プレースホルダーテキストはシンプルに。
[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: ... (設定しない)
},
},
});
スキーマが提供されると、エージェントはワーキングメモリを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**で、**ベルリン**出身です」と言った後 ---
# User Profile
- Name: Sam
- Location: Berlin
- Timezone:
--- ユーザーが「ちなみに普段は**CET**にいます」と追加した後 ---
# User Profile
- Name: Sam
- Location: Berlin
- Timezone: CET
エージェントは情報を再度要求することなく、後の応答でSam
やBerlin
を参照できるようになります。これは、その情報がワーキングメモリに保存されているためです。
エージェントが期待通りにワーキングメモリを更新していない場合は、エージェントのinstructions
設定でこのテンプレートを_どのように_、_いつ_使用するかについてのシステム指示を追加することができます。
例
- ストリーミングワーキングメモリ
- ワーキングメモリテンプレートの使用
- ワーキングメモリスキーマの使用
- リソース別ワーキングメモリ - リソーススコープのメモリ永続化を示す完全な例