├── README.md ├── 01-agents ├── README.md ├── 0_setup.py ├── 1_routines.py ├── 3_tools.py ├── 4_tools.py ├── demo_util.py ├── 2_tools.py ├── 5_escalation.py ├── 6_agents.py └── 7_orchestration.py └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # Learning Lab 2 | 3 | Learning Lab is a webinar for developers in Japan to get specific ideas for how to use OpenAI’s API and Models across a variety of topic areas. 4 | 5 | OpenAI is proud to support developers from concept to scale. We look forward to building together! 6 | -------------------------------------------------------------------------------- /01-agents/README.md: -------------------------------------------------------------------------------- 1 | # Assistants, Agents, and Orchestration 2 | 3 | Demo code used in the Assistants Build Hours. 4 | 5 | For an in-depth walkthrough, check out the [recording](https://vimeo.com/990334325/56b552bc7a). 6 | 7 | ## Setup 8 | 9 | 1. Set your OpenAI key. 10 | 11 | ```bash 12 | export OPENAI_API_KEY="sk_XXX..." 13 | ``` 14 | 15 | 2. Run. 16 | 17 | ```bash 18 | python 7_orchestration.py 19 | ``` 20 | -------------------------------------------------------------------------------- /01-agents/0_setup.py: -------------------------------------------------------------------------------- 1 | from openai import OpenAI 2 | from demo_util import color 3 | 4 | # OpenAI クライアントの初期化 5 | client = OpenAI() 6 | 7 | # === デモループ === 8 | 9 | # 使用するモデルを定義 10 | model = "gpt-4o-mini" 11 | 12 | # アシスタントの振る舞いを設定するためのシステムメッセージ 13 | system_message = "You are a helpful Assistant." 14 | 15 | # メッセージ履歴を保存するリストを初期化 16 | messages = [] 17 | 18 | # ユーザーとの対話を開始するループ 19 | while True: 20 | # ユーザーの入力を取得 21 | user = input(color("User: ", "blue") + "\033[90m") 22 | messages.append({"role": "user", "content": user}) # ユーザーのメッセージを履歴に追加 23 | 24 | # モデルからの応答を取得 25 | response = client.chat.completions.create( 26 | model=model, # 使用するモデルを指定 27 | messages=[{"role": "system", "content": system_message}] + messages, # システムメッセージと履歴を送信 28 | ) 29 | 30 | # モデルからの応答メッセージを抽出 31 | message = response.choices[0].message 32 | 33 | # アシスタントの応答を出力 34 | print(color("Assistant:", "yellow"), message.content) 35 | 36 | # モデルの応答を履歴に追加 37 | messages.append(message) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 OpenAI 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /01-agents/1_routines.py: -------------------------------------------------------------------------------- 1 | from openai import OpenAI 2 | from demo_util import color 3 | 4 | # OpenAI クライアントの初期化 5 | client = OpenAI() 6 | 7 | # === デモループ === 8 | 9 | # 使用するモデルを定義 10 | model = "gpt-4o-mini" 11 | 12 | # アシスタントの振る舞いを設定するためのシステムメッセージ 13 | system_message = ( 14 | "あなたはACME Inc.のカスタマーサポート担当者です。" 15 | "常に1文以内で回答してください。" 16 | "以下の手順に従ってユーザーと対話してください:" 17 | "1. 最初に質問をして、ユーザーの問題を深く理解してください。\n" 18 | " - ただし、ユーザーが既に理由を提供している場合を除きます。\n" 19 | "2. 修正案を提案してください(適当なものを考え出してください)。\n" 20 | "3. ユーザーが満足しない場合のみ、返金を提案してください。\n" 21 | "4. ユーザーが受け入れた場合、IDを検索して返金を実行してください。" 22 | "" 23 | ) 24 | 25 | # メッセージ履歴を保存するリストを初期化 26 | messages = [] 27 | 28 | # ユーザーとの対話を開始するループ 29 | while True: 30 | # ユーザーの入力を取得 31 | user = input(color("User: ", "blue") + "\033[90m") 32 | messages.append({"role": "user", "content": user}) # ユーザーのメッセージを履歴に追加 33 | 34 | # モデルからの応答を取得 35 | response = client.chat.completions.create( 36 | model=model, # 使用するモデルを指定 37 | messages=[{"role": "system", "content": system_message}] + messages, # システムメッセージと履歴を送信 38 | ) 39 | 40 | # モデルからの応答メッセージを抽出 41 | message = response.choices[0].message 42 | 43 | # アシスタントの応答を出力 44 | print(color("Assistant:", "yellow"), message.content) 45 | 46 | # モデルの応答を履歴に追加 47 | messages.append(message) -------------------------------------------------------------------------------- /01-agents/3_tools.py: -------------------------------------------------------------------------------- 1 | from openai import OpenAI 2 | from demo_util import color, function_to_schema 3 | import json 4 | 5 | # OpenAI クライアントの初期化 6 | client = OpenAI() 7 | 8 | # === デモループ === 9 | 10 | # 使用するモデルを定義 11 | model = "gpt-4o-mini" 12 | 13 | # アシスタントの動作を設定するためのシステムメッセージを定義 14 | system_message = ( 15 | "あなたはACME Inc.のカスタマーサポート担当者です。" 16 | "常に1文以内で回答してください。" 17 | "以下の手順に従ってユーザーと対話してください:" 18 | "1. 最初に質問をして、ユーザーの問題を深く理解してください。\n" 19 | " - ただし、ユーザーが既に理由を提供している場合を除きます。\n" 20 | "2. 修正案を提案してください(適当なものを考え出してください)。\n" 21 | "3. ユーザーが満足しない場合のみ、返金を提案してください。\n" 22 | "4. ユーザーが受け入れた場合、IDを検索して返金を実行してください。" 23 | "" 24 | ) 25 | 26 | # 商品IDを検索する関数 27 | def look_up_item(search_query): 28 | """商品IDを見つけるために使用します。 29 | 検索クエリには説明やキーワードを使用できます。""" 30 | item_id = "item_132612938" 31 | print(color("見つかった商品:", "green"), item_id) 32 | return item_id 33 | 34 | # 返金処理を実行する関数 35 | def execute_refund(item_id, reason="not provided"): 36 | print(color("\n\n=== 返金概要 ===", "green")) 37 | print(color(f"商品ID: {item_id}", "green")) 38 | print(color(f"理由: {reason}", "green")) 39 | print("=================\n") 40 | print(color("返金の実行に成功しました!", "green")) 41 | return "success" 42 | 43 | # 使用可能なツールを定義 44 | tools = [execute_refund, look_up_item] 45 | 46 | # アシスタントとの完全なやり取りを処理する関数 47 | def run_full_turn(system_message, tools, messages): 48 | 49 | # ツールをスキーマに変換 50 | tool_schemas = [function_to_schema(f) for f in tools] 51 | 52 | # OpenAI API を呼び出してレスポンスを生成 53 | response = client.chat.completions.create( 54 | model=model, 55 | messages=[{"role": "system", "content": system_message}] + messages, 56 | tools=tool_schemas, 57 | ) 58 | message = response.choices[0].message 59 | messages.append(message) 60 | 61 | # アシスタントの応答を表示 62 | if message.content: 63 | print(color("Assistant:", "yellow"), message.content) 64 | 65 | # ツール呼び出しがない場合はメッセージを返す 66 | if not message.tool_calls: 67 | return message 68 | 69 | # ツール呼び出しがある場合、その詳細を表示 70 | for tool_call in message.tool_calls: 71 | name = tool_call.function.name 72 | args = json.loads(tool_call.function.arguments) 73 | print( 74 | color("Assistant:", "yellow"), 75 | color(f"ツール呼び出しを実行中: {tool_call.function.name}({args})", "magenta"), 76 | ) 77 | 78 | return message 79 | 80 | # 会話メッセージの初期化 81 | messages = [] 82 | 83 | # ユーザーと継続的に対話するループを開始 84 | while True: 85 | # ユーザー入力を取得 86 | user = input(color("User: ", "blue") + "\033[90m") 87 | messages.append({"role": "user", "content": user}) 88 | 89 | # 1回のやり取りを実行 90 | run_full_turn(system_message, tools, messages) 91 | -------------------------------------------------------------------------------- /01-agents/4_tools.py: -------------------------------------------------------------------------------- 1 | from openai import OpenAI 2 | from demo_util import color, function_to_schema 3 | import json 4 | 5 | # OpenAI クライアントの初期化 6 | client = OpenAI() 7 | 8 | # === デモループ === 9 | 10 | # 使用するモデルを定義 11 | model = "gpt-4o-mini" 12 | 13 | # アシスタントの動作を設定するためのシステムメッセージを定義 14 | system_message = ( 15 | "あなたはACME Inc.のカスタマーサポート担当者です。" 16 | "常に1文以内で回答してください。" 17 | "以下の手順に従ってユーザーと対話してください:" 18 | "1. 最初に質問をして、ユーザーの問題を深く理解してください。\n" 19 | " - ただし、ユーザーが既に理由を提供している場合を除きます。\n" 20 | "2. 修正案を提案してください(適当なものを考え出してください)。\n" 21 | "3. ユーザーが満足しない場合のみ、返金を提案してください。\n" 22 | "4. ユーザーが受け入れた場合、IDを検索して返金を実行してください。" 23 | "" 24 | ) 25 | 26 | # 商品IDを検索する関数 27 | def look_up_item(search_query): 28 | """商品IDを見つけるために使用します。 29 | 検索クエリには説明やキーワードを使用できます。""" 30 | # ダミーのアイテムIDを返す 31 | item_id = "item_132612938" 32 | print(color("見つかった商品:", "green"), item_id) 33 | return item_id 34 | 35 | # 返金処理を実行する関数 36 | def execute_refund(item_id, reason="not provided"): 37 | """指定された商品IDと理由を元に返金処理を実行します。""" 38 | print(color("\n\n=== 返金概要 ===", "green")) 39 | print(color(f"商品ID: {item_id}", "green")) 40 | print(color(f"理由: {reason}", "green")) 41 | print("=================\n") 42 | print(color("返金の実行に成功しました!", "green")) 43 | return "success" 44 | 45 | # 使用可能なツールを定義 46 | tools = [execute_refund, look_up_item] 47 | 48 | # アシスタントとの完全なやり取りを処理する関数 49 | def run_full_turn(system_message, tools, messages): 50 | """アシスタントとの対話を処理し、必要に応じてツールを呼び出します。""" 51 | 52 | # ツールをスキーマに変換 53 | tool_schemas = [function_to_schema(f) for f in tools] 54 | 55 | # OpenAI API を呼び出してレスポンスを生成 56 | response = client.chat.completions.create( 57 | model=model, 58 | messages=[{"role": "system", "content": system_message}] + messages, 59 | tools=tool_schemas, 60 | ) 61 | message = response.choices[0].message 62 | messages.append(message) 63 | 64 | # アシスタントの応答を表示 65 | if message.content: 66 | print(color("Assistant:", "yellow"), message.content) 67 | 68 | # ツール呼び出しがない場合はメッセージを返す 69 | if not message.tool_calls: 70 | return message 71 | 72 | # ツール呼び出しがある場合、その詳細を表示 73 | for tool_call in message.tool_calls: 74 | name = tool_call.function.name 75 | args = json.loads(tool_call.function.arguments) 76 | print( 77 | color("Assistant:", "yellow"), 78 | color(f"ツール呼び出しを実行中: {tool_call.function.name}({args})", "magenta"), 79 | ) 80 | 81 | return message 82 | 83 | # 会話メッセージの初期化 84 | messages = [] 85 | 86 | # ユーザーと継続的に対話するループを開始 87 | while True: 88 | # ユーザー入力を取得 89 | user = input(color("User: ", "blue") + "\033[90m") 90 | messages.append({"role": "user", "content": user}) 91 | 92 | # 1回のやり取りを実行 93 | run_full_turn(system_message, tools, messages) 94 | -------------------------------------------------------------------------------- /01-agents/demo_util.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | 3 | 4 | def color(text, color): 5 | color_codes = { 6 | "red": "\033[91m", 7 | "green": "\033[92m", 8 | "yellow": "\033[93m", 9 | "blue": "\033[94m", 10 | "magenta": "\033[95m", 11 | "cyan": "\033[96m", 12 | "white": "\033[97m", 13 | "reset": "\033[0m", 14 | "grey": "\033[90m", 15 | } 16 | return f"{color_codes.get(color, color_codes['reset'])}{text}{color_codes['reset']}" 17 | 18 | 19 | def function_to_schema(func) -> dict: 20 | type_map = { 21 | str: "string", 22 | int: "integer", 23 | float: "number", 24 | bool: "boolean", 25 | list: "array", 26 | dict: "object", 27 | type(None): "null", 28 | } 29 | 30 | try: 31 | signature = inspect.signature(func) 32 | except ValueError as e: 33 | raise ValueError( 34 | f"Failed to get signature for function {func.__name__}: {str(e)}" 35 | ) 36 | 37 | parameters = {} 38 | for param in signature.parameters.values(): 39 | try: 40 | param_type = type_map.get(param.annotation, "string") 41 | except KeyError as e: 42 | raise KeyError( 43 | f"Unknown type annotation {param.annotation} for parameter {param.name}: {str(e)}" 44 | ) 45 | parameters[param.name] = {"type": param_type} 46 | 47 | required = [ 48 | param.name 49 | for param in signature.parameters.values() 50 | if param.default == inspect._empty 51 | ] 52 | 53 | return { 54 | "type": "function", 55 | "function": { 56 | "name": func.__name__, 57 | "description": func.__doc__ or "", 58 | "parameters": { 59 | "type": "object", 60 | "properties": parameters, 61 | "required": required, 62 | }, 63 | }, 64 | } 65 | 66 | 67 | # ===== Examle ===== 68 | 69 | 70 | def sample_function(param_1, param_2, the_third_one: int): 71 | """ 72 | This is my docstring. Call this function when you want. 73 | """ 74 | print("Hello, world") 75 | 76 | 77 | # Input: 78 | function_to_schema(sample_function) 79 | # Output: 80 | { 81 | "type": "function", 82 | "function": { 83 | "name": "sample_function", 84 | "description": "This is my docstring. Call this function when you want.", 85 | "parameters": { 86 | "type": "object", 87 | "properties": { 88 | "param_1": {"type": "string"}, 89 | "param_2": {"type": "string"}, 90 | "the_third_one": {"type": "integer"}, 91 | }, 92 | "required": ["param_1", "param_2", "the_third_one"], 93 | }, 94 | }, 95 | } 96 | -------------------------------------------------------------------------------- /01-agents/2_tools.py: -------------------------------------------------------------------------------- 1 | from openai import OpenAI 2 | from demo_util import color 3 | import json 4 | 5 | # OpenAI クライアントの初期化 6 | client = OpenAI() 7 | 8 | # === デモループ === 9 | 10 | # 使用するモデルを定義 11 | model = "gpt-4o-mini" 12 | 13 | # アシスタントの動作を設定するためのシステムメッセージを定義 14 | system_message = ( 15 | "あなたはACME Inc.のカスタマーサポート担当者です。" 16 | "常に1文以内で回答してください。" 17 | "以下の手順に従ってユーザーと対話してください:" 18 | "1. 最初に質問をして、ユーザーの問題を深く理解してください。\n" 19 | " - ただし、ユーザーが既に理由を提供している場合を除きます。\n" 20 | "2. 修正案を提案してください(適当なものを考え出してください)。\n" 21 | "3. ユーザーが満足しない場合のみ、返金を提案してください。\n" 22 | "4. ユーザーが受け入れた場合、IDを検索して返金を実行してください。" 23 | "" 24 | ) 25 | 26 | # アシスタントが利用できるツールを定義 27 | # これらのツールは、商品検索や返金処理などの外部操作を模擬します 28 | tools = [ 29 | { 30 | "type": "function", 31 | "function": { 32 | "name": "execute_refund", 33 | "description": "", 34 | "parameters": { 35 | "type": "object", 36 | "properties": { 37 | "item_id": {"type": "string"}, 38 | "reason": {"type": "string"}, 39 | }, 40 | "required": ["item_id"], 41 | }, 42 | }, 43 | }, 44 | { 45 | "type": "function", 46 | "function": { 47 | "name": "look_up_item", 48 | "description": "商品IDを見つけるために使用します。\n 検索クエリには説明やキーワードを使用できます。", 49 | "parameters": { 50 | "type": "object", 51 | "properties": {"search_query": {"type": "string"}}, 52 | "required": ["search_query"], 53 | }, 54 | }, 55 | }, 56 | ] 57 | 58 | # アシスタントとの完全なやり取りを処理する関数 59 | def run_full_turn(system_message, tools, messages): 60 | # OpenAI API を呼び出してレスポンスを生成 61 | response = client.chat.completions.create( 62 | model=model, 63 | messages=[{"role": "system", "content": system_message}] + messages, 64 | tools=tools, 65 | ) 66 | 67 | # レスポンスからアシスタントのメッセージを抽出 68 | message = response.choices[0].message 69 | messages.append(message) 70 | 71 | # ユーザーへのアシスタントの応答を表示 72 | if message.content: 73 | print(color("Assistant:", "yellow"), message.content) 74 | 75 | # ツール呼び出しがない場合はメッセージを返す 76 | if not message.tool_calls: 77 | return message 78 | 79 | # ツール呼び出しがある場合、その詳細を表示 80 | for tool_call in message.tool_calls: 81 | name = tool_call.function.name 82 | args = json.loads(tool_call.function.arguments) 83 | print( 84 | color("Assistant:", "yellow"), 85 | color(f"ツール呼び出しを実行中: {tool_call.function.name}({args})", "magenta"), 86 | ) 87 | 88 | return message 89 | 90 | # 会話メッセージの初期化 91 | messages = [] 92 | 93 | # ユーザーと継続的に対話するループを開始 94 | while True: 95 | # ユーザー入力を取得 96 | user = input(color("User: ", "blue") + "\033[90m") 97 | messages.append({"role": "user", "content": user}) 98 | 99 | # 1回のやり取りを実行 100 | run_full_turn(system_message, tools, messages) 101 | -------------------------------------------------------------------------------- /01-agents/5_escalation.py: -------------------------------------------------------------------------------- 1 | from openai import OpenAI 2 | from demo_util import color, function_to_schema 3 | import json 4 | 5 | # OpenAI クライアントの初期化 6 | client = OpenAI() 7 | 8 | # === デモループ === 9 | 10 | # 使用するモデルを定義 11 | model = "gpt-4o-mini" 12 | 13 | # アシスタントの動作を設定するためのシステムメッセージを定義 14 | system_message = ( 15 | "あなたはACME Inc.のカスタマーサポート担当者です。" 16 | "常に1文以内で回答してください。" 17 | "以下の手順に従ってユーザーと対話してください:" 18 | "1. 最初に質問をして、ユーザーの問題を深く理解してください。\n" 19 | " - ただし、ユーザーが既に理由を提供している場合を除きます。\n" 20 | "2. 修正案を提案してください(適当なものを考え出してください)。\n" 21 | "3. ユーザーが満足しない場合のみ、返金を提案してください。\n" 22 | "4. ユーザーが受け入れた場合、IDを検索して返金を実行してください。" 23 | "" 24 | ) 25 | 26 | # 商品IDを検索する関数 27 | def look_up_item(search_query): 28 | """商品IDを見つけるために使用します。 29 | 検索クエリには説明やキーワードを使用できます。""" 30 | # ダミーのアイテムIDを返す 31 | item_id = "item_132612938" 32 | print(color("見つかった商品:", "green"), item_id) 33 | return item_id 34 | 35 | # 返金処理を実行する関数 36 | def execute_refund(item_id, reason="not provided"): 37 | """指定された商品IDと理由を元に返金処理を実行します。""" 38 | print(color("\n\n=== 返金概要 ===", "green")) 39 | print(color(f"商品ID: {item_id}", "green")) 40 | print(color(f"理由: {reason}", "green")) 41 | print("=================\n") 42 | print(color("返金の実行に成功しました!", "green")) 43 | return "success" 44 | 45 | # エスカレーションを人間の担当者に引き継ぐ関数 46 | def escalate_to_human(summary): 47 | """明示的にリクエストされた場合のみ、この関数を呼び出します。""" 48 | print(color("人間の担当者にエスカレーション中...", "red")) 49 | print("\n=== エスカレーションレポート ===") 50 | print(f"概要: {summary}") 51 | print("========================\n") 52 | exit() 53 | 54 | # 使用可能なツールを定義 55 | tools = [execute_refund, look_up_item, escalate_to_human] 56 | 57 | # アシスタントとの完全なやり取りを処理する関数 58 | def run_full_turn(system_message, tools, messages): 59 | """アシスタントとの対話を処理し、必要に応じてツールを呼び出します。""" 60 | 61 | # 現在のメッセージ数を記録 62 | num_init_messages = len(messages) 63 | messages = messages.copy() 64 | 65 | while True: 66 | 67 | # Python 関数をツールに変換し、逆引きマップを保存 68 | tool_schemas = [function_to_schema(tool) for tool in tools] 69 | tools_map = {tool.__name__: tool for tool in tools} 70 | 71 | # === 1. OpenAI のコンプリートを取得 === 72 | response = client.chat.completions.create( 73 | model=model, 74 | messages=[{"role": "system", "content": system_message}] + messages, 75 | tools=tool_schemas or None, 76 | ) 77 | message = response.choices[0].message 78 | messages.append(message) 79 | 80 | # アシスタントの応答を表示 81 | if message.content: 82 | print(color("Assistant:", "yellow"), message.content) 83 | 84 | # ツール呼び出しがない場合はループを終了 85 | if not message.tool_calls: 86 | break 87 | 88 | # === 2. ツール呼び出しを処理 === 89 | 90 | for tool_call in message.tool_calls: 91 | result = execute_tool_call(tool_call, tools_map) 92 | 93 | result_message = { 94 | "role": "tool", 95 | "tool_call_id": tool_call.id, 96 | "content": result, 97 | } 98 | messages.append(result_message) 99 | 100 | # ==== 3. 新しいメッセージを返す ===== 101 | return messages[num_init_messages:] 102 | 103 | # ツール呼び出しを実行する関数 104 | def execute_tool_call(tool_call, tools_map): 105 | """指定されたツール呼び出しを実行します。""" 106 | name = tool_call.function.name 107 | args = json.loads(tool_call.function.arguments) 108 | 109 | print(color("Assistant:", "yellow"), color(f"{name}({args})", "magenta")) 110 | 111 | # 指定された引数で対応する関数を呼び出す 112 | return tools_map[name](**args) 113 | 114 | # 会話メッセージの初期化 115 | messages = [] 116 | 117 | # ユーザーと継続的に対話するループを開始 118 | while True: 119 | # ユーザー入力を取得 120 | user = input(color("User: ", "blue") + "\033[90m") 121 | messages.append({"role": "user", "content": user}) 122 | 123 | # 新しいやり取りを実行 124 | new_messages = run_full_turn(system_message, tools, messages) 125 | messages.extend(new_messages) 126 | -------------------------------------------------------------------------------- /01-agents/6_agents.py: -------------------------------------------------------------------------------- 1 | from openai import OpenAI 2 | from demo_util import color, function_to_schema 3 | import json 4 | from pydantic import BaseModel 5 | 6 | # === クラス定義 === 7 | 8 | class Agent(BaseModel): 9 | """エージェントを表現するクラス""" 10 | name: str = "Agent" 11 | model: str = "gpt-4o" 12 | instructions: str = "You are a helpful Agent" 13 | tools: list = [] 14 | 15 | class Response(BaseModel): 16 | """応答を表現するクラス""" 17 | messages: list 18 | 19 | # OpenAI クライアントの初期化 20 | client = OpenAI() 21 | 22 | # === デモループ === 23 | 24 | def run_full_turn(agent, messages): 25 | """1回のやり取りを処理する関数""" 26 | 27 | # 初期メッセージ数を記録 28 | num_init_messages = len(messages) 29 | messages = messages.copy() 30 | 31 | while True: 32 | 33 | # ツールをスキーマに変換してマッピングを保存 34 | tool_schemas = [function_to_schema(tool) for tool in agent.tools] 35 | tools_map = {tool.__name__: tool for tool in agent.tools} 36 | 37 | # === 1. OpenAI の応答を取得 === 38 | response = client.chat.completions.create( 39 | model=agent.model, 40 | messages=[{"role": "system", "content": agent.instructions}] + messages, 41 | tools=tool_schemas or None, 42 | ) 43 | message = response.choices[0].message 44 | messages.append(message) 45 | 46 | if message.content: # アシスタントの応答を表示 47 | print(color("Assistant:", "yellow"), message.content) 48 | 49 | if not message.tool_calls: # ツール呼び出しがない場合は終了 50 | break 51 | 52 | # === 2. ツール呼び出しを処理 === 53 | 54 | for tool_call in message.tool_calls: 55 | result = execute_tool_call(tool_call, tools_map) 56 | 57 | result_message = { 58 | "role": "tool", 59 | "tool_call_id": tool_call.id, 60 | "content": result, 61 | } 62 | messages.append(result_message) 63 | 64 | # ==== 3. 新しいメッセージを返す ===== 65 | return messages[num_init_messages:] 66 | 67 | 68 | def execute_tool_call(tool_call, tools_map): 69 | """指定されたツールを実行する関数""" 70 | name = tool_call.function.name 71 | args = json.loads(tool_call.function.arguments) 72 | 73 | print(color("Assistant:", "yellow"), color(f"{name}({args})", "magenta")) 74 | 75 | # 対応する関数を引数付きで呼び出す 76 | return tools_map[name](**args) 77 | 78 | # === ツール定義 === 79 | 80 | def look_up_item(search_query): 81 | """商品IDを検索する関数""" 82 | item_id = "item_132612938" 83 | print(color("見つかった商品:", "green"), item_id) 84 | return item_id 85 | 86 | 87 | def execute_refund(item_id, reason="not provided"): 88 | """返金処理を実行する関数""" 89 | print(color("\n\n=== 返金概要 ===", "green")) 90 | print(color(f"商品ID: {item_id}", "green")) 91 | print(color(f"理由: {reason}", "green")) 92 | print("=================\n") 93 | print(color("返金の実行に成功しました!", "green")) 94 | return "success" 95 | 96 | 97 | def escalate_to_human(summary): 98 | """人間の担当者にエスカレーションする関数""" 99 | print(color("人間の担当者にエスカレーション中...", "red")) 100 | print("\n=== エスカレーションレポート ===") 101 | print(f"概要: {summary}") 102 | print("========================\n") 103 | exit() 104 | 105 | # エージェントの初期設定 106 | agent = Agent( 107 | name="Issues and Repairs Agent", 108 | instructions=( 109 | "あなたはACME Inc.のIssues and Repairs Agentです。" 110 | "常に1文以内で回答してください。" 111 | "まず自己紹介(会社名と役割)を行い、その後以下の手順でユーザーと対話してください:" 112 | "1. 最初に具体的な質問をして、ユーザーの問題を深く理解してください。\n" 113 | " - ただし、ユーザーが既に理由を提供している場合を除きます。\n" 114 | "2. 修正案を提案してください(適当なものを考え出してください)。試してもらいます。\n" 115 | "3. ユーザーが満足しない場合のみ、返金を提案してください。\n" 116 | "4. ユーザーが受け入れた場合、IDを検索して返金を実行してください。" 117 | ), 118 | tools=[execute_refund, look_up_item, escalate_to_human], 119 | ) 120 | 121 | # メッセージ履歴を保存するリストを初期化 122 | messages = [] 123 | 124 | # ユーザーとの対話を開始するループ 125 | while True: 126 | # ユーザーの入力を取得 127 | user = input(color("User: ", "blue") + "\033[90m") 128 | messages.append({"role": "user", "content": user}) # ユーザーのメッセージを履歴に追加 129 | 130 | # 1回の対話を実行 131 | new_messages = run_full_turn(agent, messages) 132 | messages.extend(new_messages) 133 | -------------------------------------------------------------------------------- /01-agents/7_orchestration.py: -------------------------------------------------------------------------------- 1 | from openai import OpenAI 2 | from demo_util import color, function_to_schema 3 | import json 4 | from pydantic import BaseModel 5 | 6 | # === クラス定義 === 7 | 8 | class Agent(BaseModel): 9 | """エージェントを定義するクラス。 10 | name: エージェント名 11 | model: 使用するモデル名 12 | instructions: システムメッセージとしてエージェントの振る舞いを定義 13 | tools: 使用可能なツールのリスト""" 14 | name: str = "Agent" 15 | model: str = "gpt-4o" 16 | instructions: str = "You are a helpful Agent" 17 | tools: list = [] 18 | 19 | class Response(BaseModel): 20 | """レスポンスメッセージを管理するクラス。""" 21 | messages: list 22 | 23 | # OpenAI クライアントの初期化 24 | client = OpenAI() 25 | 26 | # === デモループ === 27 | 28 | def run_full_turn(agent, messages): 29 | """エージェントとの1ターンの対話を処理します。 30 | 31 | Args: 32 | agent (Agent): エージェントの設定を含むオブジェクト。 33 | messages (list): ユーザーとエージェントの間のメッセージリスト。 34 | 35 | Returns: 36 | list: 新しく追加されたメッセージ。""" 37 | 38 | num_init_messages = len(messages) 39 | messages = messages.copy() 40 | 41 | while True: 42 | 43 | # ツールをスキーマに変換し、逆引きマップを保存 44 | tool_schemas = [function_to_schema(tool) for tool in agent.tools] 45 | tools_map = {tool.__name__: tool for tool in agent.tools} 46 | 47 | # === 1. OpenAI のコンプリートを取得 === 48 | response = client.chat.completions.create( 49 | model=agent.model, 50 | messages=[{"role": "system", "content": agent.instructions}] + messages, 51 | tools=tool_schemas or None, 52 | ) 53 | message = response.choices[0].message 54 | messages.append(message) 55 | 56 | # アシスタントの応答を表示 57 | if message.content: 58 | print(color("Assistant:", "yellow"), message.content) 59 | 60 | # ツール呼び出しがない場合はループを終了 61 | if not message.tool_calls: 62 | break 63 | 64 | # === 2. ツール呼び出しを処理 === 65 | 66 | for tool_call in message.tool_calls: 67 | result = execute_tool_call(tool_call, tools_map) 68 | 69 | result_message = { 70 | "role": "tool", 71 | "tool_call_id": tool_call.id, 72 | "content": result, 73 | } 74 | messages.append(result_message) 75 | 76 | # ==== 3. 新しいメッセージを返す ===== 77 | return messages[num_init_messages:] 78 | 79 | # ツール呼び出しを実行する関数 80 | def execute_tool_call(tool_call, tools_map): 81 | """指定されたツール呼び出しを実行します。 82 | 83 | Args: 84 | tool_call (dict): 呼び出すツールの詳細。 85 | tools_map (dict): ツール名と関数のマッピング。 86 | 87 | Returns: 88 | any: ツールの実行結果。""" 89 | name = tool_call.function.name 90 | args = json.loads(tool_call.function.arguments) 91 | 92 | print(color("Assistant:", "yellow"), color(f"{name}({args})", "magenta")) 93 | 94 | # 指定された引数で対応する関数を呼び出す 95 | return tools_map[name](**args) 96 | 97 | # === ツール関数 === 98 | 99 | def look_up_item(search_query): 100 | """商品IDを見つけるために使用します。 101 | 検索クエリには説明やキーワードを使用できます。 102 | 103 | Args: 104 | search_query (str): 検索クエリ。 105 | 106 | Returns: 107 | str: 商品ID。""" 108 | item_id = "item_132612938" 109 | print(color("見つかった商品:", "green"), item_id) 110 | return item_id 111 | 112 | def execute_refund(item_id, reason="not provided"): 113 | """指定された商品IDと理由を元に返金処理を実行します。 114 | 115 | Args: 116 | item_id (str): 商品ID。 117 | reason (str, optional): 返金理由。デフォルトは "not provided"。 118 | 119 | Returns: 120 | str: 処理結果。""" 121 | print(color("\n\n=== 返金概要 ===", "green")) 122 | print(color(f"商品ID: {item_id}", "green")) 123 | print(color(f"理由: {reason}", "green")) 124 | print("=================\n") 125 | print(color("返金の実行に成功しました!", "green")) 126 | return "success" 127 | 128 | def escalate_to_human(summary): 129 | """明示的にリクエストされた場合のみ、この関数を呼び出します。 130 | 131 | Args: 132 | summary (str): エスカレーションの概要。 133 | 134 | Exits: 135 | プログラムを終了。""" 136 | print(color("人間の担当者にエスカレーション中...", "red")) 137 | print("\n=== エスカレーションレポート ===") 138 | print(f"概要: {summary}") 139 | print("========================\n") 140 | exit() 141 | 142 | # === エージェントの設定 === 143 | 144 | agent = Agent( 145 | name="Issues and Repairs Agent", 146 | instructions=( 147 | "あなたはACME Inc.の問題解決と修理の担当者です。" 148 | "常に1文以内で回答してください。" 149 | "まず自己紹介(会社と役割)を行い、以下の手順に従ってください:" 150 | "1. 最初に具体的で深掘りする質問を行い、ユーザーの問題をより深く理解してください。\n" 151 | " - ただし、ユーザーが既に理由を提供している場合を除きます。\n" 152 | "2. 修正案を提案してください(適当なものを考え出してください)。ユーザーが試すまで待ちます。\n" 153 | "3. ユーザーが満足しない場合のみ、返金を提案してください。\n" 154 | "4. ユーザーが受け入れた場合、IDを検索して返金を実行してください。" 155 | ), 156 | tools=[execute_refund, look_up_item, escalate_to_human], 157 | ) 158 | 159 | # 会話メッセージの初期化 160 | messages = [] 161 | 162 | # ユーザーと継続的に対話するループを開始 163 | while True: 164 | # ユーザー入力を取得 165 | user = input(color("User: ", "blue") + "\033[90m") 166 | messages.append({"role": "user", "content": user}) 167 | 168 | # 新しいやり取りを実行 169 | new_messages = run_full_turn(agent, messages) 170 | messages.extend(new_messages) 171 | --------------------------------------------------------------------------------