Gemini API を使ったチャットボット

はじめに

近年、AI の急速な進化により、自然言語処理(NLP)の分野では高性能な生成系モデルが次々と登場しています。Google Generative AI は、その一例として、対話やコンテンツ生成に活用できる先進的なモデルです。この記事では、Google Generative AI を用いてシンプルなチャットボットを構築する方法を、サンプルコードを交えながら解説します。

対象は、プログラミング初心者から上級者まで幅広い読者を想定しています。まずは、基本的な構成や仕組みを理解し、次に実際のコードの解説、そして応用的な機能追加やセキュリティ対策について学んでいきます。

この記事を読み終える頃には、以下の内容が身につくことを目標とします。

  • Google Generative AI の基本的な仕組みと利用方法
  • フロントエンドとバックエンドの連携方法
  • API キーや CORS の設定、エラーハンドリングの実装方法
  • 今後の拡張や実運用に向けた考慮点

1.Google Generative AI の概要

Google Generative AI は、自然言語生成(NLG)をはじめとする高度な生成系タスクに対応する AI モデルです。以下の特徴があります。

  • 高い生成能力
    自然な文章生成や対話システムの構築に最適なモデルであり、入力プロンプトに対して多様な出力を生成します。
  • API 経由での利用
    サーバーレスな環境や Web アプリケーションから、シンプルな API 呼び出しにより利用できるため、実装が容易です。
  • 柔軟なカスタマイズ性
    利用するモデルやパラメータの設定により、チャットボットの応答内容やスタイルを調整可能です。

Google Generative AI を活用することで、ユーザーからの質問や依頼に対して、柔軟で自然な応答を返すチャットボットを構築することが可能になります。


2.システム全体のアーキテクチャ

ここで実装するチャットボットは、シンプルなクライアント・サーバーモデルに基づいています。

  1. フロントエンド
    ユーザーが API キーとプロンプトを入力するシンプルな HTML ページです。ユーザー入力を受け取り、サーバーに AJAX リクエストを送信します。
  2. バックエンド(サーバーレス関数)
    受け取ったプロンプトと API キーを元に、Google Generative AI の API を呼び出し、生成された応答をフロントエンドに返します。ここでは、CORS 対応やエラーハンドリングが実装されています。
  3. Google Generative AI API
    実際にプロンプトに対する生成処理を行う部分です。Google が提供するライブラリやエンドポイントを利用して、応答テキストを取得します。

このような構造により、フロントエンドはシンプルなユーザーインターフェイスを提供し、バックエンドが実際の AI 処理を担う役割分担がなされています。


3.サンプルコードの全体像

以下は、今回のサンプルコード全体です。まずは全体像を確認し、その後各パートごとに詳細に解説していきます。

GitHub:https://github.com/ar-note/gemini_api
サンプルサイト:https://gemini-api-delta-jade.vercel.app
アプリURL:https://gemini-api-delta-jade.vercel.app/api

バックエンド(api/index.js)

// api/index.js
import { GoogleGenerativeAI } from 'generative-ai';

export default async function handler(req, res) {
  // CORS ヘッダーを設定
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
  res.setHeader("Access-Control-Allow-Headers", "Content-Type");

  // プリフライト(OPTIONS)リクエストの処理
  if (req.method === "OPTIONS") {
    return res.status(200).end();
  }

  if (req.method === "POST") {
    try {
      const { prompt, api_key } = req.body;
      if (!prompt) {
        return res.status(400).json({ error: 'Prompt is required.' });
      }
      if (!api_key) {
        return res.status(400).json({ error: 'API Key is required.' });
      }
      
      // クライアントから送られてきた API キーを利用
      const genAI = new GoogleGenerativeAI(api_key);
      
      // 利用するモデル名は、利用可能な正しいものに更新してください
      const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });
      const result = await model.generateContent(prompt);
      const responseObj = await result.response;
      const text = responseObj.text();
      
      res.status(200).json({ result: text });
    } catch (error) {
      console.error('Gemini API 呼び出しエラー:', error);
      res.status(500).json({ error: error.message });
    }
  } else {
    res.setHeader("Allow", "POST, OPTIONS");
    res.status(405).end("Method Not Allowed");
  }
}

