2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cloud Run functions × Gemini × Firestoreで作る!会話履歴を覚えるLINE AIアシスタント構築入門

2
Posted at

はじめに

本記事では、LINEの中に自分専用のAIアシスタント(Gemini)を組み込む手順を解説します。

普段使いしているLINEなら、何かのついでにトーク一覧からすぐ呼び出せます。ちょっとした空き時間にも気軽に開ける手軽さが魅力です。

また、この構成を組み立てる過程では、「ユーザーの入力を受け取る→文脈を保持する→LLMに渡して応答を生成する→ユーザーに返す」というLLMアプリケーションの基本的な一連の流れを、実際に手を動かしながら学ぶことができます。

ChatGPTやGeminiのWebアプリを普段使っているだけでは見えてこない裏側の仕組みを、LINEという身近なツールを題材にすることで直感的に理解できるのも、この構成で作ってみるメリットだと思います。

今回はGoogle CloudのAlways Free(無料枠)をフル活用し、個人利用の範囲であればランニングコストをほぼゼロに抑えながらお試しいただけます。

この記事でできること

  • Cloud Run functions(第2世代)を用いたLINE Webhook受信基盤の構築
  • Cloud Firestoreを活用したステートレス環境での会話コンテキスト(文脈)維持
  • Google AI Studio(Gemini API)と連携したインテリジェントな返答生成

動作環境

  • Python 3.11
  • functions-framework 3.*
  • line-bot-sdk 3.*
  • google-genai 1.*(Google Gen AI SDK)
  • google-cloud-firestore 2.*

全体構成と利用サービス

[ ユーザー (LINEアプリ) ]
           │ メッセージ送信
           ▼
[ LINEプラットフォーム (Messaging API) ]
           │ Webhook (HTTPS POST)
           ▼
[ Cloud Run functions (第2世代 / Python 3.11) ]
     ├── 会話履歴の読み書き ──▶ [ Cloud Firestore (NoSQL) ]
     └── 応答生成リクエスト  ──▶ [ Google AI Studio (Gemini API) ]
サービス 役割 無料枠(Always Free)の目安
Cloud Run functions Webhook受信・制御 毎月200万回呼び出し無料(第2世代)
Cloud Firestore 会話履歴の保存(NoSQL) 1日あたり5万回読み取り / 2万回書き込み / 1GB無料
Google AI Studio Geminiモデル呼び出し Freeプラン(レートリミット内で完全無料利用可能)
LINE Messaging API チャットインターフェース コミュニケーションプランで月200通まで無料送信

※上記の無料枠の条件は記事執筆時点のものです。改定される場合があるため、最新の条件は各公式ドキュメントをご確認ください。


前提条件・準備するもの

  1. Google Cloud アカウント
    • 請求先アカウントが紐付いたプロジェクト(無料枠利用でも必要です)
  2. LINE Developers アカウント
    • LINEアカウントがあれば即時登録可能
  3. Google AI Studio アカウント
    • Gemini API Key を取得(Googleアカウントで発行可能)

構築手順

STEP 1. Google Cloud の API 有効化

Google Cloud コンソール上部の検索バー、または Cloud Shell から以下の必要なAPIを有効化します。

gcloud services enable \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  cloudfunctions.googleapis.com \
  run.googleapis.com \
  firestore.googleapis.com

STEP 2. Firestoreデータベースの作成

  1. Google Cloud コンソールで Firestore を検索して開きます。
  2. データベースが存在しない場合、[データベースを作成] をクリックします。
  3. データベース タイプは [ネイティブ モード] を選択します。
  4. データベース ID は (default) のままとし、ロケーション(例: asia-northeast1 (Tokyo))を選択して [データベースを作成] をクリックします。

STEP 3. LINE公式アカウント(Messaging API)の準備

  1. LINE Developers コンソール にログインします。
  2. 開発者用の「プロバイダー」を作成(既存のものがあれば選択)します。
  3. [新規チャネル作成] をクリックし、[Messaging API] を選択して必要項目を入力・作成します。
  4. 作成後、以下の2つのキーを取得します。
    • チャネルシークレット:[チャネル基本設定]タブの下部に記載
    • チャネルアクセストークン(長期):[Messaging API設定]タブの最下部にある「発行」ボタンをクリックして取得

STEP 4. 実装コードの用意

作業用フォルダを作成し、以下の2ファイルを用意します。

requirements.txt

requirements.txt
functions-framework==3.*
line-bot-sdk==3.*
google-genai>=1.0.0
google-cloud-firestore==2.*

