【Python × Langchain × Gemini】AI Agent の関数実行を試してみた
dotD で Web エンジニアをしている奥山です。今回は、LangChain で Gemini の AI Agent を作成して、関数を実行させる際に遭遇した課題と、その解決策について説明します。
Python のコードで AI の活用を検証するために、無料で使用できる Gemini を使いたいと思いました。しかし、Gemini では、使用する予定だった OPENAI_FUNCTIONS タイプの AI Agent が使用できませんでした。
OpenAI と Gemini の違い
OpenAI は、多くの開発者が利用するメジャーな AI モデルで、従量課金制となっています。Gemini は Google が提供する AI モデルで、ある程度までは無料で使用できるので、手元で簡単なアプリ制作を試すくらいであれば Gemini で十分です。そのため、今回は Gemini を選択しました。
OpenAI Functions とは
OPENAI_FUNCTIONS は、OpenAI の API を使用して関数呼び出しを可能にする機能です。これにより、単なるテキスト生成を超えて、AI モデルが自身で推論して、関数呼び出し、外部データベースへのアクセス、外部 API の呼び出し等、より高度なタスクを実行することが可能となります。
Gemini のインスタンスで OPENAI_FUNCTIONS は使えない
Gemini をインスタンス化して Agent を作成する場合、OpenAI ライブラリの機能を使用するOPENAI_FUNCTIONSタイプの Agent では、活用したかった関数呼び出し(Function Calling)機能が使用できません。
Gemini で利用できる Agent タイプ
OPENAI_FUNCTIONS は OpenAI の関数呼び出し機能を活用しており、特定の OpenAI モデルがサポートしている場合に使用できます。つまり、Gemini では使用できないのです。
一方、ZERO_SHOT_REACT_DESCRIPTION という Agent タイプがあり、こちらは OpenAI 以外でも使用できるため、より柔軟なツール選択が可能だということが分かりました。
以下参考↓
天気を教えてくれるアプリを作ってみた(完成形)
/src/weather_agent.py
"""Gemini と LangChain を使用したお天気 Agent の実装"""
import os
from typing import Dict, Any
import sqlite3
from dotenv import load_dotenv
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain.agents import AgentType, initialize_agent
from langchain.tools import Tool
# 環境変数をロード
load_dotenv()
def setup_database():
    """weather テーブルを使用して SQLite データベースを初期化"""
    conn = sqlite3.connect("weather.db")
    c = conn.cursor()
    c.execute("""
        CREATE TABLE IF NOT EXISTS weather
        (id INTEGER PRIMARY KEY AUTOINCREMENT,
         location TEXT,
         result TEXT)
    """)
    conn.commit()
    conn.close()
def get_weather(location: str) -> str:
    """特定の場所の天気情報を取得"""
    # 天気取得 API を呼び出す(ここでは簡易的に"晴れ"固定で返す)
    return f"{location}の天気は晴れです。"
def save_to_db(result: str) -> None:
    """天気の結果をデータベースに保存"""
    conn = sqlite3.connect("weather.db")
    c = conn.cursor()
    location = result.split(" ")[0]
    c.execute("INSERT INTO weather (location, result) VALUES (?, ?)", (location, result))
    conn.commit()
    conn.close()
def create_weather_agent():
    """お天気 Agent を初期化"""
    # 環境変数から API キーを取得
    api_key = os.getenv("GEMINI_API_KEY")
    if not api_key:
        raise ValueError("GEMINI_API_KEY environment variable is not set")
    # API キーを使用して LLM インスタンスを作成
    llm = ChatGoogleGenerativeAI(
        model="gemini-1.5-pro",
        temperature=0,
        google_api_key=api_key
    )
    # ツールを定義
    tools = [
        Tool(
            name="Weather",
            func=get_weather,
            description="天気を知りたい場所の天気情報を取得します。入力: 場所の名前(例: 東京)"
        ),
        Tool(
            name="SaveToDB",
            func=save_to_db,
            description="天気情報をデータベースに保存します。入力: 保存したい天気情報の文字列(例: 東京の天気は晴れです)"
        )
    ]
    # エージェントを生成
    agent = initialize_agent(
        tools,
        llm,
        agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
        verbose=True,
        handle_parsing_errors=True,
        max_iterations=3,  # 無限ループを防ぐ
        return_intermediate_steps=True
    )
    return agent
/main.py
"""お天気 Agent のメイン script"""
from src.weather_agent import get_weather_info
def main():
    """お天気 Agent の例を実行"""
    try:
        # 天気情報を取得して保存
        response = get_weather_info("東京")
        print("Response:", response)
    except Exception as e:
        print(f"Error occurred: {e}")