フロントエンド(index.html)

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Generative AI Demo</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      padding: 20px;
    }
    input, button {
      padding: 8px;
      margin: 5px 0;
      font-size: 16px;
    }
    #result {
      margin-top: 20px;
      padding: 10px;
      border: 1px solid #ccc;
      background-color: #f9f9f9;
    }
  </style>
</head>
<body>
  <div>
    <label for="api-key">API Key:</label>
    <input type="password" id="api-key" placeholder="Enter your API Key">
  </div>
  <div>
    <label for="prompt">Prompt:</label>
    <input type="text" id="prompt" placeholder="Enter your prompt">
  </div>
  <button id="send">送信</button>
  <div id="result"></div>
  
  <script>
    const apiKeyInput = document.getElementById('api-key');
    const promptInput = document.getElementById('prompt');
    const resultDiv = document.getElementById('result');

    // localStorage に保存された API キーを読み込む
    if (localStorage.getItem('apiKey')) {
      apiKeyInput.value = localStorage.getItem('apiKey');
    }

    // API キーが変更されたら localStorage に保存
    apiKeyInput.addEventListener('input', () => {
      localStorage.setItem('apiKey', apiKeyInput.value);
    });

    document.getElementById('send').addEventListener('click', async () => {
      const prompt = promptInput.value.trim();
      const apiKey = apiKeyInput.value.trim();
      if (!prompt) {
        resultDiv.innerText = "プロンプトを入力してください。";
        return;
      }
      if (!apiKey) {
        resultDiv.innerText = "API キーを入力してください。";
        return;
      }
      
      resultDiv.innerText = "送信中...";

      try {
        // Vercel Function の URL に合わせて書き換えてください
        const response = await fetch('/api', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ prompt, api_key: apiKey })
        });
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }
        const data = await response.json();
        resultDiv.innerText = data.result || '応答がありません';
      } catch (error) {
        console.error('AJAX error:', error);
        resultDiv.innerText = 'エラー: ' + error.message;
      }
    });
  </script>
</body>
</html>

このサンプルコードをもとに、バックエンドとフロントエンドの各パートについて詳細に解説していきます。


4.バックエンドの実装(api/index.js)

バックエンドは、サーバーレス関数として実装され、クライアントからのリクエストを受け付けて Google Generative AI を呼び出す役割を果たします。ここでは主な実装ポイントを解説します。

CORS の設定

Web アプリケーションにおいて、異なるオリジン間でリソースを共有する場合、CORS(クロスオリジンリソースシェアリング)の設定が不可欠です。
以下のコードでは、全てのオリジンからのリクエストを受け入れる設定をしています。

res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");

特に プリフライトリクエスト(OPTIONS メソッド) に対しては、すぐにステータスコード 200 を返すことで、実際のリクエストがスムーズに行われるようにしています。

API キーとプロンプトの受け取り

クライアント側からは、ユーザーが入力した API キーとプロンプトが JSON 形式で送信されます。
まず、これらの値が正しく存在するかをチェックし、不足している場合は 400 番のエラーを返す処理を実装しています。

const { prompt, api_key } = req.body;
if (!prompt) {
  return res.status(400).json({ error: 'Prompt is required.' });
}
if (!api_key) {
  return res.status(400).json({ error: 'API Key is required.' });
}

Google Generative AI の呼び出し

受け取った API キーを用いて、Google Generative AI のインスタンスを生成します。
利用するモデル名は 'gemini-2.0-flash' と記述していますが、実際の利用環境に合わせて更新してください。

const genAI = new GoogleGenerativeAI(api_key);
const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });
const result = await model.generateContent(prompt);
const responseObj = await result.response;
const text = responseObj.text();

この処理で、ユーザーのプロンプトに応じた生成コンテンツが取得され、そのテキストを JSON 形式でクライアントに返しています。