旧SDK(google-generativeai)について
以前は google-generativeai というSDKが使われていましたが、Gemini 2.0以降はGoogleの統合SDKである google-genai に一本化されており、旧SDKは2025年11月30日をもってサポートが終了しています。本記事では新SDKの書き方に統一しています。

main.py

main.py
import os
import time
import functions_framework
from google.cloud import firestore
from google import genai
from google.genai import types
from linebot.v3.exceptions import InvalidSignatureError
from linebot.v3.messaging import (
    ApiClient,
    Configuration,
    MessagingApi,
    ReplyMessageRequest,
    TextMessage,
)
from linebot.v3.webhook import WebhookHandler
from linebot.v3.webhooks import MessageEvent, TextMessageContent

# --- 1. 初期化・環境変数設定 ---
CHANNEL_ACCESS_TOKEN = os.environ.get('LINE_CHANNEL_ACCESS_TOKEN')
CHANNEL_SECRET = os.environ.get('LINE_CHANNEL_SECRET')
GEMINI_API_KEY = os.environ.get('GEMINI_API_KEY')

configuration = Configuration(access_token=CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(CHANNEL_SECRET)

# Gemini APIクライアント(google-genai SDK)
client = genai.Client(api_key=GEMINI_API_KEY)
MODEL_NAME = 'gemini-3.5-flash'

# Geminiのキャラクター設定(※お好きな内容に書き換えてください)
SYSTEM_PROMPT = """
あなたはユーザーをサポートする親切で優秀なAIアシスタントです。
以下のルールを守って回答してください:
1. LINEのトーク画面で読みやすい長さにまとめ、適度に改行を入れる。
2. 過去の会話の文脈を踏まえて回答する。
3. 専門用語をわかりやすく解説する。
"""

# Firestoreクライアント(Cloud Run functions環境下では認証は自動解決)
db = firestore.Client()

# 保持する会話履歴の上限(直近10往復)
HISTORY_LIMIT = 10


# --- 2. エントリーポイント(Webhook受信) ---
@functions_framework.http
def line_webhook(request):
    signature = request.headers.get('X-Line-Signature')
    body = request.get_data(as_text=True)

    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        return 'Invalid signature', 400
    except Exception as e:
        print(f"Internal Error: {e}")
        return 'Internal Server Error', 500

    return 'OK', 200


# --- 3. 会話履歴の取得(ステートレス対策) ---
def get_chat_history(user_id: str):
    """Firestoreから過去の会話を取得し、Gemini API形式に整形"""
    doc_ref = db.collection('users').document(user_id)
    doc = doc_ref.get()

    history = []
    if doc.exists:
        saved_history = doc.to_dict().get('history', [])
        # 古い順に並んでいる前提で展開
        for turn in saved_history:
            history.append(types.Content(role="user", parts=[types.Part(text=turn['user'])]))
            history.append(types.Content(role="model", parts=[types.Part(text=turn['model'])]))

    return history


# --- 4. 会話履歴の保存・上限管理 ---
def save_chat_history(user_id: str, user_text: str, model_text: str):
    """新しい会話を追加し、上限を超えた古い会話をトリムして保存"""
    doc_ref = db.collection('users').document(user_id)
    doc = doc_ref.get()

    current_history = []
    if doc.exists:
        current_history = doc.to_dict().get('history', [])

    current_history.append({
        "user": user_text,
        "model": model_text,
        "timestamp": time.time()
    })

    if len(current_history) > HISTORY_LIMIT:
        current_history = current_history[-HISTORY_LIMIT:]

    doc_ref.set({'history': current_history}, merge=True)


# --- 5. メッセージイベント処理 ---
@handler.add(MessageEvent, message=TextMessageContent)
def handle_message(event):
    user_id = event.source.user_id
    user_message = event.message.text

    try:
        # 1. 過去の会話ログをFirestoreから取得
        history = get_chat_history(user_id)

        # 2. 過去の文脈とシステムプロンプトを渡してチャットセッションを開始
        chat = client.chats.create(
            model=MODEL_NAME,
            history=history,
            config=types.GenerateContentConfig(
                system_instruction=SYSTEM_PROMPT
            ),
        )

        # 3. Geminiで返答を生成
        response = chat.send_message(user_message)
        reply_text = response.text

        # 4. 今回の往復を履歴に追加保存
        save_chat_history(user_id, user_message, reply_text)

        # 5. LINEに返信
        with ApiClient(configuration) as api_client:
            line_bot_api = MessagingApi(api_client)
            line_bot_api.reply_message_with_http_info(
                ReplyMessageRequest(
                    reply_token=event.reply_token,
                    messages=[TextMessage(text=reply_text)]
                )
            )

    except Exception as e:
        print(f"Error handling message: {e}")

簡易実装であることについて
save_chat_history は「読み取り→追記→書き込み」という流れのため、同一ユーザーから短時間に連続でメッセージが送られると、まれに履歴の一部が上書きされる可能性があります。個人利用を想定した簡易実装のため、複数リクエストが同時に飛んでくるような使い方をする場合は、トランザクション処理(@firestore.transactional)などの対策を検討してください。


STEP 5. Cloud Run functions へのデプロイ

ターミナルから gcloud コマンドでデプロイを実行します。

gcloud functions deploy line-gemini-bot \
  --gen2 \
  --runtime=python311 \
  --region=asia-northeast1 \
  --source=. \
  --entry-point=line_webhook \
  --trigger-http \
  --allow-unauthenticated \
  --set-env-vars LINE_CHANNEL_ACCESS_TOKEN="<発行したチャネルアクセストークン>",LINE_CHANNEL_SECRET="<チャネルシークレット>",GEMINI_API_KEY="<取得したGemini_API_KEY>"

ハマりポイント:IAM権限の確認
Cloud Run functions(第2世代)のデフォルトの実行サービスアカウント(通常は プロジェクト番号-compute@developer.gserviceaccount.com)に、Firestoreへの読み書き権限が付与されている必要があります。
Google Cloud コンソールの [IAM と管理]>[IAM] で対象のサービスアカウントを探し、「Cloud Datastore ユーザー」 ロールが付与されていることを確認してください。

補足:機密情報の扱いについて
上記のコマンドでは動作確認のしやすさを優先してAPIキーなどを --set-env-vars に直接記述していますが、シェル履歴などに値が残るリスクがあります。本番運用や第三者と共有する環境では、Secret Managerに登録した上で --set-secrets オプション経由で読み込む方法をおすすめします。


STEP 6. LINE側のWebhookおよび応答設定

デプロイ完了時に表示される url: https://... をコピーし、LINE側の設定を行います。

1. Webhook URLの登録

  1. LINE Developers コンソールの [Messaging API設定] タブを開きます。
  2. Webhook URL の[編集]をクリックし、デプロイした関数のURLを入力して保存します。
  3. [検証] ボタンをクリックし、「成功」と表示されることを確認します。
  4. [Webhookの利用] トグルを オン にします。

2. LINE公式アカウント側の自動応答をオフにする

これを設定しないと、ボットの返答とLINEのデフォルト応答メッセージが二重で返ってきてしまいます。

  1. [Messaging API設定]タブ内にある [LINE Official Account Manager] のリンクをクリック(または LINE Official Account Manager を直接開く)。
  2. 右上の [設定](歯車アイコン)>[応答設定] を開きます。
  3. 詳細設定の [応答メッセージ] を オフ にします。

動作確認

LINE Developers の[Messaging API設定]タブにある QRコード をスマートフォンで読み取り、友だち追加します。

  1. ユーザー:「私の好きなクラウドサービスはGoogle Cloudです」
  2. ボット:「Google Cloudですね!特にどのサービスがお気に入りですか?」
  3. ユーザー:「私が好きなクラウドサービスは何でしたっけ?」
  4. ボット:「Google Cloudですね!先ほどそう教えていただきました。」

直前の会話内容を踏まえた返答が返ってくれば、履歴連携を含めて成功です。


応用アイデア:定期プッシュ配信への拡張

このボット基盤は、メッセージを受け取って返すだけでなく能動的な情報配信ツールとしても活用できます。

Compute Engine (GCE) と連携した定期的な配信

GCEの無料枠インスタンス(e2-micro)や Cloud Scheduler + Cloud Tasks を組み合わせることで、以下のような自動配信システムへ拡張できます。

私は毎朝8時に以下のような情報を受け取るBotを作成しています。

  • Google Cloud認定資格の模擬問題配信
    • 「Associate Cloud Engineer」などの模擬問題と解説をGeminiに自動生成させ、毎朝決まった時間にLINEへプッシュ通知する。
  • Google Cloud最新アップデートの要約配信
    • 公式ブログやリリースノートのRSSを巡回し、重要な更新情報をGeminiに要約させて配信する。

日常的に利用するLINEに学習コンテンツが届くため、スキマ時間を活用した学習の習慣化に効果的です。


まとめ

Google Cloudのサーバーレス環境(Cloud Run functions)とNoSQL(Firestore)、そしてGemini APIを組み合わせることで、完全無料枠の範囲内でコンテキストを理解する本格的なLINE AIアシスタントを構築できました。

サーバーレス構成のため利用していない時間帯の待機コストも一切かかりません。ぜひプロンプトを工夫して、自分好みのパートナーボットに育ててみてください!

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?