본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 05. 08. 20:24

学習記録 #5 後編】GAS × Gemini で RAG チャットボットを自作した

요약

본 기술 기사는 Google Apps Script(GAS)와 Gemini API를 활용하여 RAG(Retrieval-Augmented Generation) 챗봇을 직접 구축하는 과정을 심층적으로 다루고 있습니다. 특히, 임베딩 과정에서 L2 정규화의 중요성, `taskType`에 따른 벡터 최적화 사용법 등 기술적인 핵심 원리를 상세히 설명합니다. 또한, Google Sheets를 벡터 DB로 활용하고 쿼리 차이 감지를 위한 '차분 빌드' 기법을 적용하는 실질적인 구현 방법과, 실제 서비스 배포 시 발생할 수 있는 API 할당량(Quota) 문제 해결책까지 제시하여, 독자가 높은 수준의 이해도를 바탕으로 프로젝트를 완성할 수 있도록 안내합니다.

핵심 포인트

  • RAG 파이프라인 구축 과정: 임베딩 → 검색 → LLM 호출의 각 단계별 구현 상세 내용을 다룹니다.
  • 임베딩 벡터 처리 핵심 기술: Gemini Embedding API 사용 시 L2 정규화가 필수적이며, 이를 통해 코사인 유사도 계산을 단순 내적으로 대체할 수 있습니다.
  • 검색 정확도 향상 기법: `taskType` (RETRIEVAL_DOCUMENT vs RETRIEVAL_QUERY)을 구분하여 사용하는 것이 검색 성능에 큰 영향을 미칩니다.
  • 실용적인 데이터 관리 전략: Google Sheets를 벡터 DB로 사용하며, MD5 해시 기반의 '차분 빌드' 기능을 구현하여 API 호출 비용과 시간을 절약합니다.
  • 배포 및 운영 팁: Gemini Pro Preview 모델은 무료 할당량이 적으므로, 검증 단계에서는 Gemini 2.5 Flash와 같은 안정적인 모델을 사용하는 것이 권장됩니다.

[前編] では、GAS で自作する理由、AI のデータ処理の仕組み、環境構築のつまずきポイントを記録しました。

後編では、実装の詳細デプロイテスト結果、そして精度改善計画を記録します。

対象読者:エンジニア経験 1 年程度の方。前編を読んで環境構築まで完了している前提です。

この記事で分かる 3 点を最初に示します。

  • RAG パイプラインの実装詳細― Embedding、検索、LLM 呼び出しの各工程 -
  • デプロイとテスト― Web App として公開し、動作確認するまで -
  • 精度改善計画― 回答精度を上げるための具体的な改善ロードマップ

Claude Code で一気にコード生成しましたが、生成されたコードの中で特に重要なポイントを解説します。

テキストを数値配列に変換する処理です。RAG の精度を左右する最重要コンポーネント。

使用モデル: gemini-embedding-001

テキスト:「腹水の主症は腹部膨大である」
│
│ Gemini Embedding API
...

重要ポイント:L2 正規化

Gemini Embedding 2 は MRL(Matryoshka Representation Learning)により、3072 次元→768 次元にスケールダウンできます。API リクエスト時に outputDimensionality: 768 を明示指定することで次元数を削減できます。ただし次元数を変えるとベクトルの長さ(ノルム)が変わるため、L2 正規化(ベクトルの長さを 1 に揃える処理)が必須です。

【L2 正規化のイメージ】
正規化前:
ベクトル A: 長さ 2.5 → [0.5, 1.0, 2.0, ...]
...

L2 正規化を行うことで、コサイン類似度の計算が単純な内積で済むようになります。

taskType の使い分け:

場面taskType理由
資料をベクトル化するときRETRIEVAL_DOCUMENTドキュメント用の最適化がかかる
質問をベクトル化するときRETRIEVAL_QUERYクエリ用の最適化がかかる

