AI Google Drive Integration: Semantic Knowledge Base from Drive
TL;DR — AI Google Drive integration transforms Drive into a semantic knowledge base using RAG. AgentC2: "RAG-powered semantic search transforms Google Drive from a file storage system into a queryable knowledge base. Ask what is our policy on remote work during probation and get the specific policy section, regardless of what the file is named." K-AI: "K-AI ingests files from Google Drive via the Drive v3 API. The connector authenticates as a service account, traverses the drive estate, and syncs incrementally using the Drive Changes API. Drive's native permission model is preserved as ACLs." Google Cloud RAG Engine supports importing from Google Drive with automatic version_id hash to prevent re-indexing unchanged files. Pipeline: OAuth → files.list → export (Docs/Sheets/Slides/PDF/OCR) → chunk → embed → vector DB with ACLs. Incremental sync via changes.list with pageToken every 15 minutes. AgentC2: "Initial indexing for 500-2,000 documents takes 30-60 minutes." Integrate with Gmail, company knowledge, Nango, and Slack.
AgentC2 describes the transformation: "An AI-powered knowledge base transforms Drive from a file storage system into a semantic search engine. Instead of matching keywords, the system understands the meaning behind queries and retrieves documents based on conceptual relevance."
eesel.ai notes the business value: "Connecting AI with your Google Drive fundamentally changes how you can interact with the information you've got stored. It basically turns your static file collection into a lively, interactive knowledge hub."
Google Drive RAG Architecture
(Docs, Sheets, Slides, PDFs)"] Perms["Permissions
(ACLs)"] end subgraph Auth["Authentication"] OAuth["OAuth 2.0
(user consent)"] SA["Service Account
(domain-wide delegation)"] end subgraph Ingestion["RAG Ingestion Pipeline"] List["List Files
(files.list)"] Export["Export Content
(Docs→text, Sheets→CSV)"] OCR["OCR
(images, scanned PDFs)"] Normalize["Normalize Text"] Chunk["Chunk Documents
(paragraph/row/section)"] Embed["Generate Embeddings
(vector model)"] end subgraph VectorDB["Vector Database"] Store["Store Embeddings
+ metadata + ACLs"] Index["Vector Index
(pgvector / Pinecone)"] end subgraph Sync["Incremental Sync"] Changes["Drive Changes API
(changes.list + pageToken)"] Detect["Detect Upserts
and Deletions"] Reindex["Re-process Changed
Files Only"] end subgraph Query["Query Pipeline"] Ask["Natural Language Query"] Search["Semantic Search
(filter by user ACLs)"] Retrieve["Retrieve Relevant Chunks"] Generate["Generate Answer
+ source citations"] end Shared --> OAuth Folders --> SA Files --> List Perms --> List OAuth --> List SA --> List List --> Export List --> OCR Export --> Normalize OCR --> Normalize Normalize --> Chunk Chunk --> Embed Embed --> Store Store --> Index Changes --> Detect Detect --> Reindex Reindex --> Export Ask --> Search Search --> Retrieve Retrieve --> Generate
Supported File Types
| File Type | Export Method | Chunking Strategy | Limitation |
|---|---|---|---|
| Google Docs | Export as plain text | Paragraph-level | 10MB export cap |
| Google Sheets | Export as CSV/markdown | Row-level or section | 100MB export cap |
| Google Slides | Extract text + slide numbers | Slide-level | — |
| Text extraction (Document AI) | Paragraph-level | Scanned PDFs need OCR | |
| Word (.docx) | Text extraction | Paragraph-level | — |
| Excel (.xlsx) | Structured data extraction | Row-level | — |
| PowerPoint (.pptx) | Slide text extraction | Slide-level | — |
| Text/Markdown | Direct ingestion | Paragraph-level | — |
| Images | OCR (JPG, PNG, GIF, WebP) | Section-level | Metadata only if OCR fails |
| Encrypted (CSE) | Skipped | — | Cannot decrypt |
ACL Permission Mapping
| Drive Permission | ACL Type | Vector DB Filter | Example |
|---|---|---|---|
| User access | user ACL | user_id IN file_acls | user@company.com |
| Group access | group ACL | user_groups IN file_acls | engineering@company.com |
| Shared drive member | group grant | shared_drive_id IN user_drives | Marketing Drive |
| Anyone with link | public ACL | public = true | Shared policy doc |
| Domain-wide | domain ACL | user_domain = file_domain | company.com |
Implementation
from google.oauth2.service_account import Credentials
from googleapiclient.discovery import build
import base64
import hashlib
class GoogleDriveRAGConnector:
"""Google Drive connector for RAG knowledge base ingestion."""
def __init__(self, db, vector_db, embedding_model, audit):
self.db = db
self.vector_db = vector_db
self.embeddings = embedding_model
self.audit = audit
self.page_token = None # For incremental sync
async def initial_ingestion(self, tenant_id: str, folder_id: str = None,
creds: Credentials = None):
"""Full ingestion of Google Drive files into RAG knowledge base."""
service = build("drive", "v3", credentials=creds)
# 1. List all files in the folder (or entire drive)
files = await self._list_all_files(service, folder_id)
# 2. Get start page token for incremental sync
token_response = service.changes().getStartPageToken().execute()
self.page_token = token_response["startPageToken"]
await self.db.update("tenants", {"id": tenant_id}, {
"drive_sync_token": self.page_token,
})
# 3. Process each file
for file in files:
await self._ingest_file(service, tenant_id, file)
# 4. Audit log
await self.audit.log(
tenant_id=tenant_id,
action="drive_initial_ingestion",
entity_type="drive_folder",
entity_id=folder_id or "root",
after={"files_processed": len(files)},
)
return {"files_processed": len(files), "sync_token": self.page_token}
async def incremental_sync(self, tenant_id: str, creds: Credentials):
"""Incremental sync using Drive Changes API."""
service = build("drive", "v3", credentials=creds)
# Get stored page token
tenant = await self.db.find_one("tenants", {"id": tenant_id})
page_token = tenant.get("drive_sync_token")
if not page_token:
# No previous sync — do full ingestion
return await self.initial_ingestion(tenant_id, creds=creds)
changes_processed = 0
while True:
response = service.changes().list(
pageToken=page_token,
fields="changes(fileId,file(name,mimeType,trashed,modifiedTime),type),newStartPageToken,nextPageToken",
).execute()
for change in response.get("changes", []):
file = change.get("file", {})
if file.get("trashed"):
# File deleted or trashed — remove from vector DB
await self._remove_file(tenant_id, change["fileId"])
elif change["type"] == "file":
# File added or modified — re-ingest
await self._ingest_file(service, tenant_id, file)
changes_processed += 1
if "nextPageToken" in response:
page_token = response["nextPageToken"]
else:
page_token = response.get("newStartPageToken")
break
# Update sync token
await self.db.update("tenants", {"id": tenant_id}, {
"drive_sync_token": page_token,
})
await self.audit.log(
tenant_id=tenant_id,
action="drive_incremental_sync",
entity_type="drive_sync",
after={"changes_processed": changes_processed},
)
return {"changes_processed": changes_processed}
async def _ingest_file(self, service, tenant_id: str, file: dict):
"""Ingest a single file into the RAG knowledge base."""
file_id = file["id"]
mime_type = file["mimeType"]
# Skip if not a supported type
if not self._is_supported(mime_type):
return
# 1. Export/download file content
content = await self._export_file(service, file_id, mime_type)
if not content:
return
# 2. Get file permissions (ACLs)
permissions = await self._get_permissions(service, file_id)
# 3. Compute content hash (version_id)
content_hash = hashlib.sha256(content.encode()).hexdigest()
# Check if file has changed since last ingestion
existing = await self.db.find_one("ingested_files", {
"tenant_id": tenant_id,
"file_id": file_id,
})
if existing and existing["content_hash"] == content_hash:
return # File unchanged — skip re-indexing
# 4. Chunk the content
chunks = self._chunk_content(content, mime_type)
# 5. Generate embeddings for each chunk
for i, chunk in enumerate(chunks):
embedding = await self.embeddings.embed(chunk["text"])
# 6. Store in vector database with metadata and ACLs
await self.vector_db.insert({
"tenant_id": tenant_id,
"file_id": file_id,
"file_name": file["name"],
"mime_type": mime_type,
"chunk_index": i,
"chunk_text": chunk["text"],
"embedding": embedding,
"content_hash": content_hash,
"modified_time": file.get("modifiedTime"),
"acls": permissions, # Drive permissions as ACLs
"ingested_at": datetime.utcnow(),
})
# 7. Update ingested file record
await self.db.upsert("ingested_files", {
"tenant_id": tenant_id,
"file_id": file_id,
}, {
"tenant_id": tenant_id,
"file_id": file_id,
"file_name": file["name"],
"content_hash": content_hash,
"chunk_count": len(chunks),
"modified_time": file.get("modifiedTime"),
"ingested_at": datetime.utcnow(),
})
async def _export_file(self, service, file_id: str, mime_type: str) -> str:
"""Export file content based on MIME type."""
EXPORT_MAP = {
"application/vnd.google-apps.document": "text/plain",
"application/vnd.google-apps.spreadsheet": "text/csv",
"application/vnd.google-apps.presentation": "text/plain",
}
if mime_type in EXPORT_MAP:
# Google Workspace file — export
response = service.files().export(
fileId=file_id, mimeType=EXPORT_MAP[mime_type]
).execute()
return response.decode("utf-8") if isinstance(response, bytes) else response
elif mime_type == "application/pdf":
# PDF — download and extract text
content = service.files().get_media(fileId=file_id).execute()
return self._extract_pdf_text(content)
elif mime_type.startswith("image/"):
# Image — OCR
content = service.files().get_media(fileId=file_id).execute()
return await self._ocr_image(content)
elif mime_type in ("text/plain", "text/markdown"):
# Plain text — download directly
content = service.files().get_media(fileId=file_id).execute()
return content.decode("utf-8")
else:
return None
def _chunk_content(self, content: str, mime_type: str) -> list:
"""Split content into chunks based on file type."""
if "spreadsheet" in mime_type:
# Row-level chunking for spreadsheets
lines = content.split("\n")
return [{"text": "\n".join(lines[i:i+5]), "position": i}
for i in range(0, len(lines), 5)]
else:
# Paragraph-level chunking for documents
paragraphs = content.split("\n\n")
chunks = []
current = ""
for para in paragraphs:
if len(current) + len(para) > 1000: # 1000 char chunks
if current:
chunks.append({"text": current, "position": len(chunks)})
current = para
else:
current = current + "\n\n" + para if current else para
if current:
chunks.append({"text": current, "position": len(chunks)})
return chunks
async def query_with_acls(self, tenant_id: str, user_id: str,
query: str, top_k: int = 10) -> dict:
"""Query the knowledge base with ACL filtering."""
# Get user's Drive permissions
user_acls = await self._get_user_acls(tenant_id, user_id)
# Generate query embedding
query_embedding = await self.embeddings.embed(query)
# Search with ACL filter
results = await self.vector_db.search(
embedding=query_embedding,
filter={
"tenant_id": tenant_id,
"$or": [
{"acls.users": {"$in": user_acls["users"]}},
{"acls.groups": {"$in": user_acls["groups"]}},
{"acls.drives": {"$in": user_acls["drives"]}},
{"acls.public": True},
],
},
top_k=top_k,
)
return {
"query": query,
"results": [
{
"file_name": r["file_name"],
"chunk_text": r["chunk_text"],
"score": r["score"],
"file_id": r["file_id"],
}
for r in results
],
}
Sync Schedule Comparison
| Sync Method | Frequency | API Used | Bandwidth | Best For |
|---|---|---|---|---|
| Real-time | On file change | Push notifications | Low | Small drives, real-time needs |
| Incremental | Every 15 min | Drive Changes API | Low | Most use cases (recommended) |
| Hourly | Every 1 hour | Drive Changes API | Low | Low-activity drives |
| Daily | Once per day | Full re-list | Medium | Rarely updated drives |
| Weekly | Once per week | Full re-list | High | Archive drives |
AI Google Drive Integration Checklist
- [ ] Set up Google Cloud project with Drive API enabled
- [ ] Configure OAuth 2.0 with required scopes (drive.readonly, drive.metadata.readonly)
- [ ] For enterprise: use service account with domain-wide delegation
- [ ] Store credentials encrypted at rest (AES-256-GCM)
- [ ] Implement initial full ingestion (files.list)
- [ ] Store Drive Changes API start page token after initial ingestion
- [ ] Implement incremental sync using changes.list with pageToken
- [ ] Set sync schedule (every 15 minutes recommended)
- [ ] Handle file upserts (re-ingest modified files)
- [ ] Handle file deletions (remove from vector DB)
- [ ] Handle file trashing (remove from vector DB, re-add if restored)
- [ ] Support Google Docs export (text/plain, 10MB cap)
- [ ] Support Google Sheets export (text/csv, 100MB cap)
- [ ] Support Google Slides export (text with slide numbers)
- [ ] Support PDF text extraction (Document AI or pdfplumber)
- [ ] Support OCR for images and scanned PDFs
- [ ] Support Word, Excel, PowerPoint file extraction
- [ ] Support plain text and Markdown direct ingestion
- [ ] Skip encrypted files (CSE) — cannot decrypt with service account
- [ ] Skip trashed files (trashed=false filter)
- [ ] Compute content hash (SHA-256) to detect unchanged files
- [ ] Skip re-indexing if content hash matches (version_id)
- [ ] Implement configurable chunking strategies (paragraph, row, section)
- [ ] Store chunk metadata: file_id, file_name, chunk_index, modified_time
- [ ] Preserve Drive permissions as ACLs in vector database
- [ ] Map user permissions to user ACLs
- [ ] Map group permissions to group ACLs
- [ ] Map shared drive membership to drive ACLs
- [ ] Map anyone-with-link to public ACL
- [ ] Filter vector search results by user's ACLs at query time
- [ ] Implement natural language query with semantic search
- [ ] Generate answers with source citations (file name + chunk)
- [ ] Connect to company knowledge RAG pipeline
- [ ] Integrate with Slack for query interface
- [ ] Use Nango for OAuth and connection management
- [ ] Integrate with Gmail for email-to-knowledge flow
- [ ] Log all ingestion and query events in audit logs
- [ ] Apply RBAC to Drive knowledge base access
- [ ] Apply zero-trust architecture to Drive data
- [ ] Apply OWASP LLM Top 10 security controls
- [ ] Set up platform monitoring for ingestion health
- [ ] Monitor: files indexed, sync latency, query latency, error rate
- [ ] Handle Drive API rate limits (1,000 queries per 100 seconds per user)
- [ ] Implement retry logic for API failures
- [ ] Consider multi-tenant Drive isolation
- [ ] Support multiple Drive accounts per tenant
- [ ] Test: initial ingestion with various file types
- [ ] Test: incremental sync detects changes and deletions
- [ ] Test: ACL filtering prevents unauthorized access
- [ ] Test: content hash prevents unnecessary re-indexing
- [ ] Document supported file types and limitations
- [ ] Consider AI incident response for ingestion failures
FAQ
What is AI Google Drive integration?
AI Google Drive integration transforms Google Drive from a file storage system into a queryable knowledge base using RAG (Retrieval-Augmented Generation). AgentC2 describes it: "RAG-powered semantic search transforms Google Drive from a file storage system into a queryable knowledge base. Ask what is our policy on remote work during probation and get the specific policy section, regardless of what the file is named or where it is stored." The process: (1) Connect Drive via OAuth, select folders to index. (2) RAG pipeline reads each document, splits into chunks, generates vector embeddings. (3) Store embeddings in vector database with metadata (file ID, title, modified date, ACLs). (4) Query via natural language — agent retrieves relevant chunks and generates answers with source citations. Initial indexing for 500-2,000 documents takes 30-60 minutes. Incremental sync via Drive Changes API keeps the index current.
How does the Google Drive RAG ingestion pipeline work?
The RAG ingestion pipeline for Google Drive works in stages: (1) Connect — authenticate via OAuth 2.0 or service account, select folders to index. (2) List files — use Drive API files.list to enumerate all documents. (3) Export — Google Docs exported as plain text, Sheets as CSV/markdown tables, Slides as text with slide numbers, PDFs via text extraction, images via OCR. (4) Normalize — convert all formats to consistent text. (5) Chunk — split using configurable strategies: paragraph-level for narratives, row-level for spreadsheets, section-level for structured docs. (6) Embed — generate vector embeddings for each chunk. (7) Store — save embeddings in vector database with metadata (file_id, title, modified_date, chunk_position, ACLs). (8) Incremental sync — use Drive Changes API (changes.list with pageToken) to detect modifications and deletions, re-process only changed files. K-AI syncs every 15 minutes by default.
How do you handle Google Drive permissions in a RAG knowledge base?
Preserve Google Drive's native permission model as ACLs (Access Control Lists) in the vector database. K-AI implements this: "Drive's native permission model (per-file collaborators, shared drive members) is preserved as ACLs." Each file's permissions are read from the Drive API (type, emailAddress, role, domain). Three key patterns: (1) Per-file collaborators — individual user grants mirrored as user ACLs. (2) Shared drive membership — captured as group grants, every member inherits read access to every file in the drive. (3) Anyone-with-the-link sharing — mirrored as public ACL, flagged in metadata. At query time, the vector search filters results by the user's ACLs — a user can only retrieve chunks from documents they have access to. This ensures Drive's permission model is enforced in the RAG pipeline, not just in the file browser.
What file types can AI agents process from Google Drive?
AI agents can process these file types from Google Drive: (1) Google Docs — exported as plain text via Drive API export. (2) Google Sheets — converted to CSV or markdown tables. (3) Google Slides — extracted as text with slide number metadata. (4) PDF files — text extraction via Document AI or pdfplumber. (5) Word documents (.docx) — text extraction. (6) Excel files (.xlsx) — structured data extraction. (7) PowerPoint (.pptx) — slide text extraction. (8) Plain text and Markdown — direct ingestion. (9) Images (JPG, PNG, GIF, WebP) — OCR for text extraction. Limitations: Google Docs export capped at 10MB, Sheets at 100MB. Encrypted files (CSE) cannot be decrypted by service account and are skipped. Comments and suggestions on Google Docs are not ingested as separate entities. Trash is excluded by default.
How do you keep the Google Drive knowledge base current?
Keep the Google Drive knowledge base current with incremental sync using the Drive Changes API. K-AI implements this: "Incremental sync uses the Drive Changes API (changes.list?pageToken=...). After an initial full listing (files.list), the connector calls changes.getStartPageToken once and persists it. Subsequent syncs request only the changes since the token; the response contains both upserts and deletions, so removals propagate immediately." AgentC2: "Configure the agent to re-index on a daily or weekly schedule, or trigger re-indexing when documents are modified. The incremental update process only re-processes changed documents, keeping the index current without re-processing the entire corpus." Google Cloud RAG Engine uses version_id (file content hash) to prevent re-indexing unchanged files.
Want a self-hosted AI company brain that does all of this out of the box?
Book a demo →