スーパーの献立提案チームをAutoGenのSelectorで作る
AutoGenで簡単に作れるチームのアーキテクチャの一つに、「Selector」というのがある
Selectorっていう司令塔的なAIを一個置いて、そのAIに次に発言するAIを考えさせるというもの
今回はスーパーのお客さんの気分と予算を聞いて、
- 「献立を提案するエージェント」
- 「材料を提案するエージェント」
- 「合計価格をチェックするエージェント」 をSelectorで協働させてみたよぃ
# Selectorグループチャット形式で、スーパーマーケットの客にサービスを提供するチームの例.
import asyncio
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.conditions import TextMentionTermination, MaxMessageTermination
from autogen_agentchat.teams import SelectorGroupChat
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient
async def main():
# Create an OpenAI model client.
model_client = OpenAIChatCompletionClient(
model="gemini-1.5-flash-8b",
api_key="xxx..",
)
# 献立プランナーエージェント.
dish_planner_agent = AssistantAgent(
"dish_planner",
description="スーパーの献立プランナー。お客さんが作りたい料理を提案します。提案が終わったら、'FINISH'と返答します。",
model_client=model_client,
system_message="あなたはスーパーの献立プランナーです。予算を考えずに、お客さんの気分にぴったりな料理を一品考えます。提案が終わったら、'FINISH'と返答してください。",
)
# 商品選びエージェント.
ingredient_coordinator_agent = AssistantAgent(
"ingredient_coordinator",
description="スーパーの食材コーディネーター。献立プランナーが考えた料理に必要な食材を考えます。提案が終わったら、'FINISH'と返答します。",
model_client=model_client,
system_message="あなたはスーパーの食材コーディネーターです。献立プランナーが考えた料理に必要な食材を提案します。食材を選ぶ際は、季節や地域の特産品を考慮してください。それぞれの食材の個数を言ってください。提案が終わったら、'FINISH'と返答してください。",
)
# 清算担当エージェント.
cashier_agent = AssistantAgent(
"cashier",
description="スーパーの清算担当。選び終わった食材を精算します。合計金額がお客さんの予算内におさまったら、金額を伝え、'TERMINATE'と返答します。予算を超えた場合は、予算内に収まるように食材を減らすようにingredient_coordinatorに提案します。提案が終わったら、'FINISH'と返答します。",
model_client=model_client,
system_message="あなたはスーパーの清算担当です。商品選びエージェントが提案した食材のそれぞれの価格を現実的に妥当な範囲で考えて、合計金額を計算します。合計金額がお客さんの予算内におさまったら、金額を伝え、'TERMINATE'と返答してください。予算を超えた場合は、予算内に収まるように食材を減らすように商品選びエージェントに提案してください。提案が終わったら、'FINISH'と返答してください。",
)
text_mention_termination = TextMentionTermination("TERMINATE")
max_messages_termination = MaxMessageTermination(max_messages=10)
termination = text_mention_termination | max_messages_termination
selector_prompt = """あなたはスーパーマーケットのエージェントチームを指揮するAIです。
以下のエージェントがいます:
{roles}
これまでの会話履歴:
{history}
上記の会話を読んで、次に発言するエージェントを {participants} から選択してください。
1人のエージェントのみを選択してください。
"""
team = SelectorGroupChat(
[dish_planner_agent, ingredient_coordinator_agent, cashier_agent],
model_client=model_client,
termination_condition=termination,
selector_prompt=selector_prompt,
allow_repeated_speaker=True,
)
result = await team.run(task="なんだかステーキを使ったガッツリしたものを食べたいです。予算は500円です。")
print("=== CONVERSATION MESSAGES ===")
for i, message in enumerate(result.messages, 1):
print(f"\n{i}. {message.source.upper()}:")
print(f" {message.content}")
print(f"\n=== STOP REASON ===")
print(f" {result.stop_reason}")
if __name__ == "__main__":
asyncio.run(main())
↓実行結果
(venv) nitta@nittasachikonoMacBook-Pro autogen % python supermarket.py
=== CONVERSATION MESSAGES ===
1. USER:
なんだかステーキを使ったガッツリしたものを食べたいです。予算は500円です。
2. DISH_PLANNER:
せっかくステーキ気分なら、豪快に!
**材料:**
* ステーキ(200g):2枚
* 玉ねぎ:1個
* マッシュルーム:10個
* オリーブオイル:大さじ2
* 塩胡椒:少々
* ケチャップ:大さじ2
* ウスターソース:大さじ1
* 醤油:小さじ1
* ブラックペッパー:少々
* パセリ:少々(飾り用)
* ご飯:適量
**作り方:**
1. ステーキは冷蔵庫から出して常温に戻す。塩胡椒で下味をつけ、オリーブオイルで両面を焼き色がつくまで強火で焼く。焼き加減はお好みで。
2. 玉ねぎは薄切り、マッシュルームは半分に切る。
3. ステーキが焼き終わったら、フライパンに玉ねぎとマッシュルームを入れて、オリーブオイルで炒め、しんなりするまで炒める。
4. ケチャップ、ウスターソース、醤油、ブラックペッパーを加えて、ソースを煮詰める。
5. ステーキを皿に盛り、その上に作ったソースをかけ、パセリを散らす。
6. ご飯と一緒にいただく。
**ポイント:**
* ステーキは、焼き加減が重要!レア、ミディアムレア、ミディアム、ウェルダンなど、好みに合わせて焼いてください。
* ソースは、ケチャップとウスターソースで簡単に作れます。お好みで砂糖や酢を加えて味を調整してください。
* 玉ねぎやマッシュルームも一緒に焼くと、味がより一層引き立ちます。
* ステーキに合う副菜をプラスすると、さらに満足感があります。(例:ポテトサラダ、レタス、など)
このステーキは、素材本来の味を活かしつつ、簡単でガッツリ食べられる一品。お好みの付け合わせと楽しんでください!
FINISH
3. INGREDIENT_COORDINATOR:
ステーキ(200g):2枚
玉ねぎ:1個
マッシュルーム:10個
オリーブオイル:20ml
塩:適量
こしょう:適量
ケチャップ:20ml
ウスターソース:10ml
醤油:5ml
ブラックペッパー:適量
パセリ:少々(飾り用)
ご飯:適量
牛肉は、今は国産のものが安価に入手できます。
玉ねぎ、マッシュルームは旬の物を使うとよりおいしくいただけます。
FINISH
4. CASHIER:
ステーキ(200g):2枚 価格を想定 1枚150円
玉ねぎ:1個 価格を想定 50円
マッシュルーム:10個 価格を想定 100円
オリーブオイル:20ml 価格を想定 50円
塩:適量 価格を想定 10円
こしょう:適量 価格を想定 10円
ケチャップ:20ml 価格を想定 20円
ウスターソース:10ml 価格を想定 30円
醤油:5ml 価格を想定 10円
ブラックペッパー:適量 価格を想定 10円
パセリ:少々(飾り用) 価格を想定 5円
ご飯:適量 価格を想定 100円
合計:150円×2 + 50円 + 100円 + 50円 + 10円 + 10円 + 20円 + 30円 + 10円 + 10円 + 5円 + 100円 = 555円
予算500円を超えています。
商品選びエージェント様、予算内に収めるために、ステーキの枚数を減らすか、他の食材の量を減らすことを提案します。
例えば、ステーキを1枚に減らす、玉ねぎを半分にする、マッシュルームを5個にするなどです。
また、ご飯の量を減らすのも良いでしょう。
提案は以上です。
FINISH
5. INGREDIENT_COORDINATOR:
ステーキ(200g):1枚 価格を想定 150円
玉ねぎ:1/2個 価格を想定 25円
マッシュルーム:5個 価格を想定 50円
オリーブオイル:大さじ1 価格を想定 30円
塩:適量 価格を想定 10円
こしょう:適量 価格を想定 10円
ケチャップ:大さじ1 価格を想定 10円
ウスターソース:小さじ2 価格を想定 15円
醤油:小さじ1 価格を想定 5円
ブラックペッパー:適量 価格を想定 10円
パセリ:少々(飾り用) 価格を想定 5円
ご飯:少なめ 価格を想定 50円
合計:150円 + 25円 + 50円 + 30円 + 10円 + 10円 + 10円 + 15円 + 5円 + 10円 + 5円 + 50円 = 405円
予算内に収まりました。
FINISH
6. CASHIER:
TERMINATE
=== STOP REASON ===
Text 'TERMINATE' mentioned
献立提案エージェント→食材選びエージェント→清算エージェント→食材選びエージェント→清算エージェントの順に呼ばれてるのがわかる
実行する回によって、
- 最後になぜか食材選びエージェントが合計金額を言っちゃったり
- 清算エージェントが食材を勝手に間引いて合計金額だしちゃったり
完成度的にはだめだめなんだけどSelectorが動いてるところを見てみたかったので満足ですぃ
ちょっと解説すると、各エージェントのdescriptionがSelector向けの説明になってて、Selectorはそれを見て誰を呼び出すか判断してるよぃ
最初のほうは全然うまく動かなかったけど、「提案が終わったら、'FINISH'と返答します。」とdescriptionに書いて、プロンプト側でも「提案が終わったら、'FINISH'と返答してください」って指示しておくと、うまく動いたよぃ