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

flowchart TD subgraph Drive["Google Drive"] Shared["Shared Drives"] Folders["Folders"] Files["Files
(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
PDF 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 →