ai-agents-for-beginners

良いAIエージェントの設計方法

(上の画像をクリックすると、このレッスンの動画をご覧いただけます)

ツール利用デザインパターン

ツールは、AIエージェントに幅広い能力を与えるため、非常に興味深い存在です。エージェントが実行できるアクションが限られている代わりに、ツールを追加することで、エージェントは多様なアクションを実行できるようになります。この章では、ツール利用デザインパターンについて学び、AIエージェントが特定のツールを使用して目標を達成する方法を説明します。

はじめに

このレッスンでは、以下の質問に答えることを目指します:

学習目標

このレッスンを完了すると、以下ができるようになります:

ツール利用デザインパターンとは?

ツール利用デザインパターンは、LLMが外部ツールと連携して特定の目標を達成する能力を与えることに焦点を当てています。ツールとは、エージェントがアクションを実行するために使用できるコードのことです。ツールは、電卓のような単純な関数や、株価検索や天気予報などのサードパーティサービスへのAPI呼び出しなどがあります。AIエージェントの文脈では、ツールはモデル生成関数呼び出しに応じてエージェントによって実行されるように設計されています。

適用可能なユースケースは何か?

AIエージェントはツールを活用して複雑なタスクを完了したり、情報を取得したり、意思決定を行ったりすることができます。ツール利用デザインパターンは、データベース、Webサービス、コードインタープリターなどの外部システムとの動的なやり取りが必要なシナリオでよく使用されます。この能力は、以下のようなさまざまなユースケースに役立ちます:

ツール利用デザインパターンを実装するために必要な要素/構成要素は何か?

これらの構成要素により、AIエージェントは幅広いタスクを実行できるようになります。ツール利用デザインパターンを実装するために必要な主要な要素を見てみましょう:

次に、関数/ツール呼び出しについて詳しく見ていきましょう。

関数/ツール呼び出し

関数呼び出しは、LLMがツールと連携するための主要な方法です。「関数」と「ツール」はしばしば同義で使用されます。なぜなら、「関数」(再利用可能なコードのブロック)がエージェントがタスクを実行するために使用する「ツール」だからです。関数のコードを呼び出すためには、LLMがユーザーのリクエストを関数の説明と比較する必要があります。このため、利用可能なすべての関数の説明を含むスキーマがLLMに送信されます。LLMはタスクに最も適した関数を選択し、その名前と引数を返します。選択された関数が呼び出され、その応答がLLMに送られ、ユーザーのリクエストに応答するために使用されます。

エージェントの関数呼び出しを実装するために、開発者が必要とするもの:

  1. 関数呼び出しをサポートするLLMモデル
  2. 関数の説明を含むスキーマ
  3. 説明された各関数のコード

