コンテンツにスキップ

セッション

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

  1. これまでに保存された会話アイテムを取得し、次のターンに先頭付加する
  2. 各実行完了後に新しい user 入力と assistant 出力を永続化する
  3. 新しい user テキストでランナーを呼び出す場合も、中断された RunState から再開する場合も、将来のターンのためにセッションを保持する

これにより、手動で toInputList() を呼び出したり、ターン間で履歴をつなぎ合わせる必要がなくなります。TypeScript SDK には 2 つの実装が同梱されています。Conversations API 用の OpenAIConversationsSession と、ローカル開発向けの MemorySession です。両者は Session インターフェースを共有しているため、独自のストレージバックエンドを差し替えることができます。Conversations API 以外の参考例としては、examples/memory/ 配下のサンプルセッションバックエンド(Prisma、ファイルバック、その他)をご覧ください。OpenAI Responses モデルを使用する場合は、responses.compact によって保存済みのトランスクリプトを自動で縮小するために、任意のセッションを OpenAIResponsesCompactionSession でラップします。

Tip: このページの OpenAIConversationsSession の例を実行するには、OPENAI_API_KEY 環境変数を設定する(またはセッションの構築時に apiKey を指定する)ことで、SDK が Conversations API を呼び出せるようにしてください。


OpenAIConversationsSession を使用して Conversations API とメモリを同期するか、他の任意の 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 実装へ切り替えても、他のコード変更は不要です。

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

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

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


ランナーにおけるセッションの使われ方

Section titled “ランナーにおけるセッションの使われ方”
  • 各実行の前に セッション履歴を取得し、新しいターンの入力とマージして、結合済みのリストをエージェントへ渡す
  • 非ストリーミング実行では 1 回の session.addItems() 呼び出しで、直近ターンの元の user 入力とモデル出力の両方を永続化する
  • ストリーミング実行では まず user 入力を書き込み、ターン完了後にストリーミング出力を追記する
  • 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() を呼び出すと最後のエントリを削除できます。エージェントを再実行する前の user 修正に便利です。


カスタムストレージとマージ挙動

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 “履歴と新規アイテムのマージ方法を制御する”

実行入力として 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];
},
});

文字列入力の場合、ランナーは履歴を自動でマージするため、コールバックは任意です。


承認とレジューム可能な実行の扱い

Section titled “承認とレジューム可能な実行の扱い”

Human-in-the-loop フローでは、承認待ちのために実行を一時停止することがよくあります。

const result = await runner.run(agent, 'Search the itinerary', {
session,
stream: true,
});
if (result.requiresApproval) {
// ... collect user feedback, then resume the agent in a later turn
const continuation = await runner.run(agent, result.state, { session });
console.log(continuation.finalOutput);
}

以前の RunState から再開すると、新しいターンは同じメモリレコードに追記され、単一の会話履歴が維持されます。Human in the loop (人間の介入)(HITL)フローとの互換性は完全に保たれます。承認チェックポイントは引き続き RunState を往復しつつ、セッションが完全なトランスクリプトを保持します。


OpenAI Responses の履歴を自動で圧縮する

Section titled “OpenAI Responses の履歴を自動で圧縮する”

OpenAIResponsesCompactionSession は任意の Session をデコレートし、OpenAI Responses API に依存してトランスクリプトを短く保ちます。各ターンの永続化後、ランナーは最新の responseIdrunCompaction に渡し、意思決定フックが true を返すと responses.compact を呼び出します。デフォルトのトリガーでは、少なくとも 10 件の非 user アイテムが蓄積された時点で 1 回圧縮します。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.2',
});
// 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.2',
// (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圧縮リクエストに使用するモデル
compactionMode'auto' | 'previous_response_id' | 'input'圧縮がサーバーのレスポンス連鎖を使うか、ローカルの入力アイテムを使うかの制御
shouldTriggerCompaction(context) => boolean | Promise<boolean>responseIdcompactionMode、候補アイテム、現在のセッションアイテムに基づくカスタムトリガーフック

runCompaction(args) のオプション:

オプションメモ
responseIdstringprevious_response_id モード用の最新 Responses API レスポンス ID
compactionMode'auto' | 'previous_response_id' | 'input'設定済みモードの呼び出し単位での任意の上書き
storeboolean直近の実行でサーバー状態を保存したかどうかを示す
forcebooleanshouldTriggerCompaction をバイパスして即時に圧縮する

低遅延ストリーミングのための手動圧縮

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.2',
});
// 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 を有効にして、圧縮の意思決定をトレースできます。