if __name__ == "__main__":
    main()
/.env
GEMINI_API_KEY="YOUR_API_KEY"
/requirements.txt
pytest>=7.4.0
black>=23.7.0
flake8>=6.1.0
mypy>=1.5.0
langchain>=0.1.0
google-generativeai>=0.3.0
openai>=1.3.0
python-dotenv>=1.0.0
結果を保存する SQLite BD を作成
def setup_database():
    """weather テーブルを使用して SQLite データベースを初期化"""
    conn = sqlite3.connect("weather.db")
    c = conn.cursor()
    c.execute("""
        CREATE TABLE IF NOT EXISTS weather
        (id INTEGER PRIMARY KEY AUTOINCREMENT,
         location TEXT,
         result TEXT)
    """)
    conn.commit()
    conn.close()
Agent に実行してもらう関数を作成
def get_weather(location: str) -> str:
    """特定の場所の天気情報を取得"""
    # 天気取得 API を呼び出す(ここでは簡易的に"晴れ"固定で返す)
    return f"{location}の天気は晴れです。"
def save_to_db(result: str) -> None:
    """天気の結果をデータベースに保存"""
    conn = sqlite3.connect("weather.db")
    c = conn.cursor()
    # '東京の天気は晴れです。' から '東京' を抽出
    location = result.split('の天気')[0]
    c.execute("INSERT INTO weather (location, result) VALUES (?, ?)", (location, result))
    conn.commit()
    conn.close()
Gemini の LLM インスタンスを生成
import os
from langchain_google_genai import ChatGoogleGenerativeAI
def create_weather_agent():
    """お天気 Agent を初期化"""
    # 環境変数から API キーを取得
    api_key = os.getenv("GEMINI_API_KEY")
    if not api_key:
        raise ValueError("GEMINI_API_KEY environment variable is not set")
    # API キーを使用して LLM インスタンスを作成
    llm = ChatGoogleGenerativeAI(
        model="gemini-1.5-pro",
        temperature=0,
        google_api_key=api_key
    )
関数をエージェントが使用する Tool として登録
from langchain.tools import Tool
    # ツールを定義
    tools = [
        Tool(
            name="Weather",
            func=get_weather,
            description="天気を知りたい場所の天気情報を取得します。入力: 場所の名前(例: 東京)"
        ),
        Tool(
            name="SaveToDB",
            func=save_to_db,
            description="天気情報をデータベースに保存します。入力: 保存したい天気情報の文字列(例: 東京の天気は晴れです)"
        )
    ]
 ZERO_SHOT_REACT_DESCRIPTION タイプでエージェントを生成
from langchain.agents import AgentType, initialize_agent
from langchain.tools import Tool
    # エージェントを生成
    agent = initialize_agent(
        tools,
        llm,
        agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
        verbose=True,
        handle_parsing_errors=True,
        max_iterations=3,  # 無限ループを防ぐ
        return_intermediate_steps=True
    )
    return agent
お天気エージェント呼び出し関数を定義
def get_weather_info(location: str) -> Dict[str, Any]:
    """天気情報を取得し、データベースに保存"""
    setup_database()
    agent = create_weather_agent()
    prompt = f"""あなたは次の2つのツールを使って天気情報を処理するエージェントです:
1. Weather: 場所を入力すると天気情報を返します
2. SaveToDB: 天気情報の文字列を入力するとデータベースに保存します
以下のタスクを順番に実行してください:
1. Weather ツールを使用して{location}の天気を取得してください
2. 取得した天気情報(例: 東京の天気は晴れです)をそのままSaveToDBツールに渡してデータベースに保存してください
3. 各ステップの結果を確認してください
必ず両方のツールを使用し、結果を確認してください。"""
    
    response = agent({"input": prompt})
    return response
実行コマンド
- 
仮想環境を作成して有効化します:
python -m venv venv && source venv/bin/activate - 
.envファイルを作成して、自身の Gemini APIキーを設定します:
GEMINI_API_KEY=YOUR_API_KEY - 
必要なパッケージをインストール:
pip install -r requirements.txt - 
実行結果
 