サンフランシスコの現在時刻を取得する例を使って説明します:

  1. 関数呼び出しをサポートするLLMを初期化する:

    すべてのモデルが関数呼び出しをサポートしているわけではないため、使用しているLLMが対応しているか確認することが重要です。Azure OpenAIは関数呼び出しをサポートしています。Azure OpenAIクライアントを初期化することから始めます。

     # Initialize the Azure OpenAI client
     client = AzureOpenAI(
         azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT"), 
         api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
         api_version="2024-05-01-preview"
     )
    
  2. 関数スキーマを作成する:

    次に、関数名、関数の目的、関数パラメータの名前と説明を含むJSONスキーマを定義します。このスキーマを、サンフランシスコの時刻を見つけるためのユーザーリクエストとともに、先ほど作成したクライアントに渡します。重要なのは、ツール呼び出しが返されることであり、質問への最終的な答えではないことです。前述のように、LLMはタスクに選択した関数の名前と渡される引数を返します。

     # Function description for the model to read
     tools = [
         {
             "type": "function",
             "function": {
                 "name": "get_current_time",
                 "description": "Get the current time in a given location",
                 "parameters": {
                     "type": "object",
                     "properties": {
                         "location": {
                             "type": "string",
                             "description": "The city name, e.g. San Francisco",
                         },
                     },
                     "required": ["location"],
                 },
             }
         }
     ]
    
      
     # Initial user message
     messages = [{"role": "user", "content": "What's the current time in San Francisco"}] 
      
     # First API call: Ask the model to use the function
       response = client.chat.completions.create(
           model=deployment_name,
           messages=messages,
           tools=tools,
           tool_choice="auto",
       )
      
       # Process the model's response
       response_message = response.choices[0].message
       messages.append(response_message)
      
       print("Model's response:")  
    
       print(response_message)
      
    
     Model's response:
     ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_pOsKdUlqvdyttYB67MOj434b', function=Function(arguments='{"location":"San Francisco"}', name='get_current_time'), type='function')])
    
  3. タスクを実行するために必要な関数コード:

    LLMが実行する必要がある関数を選択した後、タスクを実行するコードを実装して実行する必要があります。Pythonで現在時刻を取得するコードを実装できます。また、response_messageから名前と引数を抽出して最終結果を取得するコードも記述する必要があります。

       def get_current_time(location):
         """Get the current time for a given location"""
         print(f"get_current_time called with location: {location}")  
         location_lower = location.lower()
            
         for key, timezone in TIMEZONE_DATA.items():
             if key in location_lower:
                 print(f"Timezone found for {key}")  
                 current_time = datetime.now(ZoneInfo(timezone)).strftime("%I:%M %p")
                 return json.dumps({
                     "location": location,
                     "current_time": current_time
                 })
          
         print(f"No timezone data found for {location_lower}")  
         return json.dumps({"location": location, "current_time": "unknown"})
    
      # Handle function calls
       if response_message.tool_calls:
           for tool_call in response_message.tool_calls:
               if tool_call.function.name == "get_current_time":
         
                   function_args = json.loads(tool_call.function.arguments)
         
                   time_response = get_current_time(
                       location=function_args.get("location")
                   )
         
                   messages.append({
                       "tool_call_id": tool_call.id,
                       "role": "tool",
                       "name": "get_current_time",
                       "content": time_response,
                   })
       else:
           print("No tool calls were made by the model.")  
      
       # Second API call: Get the final response from the model
       final_response = client.chat.completions.create(
           model=deployment_name,
           messages=messages,
       )
      
       return final_response.choices[0].message.content
    
       get_current_time called with location: San Francisco
       Timezone found for san francisco
       The current time in San Francisco is 09:24 AM.
    

関数呼び出しは、ほとんどすべてのエージェントツール利用設計の中心にありますが、ゼロから実装するのは時に困難です。レッスン2で学んだように、エージェントフレームワークはツール利用を実装するための事前構築された構成要素を提供してくれます。

エージェントフレームワークを使用したツール利用の例

以下は、異なるエージェントフレームワークを使用してツール利用デザインパターンを実装する方法の例です:

Semantic Kernel

Semantic Kernelは、LLMを使用する.NET、Python、Java開発者向けのオープンソースAIフレームワークです。関数呼び出しを簡素化し、関数とそのパラメータをモデルに自動的に説明するシリアル化プロセスを通じて実現します。また、モデルとコード間のやり取りを処理します。Semantic Kernelのようなエージェントフレームワークを使用するもう一つの利点は、ファイル検索コードインタープリターなどの事前構築されたツールにアクセスできることです。

以下の図は、Semantic Kernelを使用した関数呼び出しのプロセスを示しています:

function calling

Semantic Kernelでは、関数/ツールはプラグインと呼ばれます。先ほど見たget_current_time関数をクラスに変換し、その中に関数を含めることでプラグインに変換できます。また、関数の説明を受け取るkernel_functionデコレーターをインポートすることができます。その後、GetCurrentTimePluginを使用してカーネルを作成すると、カーネルは関数とそのパラメータを自動的にシリアル化し、スキーマを作成してLLMに送信します。

from semantic_kernel.functions import kernel_function

class GetCurrentTimePlugin:
    async def __init__(self, location):
        self.location = location

    @kernel_function(
        description="Get the current time for a given location"
    )
    def get_current_time(location: str = ""):
        ...

from semantic_kernel import Kernel

# Create the kernel
kernel = Kernel()

# Create the plugin
get_current_time_plugin = GetCurrentTimePlugin(location)

