セッション
セッションは Agents SDK に 永続的なメモリレイヤー を提供します。Session インターフェースを実装する任意のオブジェクトを Runner.run に渡せば、残りは SDK が処理します。セッションが存在する場合、ランナーは自動的に次の処理を行います。
- 以前に保存された会話項目を取得し、次のターンの先頭に追加します。
- 各実行の完了後に、新しいユーザー入力とアシスタント出力を永続化します。
- 新しいユーザーテキストでランナーを呼び出す場合でも、中断された
RunStateから再開する場合でも、今後のターンでセッションを引き続き利用できるようにします。
これにより、toInputList() を手動で呼び出したり、ターン間で履歴をつなぎ合わせたりする必要がなくなります。TypeScript SDK には、Conversations API 用の OpenAIConversationsSession と、ローカル開発向けの MemorySession という 2 つの実装が付属しています。どちらも Session インターフェースを共有しているため、独自のストレージバックエンドに差し替えられます。Conversations API 以外の実装例については、examples/memory/ 配下のサンプルセッションバックエンド(Prisma、ファイルベースなど)を参照してください。OpenAI Responses モデルを使用する場合は、任意のセッションを OpenAIResponsesCompactionSession でラップすると、responses.compact を介して保存済みの会話履歴を自動的に縮小できます。
ヒント: このページの
OpenAIConversationsSessionのコード例を実行するには、SDK が Conversations API を呼び出せるように、OPENAI_API_KEY環境変数を設定してください。または、セッションの構築時にapiKeyを指定してください。
SDK にクライアント側メモリを管理させたい場合は、セッションを使用します。すでに conversationId または previousResponseId を使用して OpenAI のサーバー管理状態を利用している場合、通常は同じ会話履歴にセッションも併用する必要はありません。
クイックスタート
Section titled “クイックスタート”OpenAIConversationsSession を使用してメモリを Conversations API と同期するか、ほかの任意の Session 実装に差し替えます。
import { Agent, OpenAIConversationsSession, run } from '@openai/agents';
const agent = new Agent({ name: 'TourGuide', instructions: 'Answer with compact travel facts.',});
// Any object that implements the Session interface works here. This example uses// the built-in OpenAIConversationsSession, but you can swap in a custom Session.const session = new OpenAIConversationsSession();
const firstTurn = await run(agent, 'What city is the Golden Gate Bridge in?', { session,});console.log(firstTurn.finalOutput); // "San Francisco"
const secondTurn = await run(agent, 'What state is it in?', { session });console.log(secondTurn.finalOutput); // "California"同じセッションインスタンスを再利用すると、エージェントは各ターンの前に完全な会話履歴を受け取り、新しい項目も自動的に永続化されます。別の Session 実装に切り替える場合も、ほかのコードを変更する必要はありません。
ローカルデモ、テスト、またはプロセス内のチャット状態には、MemorySession が OpenAI と通信せずに同じインターフェースを提供します。
import { Agent, MemorySession, run } from '@openai/agents';
const agent = new Agent({ name: 'TourGuide', instructions: 'Answer with compact travel facts.',});
const session = new MemorySession();const result = await run(agent, 'What city is the Golden Gate Bridge in?', { session,});
console.log(result.finalOutput);OpenAIConversationsSession のコンストラクターオプション:
| オプション | 型 | 備考 |
|---|---|---|
conversationId | string | 必要になったときに新しく作成する代わりに、既存の会話を再利用します。 |
client | OpenAI | 設定済みの OpenAI クライアントを渡します。 |
apiKey | string | 内部 OpenAI クライアントの作成時に使用する API キーです。 |
baseURL | string | OpenAI 互換エンドポイントのベース URL です。 |
organization | string | リクエストで使用する OpenAI 組織 ID です。 |
project | string | リクエストで使用する OpenAI プロジェクト ID です。 |
MemorySession のコンストラクターオプション:
| オプション | 型 | 備考 |
|---|---|---|
sessionId | string | ログやテスト用の固定識別子です。デフォルトでは自動的に生成されます。 |
initialItems | AgentInputItem[] | 既存の履歴をセッションの初期値として設定します。 |
logger | Logger | デバッグ出力に使用するロガーを上書きします。 |
MemorySession はすべてをローカルプロセスのメモリに保存するため、プロセスが終了するとリセットされます。
セッションを構築する前に会話 ID を作成する必要がある場合は、 startOpenAIConversationsSession(client?) を使用し、返された ID を conversationId として渡します。
セッションの基本動作
Section titled “セッションの基本動作”ランナーによるセッションの使用
Section titled “ランナーによるセッションの使用”- 各実行前 にセッション履歴を取得し、新しいターンの入力とマージして、結合したリストをエージェントに渡します。
- 非ストリーミング実行後 に
session.addItems()を 1 回呼び出し、元のユーザー入力と直近のターンのモデル出力をまとめて永続化します。 - ストリーミング実行では 先にユーザー入力を書き込み、ターンが完了するとストリーミングされた出力を追加します。
RunResult.stateから再開する場合 は、承認やその他の中断からの再開時にも、同じsessionを渡し続けます。再開されたターンは、入力を再準備することなくメモリに追加されます。
履歴の確認と編集
Section titled “履歴の確認と編集”セッションにはシンプルな CRUD ヘルパーが用意されているため、「元に戻す」、「チャットをクリア」、監査などの機能を構築できます。
import { OpenAIConversationsSession } from '@openai/agents';import type { AgentInputItem } from '@openai/agents-core';
// Replace OpenAIConversationsSession with any other Session implementation that// supports get/add/pop/clear if you store history elsewhere.const session = new OpenAIConversationsSession({ conversationId: 'conv_123', // Resume an existing conversation if you have one.});
const history = await session.getItems();console.log(`Loaded ${history.length} prior items.`);
const followUp: AgentInputItem[] = [ { type: 'message', role: 'user', content: [{ type: 'input_text', text: 'Let’s continue later.' }], },];await session.addItems(followUp);
const undone = await session.popItem();
if (undone?.type === 'message') { console.log(undone.role); // "user"}
await session.clearSession();session.getItems() は、保存されている AgentInputItem[] を返します。popItem() を呼び出すと最後の項目を削除できます。これは、エージェントを再実行する前にユーザーの入力を修正する場合に便利です。
カスタムストレージとマージ動作
Section titled “カスタムストレージとマージ動作”独自ストレージの使用
Section titled “独自ストレージの使用”Session インターフェースを実装すると、Redis、DynamoDB、SQLite、またはその他のデータストアをメモリのバックエンドとして使用できます。必要なのは 5 つの非同期メソッドだけです。
import { Agent, run } from '@openai/agents';import { randomUUID } from '@openai/agents-core/_shims';import { getLogger } from '@openai/agents-core';import type { AgentInputItem, Session } from '@openai/agents-core';
/** * Minimal example of a Session implementation; swap this class for any storage-backed version. */export class CustomMemorySession implements Session { private readonly sessionId: string; private readonly logger: ReturnType<typeof getLogger>;
private items: AgentInputItem[];
constructor( options: { sessionId?: string; initialItems?: AgentInputItem[]; logger?: ReturnType<typeof getLogger>; } = {}, ) { this.sessionId = options.sessionId ?? randomUUID(); this.items = options.initialItems ? options.initialItems.map(cloneAgentItem) : []; this.logger = options.logger ?? getLogger('openai-agents:memory-session'); }
async getSessionId(): Promise<string> { return this.sessionId; }
async getItems(limit?: number): Promise<AgentInputItem[]> { if (limit === undefined) { const cloned = this.items.map(cloneAgentItem); this.logger.debug( `Getting items from memory session (${this.sessionId}): ${JSON.stringify(cloned)}`, ); return cloned; } if (limit <= 0) { return []; } const start = Math.max(this.items.length - limit, 0); const items = this.items.slice(start).map(cloneAgentItem); this.logger.debug( `Getting items from memory session (${this.sessionId}): ${JSON.stringify(items)}`, ); return items; }
async addItems(items: AgentInputItem[]): Promise<void> { if (items.length === 0) { return; } const cloned = items.map(cloneAgentItem); this.logger.debug( `Adding items to memory session (${this.sessionId}): ${JSON.stringify(cloned)}`, ); this.items = [...this.items, ...cloned]; }
async popItem(): Promise<AgentInputItem | undefined> { if (this.items.length === 0) { return undefined; } const item = this.items[this.items.length - 1]; const cloned = cloneAgentItem(item); this.logger.debug( `Popping item from memory session (${this.sessionId}): ${JSON.stringify(cloned)}`, ); this.items = this.items.slice(0, -1); return cloned; }
async clearSession(): Promise<void> { this.logger.debug(`Clearing memory session (${this.sessionId})`); this.items = []; }}
function cloneAgentItem<T extends AgentInputItem>(item: T): T { return structuredClone(item);}
const agent = new Agent({ name: 'MemoryDemo', instructions: 'Remember the running total.',});
// Using the above custom memory session implementation hereconst session = new CustomMemorySession({ sessionId: 'session-123-4567',});
const first = await run(agent, 'Add 3 to the total.', { session });console.log(first.finalOutput);
const second = await run(agent, 'Add 4 more.', { session });console.log(second.finalOutput);カスタムセッションを使用すると、保持ポリシーの適用、暗号化の追加、永続化前の各会話ターンへのメタデータの付加が可能です。
実行コンテキストによるカスタムセッションのスコープ設定
Section titled “実行コンテキストによるカスタムセッションのスコープ設定”カスタムセッションがストレージのルーティングやメタデータのためにアクティブな RunContext を必要とする場合は、RunContextAwareSession<TContext> を実装し、acceptsRunContext を true に設定します。ランナーは、ストリーミング時の永続化や再開された実行を含め、1 回の実行中に行われるすべての履歴操作へ同じコンテキストインスタンスを渡します。Session のみを実装するセッションでは、従来のメソッドシグネチャが維持され、追加の引数なしで呼び出されます。
import { Agent, run, type AgentInputItem, type RunContext, type RunContextAwareSession,} from '@openai/agents';
type TenantContext = { tenantId: string;};
class TenantSession implements RunContextAwareSession<TenantContext> { readonly acceptsRunContext = true; private readonly itemsByTenant = new Map<string, AgentInputItem[]>();
async getSessionId(): Promise<string> { return 'shared-tenant-session'; }
async getItems( limit?: number, runContext?: RunContext<TenantContext>, ): Promise<AgentInputItem[]> { const items = this.getTenantItems(runContext); return limit === undefined ? [...items] : items.slice(-limit); }
async addItems( items: AgentInputItem[], runContext?: RunContext<TenantContext>, ): Promise<void> { this.getTenantItems(runContext).push(...items); }
async popItem( runContext?: RunContext<TenantContext>, ): Promise<AgentInputItem | undefined> { return this.getTenantItems(runContext).pop(); }
async clearSession(runContext?: RunContext<TenantContext>): Promise<void> { this.itemsByTenant.set(this.getTenantId(runContext), []); }
private getTenantItems( runContext: RunContext<TenantContext> | undefined, ): AgentInputItem[] { const tenantId = this.getTenantId(runContext); const items = this.itemsByTenant.get(tenantId) ?? []; this.itemsByTenant.set(tenantId, items); return items; }
private getTenantId( runContext: RunContext<TenantContext> | undefined, ): string { if (!runContext) { throw new Error('TenantSession requires a run context.'); } return runContext.context.tenantId; }}
const agent = new Agent<TenantContext>({ name: 'Assistant', instructions: 'Reply concisely.',});const session = new TenantSession();
await run(agent, 'Remember that my favorite color is green.', { context: { tenantId: 'tenant-a' }, session,});
await run(agent, 'What is my favorite color?', { context: { tenantId: 'tenant-a' }, session,});OpenAIResponsesCompactionSession は、実行コンテキストを基盤セッションに転送しません。これらの機能を組み合わせる場合は、コンテキストのスコープごとに 1 つの圧縮セッションインスタンスを使用してください。
履歴と新しい項目のマージ方法の制御
Section titled “履歴と新しい項目のマージ方法の制御”AgentInputItem の配列を実行入力として渡す場合は、sessionInputCallback を指定して、保存済み履歴と決定論的にマージします。ランナーは既存の履歴を読み込み、モデルを呼び出す前 にコールバックを呼び出し、返された配列をそのターンの完全な入力としてモデルに渡します。このフックは、古い項目の削除、ツール結果の重複排除、モデルに見せたいコンテキストだけを残す処理に最適です。
import { Agent, OpenAIConversationsSession, run } from '@openai/agents';import type { AgentInputItem } from '@openai/agents-core';
const agent = new Agent({ name: 'Planner', instructions: 'Track outstanding tasks before responding.',});
// Any Session implementation can be passed here; customize storage as needed.const session = new OpenAIConversationsSession();
const todoUpdate: AgentInputItem[] = [ { type: 'message', role: 'user', content: [ { type: 'input_text', text: 'Add booking a hotel to my todo list.' }, ], },];
await run(agent, todoUpdate, { session, // function that combines session history with new input items before the model call sessionInputCallback: (history, newItems) => { const recentHistory = history.slice(-8); return [...recentHistory, ...newItems]; },});文字列入力の場合、ランナーが履歴を自動的にマージするため、コールバックは省略できます。コールバックは、ターン入力がすでに項目の配列である場合にのみ実行されます。
conversationId または previousResponseId も使用している場合は、コールバックの結果に現在のターンから少なくとも 1 つの新しい項目を残してください。これらのサーバー管理 API は、現在のターンの差分に依存しています。コールバックですべての新しい項目が削除された場合、SDK は空の差分を送信する代わりに元の新しい入力を復元し、警告をログに記録します。
再開可能な実行
Section titled “再開可能な実行”承認と再開可能な実行の処理
Section titled “承認と再開可能な実行の処理”人間の介入(HITL)フローでは、承認を待つために実行を一時停止することがよくあります。
import { Agent, MemorySession, Runner } from '@openai/agents';
const agent = new Agent({ name: 'Trip Planner', instructions: 'Plan trips and ask for approval before booking anything.',});
const runner = new Runner();const session = new MemorySession();
const result = await runner.run(agent, 'Search the itinerary', { session,});
if (result.interruptions?.length) { // ... collect user feedback, then resume the agent in a later turn. for (const interruption of result.interruptions) { result.state.approve(interruption); }
const continuation = await runner.run(agent, result.state, { session }); console.log(continuation.finalOutput);}以前の RunState から再開すると、新しいターンは同じメモリレコードに追加され、単一の会話履歴が維持されます。人間の介入(HITL)フローとの互換性も完全に維持されます。承認チェックポイントは引き続き RunState を介して受け渡され、セッションによって完全な会話履歴が保持されます。
高度な履歴圧縮
Section titled “高度な履歴圧縮”OpenAI Responses 履歴の自動圧縮
Section titled “OpenAI Responses 履歴の自動圧縮”OpenAIResponsesCompactionSession は任意の Session をデコレートし、OpenAI Responses API を使用して、保存された長い履歴を、それと同等の短い会話項目リストに置き換えます。永続化された各ターンの後、ランナーは最新の responseId を runCompaction に渡します。判定フックが true を返すと、runCompaction は responses.compact を呼び出します。compactionMode に応じて、リクエストは最新の Responses API チェーンまたはセッションの現在の項目から構築されます。デフォルトのトリガーでは、ユーザー以外の項目が 10 件以上蓄積すると圧縮されます。トークン数やカスタムヒューリスティクスに基づいて判定するには、shouldTriggerCompaction を上書きします。圧縮結果が返されると、デコレーターは基盤セッションをクリアし、縮小された項目リストで書き換えます。そのため、異なるサーバー管理履歴フローを使用する OpenAIConversationsSession との併用は避けてください。
import { Agent, MemorySession, OpenAIResponsesCompactionSession, run,} from '@openai/agents';
const agent = new Agent({ name: 'Support', instructions: 'Answer briefly and keep track of prior context.', model: 'gpt-5.4',});
// Wrap any Session to trigger responses.compact once history grows beyond your threshold.const session = new OpenAIResponsesCompactionSession({ // You can pass any Session implementation except OpenAIConversationsSession underlyingSession: new MemorySession(), // (optional) The model used for calling responses.compact API model: 'gpt-5.4', // (optional) your custom logic here shouldTriggerCompaction: ({ compactionCandidateItems }) => { return compactionCandidateItems.length >= 12; },});
await run(agent, 'Summarize order #8472 in one sentence.', { session });await run(agent, 'Remind me of the shipping address.', { session });
// Compaction runs automatically after each persisted turn. You can also force it manually.await session.runCompaction({ force: true });OpenAIResponsesCompactionSession のコンストラクターオプション:
| オプション | 型 | 備考 |
|---|---|---|
client | OpenAI | responses.compact に使用する OpenAI クライアントです。 |
underlyingSession | Session | 圧縮済み項目でクリアして書き換える基盤セッションストアです。デモ用にはデフォルトでインメモリセッションが使用され、OpenAIConversationsSession は指定できません。 |
model | OpenAI.ResponsesModel | 圧縮リクエストに使用するモデルです。デフォルトでは、SDK の現在のデフォルト OpenAI モデルが使用されます。 |
compactionMode | 'auto' | 'previous_response_id' | 'input' | 圧縮でサーバーレスポンスのチェーンとローカル入力項目のどちらを使用するかを制御します。 |
shouldTriggerCompaction | (context) => boolean | Promise<boolean> | responseId、compactionMode、候補項目、現在のセッション項目に基づくカスタムトリガーフックです。 |
compactionMode: 'previous_response_id' は、Responses API のレスポンス ID を使用してターンをすでに連結している場合に便利です。 compactionMode: 'input' は、現在のセッション項目から圧縮リクエストを再構築します。レスポンスチェーンを利用できない場合や、基盤セッションの内容を正とするデータにしたい場合に役立ちます。
runCompaction(args) のオプション:
| オプション | 型 | 備考 |
|---|---|---|
responseId | string | previous_response_id モードで使用する最新の Responses API レスポンス ID です。 |
compactionMode | 'auto' | 'previous_response_id' | 'input' | 設定済みモードを呼び出しごとに上書きするためのオプションです。 |
store | boolean | 直前の実行でサーバー状態が保存されたかどうかを示します。 |
force | boolean | shouldTriggerCompaction を迂回し、直ちに圧縮します。 |
低レイテンシーストリーミング向けの手動圧縮
Section titled “低レイテンシーストリーミング向けの手動圧縮”圧縮では基盤セッションをクリアして書き換えるため、SDK はストリーミング実行を完了する前に圧縮の終了を待ちます。圧縮処理が重い場合、最後の出力トークンから数秒間、result.completed が保留状態のままになることがあります。低レイテンシーのストリーミングや、より迅速なターン切り替えが必要な場合は、自動圧縮を無効にし、ターン間またはアイドル時間中に runCompaction を呼び出してください。
import { Agent, MemorySession, OpenAIResponsesCompactionSession, run,} from '@openai/agents';
const agent = new Agent({ name: 'Support', instructions: 'Answer briefly and keep track of prior context.', model: 'gpt-5.4',});
// Disable auto-compaction to avoid delaying stream completion.const session = new OpenAIResponsesCompactionSession({ underlyingSession: new MemorySession(), shouldTriggerCompaction: () => false,});
const result = await run(agent, 'Share the latest ticket update.', { session, stream: true,});
// Wait for the streaming run to finish before compacting.await result.completed;
// Choose force based on your own thresholds or heuristics, between turns or during idle time.await session.runCompaction({ force: true });履歴をアーカイブまたはハンドオフする前に縮小するには、いつでも runCompaction({ force: true }) を呼び出せます。圧縮の判定を追跡するには、DEBUG=openai-agents:openai:compaction でデバッグログを有効にしてください。