エージェントの定義は準備段階です。実行時には、1 回の実行で何が起きるか、次のターンにどう引き継ぐか、承認やツールの処理を待つために一時停止したときにワークフローがどう動くかを把握しておく必要があります。
エージェントループの仕組み
SDK での 1 回の実行が、アプリケーションの 1 ターンに相当します。ランナーは、停止条件を満たすまで次の処理を繰り返します。
- 用意した入力を渡して、現在のエージェントのモデルを呼び出します。
- モデルの出力を確認します。
- 出力にツール呼び出しが含まれていれば、それを実行してループを続けます。
- モデルが別の専門エージェントに処理を引き継いだ場合は、そのエージェントに切り替えてループを続けます。
- モデルが最終回答を生成し、ツールで処理することがもうなければ、結果を返します。
このループが SDK の基本です。ツール、ハンドオフ、承認、ストリーミングはいずれも、このループを利用する機能です。
会話状態を引き継ぐ方法の選択
次のターンに状態を引き継ぐには、主に 4 つの方法があります。
| 方法 | 状態の管理場所 | 適した用途 | 次のターンで渡すもの |
|---|---|---|---|
result.history |
アプリケーション | 小規模なチャットループや、履歴を全面的に制御したい場合 | そのまま入力として再送できる履歴 |
session |
アプリケーション側のストレージと SDK | チャット状態の永続化、実行の再開、独自に管理するストレージの利用 | 同じセッション |
conversationId |
OpenAI Conversations API | 複数のワーカーやサービスで、サーバーが管理する状態を共有する場合 | 同じ会話 ID と新しいターンの入力のみ |
previousResponseId |
OpenAI Responses API | サーバー側で状態を管理し、最小限の負荷で応答を引き継ぐ場合 | 直前の応答 ID と新しいターンの入力のみ |
ほとんどのアプリケーションでは、会話ごとに方法を 1 つ選びます。ローカルに保存した履歴の再送とサーバー側での状態管理を併用する場合、双方の内容を照合して調整しないと、コンテキストが重複することがあります。
import { Agent, MemorySession, run } from "@openai/agents";
const agent = new Agent({
name: "Tour guide",
instructions: "Answer with compact travel facts.",
});
const session = new MemorySession();
const firstTurn = await run(agent, "What city is the Golden Gate Bridge in?", {
session,
});
console.log(firstTurn.finalOutput);
const secondTurn = await run(agent, "What state is it in?", { session });
console.log(secondTurn.finalOutput);会話の内容を永続化したい場合、承認フローを途中から再開したい場合、またはアプリケーション側でストレージを管理したい場合は、セッションが第一の選択肢です。
import { Agent, run } from "@openai/agents";
import OpenAI from "openai";
const agent = new Agent({
name: "Assistant",
instructions: "Reply very concisely.",
});
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);
const second = await run(agent, "What state is it in?", {
conversationId,
});
console.log(second.finalOutput);複数のシステムで同じ名前付き会話を共有するには、conversationId を使います。応答から次の応答への引き継ぎにかかるコストを最小限に抑えたい場合は、previousResponseId
実行中のストリーミング
ストリーミングでも、エージェントループの仕組みと状態の引き継ぎ方は同じです。違いは、実行中にイベントを順次受け取って処理する点だけです。
import { Agent, run } from "@openai/agents";
const agent = new Agent({
name: "Planet guide",
instructions: "Answer with short facts.",
});
const stream = await run(agent, "Give me three short facts about Saturn.", {
stream: true,
});
for await (const event of stream) {
if (
event.type === "raw_model_stream_event" &&
event.data.type === "output_text_delta"
) {
process.stdout.write(event.data.delta);
}
}
await stream.completed;
console.log("\nFinal:", stream.finalOutput);実装時には次の 3 点に注意してください。
- 実行結果を確定したものとして扱う前に、ストリームの終了を待ちます。
- 承認待ちで実行が一時停止した場合は、新しいユーザーターンを開始せず、
interruptionsを解決してstateから実行を再開します。 - ターンの途中でストリームをキャンセルした後、同じターンを続けたい場合は、
stateから未完了のターンを再開します。
一時停止とエラーへの対処
実行が通常どおりに完了しない場合は、大きく次の 2 種類に分けて考えます。
- 実行時または検証時のエラーには、ターン数が上限に達した場合や、ガードレールの例外、ツールのエラーが発生した場合が該当します。
- 想定内の一時停止の例として、人による承認を求める場合があります。この場合は意図的に実行を中断し、後で同じ状態から再開します。
承認待ちは、進行中の実行の一時停止として扱います。ここで新しいターンを開始せずに実行を再開することで、ターン数、履歴、サーバー側で実行を継続するための ID の整合性を保てます。
次のステップ
実行時のループの仕組みを理解したら、ワークフローで次に設計する部分に応じて、以下のガイドに進んでください。