コンテンツにスキップ

セッション

セッションは Agents SDK に 永続的なメモリレイヤー を提供します。Session インターフェースを実装する任意のオブジェクトを Runner.run に渡すと、残りは SDK が処理します。セッションが存在する場合、ランナーは自動的に次の処理を行います。

  1. 以前に保存された会話項目を取得し、次のターンの先頭に追加します。
  2. 各実行の完了後、新しいユーザー入力とアシスタント出力を永続化します。
  3. 新しいユーザーテキストでランナーを呼び出す場合でも、中断された 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 のサーバー管理状態をすでに使用している場合、通常は同じ会話履歴に対してセッションも併用する必要はありません。


Conversations API とメモリを同期するには OpenAIConversationsSession を使用します。または、ほかの任意の Session 実装に置き換えられます。

Conversations API のセッションメモリとしての利用
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 実装に切り替える場合でも、ほかのコードを変更する必要はありません。

ローカルデモ、テスト、またはプロセス内のチャット状態には、OpenAI と通信せずに同じインターフェースを提供する MemorySession を使用できます。

ローカル状態での MemorySession の利用
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 のコンストラクターオプション:

オプション備考
conversationIdstring遅延作成する代わりに、既存の会話を再利用します。
clientOpenAI事前設定済みの OpenAI クライアントを渡します。
apiKeystring内部の OpenAI クライアントを作成するときに使用する API キーです。
baseURLstringOpenAI 互換エンドポイントのベース URL です。
organizationstringリクエストに使用する OpenAI 組織 ID です。
projectstringリクエストに使用する OpenAI プロジェクト ID です。

MemorySession のコンストラクターオプション:

オプション備考
sessionIdstringログまたはテスト用の安定した識別子です。デフォルトでは自動生成されます。
initialItemsAgentInputItem[]既存の履歴を使ってセッションを初期化します。
loggerLoggerデバッグ出力に使用するロガーを上書きします。

MemorySession はすべてをローカルプロセスのメモリに保存するため、プロセスが終了するとリセットされます。

セッションを構築する前に会話 ID を作成する必要がある場合は、startOpenAIConversationsSession(client?) を使用し、返された ID を conversationId として渡します。


ランナーによるセッションの利用

Section titled “ランナーによるセッションの利用”
  • 各実行の前に、セッション履歴を取得して新しいターンの入力とマージし、結合したリストをエージェントに渡します。
  • 非ストリーミング実行の後にsession.addItems() を 1 回呼び出し、元のユーザー入力と最新ターンのモデル出力の両方を永続化します。
  • ストリーミング実行では、最初にユーザー入力を書き込み、ターンの完了後にストリーミングされた出力を追加します。
  • 承認やその他の中断のために RunResult.state から再開する場合は、同じ session を引き続き渡してください。再開されたターンは、入力を再準備することなくメモリに追加されます。

出力ガードレールが終端となる関数ツールの結果を拒否した場合、ランナーは拒否された元の結果を新しいセッション履歴に追加しません。ランナーは、認識された関数呼び出しと結果のペアに対して再実行しても安全なプレースホルダーを永続化し、所有関係を証明できない出力を保存する代わりに、現在のレスポンス内の曖昧な末尾部分を省略します。この保護は、外部ツールの副作用を元に戻したり、アプリケーションがセッションの処理経路外に保存したデータを消去したりするものではありません。


セッションではシンプルな 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 “カスタムストレージとマージ動作”

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 here
const 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 “カスタム永続化の原子性と冪等性”

履歴の更新と操作識別子の記録を原子的に実行できるバックエンドでは、オプションの SessionHistoryTransactionAwareSession インターフェースを実装します。その applyHistoryTransaction(...) メソッドは、安定した operationId と、次の 2 種類のトランザクション形式のいずれかを受け取ります。

  • append_items は、履歴項目のグループを追加します。
  • replace_suffix は、現在保存されている末尾部分が引き続き想定された内容と一致する場合に限り、その末尾部分を置き換えます。

操作識別子と履歴の変更は、同じバックエンドトランザクション内で永続化します。同じトランザクションで操作識別子が繰り返された場合は、2 回適用することなく成功する必要があります。異なる内容で識別子を再利用した場合や、想定された末尾部分が変更された後に replace_suffix を適用した場合は、履歴を変更せずに失敗する必要があります。これにより、ランナーは再試行や再開可能な出力ガードレールのフロー全体で、永続化された出力を安全に整合させられます。

MemorySession は、この契約をリファレンス実装として実装しています。既存の Session 実装は、これを実装しなくても引き続き有効ですが、トランザクション対応の永続化経路は使用されません。

実行コンテキストによるカスタムセッションのスコープ設定

Section titled “実行コンテキストによるカスタムセッションのスコープ設定”

カスタムセッションがストレージのルーティングやメタデータのためにアクティブな RunContext を必要とする場合は、RunContextAwareSession<TContext> を実装し、acceptsRunContexttrue に設定します。ランナーは、ストリーミング時の永続化や再開された実行を含め、実行中のすべての履歴操作に同じコンテキストインスタンスを渡します。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 つの圧縮セッションインスタンスを使用してください。


