LiteLLMで無料Gemini+gpt-4o-mini+Haiku+sonnet3.5のParallel Function Callingを試す

## はじめに

こんにちは！今回は、LiteLLMライブラリを使って、GoogleのGemini Pro、OpenAIのgpt-4-o-mini、AnthropicのClaude 3.5 sonnet、Claude 3 haikuといった複数のモデルでパラレル関数呼び出し（Parallel Function Calling）機能を試してみます。この記事は、AIと自然言語処理に興味がある初心者の方でも理解できるように、丁寧に解説していきます。

パラレル関数呼び出しとは、AIモデルが複数の外部関数を同時に呼び出し、その結果を統合して回答を生成する機能です。この機能を使うことで、より正確で詳細な情報をAIに提供することができます。

<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="ja" dir="ltr">LiteLLM越しにGoogleのGemini Pro 1.5、OpenAIのgpt-4-o-mini、AnthropicのClaude 3.5 sonnet、Claude 3 haikuでParallel Function Callingを試してみました！！その結果「gpt-4-o-mini」と「Claude 3.5 sonnet」がParallel Function Callingを実行<br>その結果「gpt-4-o-mini」と「Claude 3.5… <a href="https://t.co/toi4XuneyW">https://t.co/toi4XuneyW</a> <a href="https://t.co/FFmhswIiH6">pic.twitter.com/FFmhswIiH6</a></p>&mdash; Maki@Sunwood AI Labs. (@hAru_mAki_ch) <a href="https://twitter.com/hAru_mAki_ch/status/1819937007138951481?ref_src=twsrc%5Etfw">August 4, 2024</a></blockquote> 

## 環境設定

まず、必要なライブラリをインストールし、環境を設定します。Google Colabで新しいノートブックを開き、以下のセルを実行してください。

```python
# 必要なライブラリをインストール
!pip install -q litellm rich
```

次に、必要なモジュールをインポートします。

```python
# 必要なモジュールをインポート
import os
from litellm import completion
from rich.console import Console
from rich.tree import Tree
from rich.table import Table
from rich.panel import Panel
from rich.syntax import Syntax
from typing import Dict, Any

# Richコンソールを初期化
console = Console()
```

## APIキーの設定

各サービスのAPIを使用するには、APIキーが必要です。以下のコードでAPIキーを環境変数として設定します。

**注意: 実際のAPIキーに置き換えてください**

```python
# Google ColabのユーザデータからAPIキーを取得
from google.colab import userdata

# AnthropicのAPIキーを設定
os.environ["ANTHROPIC_API_KEY"] = userdata.get('ANTHROPIC_API_KEY') 
# GoogleのAPIキーを設定
os.environ["GEMINI_API_KEY"] = userdata.get('GEMINI_API_KEY')
# OpenAIのAPIキーを設定
os.environ["OPENAI_API_KEY"] = userdata.get('OPENAI_API_KEY')
```

## 便利関数の定義

AIモデルからの応答を見やすく表示するための関数を定義します。この関数は、Rich libraryを使用して、応答の詳細を整形して表示します。

```python
# 関数呼び出しの結果をリッチに表示する関数
def display_rich_function_call_response(response: Dict[str, Any]) -> None:
    # モデル情報を表示するためのツリーを作成
    model_info = Tree("Function Call Response")
    model_info.add(f"ID: [cyan]{response['id']}[/cyan]")
    model_info.add(f"Created: [cyan]{response['created']}[/cyan]")
    model_info.add(f"Model: [cyan]{response['model']}[/cyan]")
    model_info.add(f"Object: [cyan]{response['object']}[/cyan]")
    model_info.add(f"System Fingerprint: [cyan]{response.get('system_fingerprint', 'N/A')}[/cyan]") # system_fingerprintはモデルによっては存在しない場合がある

    # 使用量情報を表示するためのテーブルを作成
    usage_table = Table(title="Usage", show_header=True, header_style="bold magenta")
    usage_table.add_column("Metric", style="dim")
    usage_table.add_column("Value", justify="right")
    usage = response['usage']
    usage_table.add_row("Prompt Tokens", str(usage['prompt_tokens']))
    usage_table.add_row("Completion Tokens", str(usage['completion_tokens']))
    usage_table.add_row("Total Tokens", str(usage['total_tokens']))

    # 選択肢（Choices）情報を表示するためのツリーを作成
    choices_tree = Tree("Choices")
    for idx, choice in enumerate(response['choices']):
        choice_tree = choices_tree.add(f"Choice {idx}")
        choice_tree.add(f"Finish Reason: [green]{choice['finish_reason']}[/green]")
        choice_tree.add(f"Index: [green]{choice['index']}[/green]")

        message = choice['message']
        message_tree = choice_tree.add("Message")
        message_tree.add(f"Role: [blue]{message['role']}[/blue]")

        if message['content']:
            # Content を Syntax オブジェクトとして作成
            content_syntax = Syntax(message['content'], "markdown", theme="monokai", line_numbers=True, word_wrap=True)
            # Panel でラップして message_tree に追加
            message_tree.add(Panel(content_syntax, title="Content", border_style="blue"))

        if message.get('tool_calls'): # tool_callsはモデルによっては存在しない場合がある
            tool_calls_tree = message_tree.add("Tool Calls")
            for tool_call_index, tool_call in enumerate(message['tool_calls']):
                tool_call_tree = tool_calls_tree.add(f"Tool Call {tool_call_index}")
                tool_call_tree.add(f"ID: [yellow]{tool_call['id']}[/yellow]")
                tool_call_tree.add(f"Type: [yellow]{tool_call['type']}[/yellow]")
                function_tree = tool_call_tree.add("Function")
                function_tree.add(f"Name: [red]{tool_call['function']['name']}[/red]")

                # Arguments を Syntax オブジェクトとして作成
                args_syntax = Syntax(tool_call['function']['arguments'], "json", theme="monokai", word_wrap=True)
                # Panel でラップして function_tree に追加
                function_tree.add(Panel(args_syntax, title="Arguments", border_style="red"))

    # 作成したツリーとテーブルを表示
    console = Console(width=120)  # コンソールの幅を広げる
    console.print(Panel(model_info, title="Function Call Response", border_style="bold"))
    console.print(usage_table)
    console.print(Panel(choices_tree, title="Choices", border_style="bold"))
```

