openai-agents-pythonを使ってマルチエージェントシステムを作ってみた
今回は、マルチエージェントワークフローを開発するためのライブラリであるopenai-agents-pythondを使ってみたので紹介します。
openai-agents-pythonとは?
公式GitHubによると、
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows. It is provider-agnostic, supporting the OpenAI Responses and Chat Completions APIs, as well as 100+ other LLMs.
ということで、マルチエージェントワークフローを構築するための軽量かつ強力なフレームワークであり、プロバイダーに依存せず、OpenAI Responses APIとChat Completions APIに加え、100以上のLLMをサポートしているライブラリであるということです。OpenAIのモデルだけでなく、様々なプロバイダーに対応しているということで、柔軟に開発できそうなのが印象として受けました。
使ってみる
今回はGitHubで提供されているサンプルコードを中心に検証を進めようと思います。進めるにあたり、OpenAIのAPIキーは発行されている前提で進めます。
インストール
今回もuv
を利用して環境を整えます。以下のようにすることでopenai-agents-pythonの環境を構築します。
uv init openai-agents-python-test -p 3.12 && cd openai-agents-python-test
uv add openai-agents
公式サンプルを利用してみる
まずは最も簡単なサンプルを試してみます。なお、サンプルの変更点として、APIキーをコード上で指定すること、そして結果は日本語で返させることです。
import os
from agents import Agent, Runner
os.environ["OPENAI_API_KEY"] = "<OpenAI API key>"
agent = Agent(name="Assistant", instructions="You are a helpful assistant")
result = Runner.run_sync(
agent,
"Write a haiku about recursion in programming. Please answer in Japanese.",
)
print(result.final_output)
実行すると、以下のような結果になりました。
uv run create_haiku.py
# 結果1
再帰する
コード無限の螺旋
終わりなき
# 結果2
プログラムの
再帰する波に
終わり見えず
# 結果3
プログラムの
循環の中で
無限を見る
結果を見ると、文字数は順番に20文字、20文字、21文字になっています。俳句は5、7、5の17文字が基本だと思うので、全て字余りになっています。そこで、17文字で結果を作るようにプロンプトを調整してみました。
import os
from agents import Agent, Runner
os.environ["OPENAI_API_KEY"] = "<OpenAI API key>"
agent = Agent(name="Assistant", instructions="You are a helpful assistant")
result = Runner.run_sync(
agent,
""""Write a haiku about recursion in programming. Please answer in Japanese.
And you must craete haiku with 17 characters.
""",
)
print(result.final_output)
実行すると以下のようになりました。
uv run create_haiku_character_regulation.py
# 結果1
プログラム
再帰の中に
また再帰
# 結果2
再帰する
コードの中で
無限の道
# 結果3
再び呼ぶ
終わりなき旅
コードの中
結果を見ると、文字数は順番に17文字、18文字、19文字になっています。最初の結果は17文字で思ったとおりになっていますが、そのほかの例はまたまた字余りになっています。AIでも厳密に指定した文字数で俳句を作るのは大変なのかもしれません笑
ハンズオフの実装
サンプルの例を改良し、複数のプログラミング言語についてそれぞれエージェントを用意し、プロンプトの内容をもとに適切なエージェントを呼び出すことができるか試してみます。コードは以下になります。
import click
from agents import Agent, Runner
import asyncio
import os
os.environ["OPENAI_API_KEY"] = "<OpenAI API key>"
python_agent = Agent(
name="Python agent",
instructions="You only use Python.",
)
ts_agent = Agent(
name="TypeScript Agent",
instructions="You only use TypeScript.",
)
rust_agent = Agent(
name="rust Agent",
instructions="You only use rust.",
)
triage_agent = Agent(
name="Triage agent",
instructions="Handoff to the appropriate agent based on request detail.",
handoffs=[python_agent, ts_agent, rust_agent],
)
async def call_agent(prompt):
result = await Runner.run(triage_agent, input=prompt)
print(result.last_agent.name)
print(result.final_output)
@click.command()
@click.argument("prompt", type=str)
def main(prompt):
asyncio.run(call_agent(prompt))
if __name__ == "__main__":
main()
まずは言語ごとのエージェントを作ります。例えばPythonエージェントであれば以下のように実装しています。
python_agent = Agent(
name="Python agent",
instructions="You only use Python.",
)
エージェントは今回はPython、TypeScriptそしてrustのエージェントを作りましたが、インストラクションズはシンプルにその言語だけ使うと書いているだけです。
そして作成したエージェントをトリアージ、つまり受け取ったプロンプトによりどのエージェントに繋ぐかを判断するエージェントを以下のように定義します。
triage_agent = Agent(
name="Triage agent",
instructions="Handoff to the appropriate agent based on request detail.",
handoffs=[python_agent, ts_agent, rust_agent],
)
最後に、エージェントを呼び出す関数をcall_agent
に実装しています。なお、今回はプロンプトをコマンドラインから指定するためにclick
というライブラリを採用しました。click
については以下の記事で解説していますのでぜひ参照ください。
それでは使ってみます。おそらくその言語で実装されることが多いだろうというプロンプトを利用して選択されたエージェントを見てみます。結果からもわかるように、入力したプロンプトに応じて呼び出すエージェントが変わっていることがわかります。省略はしていますが、どのような開発をすればいいかなど説明をエージェントにしてもらっています。
# Pythonを想定
uv run handoff.py "I want to implement Machine Learning Model."
# 結果
Python agent
Implementing a machine learning model involves several steps. Here's a basic outline using Python and popular libraries like scikit-learn:
(以下省略)
# TypeScriptを想定
uv run handoff.py "I want to implement web application's frontend."
# 結果
TypeScript Agent
// To implement a web application's frontend using TypeScript, you can follow these steps:
(以下省略)
# rustを想定
uv run handoff.py "I want to implement CLI application.
# 結果
Rust Agent
To implement a CLI application in Rust, you can use the `clap` crate, which makes parsing command-line arguments easy. Here's a basic outline to get you started:
(以下省略)
ファンクションの実装
最後にファンクションの設定をしてみます。今回はファンクションとして市が指定された時と府が指定された時で呼び出される関数を変えた上で、天気を聞く機能を実装してみます。挙動の理想としては、大阪市の天気が聞かれたら晴れ、大阪府の天気は雨と回答させる目的となっています。
import os
import asyncio
from agents import Agent, Runner, function_tool
os.environ["OPENAI_API_KEY"] = "<OpenAI API key>"
@function_tool
def get_weather(city: str) -> str:
return f"The weather in {city} is sunny."
@function_tool
def get_weather_of_prefecture(city: str) -> str:
return f"The weather in {city} is rainy."
agent = Agent(
name="Hello world",
instructions="You are a helpful agent. Please answer in Japanese.",
tools=[get_weather, get_weather_of_prefecture],
)
async def call(prompt):
result = await Runner.run(agent, input=prompt)
return result.final_output
async def main():
city_prompt = "What's the weather in Osaka city?"
prefecture_prompt = "What's the weather in Osaka prefecture?"
city_output = await call(city_prompt)
prefecture_output = await call(prefecture_prompt)
print(f"{city_prompt} -> {city_output}")
print(f"{prefecture_prompt} -> {prefecture_output}")
if __name__ == "__main__":
asyncio.run(main())
これを実行すると、以下のような結果となりました。入力されたプロンプトに応じて呼び出される機能を自動で判定してくれることを確認できました。
uv run function.py
# 結果
What's the weather in Osaka city? -> 大阪市の天気は晴れです。
What's the weather in Osaka prefecture? -> 大阪の天気は晴れです。
まとめ
今回はOpenAIが出しているエージェント実装ライブラリであるopenai-agents-pythonを使ってみました。実装がとてもシンプルにでき、かつ複数エージェントの組み合わせや関数呼び出しなどが簡単に実装できます。エージェントをこれから作ってみたい方はぜひ参考にしてください。
Discussion