# Add the plugin to the kernel
kernel.add_plugin(get_current_time_plugin)

Azure AI Agent Service

Azure AI Agent Serviceは、開発者が高品質で拡張性のあるAIエージェントを安全に構築、展開、スケールできるように設計された新しいエージェントフレームワークです。基盤となるコンピュートやストレージリソースを管理する必要がないため、特にエンタープライズアプリケーションに役立ちます。完全に管理されたサービスであり、エンタープライズグレードのセキュリティを備えています。

LLM APIを直接使用して開発する場合と比較して、Azure AI Agent Serviceは以下の利点を提供します:

Azure AI Agent Serviceで利用可能なツールは、以下の2つのカテゴリに分けられます:

  1. 知識ツール:
  2. アクションツール:

Agent Serviceを使用すると、これらのツールをtoolsetとして一緒に使用できます。また、特定の会話のメッセージ履歴を追跡するthreadsを利用します。

例えば、Contosoという会社の営業担当者であると仮定します。営業データに関する質問に答える会話型エージェントを開発したいとします。

以下の画像は、Azure AI Agent Serviceを使用して営業データを分析する方法を示しています:

Agentic Service In Action

このサービスでツールを使用するには、クライアントを作成し、ツールまたはツールセットを定義します。これを実際に実装するには、以下のPythonコードを使用できます。LLMはツールセットを確認し、ユーザーが作成した関数fetch_sales_data_using_sqlite_queryを使用するか、事前構築されたコードインタープリターを使用するかをユーザーリクエストに応じて判断します。

import os
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from fetch_sales_data_functions import fetch_sales_data_using_sqlite_query # fetch_sales_data_using_sqlite_query function which can be found in a fetch_sales_data_functions.py file.
from azure.ai.projects.models import ToolSet, FunctionTool, CodeInterpreterTool

project_client = AIProjectClient.from_connection_string(
    credential=DefaultAzureCredential(),
    conn_str=os.environ["PROJECT_CONNECTION_STRING"],
)

# Initialize function calling agent with the fetch_sales_data_using_sqlite_query function and adding it to the toolset
fetch_data_function = FunctionTool(fetch_sales_data_using_sqlite_query)
toolset = ToolSet()
toolset.add(fetch_data_function)

# Initialize Code Interpreter tool and adding it to the toolset. 
code_interpreter = code_interpreter = CodeInterpreterTool()
toolset = ToolSet()
toolset.add(code_interpreter)

agent = project_client.agents.create_agent(
    model="gpt-4o-mini", name="my-agent", instructions="You are helpful agent", 
    toolset=toolset
)

信頼性の高いAIエージェントを構築するためにツール利用デザインパターンを使用する際の特別な考慮事項は何か?

LLMによって動的に生成されるSQLに関する一般的な懸念は、セキュリティ、特にSQLインジェクションやデータベースの削除や改ざんなどの悪意ある行動のリスクです。これらの懸念は有効ですが、データベースアクセス権限を適切に設定することで効果的に軽減できます。ほとんどのデータベースでは、データベースを読み取り専用に設定することが含まれます。PostgreSQLやAzure SQLのようなデータベースサービスでは、アプリに読み取り専用(SELECT)ロールを割り当てる必要があります。

アプリを安全な環境で実行することで、さらに保護が強化されます。エンタープライズシナリオでは、データは通常、運用システムから抽出され、読み取り専用データベースまたはデータウェア Azure AI Foundry Discordに参加して、他の学習者と交流したり、オフィスアワーに参加したり、AI Agentsに関する質問に答えてもらいましょう。

追加リソース

前のレッスン

エージェントデザインパターンの理解

次のレッスン

Agentic RAG


免責事項:
この文書は、AI翻訳サービス Co-op Translator を使用して翻訳されています。正確性を追求しておりますが、自動翻訳には誤りや不正確な部分が含まれる可能性があることをご承知ください。元の言語で記載された文書が正式な情報源とみなされるべきです。重要な情報については、専門の人間による翻訳を推奨します。この翻訳の使用に起因する誤解や誤解釈について、当社は責任を負いません。