この関数は、AIモデルからの応答を構造化し、色分けして表示することで、応答の内容を視覚的に理解しやすくなります。


## 関数定義

天気情報を取得する関数を定義します。これは、AIモデルが呼び出す外部関数の例です。

```python
# AIモデルが呼び出す外部関数を定義
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather", # 関数の名前
            "description": "指定された場所の現在の天気を取得します", # 関数の説明
            "parameters": { # 関数のパラメータ
                "type": "object",
                "properties": {
                    "location": { # 場所
                        "type": "string",
                        "description": "都市名と州名、例：San Francisco, CA",
                    },
                    "unit": { # 温度単位
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                    },
                },
                "required": ["location"], # 必須パラメータ
            },
        },
    }
]
```

この`tools`リストは、AIモデルが利用可能な外部関数を定義しています。ここでは、`get_current_weather`という関数を定義し、場所と温度単位を引数として受け取り、天気情報を返す想定です。


## 関数呼び出しの実行

ユーザーからの質問に基づいて、各AIモデルに関数呼び出しを行わせます。

### Gemini Pro

```python
# ユーザーからの質問
messages = [
    {
        "role": "user",
        "content": "ボストンと東京の今日の天気は何度（華氏）ですか？",
    }
]

# Gemini Proで関数呼び出し
response = completion(
    model="gemini/gemini-1.5-pro-latest",
    messages=messages,
    tools=tools,
    tool_choice="auto", # モデルが自動的にツールを選択
)

# 結果を表示
display_rich_function_call_response(response)
```

### GPT-4-o-mini

```python
# ユーザーからの質問 (Gemini Proと同じ)

# GPT-4-o-miniで関数呼び出し
response = completion(
    model="gpt-4o-mini",
    messages=messages,
    tools=tools,
    tool_choice="auto", 
)

# 結果を表示
display_rich_function_call_response(response)
```

### Claude 3 Haiku

```python
# ユーザーからの質問 (Gemini Proと同じ)

# Claude 3 Haikuで関数呼び出し
response = completion(
    model="anthropic/claude-3-haiku-20240307",
    messages=messages,
    tools=tools,
    tool_choice="auto", 
)

# 結果を表示
display_rich_function_call_response(response)
```

### Claude 3.5 Sonnet

```python
# ユーザーからの質問 (Gemini Proと同じ)

# Claude 3.5 Sonnetで関数呼び出し
response = completion(
    model="anthropic/claude-3-5-sonnet-20240620",
    messages=messages,
    tools=tools,
    tool_choice="auto", 
)

# 結果を表示
display_rich_function_call_response(response)
```


## まとめ

以上で、LiteLLMを使用して、Google Gemini Pro、OpenAI GPT-4-o-mini、Anthropic Claude 3.5 sonnet、Claude 3 haikuといった複数のモデルでパラレル関数呼び出し機能を試す方法を紹介しました。

それぞれのモデルがどのように関数呼び出しを行い、結果を返すかを確認することで、各モデルの特徴を理解するのに役立ちます。

実際のアプリケーションでは、外部APIとの連携や、より複雑な関数の定義など、様々な拡張が可能です。AIモデルの能力を最大限に活用するために、この機能を活用してみてください。

ぜひ、このコードを実行して、AIモデルがどのように外部関数を呼び出し、その結果を使って回答を生成するかを体験してみてください。AIの可能性は無限大です！

## 📒ノートブック

https://colab.research.google.com/drive/12j8rXzZnLt68xSfxGZ8DfEOItHH1lXU8?usp=sharing

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>