前回の設計では、ワークフローツールを中継してLINEとスプレッドシートをつないでいました。しかしユーザーがWebの回答画面へ誘導され、通常のLINEチャット体験から外れるという問題がありました。
この検証では中継を廃し、LINE Webhook → GAS 直結の構成に変更。実機でのE2E疎通まで確認しています。
この検証の概要
検証時期 | 2026年5月18日〜5月20日(実機E2E疎通まで) |
|---|---|
構成 | LINE Messaging API + GAS Web App( |
やりたかったこと | 中継を挟まずに三段構え判定を成立させ、運用者がスプレッドシートだけで完結できるFAQ更新フローを確立する |
応答時間 | 埋め込み 約0.3秒 + LLM判定 約1〜3秒(replyToken有効時間内に余裕で完結) |
ナレッジ件数 | 約20数件(FAQ初期投入分) |
状況 | 実機動作確認済み・回答精度の本格評価は未完了 |
結論:構成要素を減らすと運用が回る
中継を外した効果は明確でした。
構成要素が「LINE / GAS / スプレッドシート / OpenAI」だけに収まり、障害点・運用窓口がシンプルになりました。
運用者から見た変化はさらに大きくなります。管理画面を一切触らず、スプレッドシートのみで完結できる体験になりました。
中継ツールを入れる判断は、機能面では合理的でも運用窓口を1つ増やします。誰がどこを触るのかが分かれると、更新が止まります。
スプレッドシートの設計
列 | 内容 | 編集者 |
|---|---|---|
A | ID | 運用者が編集 |
B | 質問 | |
C | 回答 | |
D | embedding | スクリプトが自動管理 |
E | hash |
運用者が触る列と、スクリプトが管理する列を明確に分けています。この境界が曖昧だと、手作業で壊されます。
差分検知で再実行コストをほぼゼロに
実装上、最も効いた工夫です。
行ごとに sha256(質問+回答) をhash列に保存し、不一致の行だけを再ベクトル化します。
全件を毎回ベクトル化すると、API呼び出しが件数分発生します。1行編集したら1行だけ処理する——この差分検知により、更新コストがほぼゼロになりました。
実行のトリガーは2つ用意しています。
- スプレッドシートのカスタムメニュー(「チャットボット > インデックス再構築」)
- 毎日4時の自動再ベクトル化トリガ
データ更新はリアルタイム不要という前提を確定させたうえでの設計です。
三段構え判定
1. 質問を埋め込み(text-embedding-3-small / 1536次元)
2. 全行とコサイン類似度を取り、上位K=5件を抽出
3. 最大類似度 ≥ 0.7 かつ LLMが relevant=true と判定したときのみ回答
→ 閾値未満・LLM不一致時は固定フォールバック文言2つの条件を両方満たさないと回答しません。数値的な近さと、意味的な妥当性の両方でフィルタしています。
LLM判定の出力を安定させる工夫もあります。response_format: json_object と厳格なプロンプト指定により、出力を relevant と index の2フィールドに固定しました。
判定用途でLLMを使う場合、出力形式を固定しないと後続処理が壊れます。
GASの制約:署名検証ができない
この構成で最も重要な制約であり、正直に記録しておくべき点です。
GASの doPost(e) はHTTPヘッダにアクセスできません。そのため、LINE公式の X-Line-Signature(HMAC-SHA256)検証は実装できません。
この検証では代替として、Webhook URLにクエリトークンを付与し、受信側で一致判定する簡易認証を採用しました。
ただし、これは署名検証と同等のセキュリティではありません。共有シークレットをURLに乗せる方式であり、URLが漏れれば偽のリクエストを受け付けます。検証ログでも「妥協せざるを得なかった」と明記されています。
そのため、次の備えを残しています。
HMAC署名検証のロジック自体は関数として残置し、GAS以外(Cloud Run等)への移行時にそのまま流用できる状態にしてあります。
制約による妥協は、妥協だと分かる形で記録し、移行経路を用意しておくことが重要です。本番運用で外部公開する場合は、署名検証が可能な実行環境への移行を前提に考えるべき部分です。
再送ループを起こさない
Webhookを扱う際の必須知識です。
doPost は例外時もHTTP 200({ok:false, reason})を返します。
理由は明確です。LINEは非2xxのレスポンスで再送するため、エラー時に5xxを返すと処理が嵐になります。
エラーの詳細はレスポンスではなく、ログに残します。「エラーを伝える先」と「処理を受け付けたことを伝える先」を分ける設計です。
ログを2系統で持つ
デバッグ効率を大きく左右した判断です。
処理経路の各イベントを「ログ」シートに構造化JSONで追記し、標準のログ機能と二系統で追跡可能にしました。
効果として、標準ログだけでは追いにくい本番挙動(特にLINE側からの実リクエストの形状)を即座に俯瞰できました。
外部サービスからのWebhookは、ドキュメントどおりの形で来るとは限りません。実際に届いたものを見られる仕組みを最初に作っておくと、切り分けが速くなります。
実装上のTips
項目 | 知見 |
|---|---|
CacheServiceの100KB上限 | シリアライズ後サイズで判定し、超える場合は無条件にシート直読みへフォールバックする実装が安全 |
embedding列の見た目 | JSON文字列のままだと行高が伸びて視認性が下がる。書き込み後に |
推論モデル系のAPI仕様 |
|
2つ目は地味ですが実務的です。運用者が毎日見る画面が崩れると、それだけで使われなくなります。
コード構成
役割を4ファイルに分離しています。
main.js— doPost / リクエスト検証 / LINE返信 / オーケストレーション / ロガーConfig.js— 設定値アクセサ・動作パラメータ(モデル名・閾値・キャッシュTTL等)Embedding.js— 埋め込みAPI呼び出しKnowledge.js— インデックス構築 / 検索 / 判定 / 回答 / テスト / メニュー・トリガ
責務が分離されていることで、後続のスケール検証や置き換え(専用ベクトルDB化等)に向けた拡張ポイントが見えやすくなります。
なお、認証情報はScript Propertiesのみに保存し、リポジトリに乗らない構造にしています。
残っている課題
- 回答精度の本格評価が未完了
- 現行20数件規模では類似度閾値(0.7)の妥当性が判断しきれない——件数が増えるとスコア分布が変わる可能性がある
- ナレッジが数百〜数千件規模に拡大した際のスケール耐性が未測定(キャッシュ上限・全件計算コスト・スコア分布)
- マニュアル文書をQ&A形式に分解するチャンキングが属人的
2つ目は重要な指摘です。閾値はデータ量によって適切な値が変わります。少数のデータでチューニングした値が、増えた後も有効とは限りません。
よくある質問
中継ツールを外すメリットは何ですか?
構成要素が減り、運用窓口がスプレッドシートだけに絞られることです。ユーザー体験の面でも、通常のLINEチャット内で完結します。
GASで署名検証はできますか?
できません。doPost(e) がHTTPヘッダにアクセスできないためです。この検証ではクエリトークンによる簡易認証で代替していますが、署名検証と同等のセキュリティではありません。本番運用では、署名検証が可能な実行環境への移行が前提になります。
Webhookでエラー時に何を返すべきですか?
200を返し、エラー詳細はログに残してください。非2xxを返すと再送が発生し、処理が繰り返されます。
データが増えたらどうしますか?
キャッシュ上限を超えた場合はシート直読みへ自動フォールバックします。数百件超の規模では、専用ベクトルDBへの移行を検討する設計になっています。
まとめ
- 中継を外して構成要素を「LINE / GAS / スプレッドシート / OpenAI」だけに絞ると、運用が回る
- 運用者が触る列とスクリプトが管理する列を明確に分ける
- hashによる差分検知で、更新時の再ベクトル化コストがほぼゼロになる
- 判定は類似度閾値とLLM判定の両方を満たしたときのみ回答。出力形式はJSONで固定する
- GASでは署名検証ができない。簡易認証は妥協であることを明示し、移行経路を残しておく
- Webhookはエラー時も200を返す。非2xxは再送ループを招く
- 実リクエストの形状を見られるログを最初に作ると切り分けが速い
- 閾値はデータ量が変わると適切な値も変わる
仕組みを作るときは、機能を足すより構成要素を減らすほうが運用は安定します。誰がどこを触るのかが1箇所に収まっているかどうかが、続くかどうかを決めます。
株式会社AI棒では、こうしたAI活用の検証を日々行い、企業のチャットボット・RAG導入を支援しています。ご相談はお問い合わせフォームから、継続的な伴走支援についてはAI参謀をご覧ください。