同じテキストでも taskType が異なるとベクトルが変わります。ドキュメントとクエリで別の最適化がかかるため、正しく使い分けることで検索精度が向上します。

実装コード(核心部分):

// TODO: 実際のコードに差し替え(embedDocument / embedQuery の核心部分)
function embedText(text: string, taskType: string): number[] {
const response = UrlFetchApp.fetch(
...

本格的なベクトル DB(Pinecone、Chroma など)の代わりに、Google Sheets をベクトルデータベースとして使っています。

Sheets の構成:

内容
Aiddoc_001_chunk_0
Btypesubject or topic
Ctextチャンクのテキスト
Dmetadata{"fileName":"鼓脹.pdf","chunkIndex":0}
Eembedding[0.12,-0.34,0.56,...]
FtextHasha1b2c3d4...(差分ビルド用)

差分ビルド: テキストの MD5 ハッシュを保存しておき、次回実行時にハッシュが変わっていないチャンクはスキップ。大量資料でも Embedding API の呼び出し回数を最小限に抑えられます。

【差分ビルドの流れ】
初回:全チャンク → ベクトル化 → Sheets 保存(100 チャンク)
処理時間:約 5 分
...

質問ベクトルと各チャンクベクトルの「意味の近さ」を計算して、最も関連性の高いチャンクを取得します。

質問:「腹水の主症は?」
│
│ ベクトル化
...

実装コード(核心部分):

// TODO: 実際のコードに差し替え(cosineSimilarity の核心部分)
function cosineSimilarity(vecA: number[], vecB: number[]): number {
// L2 正規化済みベクトルなら内積 = コサイン類似度
...

ハイブリッド検索: 2 種類のチャンクを別レーンで検索してマージする設計にしています。

【subject チャンク】科目・テーマごとのまとまり
→ 「腹水について」の情報を広く取得
【topic チャンク】トピックごとのまとまり
...

検索結果(関連チャンク)と質問を組み合わせて、LLM に回答を生成させます。

┌─────────────────────────────────────┐
│ LLM に渡すプロンプト │
│ │
...

Gemini Pro Preview 系を使って 429 エラーが出た話:

実際に GAS 版でも Gemini Pro Preview 系モデルで 429 エラーが発生しました(gemini-2.5-pro 系は無料枠が非常に少ない)。

429 RESOURCE_EXHAUSTED
Quota exceeded for metric: generate_content_free_tier_requests
limit: 0, model: gemini-2.5-pro

교훈: Preview 버전의 Pro 모델은 무료 쿼트가 거의 없는 경우가 있습니다. 검증 단계에서는 Gemini 2.5 Flash 를 사용하세요.

모델무료 쿼트
gemini-2.5-pro (가정)※❌ 없음
Gemini 2.5 Flash
✅ 15req/분, 1500req/일

다음 보안 조치를 구현했습니다.

조치내용
API 키 관리스크립트 속성으로 관리 (코드에 하드코딩하지 않음)
...
【오류 처리의 생각】
❌ 나쁜 예 (사용자에게 상세 표시):
「Error: API key AIzaSy... is invalid」
...

GAS 에디터에서 「프로젝트 설정」→「스크립트 속성」에 다음을 등록하세요.

속성명확인 위치
GEMINI_API_KEY
https://aistudio.google.com/apikey
DRIVE_FOLDER_ID
Drive 폴더 URL 의 /folders/
VECTOR_SHEET_ID
스프레드시트 URL 의 /d/
1. GAS 에디터에서「initialSetup」실행 → 헤더 생성 등
2. Google Drive 폴더에 자료 업로드 (2~3 파일)
3. 「rebuildEmbeddings」실행 → 벡터화
...

고침 포인트⑥: clasp open 가 사용 불가

Unknown command "clasp open"

clasp 의 새 버전에서는 open
명령어가 폐지되었습니다.

해결책: .clasp.json
의 scriptId 를 사용하여 직접 URL 열기.

1. GAS 에디터에서「배포」→「새로운 배포」
2. 종류: 웹 앱
3. 다음 사용자로서 실행: 나
...
항목내용
자료침술·동양의학 강의 자료 (2~3 파일)
...
질문: 「鼓脹의 주증은 무엇입니까?」
답변: (TODO: 실제 답변 텍스트 붙기)
정확도:まあ悪くない 🟡
...
비교 항목Dify 버전GAS 버전
답변 속도TODO: 실측치 교체 (예: 평균 2.1 초)TODO: 실측치 교체 (예: 평균 7.8 초)
...

상세는次回記事(学習記録 #6)에서 다룹니다.

현재 정확도는「まあ悪くない」수준입니다. 여기서부터 정확도를 높이기 위한 개선 계획을 세웠습니다.

| 레벨 | 주제 | 개요 |
|---|---|
Level 1 |
チャンク 전략의 개선 | Chunk 사이즈 최적화·세맨틱 체인킹·메타데이터 충실 |
Level 2 |
프로ンプ트의 개선 | Few-shot 추가·답변 형식 지정·할루시네이션 억제 강화 |
Level 3 |
검색 정확도의 개선 | Rerank 모델 도입·HyDE·쿼리 확장·Top K 동적 조정 |
Level 4 |
평가의 체계화 | 테스트 세트 생성·자동 평가 스크립트·A/B 테스트·로그 분석 |

前後編を通じて의 고침 포인트 목록입니다.

| # | 고침 | 오류 메시지 | 해결책 |
|---|---|
| 1 | clasp 권한 승인 | (브라우저 승인 화면) | 「모든 선택」→「계속」 |
| 2 | webapp 지정 불가 | Invalid container file type |
--type standalone 변경 |
| 3 | GAS API 미활성화 | User has not enabled the Apps Script API |
설정 페이지에서 API 온 |
| 4 | TS 타입 주석 오류 | Unexpected token ':' |
clasp 3.x 는 TypeScript 를 컴파일하지 않음. tsc 로 사전 컴파일 후 dist/ 에서 push (상세는 전편 참조) |
| 5 | 마니페스트 덮어쓰기 | Do you want to push and overwrite? |
y 허용 |
| 6 | clasp open 불가 | Unknown command "clasp open" |
scriptId 로 직접 URL 생성 (→ 2-2 참조) |
| 7 | Gemini Pro 무료 쿼트 | 429 RESOURCE_EXHAUSTED |
Gemini 2.5 Flash 변경 (→ 1-4 참조) |

GAS × Gemini 로 RAG 챗봇을自作한 전 기록을 요약했습니다.

Dify 버전과 GAS 버전의 2 개의 RAG 챗봇을同日에 구축 및 테스트 완료 - GAS + TypeScript + clasp 개발환경 구축
Gemini Embedding (gemini-embedding-001) 으로 벡터화
Google Sheets 를 벡터 DB 로 사용
Gemini 2.5 Flash 로 LLM 답변 생성
Web App 으로 배포하고, 브라우저에서 챗 가능하게

  1. RAG 의 전 과정을「손맛」으로 이해함 → Embedding, 벡터 검색, 컨텍스트 구축의 메커니즘
  2. clasp 3.x 는 TypeScript 를 컴파일하지 않음...

정확도 개선 계획의 Level 1 (테스트 세트 제작 + 프롬프트 개선) 에서 시작 예정임.

  • [【학습 기록 #5 전편】GAS × Gemini 로 RAG 챗봇을自作した ― 환경構築・つまずきポイント編](TODO: 전편 기타 URL 을 붙임)
  • GAS + Gemini 로 만드는社内 RAG 챗봇 - Qiita(ogaryo 님)
  • Google Apps Script 공식
  • Gemini API 문서
  • Gemini Embedding API
  • clasp GitHub

다음회予告: [학습 기록 #6] 정밀도 개선 사이클 — 테스트 세트 제작과 프롬프트 최적화

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
2

댓글

0