エラーハンドリング

try-catch ブロックを利用して、API 呼び出し中にエラーが発生した場合の処理を行っています。
エラーメッセージはログに出力し、500 エラーと共にクライアントに返す実装です。

catch (error) {
  console.error('Gemini API 呼び出しエラー:', error);
  res.status(500).json({ error: error.message });
}


5.フロントエンドの実装(index.html)

フロントエンドは、シンプルな HTML と JavaScript により構成されています。以下にその主な機能について説明します。

UI の作成とスタイル

HTML 内では、ユーザーが API キーとプロンプトを入力できるフォーム、送信ボタン、そして結果表示用の領域が用意されています。
CSS により、シンプルかつ使いやすいレイアウトが実現されています。

<label for="api-key">API Key:</label>
<input type="password" id="api-key" placeholder="Enter your API Key">

<label for="prompt">Prompt:</label>
<input type="text" id="prompt" placeholder="Enter your prompt">

<button id="send">送信</button>
<div id="result"></div>

localStorage を利用した API キーの管理

ユーザーの利便性を向上させるため、ブラウザの localStorage を利用して API キーを保存・復元する仕組みを実装しています。
これにより、再度ページを読み込んだ際に、以前入力した API キーが自動的に反映されるようになります。

if (localStorage.getItem('apiKey')) {
  apiKeyInput.value = localStorage.getItem('apiKey');
}

apiKeyInput.addEventListener('input', () => {
  localStorage.setItem('apiKey', apiKeyInput.value);
});

AJAX による API 呼び出し

送信ボタンがクリックされると、入力されたプロンプトと API キーを JSON としてサーバーに送信します。
fetch API を利用して非同期通信を行い、レスポンスが返ってくると結果を画面に表示します。

document.getElementById('send').addEventListener('click', async () => {
  const prompt = promptInput.value.trim();
  const apiKey = apiKeyInput.value.trim();
  if (!prompt) {
    resultDiv.innerText = "プロンプトを入力してください。";
    return;
  }
  if (!apiKey) {
    resultDiv.innerText = "API キーを入力してください。";
    return;
  }
  
  resultDiv.innerText = "送信中...";

  try {
    const response = await fetch('/api', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ prompt, api_key: apiKey })
    });
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const data = await response.json();
    resultDiv.innerText = data.result || '応答がありません';
  } catch (error) {
    console.error('AJAX error:', error);
    resultDiv.innerText = 'エラー: ' + error.message;
  }
});

6.セキュリティと運用上の注意点

実際にチャットボットを運用する際には、以下の点に十分注意する必要があります。

API キーの管理

  • クライアント側での保存のリスク
    サンプルコードでは、ユーザーが入力した API キーを localStorage に保存しています。
    本番環境では、API キーが不正にアクセスされるリスクを軽減するため、サーバー側での管理や環境変数を利用する方法を検討してください。
  • アクセス制限の強化
    CORS 設定で「Access-Control-Allow-Origin: *」と全オリジンを許可していますが、
    実際の運用環境では信頼できるオリジンのみに制限することが望ましいです。

エラーハンドリングとログ管理

  • ユーザーへのフィードバック
    エラーメッセージはユーザーに対して分かりやすく表示する必要がありますが、詳細な内部エラー情報をそのまま返すと、セキュリティリスクになる場合があります。
    必要に応じて、ユーザーには一般的なエラーメッセージを表示し、詳細な情報はサーバー側のログに記録する実装にすることが推奨されます。
  • ログ管理
    サーバーサイドでのエラーは、適切にログに記録し、運用時のトラブルシューティングやセキュリティ監査に役立てましょう。

7.発展的なトピックと今後の展望

今回のサンプルは、シンプルなチャットボットのプロトタイプですが、実運用するにはさらに多くの拡張が考えられます。

会話の履歴管理

ユーザーとの対話を一時的に記憶する仕組みを導入することで、より文脈に沿った応答が可能になります。
例えば、セッション管理を導入し、前回のやり取りを参照することで、より自然な対話体験を提供できます。