実行入力として AgentInputItem の配列を渡す場合は、保存済みの履歴と確定的にマージするための sessionInputCallback を指定します。ランナーは既存の履歴を読み込み、モデルを呼び出す前にコールバックを呼び出し、返された配列をそのターンの完全な入力としてモデルに渡します。このフックは、古い項目の削減、ツール結果の重複排除、またはモデルに見せたいコンテキストだけを強調する場合に適しています。

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 は空の差分を送信する代わりに元の新規入力を復元し、警告をログに記録します。


人間の介入(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 を介して往復し、その間もセッションによって完全な会話履歴が維持されます。

承認された関数ツールの結果が最終出力になり得る場合、出力ガードレールはより厳格な境界を設けます。現在の RunState スキーマは、SDK が証明可能な場合、現在のレスポンスで生成された項目の正確な所有関係を記録します。これにより、出力を伴うサポート対象の承認チェックポイントは、セッション履歴を維持したまま再開できます。SDK は、モデル、ツール、またはセッションで追加の副作用が発生する前に、その所有関係を検証します。所有関係が欠落している、無効である、または曖昧である古いスナップショットやチェックポイントは、引き続き安全側に倒して失敗します。SDK がこの状態を報告した場合は、安全な入力から新しい実行を開始してください。元の項目を再実行して回避しないでください。


OpenAIResponsesCompactionSession は任意の Session をデコレートし、OpenAI Responses API を使用して、長い保存済み履歴を同等の短い会話項目リストに置き換えます。永続化された各ターンの後、ランナーは最新の responseIdrunCompaction に渡します。runCompaction は、判定フックが true を返すと responses.compact を呼び出します。リクエストは compactionMode に応じて、最新の Responses API チェーンまたはセッションの現在の項目から構築されます。デフォルトのトリガーでは、ユーザー以外の項目が 10 個以上蓄積されると圧縮されます。トークン数やカスタムヒューリスティックに基づいて判定するには、shouldTriggerCompaction を上書きします。圧縮から結果が返されると、デコレーターは基になるセッションを消去し、削減された項目リストで書き直します。そのため、異なるサーバー管理の履歴フローを使用する OpenAIConversationsSession とは組み合わせないでください。

OpenAIResponsesCompactionSession によるセッションのデコレート
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 のコンストラクターオプション:

オプション備考
clientOpenAIresponses.compact に使用する OpenAI クライアントです。
underlyingSessionSession圧縮済みの項目で消去および書き直す、基になるセッションストアです。デモではデフォルトでインメモリセッションが使用され、OpenAIConversationsSession は指定できません。
modelOpenAI.ResponsesModel圧縮リクエストに使用するモデルです。デフォルトでは SDK の現在のデフォルト OpenAI モデルが使用されます。
compactionMode'auto' | 'previous_response_id' | 'input'圧縮でサーバーのレスポンスチェーンとローカル入力項目のどちらを使用するかを制御します。
shouldTriggerCompaction(context) => boolean | Promise<boolean>responseIdcompactionMode、圧縮候補の項目、および現在のセッション項目に基づくカスタムトリガーフックです。

compactionMode: 'previous_response_id' は、Responses API のレスポンス ID を使ってターンをすでに連結している場合に便利です。compactionMode: 'input' は、代わりに現在のセッション項目から圧縮リクエストを再構築します。これは、レスポンスチェーンを利用できない場合や、基になるセッションの内容を信頼できる情報源にしたい場合に役立ちます。

runCompaction(args) のオプション:

オプション備考
responseIdstringprevious_response_id モードで使用する、最新の Responses API レスポンス ID です。
compactionMode'auto' | 'previous_response_id' | 'input'設定済みモードを呼び出しごとに上書きするオプションです。
storeboolean最後の実行でサーバー状態を保存したかどうかを示します。
forcebooleanshouldTriggerCompaction を迂回して、すぐに圧縮します。

OpenAIResponsesCompactionSession は、同じラッパーインスタンスを介して行われる変更を直列化します。runCompaction()addItems()popItem()clearSession() の呼び出しは呼び出し順に実行されます。また、操作が拒否された後もキューは処理を続けるため、後続のラッパーによる変更が置換やロールバックと交錯することはありません。置換またはロールバックに成功すると、ラッパーにキャッシュされた履歴と基になるセッションの整合性が維持されます。置換と復元の両方に失敗した場合は、続行する前に基になるセッションを復旧してください。この順序保証は、別のラッパーインスタンスや underlyingSession に対する直接の変更とは連携しません。アプリケーション内でこれらのアクセス経路を調整してください。

自動圧縮では、実行が読み込んだ履歴スナップショットを引き続き所有していることも検証されます。自動圧縮の開始前に別の操作がラッパーの履歴を変更した場合、SDK は新しい履歴を維持し、その圧縮処理をスキップします。後続の実行または明示的な runCompaction() の呼び出しで、更新された履歴を圧縮できます。

低レイテンシーストリーミング向けの手動圧縮

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 でデバッグログを有効にします。