% python main.py
/Users/sayuri/Documents/code/sukugiru-p/src/weather_agent.py:70: LangChainDeprecationWarning: LangChain agents will continue to be supported, but it is recommended for new use cases to be built with LangGraph. LangGraph offers a more flexible and full-featured framework for building agents, including support for tool-calling, persistence of state, and human-in-the-loop workflows. For details, refer to the `LangGraph documentation <https://langchain-ai.github.io/langgraph/>`_ as well as guides for `Migrating from AgentExecutor <https://python.langchain.com/docs/how_to/migrate_agent/>`_ and LangGraph's `Pre-built ReAct agent <https://langchain-ai.github.io/langgraph/how-tos/create-react-agent/>`_.
  agent = initialize_agent(
/Users/sayuri/Documents/code/sukugiru-p/src/weather_agent.py:97: LangChainDeprecationWarning: The method `Chain.__call__` was deprecated in langchain 0.1.0 and will be removed in 1.0. Use :meth:`~invoke` instead.
  response = agent({"input": prompt})
> Entering new AgentExecutor chain...
Thought: I need to get the weather in Tokyo using the Weather tool, then save that information to the database using the SaveToDB tool.
Action: Weather
Action Input: 東京
Observation: 東京の天気は晴れです。
Thought:Question: あなたは次の2つのツールを使って天気情報を処理するエージェントです:
1. Weather: 場所を入力すると天気情報を返します
2. SaveToDB: 天気情報の文字列を入力するとデータベースに保存します
以下のタスクを順番に実行してください:
1. Weather ツールを使用して東京の天気を取得してください
2. 取得した天気情報(例: 東京の天気は晴れです)をそのままSaveToDBツールに渡してデータベースに保存してください
3. 各ステップの結果を確認してください
必ず両方のツールを使用し、結果を確認してください。
Thought: I need to get the weather in Tokyo using the Weather tool, then save that information to the database using the SaveToDB tool.
Action: Weather
Action Input: 東京
Observation: 東京の天気は晴れです。
Thought:Thought: I have the weather information for Tokyo. Now I need to save it to the database using the SaveToDB tool.
Action: SaveToDB
Action Input: 東京の天気は晴れです。
Observation: None
Thought:
> Finished chain.
Response: {
'input':
    'あなたは次の2つのツールを使って天気情報を処理するエージェントです:\n
    1. Weather: 場所を入力すると天気情報を返します\n
    2. SaveToDB: 天気情報の文字列を入力するとデータベースに保存します\n
    \n
    以下のタスクを順番に実行してください:\n
    1. Weather ツールを使用して東京の天気を取得してください\n
    2. 取得した天気情報(例: 東京の天気は晴れです)をそのままSaveToDBツールに渡してデータベースに保存してください\n
    3. 各ステップの結果を確認してください\n
    \n
    必ず両方のツールを使用し、結果を確認してください。',
'output':
    'Agent stopped due to iteration limit or time limit.',
'intermediate_steps':
    [(AgentAction(
        tool='Weather',
        tool_input='東京',
        log='Thought:
            I need to get the weather in Tokyo using the Weather tool,
            then save that information to the database using the SaveToDB tool.\n
        \n
        Action: Weather\n
        Action Input: 東京'
        ), '東京の天気は晴れです。'),
    (AgentAction(
        tool='Weather',
        tool_input='東京',
        log='Question:
            あなたは次の2つのツールを使って天気情報を処理するエージェントです:\n
                1. Weather: 場所を入力すると天気情報を返します\n
                2. SaveToDB: 天気情報の文字列を入力するとデータベースに保存します\n
                \n
            以下のタスクを順番に実行してください:\n
                1. Weather ツールを使用して東京の天気を取得してください\n
                2. 取得した天気情報(例: 東京の天気は晴れです)をそのままSaveToDBツールに渡してデータベースに保存してください\n
                3. 各ステップの結果を確認してください\n
                \n
            必ず両方のツールを使用し、結果を確認してください。\n
            Thought: I need to get the weather in Tokyo using the Weather tool,
            then save that information to the database using the SaveToDB tool.\n
            \n
        Action: Weather\n
        Action Input: 東京'
    ), '東京の天気は晴れです。'),
    (AgentAction(
        tool='SaveToDB',
        tool_input='東京の天気は晴れです。',
        log='Thought:
            I have the weather information for Tokyo.
            Now I need to save it to the database using the SaveToDB tool.\n
        \n
        Action: SaveToDB\n
        Action Input: 東京の天気は晴れです。'),
     None)]}
DB に保存されているか確認
% sqlite3 -header weather.db "SELECT * FROM weather;"
id|location|result
1|東京|東京の天気は晴れです。
まとめ
ZERO_SHOT_REACT_DESCRIPTION を使用することで、Gemini の AI Agent に関数実行を指示して実行することができました。これにより、無料枠のある Gemini を使用して、AI Agent の魅力に触れることができます。ご興味のある方は、試してみてください!
Discussion