スケーラビリティの向上

  • サーバーレスの活用
    Vercel Functions や AWS Lambda などを活用することで、トラフィックに応じたスケーラブルなバックエンドを構築できます。
  • キャッシュの導入
    頻繁に同じ問い合わせが発生する場合、結果をキャッシュすることでレスポンスの高速化や API 呼び出し回数の削減が期待できます。

セキュリティの強化

  • 認証と認可の実装
    ユーザー認証(例えば OAuth や JWT)を導入し、チャットボットの利用者を制限することで、悪意のあるアクセスを防止します。
  • データの暗号化
    通信や保存データの暗号化、SSL/TLS の利用など、全体的なセキュリティ対策を徹底しましょう。

モデルのカスタマイズ

Google Generative AI の API には、モデルのパラメータや出力フォーマットを調整するためのオプションが用意されている場合があります。
これらのオプションを利用して、チャットボットの応答スタイルやトーンをカスタマイズすることで、ユーザーのニーズに合わせた最適な体験を実現できます。

ユーザーインターフェイスの充実

シンプルな HTML ページから、モダンなフロントエンドフレームワーク(React、Vue.js、Angular など)を利用した高度な UI に移行することで、より洗練されたユーザー体験を提供できます。
リアルタイムでのチャット機能や、対話の履歴表示、さらには音声入力や画像認識など、さまざまな拡張が考えられます。


8.まとめ

この記事では、Google Generative AI を利用したチャットボットの基本的な作り方について、以下のポイントを詳しく解説しました。

  1. Google Generative AI の概要
    高度な文章生成能力を持つこのモデルを利用することで、自然な対話システムが構築可能であることを理解しました。
  2. システム全体のアーキテクチャ
    フロントエンドとバックエンドの役割分担、及び Google Generative AI API の連携方法について学びました。
  3. サンプルコードの詳細な解説
    • バックエンドでは、CORS 設定、API キーとプロンプトの検証、Google Generative AI の呼び出し、エラーハンドリングを実装。
    • フロントエンドでは、ユーザーインターフェイスの実装、localStorage による API キーの管理、AJAX を用いた非同期通信の処理方法を紹介しました。
  4. セキュリティと運用上の注意点
    API キーの安全な管理、CORS の制限、エラーハンドリング、ログ管理の重要性について解説しました。
  5. 発展的なトピックと今後の展望
    会話の履歴管理、スケーラビリティ、セキュリティの強化、モデルのカスタマイズ、ユーザーインターフェイスの充実など、今後の改善点や拡張の方向性について議論しました。

実際にこのサンプルコードを動作させることで、Google Generative AI を利用したチャットボットの基礎を理解し、さらに応用的な機能の実装やセキュリティ対策への取り組みを進めることができるでしょう。
特に、実運用に向けてはユーザー管理や認証、ログの解析など、多くの追加実装が必要ですが、今回の基本設計をしっかりと把握することが第一歩となります。

最後に、今回のサンプルはあくまでプロトタイプとしての実装例であり、実際の運用環境においてはさらなるテストやセキュリティ対策、エラーハンドリングの強化が必要です。
今後も技術の進化とともに、より高度なチャットボットが求められる時代に備え、常に最新の情報と技術動向を追いながら、システムの改善を図っていってください。


おわりに

この記事を通じて、Google Generative AI を利用したチャットボットの構築方法について、初心者でも理解できる基礎から、上級者向けの拡張アイデアまで幅広く解説しました。
まずはシンプルな実装から始め、徐々に運用環境に合わせた機能やセキュリティ対策、スケーラビリティの向上などを実装していくことが、実践的なシステム開発において非常に重要です。

この記事が、皆さんのプロジェクトにおける良い出発点となり、さらに多くの可能性を広げるヒントとなれば幸いです。今後も最新技術を取り入れながら、より優れたチャットボットシステムの構築にチャレンジしていきましょう。

Happy coding!