LangChain + Gemini 2.5対話型AIツール(画像アップロード可能)(ソースコードと実行結果)

Python開発環境,ライブラリ類

ここでは、最低限の事前準備について説明する。機械学習や深層学習を行う場合は、NVIDIA CUDA、Visual Studio、Cursorなどを追加でインストールすると便利である。これらについては別ページ https://www.kkaneko.jp/cc/dev/aiassist.htmlで詳しく解説しているので、必要に応じて参照してください。

Python 3.12 のインストール

インストール済みの場合は実行不要。

管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要である。

REM Python をシステム領域にインストール
winget install --scope machine --id Python.Python.3.12 -e --silent
REM Python のパス設定
set "PYTHON_PATH=C:\Program Files\Python312"
set "PYTHON_SCRIPTS_PATH=C:\Program Files\Python312\Scripts"
echo "%PATH%" | find /i "%PYTHON_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_PATH%" /M >nul
echo "%PATH%" | find /i "%PYTHON_SCRIPTS_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_SCRIPTS_PATH%" /M >nul

関連する外部ページ

Python の公式ページ: https://www.python.org/

AI エディタ Windsurf のインストール

Pythonプログラムの編集・実行には、AI エディタの利用を推奨する。ここでは,Windsurfのインストールを説明する。

管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行して、Windsurfをシステム全体にインストールする。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要となる。

winget install --scope machine Codeium.Windsurf -e --silent

関連する外部ページ

Windsurf の公式ページ: https://windsurf.com/

必要なライブラリのインストール

