エージェントの実行
エージェントはそれ自体では何もしません。Runner クラスまたは run() ユーティリティで 実行 します。
ターンの実行、イベントのストリーミング、会話状態の管理を行いたい場合は、まず エージェント を読んだあとにこのページを読んでください。エージェントの定義方法をまだ検討中なら、先に エージェント から始めてください。
import { Agent, run } from '@openai/agents';
const agent = new Agent({ name: 'Assistant', instructions: 'You are a helpful assistant',});
const result = await run( agent, 'Write a haiku about recursion in programming.',);console.log(result.finalOutput);
// Code within the code,// Functions calling themselves,// Infinite loop's dance.カスタム runner が不要な場合は、run() ユーティリティも使えます。これはシングルトンのデフォルト Runner インスタンスを実行します。
または、独自の runner インスタンスを作成することもできます。
import { Agent, Runner } from '@openai/agents';
const agent = new Agent({ name: 'Assistant', instructions: 'You are a helpful assistant',});
// You can pass custom configuration to the runnerconst runner = new Runner();
const result = await runner.run( agent, 'Write a haiku about recursion in programming.',);console.log(result.finalOutput);
// Code within the code,// Functions calling themselves,// Infinite loop's dance.エージェントを実行すると、最終出力と実行の完全な履歴を含む 実行結果 オブジェクトを受け取ります。
Runner のライフサイクルと設定
Section titled “Runner のライフサイクルと設定”エージェントループ
Section titled “エージェントループ”Runner の run メソッドを使うときは、開始エージェントと入力を渡します。入力は文字列(ユーザーメッセージとして扱われます)か、OpenAI Responses API の項目である入力項目のリストのいずれかです。
その後、runner は次のループを実行します。
- 現在の入力で、現在のエージェントのモデルを呼び出す
- LLM の応答を検査する
- 最終出力 → 返す
- ハンドオフ → 新しいエージェントに切り替え、蓄積済みの会話履歴を保持し、1 へ戻る
- ツール呼び出し → ツールを実行し、結果を会話に追加し、1 へ戻る
maxTurnsに達したらMaxTurnsExceededErrorを投げる
Runner のライフサイクル
Section titled “Runner のライフサイクル”アプリ起動時に Runner を作成し、リクエスト間で再利用してください。インスタンスには、モデルプロバイダーやトレーシングオプションなどのグローバル設定が保持されます。まったく異なる設定が必要な場合にのみ、別の Runner を作成してください。単純なスクリプトなら、内部でデフォルト runner を使う run() を呼ぶこともできます。
run() メソッドへの入力は、実行開始に使う初期エージェント、実行入力、オプションのセットです。
入力は、文字列(ユーザーメッセージとして扱われます)、入力項目 のリスト、または 人間の介入(HITL) エージェントを構築している場合は RunState オブジェクトのいずれかです。
追加オプションは次のとおりです。
| Option | Default | Description |
|---|---|---|
stream | false | true の場合、呼び出しは StreamedRunResult を返し、モデルから到着したイベントを逐次発行します |
context | – | すべてのツール / ガードレール / ハンドオフに渡されるコンテキストオブジェクト。詳細は コンテキスト管理ガイド を参照してください |
maxTurns | 10 | 安全上限。到達すると MaxTurnsExceededError を投げます |
signal | – | キャンセル用の AbortSignal |
session | – | セッション永続化実装。セッションガイド を参照してください |
sessionInputCallback | – | セッション履歴と新規入力のカスタムマージロジック。モデル呼び出し前に実行されます。セッション を参照してください |
callModelInputFilter | – | モデル呼び出し直前にモデル入力(項目 + 任意の instructions)を編集するフック。Call model input filter を参照してください |
toolErrorFormatter | – | モデルに返すツール承認拒否メッセージをカスタマイズするフック。Tool error formatter を参照してください |
reasoningItemIdPolicy | – | 以前の実行項目をモデル入力に戻す際、reasoning-item の id を保持するか省略するかを制御します。Reasoning item ID policy を参照してください |
tracing | – | 実行単位のトレーシング設定上書き(例: export API key) |
errorHandlers | – | サポートされる実行時エラー(現在は maxTurns)のハンドラー。Error handlers を参照してください |
conversationId | – | サーバー側会話を再利用します(OpenAI Responses API + Conversations API のみ) |
previousResponseId | – | 会話を作らずに前回の Responses API 呼び出しから継続します(OpenAI Responses API のみ) |
ストリーミング
Section titled “ストリーミング”ストリーミングを使うと、LLM 実行中のイベントも受け取れます。ストリーム開始後、StreamedRunResult には生成された新規出力を含む実行の完全情報が入ります。ストリーミングイベントは for await ループで反復できます。詳細は ストリーミングガイド を参照してください。
独自の Runner インスタンスを作る場合は、runner を設定するために RunConfig オブジェクトを渡せます。
| Field | Type | Purpose |
|---|---|---|
model | string | Model | 実行内の すべて のエージェントに特定モデルを強制します |
modelProvider | ModelProvider | モデル名を解決します。デフォルトは OpenAI provider です |
modelSettings | ModelSettings | エージェント単位設定を上書きするグローバル調整パラメーター。オプトインのリトライ設定を含む詳細は モデルガイド を参照してください |
handoffInputFilter | HandoffInputFilter | ハンドオフ実行時に入力項目を変更します(ハンドオフ側で未定義の場合) |
inputGuardrails | InputGuardrail[] | 初期 ユーザー入力に適用されるガードレール |
outputGuardrails | OutputGuardrail[] | 最終 出力に適用されるガードレール |
tracingDisabled | boolean | OpenAI Tracing を完全に無効化します |
traceIncludeSensitiveData | boolean | span は送信しつつ、トレースから LLM / ツールの入力と出力を除外します |
workflowName | string | Traces ダッシュボードに表示され、関連実行のグルーピングに役立ちます |
traceId / groupId | string | SDK に生成させず、trace または group ID を手動指定します |
traceMetadata | Record<string, string> | すべての span に付与する任意メタデータ |
tracing | TracingConfig | 実行単位のトレーシング上書き(例: export API key) |
sessionInputCallback | SessionInputCallback | この runner 上の全実行に対するデフォルト履歴マージ戦略 |
callModelInputFilter | CallModelInputFilter | 各モデル呼び出し前にモデル入力を編集するグローバルフック |
toolErrorFormatter | ToolErrorFormatter | モデルに返すツール承認拒否メッセージをカスタマイズするグローバルフック |
reasoningItemIdPolicy | ReasoningItemIdPolicy | 生成済み項目を後続モデル呼び出しへ再投入する際、reasoning-item の id を保持または省略するデフォルトポリシー |
状態と会話管理
Section titled “状態と会話管理”メモリ戦略の選択
Section titled “メモリ戦略の選択”状態を次ターンへ持ち越す一般的な方法は 4 つあります。
| Strategy | Where state lives | Best for | What you pass on the next turn |
|---|---|---|---|
result.history | アプリのメモリ | 小規模チャットループ、完全な手動制御、任意のプロバイダー | result.history |
session | ストレージ + SDK | 永続チャット状態、再開可能実行、カスタムストア | 同じ session インスタンス(またはストアバックドなもの) |
conversationId | OpenAI Conversations API | worker / サービス間で共有するサーバー側状態 | 同じ conversationId と新しいユーザーターンのみ |
previousResponseId | OpenAI Responses API のみ | 会話を作らない最小構成のサーバー管理継続 | result.lastResponseId と新しいユーザーターンのみ |
result.history と session はクライアント管理です。conversationId と previousResponseId は OpenAI 管理で、OpenAI Responses API 使用時のみ適用されます。ほとんどのアプリでは、会話ごとに永続化戦略を 1 つ選んでください。クライアント管理履歴とサーバー管理状態を混在させると、意図的に統合しない限りコンテキストが重複する可能性があります。
会話 / チャットスレッド
Section titled “会話 / チャットスレッド”runner.run()(または run() ユーティリティ)の各呼び出しは、アプリレベル会話の 1 ターン を表します。RunResult のどこまでをエンドユーザーに見せるかは選べます。finalOutput のみの場合もあれば、生成項目すべての場合もあります。
import { Agent, run } from '@openai/agents';import type { AgentInputItem } from '@openai/agents';
let thread: AgentInputItem[] = [];
const agent = new Agent({ name: 'Assistant',});
async function userSays(text: string) { const result = await run( agent, thread.concat({ role: 'user', content: text }), );
thread = result.history; // Carry over history + newly generated items return result.finalOutput;}
await userSays('What city is the Golden Gate Bridge in?');// -> "San Francisco"
await userSays('What state is it in?');// -> "California"対話版は chat example を参照してください。
サーバー管理会話
Section titled “サーバー管理会話”毎ターンでローカル会話履歴全体を送る代わりに、OpenAI Responses API に会話履歴を永続化させることができます。これは長い会話や複数サービスを調整する場合に有用です。以下のいずれのサーバー管理方式でも、各リクエストでは新しいターンの入力だけを渡します。API が過去状態を再利用します。詳細は Conversation state guide を参照してください。
OpenAI はサーバー側状態再利用の方法を 2 つ提供します。
1. 会話全体に対する conversationId
Section titled “1. 会話全体に対する conversationId”Conversations API で一度会話を作成し、すべてのターンでその ID を再利用できます。SDK は新規生成項目のみを自動的に含めます。
import { Agent, run } from '@openai/agents';import { OpenAI } from 'openai';
const agent = new Agent({ name: 'Assistant', instructions: 'Reply very concisely.',});
async function main() { // Create a server-managed conversation: const client = new OpenAI(); const { id: conversationId } = await client.conversations.create({});
const first = await run(agent, 'What city is the Golden Gate Bridge in?', { conversationId, }); console.log(first.finalOutput); // -> "San Francisco"
const second = await run(agent, 'What state is it in?', { conversationId }); console.log(second.finalOutput); // -> "California"}
main().catch(console.error);2. 最終ターンから継続する previousResponseId
Section titled “2. 最終ターンから継続する previousResponseId”Responses API だけで始めたい場合、前回応答から返る ID を使って各リクエストを連結できます。これにより、完全な会話リソースを作らずにターン間コンテキストを維持できます。
import { Agent, run } from '@openai/agents';
const agent = new Agent({ name: 'Assistant', instructions: 'Reply very concisely.',});
async function main() { const first = await run(agent, 'What city is the Golden Gate Bridge in?'); console.log(first.finalOutput); // -> "San Francisco"
const previousResponseId = first.lastResponseId; const second = await run(agent, 'What state is it in?', { previousResponseId, }); console.log(second.finalOutput); // -> "California"}
main().catch(console.error);conversationId と previousResponseId は同時に使えません。システム間で共有できる名前付き会話リソースが必要なら conversationId、応答から次の応答へ最小コストで SDK レベル継続したいだけなら previousResponseId を使ってください。
フックとカスタマイズ
Section titled “フックとカスタマイズ”Call model input filter
Section titled “Call model input filter”callModelInputFilter は、モデル呼び出しの 直前 にモデル入力を編集するために使います。このフックは現在のエージェント、コンテキスト、結合済み入力項目(存在する場合はセッション履歴を含む)を受け取ります。更新した input 配列と任意の instructions を返し、機密データのマスキング、古いメッセージの削除、追加のシステムガイダンス注入を行えます。
runner.run(..., { callModelInputFilter }) で実行単位に設定するか、Runner 設定(RunConfig の callModelInputFilter)でデフォルト設定します。
戻り値は ModelInputData オブジェクト { input: AgentInputItem[], instructions? } である必要があります。input フィールドは必須で、配列でなければなりません。これ以外の形を返すと UserError が投げられます。
SDK はフィルター呼び出し前に、準備済みターン入力を clone します。session も使っている場合、永続化されるのはこの filtered clone なので、ここで適用したマスキングや切り詰めは保存済みセッション履歴にも反映されます。
conversationId または previousResponseId では、このフックは次の Responses API 呼び出し用に準備された payload に対して実行されます。過去のサーバー管理コンテキストは API 側で復元されるため、その呼び出しの filtered 配列は過去履歴の完全再生ではなく、新ターン差分のみをすでに表している場合があります。この最終フィルター手順の前に保存履歴と現在ターンのマージ方法を変更したい場合は、sessionInputCallback を使ってください。
Tool error formatter
Section titled “Tool error formatter”toolErrorFormatter は、ツール呼び出しが拒否されたときにモデルへ返す承認拒否メッセージをカスタマイズするために使います。SDK デフォルト文言ではなく、ドメイン固有の文言(例: コンプライアンスガイダンス)を返せます。
formatter は実行単位(runner.run(..., { toolErrorFormatter }))または RunConfig(new Runner(...) の toolErrorFormatter)でグローバル設定できます。
この formatter は承認拒否時のグローバルフォールバックです。特定 interruption を result.state.reject(interruption, { message: '...' }) で拒否した場合、その呼び出し単位 message が toolErrorFormatter より優先されます。どちらもない場合、SDK はデフォルト拒否文 Tool execution was not approved. を使います。
現在この formatter は approval_rejected イベントで実行され、以下を受け取ります。
kind(現在は常に'approval_rejected')toolType('function'、'computer'、'shell'、'apply_patch')toolNamecallIddefaultMessage(SDK のフォールバック文。現在はTool execution was not approved.)runContext
メッセージを上書きするには文字列を返し、SDK デフォルトを維持するには undefined を返します。formatter が throw した場合(または文字列以外を返した場合)、SDK は warning を記録し、デフォルト承認拒否メッセージにフォールバックします。
Reasoning item ID policy
Section titled “Reasoning item ID policy”reasoningItemIdPolicy は、SDK が以前生成した実行項目を後続モデル入力用 AgentInputItem[] に変換するとき、reasoning item の id フィールドを保持するかどうかを制御します。
これは、SDK が生成済みモデル項目を入力として再生する場面に影響します。例:
-
同一実行内のフォローアップモデル呼び出し(例: ツール実行後)
-
生成済み項目を入力 / 履歴として再利用する後続ターン
-
保存済み
RunStateからの実行再開 -
result.history/result.outputのような派生結果ビュー(モデル入力形状の配列) -
'preserve'(デフォルト): reasoning-item ID を保持 -
'omit': 入力として再送する前に reasoning item のidフィールドを削除 -
非 reasoning item は影響なし
変更されない もの:
- 生のモデル応答(
result.rawResponses) - 実行項目(
result.newItems) - provider が返す現在ターンのモデル出力
つまり、このポリシーは SDK が過去生成項目から 次の入力 を構築するときに適用されます。
ポリシーは実行単位(runner.run(..., { reasoningItemIdPolicy: 'omit' }))または runner デフォルト(new Runner({ reasoningItemIdPolicy: 'omit', ... }))で設定できます。保存済み RunState から再開する場合、上書きしない限り以前に解決されたポリシーが再利用されます。
callModelInputFilter との相互作用
Section titled “callModelInputFilter との相互作用”reasoningItemIdPolicy は callModelInputFilter より先に適用されます。カスタム挙動が必要なら、callModelInputFilter で準備済み入力を検査し、モデル呼び出し前に reasoning ID を手動で再追加または削除できます。
'omit' の使用タイミング
Section titled “'omit' の使用タイミング”再生された reasoning item を ID なしで正規化したい場合(例: 転送 / 再生されるモデル入力を単純化したい、またはアプリパイプラインの統合要件に合わせたい)に 'omit' を使います。
バックエンド / provider が再生 reasoning item をリクエスト検証エラーで拒否する場合(例: フォローアップ入力中の reasoning item ID に関連する HTTP 400 エラー)にも有効なトラブルシュート手段です。その場合、'omit' で再生 reasoning ID を削除すると、新規リクエストで無効と扱われる ID の送信を避けられます。
再生入力でも reasoning-item ID を維持したく、統合先がそれを受け入れるなら 'preserve' を維持してください。
エラーと復旧
Section titled “エラーと復旧”Error handlers
Section titled “Error handlers”errorHandlers を使うと、サポートされる実行時エラーを throw せず最終出力へ変換できます。現在サポートされるのは maxTurns のみです。
errorHandlers.maxTurnsは max-turn エラーのみを処理errorHandlers.defaultはサポート対象種別のフォールバックとして使用- ハンドラーは
{ error, context, runData }を受け取り、{ finalOutput, includeInHistory? }を返せます
SDK は、捕捉可能な少数のエラーを投げます。
MaxTurnsExceededError–maxTurns到達ModelBehaviorError– モデルが無効な出力を生成(例: 不正な JSON、未知のツール)InputGuardrailTripwireTriggered/OutputGuardrailTripwireTriggered– ガードレール違反ToolInputGuardrailTripwireTriggered/ToolOutputGuardrailTripwireTriggered– ツールガードレール違反GuardrailExecutionError– ガードレールが完了できなかったToolTimeoutError– 関数ツールがtimeoutMsを超過し、timeoutBehavior: 'raise_exception'を使用ToolCallError– 関数ツール実行がタイムアウト以外で失敗UserError– 設定またはユーザー入力に基づいて投げられるエラー
すべては基底 AgentsError クラスを継承し、現在の実行状態へアクセスする state プロパティを持つ場合があります。
以下は GuardrailExecutionError を処理するコード例です。入力ガードレールは最初のユーザー入力でのみ実行されるため、この例では元の入力とコンテキストで実行を再開始します。また、保存済み状態を再利用して、モデルを再呼び出しせずに出力ガードレールを再試行する方法も示しています。
import { Agent, GuardrailExecutionError, InputGuardrail, InputGuardrailTripwireTriggered, OutputGuardrail, OutputGuardrailTripwireTriggered, run,} from '@openai/agents';import { z } from 'zod';
// Shared guardrail agent to avoid re-creating it on every fallback run.const guardrailAgent = new Agent({ name: 'Guardrail check', instructions: 'Check if the user is asking you to do their math homework.', outputType: z.object({ isMathHomework: z.boolean(), reasoning: z.string(), }),});
async function main() { const input = 'Hello, can you help me solve for x: 2x + 3 = 11?'; const context = { customerId: '12345' };
// Input guardrail example
const unstableInputGuardrail: InputGuardrail = { name: 'Math Homework Guardrail (unstable)', execute: async () => { throw new Error('Something is wrong!'); }, };
const fallbackInputGuardrail: InputGuardrail = { name: 'Math Homework Guardrail (fallback)', execute: async ({ input, context }) => { const result = await run(guardrailAgent, input, { context }); const isMathHomework = result.finalOutput?.isMathHomework ?? /solve for x|math homework/i.test(JSON.stringify(input)); return { outputInfo: result.finalOutput, tripwireTriggered: isMathHomework, }; }, };
const agent = new Agent({ name: 'Customer support agent', instructions: 'You are a customer support agent. You help customers with their questions.', inputGuardrails: [unstableInputGuardrail], });
try { // Input guardrails only run on the first turn of a run, so retries must start a fresh run. await run(agent, input, { context }); } catch (e) { if (e instanceof GuardrailExecutionError) { console.error(`Guardrail execution failed (input): ${e}`); try { agent.inputGuardrails = [fallbackInputGuardrail]; // Retry from scratch with the original input and context. await run(agent, input, { context }); } catch (ee) { if (ee instanceof InputGuardrailTripwireTriggered) { console.log('Math homework input guardrail tripped on retry'); } else { throw ee; } } } else { throw e; } }
// Output guardrail example
const replyOutputSchema = z.object({ reply: z.string() });
const unstableOutputGuardrail: OutputGuardrail<typeof replyOutputSchema> = { name: 'Answer review (unstable)', execute: async () => { throw new Error('Output guardrail crashed.'); }, };
const fallbackOutputGuardrail: OutputGuardrail<typeof replyOutputSchema> = { name: 'Answer review (fallback)', execute: async ({ agentOutput }) => { const outputText = typeof agentOutput === 'string' ? agentOutput : (agentOutput?.reply ?? JSON.stringify(agentOutput)); const flagged = /math homework|solve for x|x =/i.test(outputText); return { outputInfo: { flaggedOutput: outputText }, tripwireTriggered: flagged, }; }, };
const agent2 = new Agent<unknown, typeof replyOutputSchema>({ name: 'Customer support agent (output check)', instructions: 'You are a customer support agent. Answer briefly.', outputType: replyOutputSchema, outputGuardrails: [unstableOutputGuardrail], });
try { await run(agent2, input, { context }); } catch (e) { if (e instanceof GuardrailExecutionError && e.state) { console.error(`Guardrail execution failed (output): ${e}`); try { agent2.outputGuardrails = [fallbackOutputGuardrail]; // Output guardrails can be retried using the saved state without another model call. await run(agent2, e.state); } catch (ee) { if (ee instanceof OutputGuardrailTripwireTriggered) { console.log('Output guardrail tripped after retry with saved state'); } else { throw ee; } } } else { throw e; } }}
main().catch(console.error);入力と出力のリトライの違い:
- 入力ガードレールは実行の最初のユーザー入力でのみ動作するため、再試行には同じ入力 / コンテキストで新規実行を開始する必要があります。保存済み
stateを渡しても入力ガードレールは再トリガーされません - 出力ガードレールはモデル応答後に動作するため、
GuardrailExecutionErrorから得た保存済みstateを再利用して、モデル再呼び出しなしで再実行できます
上記例を実行すると、次の出力が表示されます。
Guardrail execution failed (input): Error: Input guardrail failed to complete: Error: Something is wrong!Math homework input guardrail tripped on retryGuardrail execution failed (output): Error: Output guardrail failed to complete: Error: Output guardrail crashed.Output guardrail tripped after retry with saved state