コンテキスト管理の比較分析と設計提案
1. 概要
Flow/Step ベースへ移行した agents‑sdk‑models に最適化したコンテキスト(Context
)設計を検討するため、下記 3 点を整理する。
- OpenAI Agents SDK における Context の位置づけ
- LangChain LCEL のコンテキスト管理手法
- それらを踏まえた agents‑sdk‑models 向け Context 案
2. OpenAI Agents SDK の Context
項目 |
概要 |
目的 |
依存性注入/局所状態を格納。LLM へは渡らない。 |
型 |
任意のユーザ定義クラス(@dataclass や Pydantic 可)。 |
主な内容 |
DB ハンドラ、ロガー、外部 API など実行時依存物。 ユーザー ID や権限などセッションスコープ情報。 |
会話履歴 |
Runner が内部管理し Agent へ供給。Context には通常含めない。 |
分岐制御 |
Handoff/複数 Agent 呼び出しロジックをコード側で記述。Context にルーティング値は保持しない。 |
メリット |
完全自由 で既存 DI パターンに近い。 |
デメリット |
フィールド定義が散在しやすく、大規模フローで可読性低下。 |
3. LangChain LCEL のコンテキスト管理
項目 |
概要 |
入力/出力 |
dict[str, Any] をチェーン間で受渡し。 |
会話履歴 |
ConversationBufferMemory など Memory クラスが保持し、入力辞書に展開。 |
ルーティング |
RunnableConditional 等でラムダ判定しブランチ。辞書に直接フラグを書く場合も。 |
追加メタ |
RunManager metadata/tags に任意データを添付。v0.2 から Context (β)が導入されスコープ型 get/set が可能。 |
メリット |
学習コストが低く、柔軟性が高い。 |
デメリット |
キー衝突・型不整合が起きやすく、可読性も低下。 |
4. 両者比較早見表
項目 |
Agents SDK |
LangChain LCEL |
型 |
任意クラス |
dict + Memory + metadata |
会話履歴 |
Runner 内部保持 |
Memory クラス |
ルーティング |
コード内 handoff |
RunnableConditional |
依存性注入 |
Context フィールド |
dict / metadata |
型安全性 |
高(型付きクラス) |
低(自由キー) |
5. agents‑sdk‑models 向け Context 設計案
5.1 設計方針
- 型安全で読みやすい : Pydantic
BaseModel
を採用し IDE 補完を活用。
- 履歴も保持 : Agents SDK 依存を避け、Flow 内で一貫管理。
- ルーティング内包 :
next_label: str | None
をフィールド化し Step 返却で更新。
- 辞書互換 :
as_dict()/from_dict()
で LCEL とのブリッジを提供。
5.2 フィールド例
class Context(BaseModel):
last_user_input: str | None = None # 直近ユーザー入力
messages: list[Message] = [] # 会話履歴
knowledge: dict[str, Any] = {} # RAG など外部知識
prev_outputs: dict[str, Any] = {} # 前 Step 生成物
next_label: str | None = None # ルーティング指示
artifacts: dict[str, Any] = {} # Flow 全体成果物
shared_state: dict[str, Any] = {} # 任意共有値
def as_dict(self) -> dict[str, Any]:
"""LCEL 互換辞書へ変換"""
d = self.dict()
d["history"] = d.pop("messages")
return d
@classmethod
def from_dict(cls, d: dict[str, Any]) -> "Context":
d = d.copy()
d["messages"] = d.pop("history", [])
return cls(**d)
5.3 利用パターン
async def step_example(ctx: Context) -> Context:
# 1. ユーザー入力を取得
user_msg = await ctx.io.ask("入力してください")
ctx.last_user_input = user_msg
ctx.messages.append(user_msg)
# 2. モデル呼び出し
answer = await model.invoke(ctx.messages)
ctx.messages.append(answer)
# 3. ルーティング判定
ctx.next_label = "done" if is_ok(answer) else "retry"
return ctx
5.4 Agents SDK 連携案
- BridgeStep: Agents SDK Runner を内部で呼び、Runner.context ↔︎ Context 変換しながら実行。
- 履歴同期: Runner 側のメッセージを取得し
ctx.messages
にマージ。
5.5 メリット & トレードオフ
利点 |
懸念 |
型安全・補完が効く |
Pydantic バリデーションコスト増 |
LCEL との双方向変換で使いまわせる |
二重保持でメモリ消費 |
ルーティングが明示的 |
Agents SDK 的には少し冗長 |
5.6 今後の拡張イメージ
- ContextPlugin API : 外部システムと自動同期する拡張ポイント(例: DB/Redis)。
- StreamingMessages : 履歴部のみ遅延ロード/ストリーミング可能に。
- TypedArtifacts : 成果物をクラスごとに型付けして安全性向上。
これにより、Agents SDK Models は 型安全・分かりやすさ・他フレームワーク互換 を両立したコンテキスト管理を実現できる。