コマンドプロンプトを管理者として実行(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する


pip install langchain-google-genai pillow langchain-core

LangChain + Gemini 2.5対話型AIツール(画像アップロード可能)(ソースコードと実行結果)

概要

本プログラムは、LangChainフレームワークとGemini 2.5 Flash APIを組み合わせた対話型AIアプリケーションである。テキストと画像の両方を入力として受け付けることが可能なマルチモーダル処理の機能を持つ。会話型インターフェースである。Gemini 2.5 Flash 思考機能による推論処理、問題解決を実現している。

主要技術

Gemini 2.5 Flash API

Google DeepMindが2025年に開発した思考機能付き言語モデルAPI [1][2]。推論プロセスを経た内部思考により、問題解決を可能とする。

LangChain

Harrison Chaseが2022年に開発した大規模言語モデル統合フレームワーク [3][4]。LLMを活用したアプリケーション開発を支援する。

技術的特徴

本実装では、複数の技術的工夫が施されている。マルチモーダルメッセージ構築にはLangChainのHumanMessageクラスを使用し、テキストと画像データを処理する。画像データはBase64エンコーディングによってテキスト形式に変換され、APIリクエストに組み込まれる。APIキー管理については、環境変数と.envファイルの双方からの自動取得機能を実装している。

実装の特色

GUI機能として、tkinterライブラリを用いたファイル選択ダイアログを実装し、画像ファイル選択を可能としている。複数行テキスト入力機能により、長文の質問や複雑な指示を受け付けることができる。対話の継続性を維持し、セッション内でのコンテキストを保持する。

参考文献

[1] Google DeepMind Team. (2025). Gemini 2.5: Our newest Gemini model with thinking. Google DeepMind Blog. https://blog.google/technology/google-deepmind/gemini-model-thinking-updates-march-2025/

[2] Google AI for Developers. (2025). Gemini 2.5 Flash API Documentation. Google AI for Developers. https://ai.google.dev/gemini-api/docs/models

[3] Chase, H. (2022). LangChain: Building applications with LLMs through composability. GitHub. https://github.com/langchain-ai/langchain

[4] LangChain Development Team. (2025). LangChain Documentation. LangChain Inc. https://python.langchain.com/docs/introduction/

ソースコード


# LangChain + Gemini 2.5 Flash対話型AIアプリケーション
# 特徴技術名: Gemini 2.5 Flash API
# 出典: Gemini Team, Google. (2025). Gemini 2.5: Our newest Gemini model with thinking. Google DeepMind. https://blog.google/technology/google-deepmind/gemini-model-thinking-updates-march-2025/
# 特徴機能: 思考機能による文脈理解推論(推論プロセスを経た思考により、単純な応答生成ではなく複雑な問題解決と文脈理解を実現)
# 学習済みモデル: 使用なし(Gemini 2.5 Flash APIによる直接呼び出し)
# 方式設計:
#   関連利用技術: LangChain(LLMフレームワーク)、tkinter(ファイル選択GUI)、PIL(画像処理)、base64(画像エンコーディング)、pathlib(ファイル操作)
#   入力と出力: 入力: テキスト(複数行対応、空行で終了)、画像(オプション、tkinterでファイル選択可能)、出力: AI応答テキストをコンソール表示
#   処理手順: APIキー取得→LangChain初期化→会話ループ(画像選択→テキスト入力→API呼び出し→結果表示)
#   前処理、後処理: 前処理: 画像のBase64エンコーディング、マルチモーダルメッセージ構築、後処理: 応答内容の表示処理
#   追加処理: マルチモーダル対応(テキスト+画像同時処理)、複数行入力機能、会話状態管理
#   調整を必要とする設定値: GEMINI_API_KEY(Gemini APIアクセスキー)、temperature(応答の一貫性制御、デフォルト0.1)
# 将来方策: APIキー自動設定機能(.env設定の自動化、環境変数設定支援)
# その他の重要事項: Windows環境対応、Python 3.10以上、Google AI Studio APIキー必要
# 前準備: pip install langchain-google-genai pillow langchain-core

import os
import re
import base64
from pathlib import Path
from datetime import datetime
from PIL import Image
import tkinter as tk
from tkinter import filedialog
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.messages import HumanMessage

# 設定定数
TEMPERATURE = 0.1
MODEL_NAME = "gemini-2.5-flash"

def load_api_key():
    # 環境変数から取得
    for var_name in ['GEMINI_API_KEY', 'GOOGLE_API_KEY']:
        if api_key := os.getenv(var_name):
            return api_key

    # .envファイルから取得
    for env_file in ['.env', '.env.development']:
        if Path(env_file).exists():
            try:
                content = Path(env_file).read_text()
                for var_name in ['GEMINI_API_KEY', 'GOOGLE_API_KEY']:
                    if match := re.search(rf'^\s*{var_name}\s*=(.+)$', content, re.M):
                        return match.group(1).strip().strip('"\'')
            except Exception:
                continue
    return None

def encode_image(image_path):
    try:
        with open(image_path, "rb") as f:
            return base64.b64encode(f.read()).decode()
    except Exception as e:
        print(f"画像読み込みエラー: {e}")
        return None

def select_image():
    root = tk.Tk()
    root.withdraw()
    return filedialog.askopenfilename(
        title="画像を選択(キャンセルでテキストのみ)",
        filetypes=[("画像", "*.jpg *.jpeg *.png *.gif *.bmp"), ("全て", "*.*")]
    )

def generate_filename():
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    return f"conversation_log_{timestamp}.txt"

def main():
    print("=== LangChain + Gemini 2.5 対話型AIアプリ ===")

    # APIキー取得
    api_key = load_api_key()
    if not api_key:
        # APIキー取得手順を表示
        print("=" * 60)
        print("Gemini APIキーが見つかりません")
        print("=" * 60)
        print()
        print("このプログラムを使用するには、Gemini APIキーが必要です。")
        print()
        print("APIキー取得手順:")
        print("1. または手動でAPIキーを取得:")
        print("   - https://aistudio.google.com/app/apikey にアクセス")
        print("   - Googleアカウントでログイン")
        print("   - 'Get API key' → 'Create API key' をクリック")
        print("   - 'Create API key in new project' を選択")
        print("   - 生成されたAPIキーをコピー")
        print()
        print("参考: 「Gemini APIキー取得支援ツール」を用意しています")
        print("   https://www.kkaneko.jp/ai/labo/geminiapikey.html")
        print()
        print("2. 設定")
        print("   **1. 環境変数での設定**")
        print("   - `GEMINI_API_KEY` または `GOOGLE_API_KEY` を環境変数に設定")
        print("   **2. .envファイルでの設定**")
        print("   - プログラムと同じフォルダに `.env` ファイルを作成し、以下のように記述:")
        print("   GEMINI_API_KEY=your_api_key_here")
        print("   または")
        print("   GOOGLE_API_KEY=your_api_key_here")
        print()
        print("3. 設定後、このプログラムを再実行してください")
        print("=" * 60)

    # LangChainでGemini初期化
    try:
        llm = ChatGoogleGenerativeAI(
            model=MODEL_NAME,
            google_api_key=api_key,
            temperature=TEMPERATURE
        )
        print("初期化完了!'quit'で終了")
        print("\n=== 使用ガイド ===")
        print("1. テキスト質問: 複数行入力可能(空行でEnter押下で入力終了)")
        print("2. 画像付き質問: 最初に画像を選択してからテキストを入力")
        print("3. 使用例:")
        print("   - 'この画像の内容を説明してください'")
        print("   - 'コードのレビューをお願いします'")
        print("   - '文章の要約をしてください'")
        print("画像を含める場合は最初に画像を選択してから質問してください\n")
    except Exception as e:
        print(f"初期化エラー: {e}")
        return

    conv_count = 0

    while True:
        try:
            # 会話状態表示
            if conv_count == 0:
                print("AIとの会話を開始")
            else:
                print("AIとの会話を続行")

            conv_count += 1

            # 設定確認表示
            print(f"\n=== 設定確認 ===")
            print(f"使用モデル: {MODEL_NAME}")
            print(f"温度設定: {TEMPERATURE}")
            print("=" * 30)

            # 画像選択オプション
            use_image = input("画像を使用しますか? (y/n/quit): ").lower()
            if use_image == 'quit':
                break

            image_data = None
            if use_image == 'y':
                if image_path := select_image():
                    image_data = encode_image(image_path)
                    print(f"画像読み込み: {Path(image_path).name}")

            # ユーザー入力
            print("あなた(複数行入力可、空の行でEnterで入力終了):")
            lines = []
            while True:
                line = input()
                if line == "":
                    break
                lines.append(line)

            user_input = "\n".join(lines)
            if user_input.lower() in ['quit', 'exit', '終了']:
                break

            if not user_input.strip():
                print("入力が空です。再入力してください。")
                continue

            print("処理開始")

            # メッセージ構築と送信
            if image_data:
                # マルチモーダル対応
                message = HumanMessage(
                    content=[
                        {"type": "text", "text": user_input},
                        {"type": "image_url", "image_url": f"data:image/jpeg;base64,{image_data}"}
                    ]
                )
                response = llm.invoke([message])
            else:
                # テキストのみ
                response = llm.invoke(user_input)

            print(f"AI: {response.content}")

        except KeyboardInterrupt:
            print("\n\nプログラムを終了します")
            break
        except Exception as e:
            print(f"エラーが発生しました: {e}")
            print("別の質問で再試行してください。")

    print("お疲れ様でした!")

if __name__ == "__main__":
    main()