AIエージェントを実装するための基本パターン
一口にAIエージェントといっても、構成パターンは多岐にわたります。共通項として認識される構成要素としては、Profile、Memory、Planning、Actionなどが挙げられます。
- Profile:タスクを担当するAIエージェントに付与される役割や性格
- Memory:背景情報を保持する短期記憶や、ユーザ情報など長期で保存される記憶
- Planning:目的を達成するためのタスクばらしや、行動/観測結果からの再計画
- Action:コード生成や検索など外部の計算/知識資源にアクセスしたり、環境に作用するもの
以降の節では、構成パターンを具体的にイメージいただくため、OpenAI Agents SDKを用いて3つの構成パターンを紹介します。
行動(ツール実行)と観測
まずは最もシンプルなエージェントの構成パターンです。ユーザーからの入力にもとづいて、事前に定義した「ツール」を必要に応じて呼び出し、その結果を解釈してユーザーに返します。
「ツール」とは、言語モデルの外部で実行されるコンピュータプログラムや関数インターフェースを指します。AIエージェントが呼び出し対象のツールと、ツールの実行に必要な情報を引数として生成することで、任意の関数を実行することができます。このパターンは、どの業務を代替するときも用いられることが非常に多いです。
from agents import Agent, Runner, function_tool
@function_tool
def get_weather(city: str) -> str:
"""与えられた都市の現在の天気を返す"""
return f"The weather in {city} is sunny" # ダミー
async def main(user_message: str) -> str:
agent = Agent(name="Agent",
instructions="You are a helpful assistant",
model="gpt-5-mini",
tools=[get_weather], # エージェントが呼び出し可能な関数
tool_use_behavior="run_llm_again") # 関数の実行結果をLLMが解釈して返す
result = await Runner.run(agent, user_message)
return result.final_output

ルーティングを伴うワークフロー
先のスライド作成のステップ例のように、業務の流れ(ワークフロー)が事前に決定している場合もあります。これらのワークフローでは、順番にフローが定義される場合もあれば、特定の条件で次の行動対象が決定する場合もあります。たとえば「導入と結論が結びついていないため全体の構成を見直す」といった差し戻しが該当します。
OpenAI Agents SDK では「handoffs」機能を用いて、次の遷移先となるエージェントを事前に設定することができます。このパターンは、関心ごとでフローを分離したいときに利用します。
たとえばカスタマーサポートでは、部署や機能単位で異なるエージェントを用意し、ユーザーの問い合わせ内容に応じてルーティングを適用します。
from agents import Agent, Runner, CodeInterpreterTool, WebSearchTool
from agents import enable_verbose_stdout_logging
enable_verbose_stdout_logging()
async def main(user_message: str) -> str:
researcher = Agent(
name="Researcher",
handoff_description="Web検索をともなう情報調査と要約の専門家",
instructions="Webから信頼できる情報を取得し、ユーザの質問に分かりやすく回答する",
tools=[WebSearchTool()]
)
programmer = Agent(
name="Programmer",
handoff_description="コーディングとプログラミングの専門家",
instructions="要件を満たす効率的なコードを実行し、ロジックについて説明を加える",
tools=[CodeInterpreterTool(tool_config={"type": "code_interpreter"})]
)
triage_agent = Agent(
name="Triage",
instructions="ユーザーの質問にもとづいて、どのエージェントを選択するかを決定する",
handoffs=[researcher, programmer],
)
result = await Runner.run(triage_agent, user_message)
return result.final_output

ループを伴うマルチエージェント
執筆などのアウトプットをともなうタスクでは、試行錯誤を繰り返すことによる質の向上を期待することがあります。複数のエージェントが協力/競争することで複雑なタスクを達成するのを目的としたのがマルチエージェントシステムになります。
また複数の機能を搭載するとその分だけ管理すべき要素が増えてしまいます。「単一責任化」や「関心の分離」といった設計思想を考慮し、プロジェクトの成熟度に応じて段階的に導入すると、システム全体の能力をより引き出しやすくなります。
from agents import Agent, Runner
from agents import enable_verbose_stdout_logging
enable_verbose_stdout_logging()
async def main(user_message: str) -> str:
moderator = Agent(
name="Moderator",
handoff_description="討論全体を管理し、進行する司会者",
instructions=(
"以下の手順で議論を進行します。\n"
"1. テーマの決定: ユーザー入力をもとに、討論の公式な論題を再定義します\n"
"2. 肯定的な立論: はじめに proponent_agent に肯定的な立論を指示します\n"
"3. 否定的な立論: 次に opponent_agent に否定的な立論を指示します\n"
"4. 弁論の指示: proponent_agent, opponent_agent 間で、弁論を行うよう指示します\n"
"5. 結論づけ: 弁論が終了したら summarizer_agent に議論の要約を指示します"
),
)
proponent_agent = Agent(
name="Proponent",
handoff_description="テーマに対し、肯定の立場から論点を提示します。",
instructions="肯定の立場から具体的な根拠・事例を挙げて、説得力のある主張を行う",
)
opponent_agent = Agent(
name="Opponent",
handoff_description="テーマに対し、否定の立場から反論や代替案を提示します。",
instructions="否定の立場から具体的な根拠・事例を挙げて、説得力のある主張を行う",
)
summarizer = Agent(
name="Summarizer",
handoff_description="討論終了時に、これまでの議論をまとめます。",
instructions="議論全体を公平に評価し、主要な論点と結論を客観的に要約する",
)
moderator.handoffs = [proponent_agent, summarizer]
proponent_agent.handoffs = [opponent_agent]
opponent_agent.handoffs = [moderator]
display(draw_graph(moderator))
result = await Runner.run(moderator, user_message)
return result.final_output

