ワーキングメモリ
会話履歴 や セマンティックリコール がエージェントの会話の記憶を助ける一方で、ワーキングメモリはインタラクションをまたいでユーザーに関する情報を持続的に保持します。
これは、エージェントのアクティブな思考やメモ帳のようなもので、ユーザーやタスクに関する重要な情報を常に手元に置いておく仕組みです。人が会話中に自然と相手の名前や好み、重要な詳細を覚えているのと似ています。
これは、常に関連性があり、常にエージェントが利用できるべき進行中の状態を維持するのに役立ちます。
ワーキングメモリは次の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,
},
},
}),
});
仕組み
作業記憶とは、エージェントが時間の経過とともに更新し、常に必要となる情報を蓄えるための 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', // すべてのユーザースレッドにわたってメモリが保持される
template: `# User Profile
- **Name**:
- **Location**:
- **Interests**:
- **Preferences**:
- **Long-term Goals**:
`,
},
},
});
ユースケース:
- ユーザーの嗜好を記憶するパーソナルアシスタント
- 顧客の状況を保持するカスタマーサービスボット
- 学習者の進捗を追跡する教育アプリ
エージェントでの使用
resource-scoped memory を使用する場合は、resourceId
パラメータを必ず渡してください:
// resource-scoped memory には resourceId が必須
const response = await agent.generate("Hello!", {
threadId: "conversation-123",
resourceId: "user-alice-456" // 異なるスレッド間で同一のユーザーを示す
});
ストレージアダプターのサポート
リソーススコープのワーキングメモリには、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 か lower 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: ... (設定しない)
},
},
});
スキーマが指定されている場合、エージェントはワーキングメモリを 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:
--- After user says "My name is **Sam** and I'm from **Berlin**" ---
# User Profile
- Name: Sam
- Location: Berlin
- Timezone:
--- After user adds "By the way I'm normally in **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"
});
// 応答: "Your blood type is O+."
作業メモリをプログラムで更新する
既存のスレッドの作業メモリを更新することもできます:
// スレッドのメタデータを更新して作業メモリを追加・変更する
await memory.updateThread({
id: "thread-123",
title: thread.title,
metadata: {
...thread.metadata,
workingMemory: `# 患者プロフィール
- 氏名: John Doe
- 血液型: O+
- アレルギー: ペニシリン、イブプロフェン // 更新
- 服用中の薬: リシノプリル 10mg(1日1回) // 追加
- 既往歴: 高血圧(コントロール良好)
`
}
});
メモリを直接更新する
別の方法として、updateWorkingMemory
メソッドを直接使用します:
await memory.updateWorkingMemory({
threadId: "thread-123",
resourceId: "user-456", // リソース単位のメモリで必須
workingMemory: "メモリ内容を更新しました..."
});
例
- 基本的なワーキングメモリ
- テンプレートを用いたワーキングメモリ
- スキーマを用いたワーキングメモリ
- リソースごとのワーキングメモリ - リソース単位のメモリ永続化を示す完全なサンプル