Building a Document-RAG Agent on GCP's Agent Development Kit (ADK) LogiChat rebuilt its document-RAG pipeline using Google's Agent Development Kit (ADK), replacing a manual Q&A curation system with a five-stage architecture that runs on Cloud Run and Firestore. The design choice of performing retrieval as a pre-model step rather than an ADK tool proved critical. The agent service is a 53-line Python 3.12 FastAPI app with five runtime dependencies, all running on GCP without API keys. LogiChat https://logichat.io is a chatbot platform. Customers upload their docs, get a chat widget, never touch a model. For two years, "training the bot" meant hand-curating a Q&A list in a dashboard form — question and answer pairs, one row at a time, dumped into the prompt as few-shot pairs. I just deleted that and rebuilt the whole pipeline on Google's Agent Development Kit ADK , with It's not a "hello world" agent demo — it's the architecture I landed on after rebuilding the whole pipeline over the last few months, three Cloud Run services touched the fourth, the Stripe top-up token subscriber, was untouched , and the one design choice that turned out to matter more than anything else: retrieval as a pre-model step, not an ADK tool. The pipeline below touches three of those services — apps/api , apps/subscribers/doc-processor , and apps/agent — plus the Firestore vector index. apps/api appears twice in the diagram because it acts as both the upload ingress AND the gateway that mints ID tokens for the agent. Click through the five stages to see which service is active at each hop and what payload crosses each boundary: Interactive diagram:step through the five-stage pipeline —Upload → Process → Retrieve → Ground → Answer— on the original post on dalenguyen.me . Each hop shows which service is active dashboard → apps/api → Cloud Storage → doc-processor → Firestore → apps/agent → Vertex AI and what payload crosses the boundary. Two pieces are different from a textbook RAG stack: apps/api , Express is a separate service that mints Cloud Run ID tokens to call the agent. The agent is not publicly reachable.Both are deliberate. Both are what this post is really about. I picked Google's Agent Development Kit https://github.com/google/adk-python over LangGraph, CrewAI, and a hand-rolled loop for one reason: the agent service runs entirely on GCP, and ADK is the only one of those that was built for Vertex AI from the ground up. The Python SDK ships a google.adk.agents.Agent and a google.adk.runners.InMemoryRunner that route through google-genai , which already speaks Vertex AI when GOOGLE GENAI USE VERTEXAI=TRUE . No LangChain adapter, no provider shim, no surprise model name drift between staging and prod. Concretely, my apps/agent is a 53-line pyproject.toml Python 3.12 service: project name = "logichat-agent" version = "0.1.0" description = "LogiChat ADK agent service Python — Gemini 2.5 Flash via Vertex AI." requires-python = " =3.12" dependencies = "fastapi =0.115", "pydantic =2.7", "uvicorn =0.30", google-adk is the Agent Development Kit Python SDK. Pinned loosely because ADK's API is still moving; follow-up issues will exercise the real Runner sessions / tools / streaming . "google-adk =0.1.0", google-genai comes with ADK, but I import google.genai.types directly in the runner adapter, so declare it explicitly. "google-genai =1.0", google-cloud-firestore is the client I use for the per-app chunk vector search. Required by the retrieval layer. "google-cloud-firestore =2.20", Five runtime deps. FastAPI for the HTTP surface, Pydantic for the wire contract, uvicorn for the Cloud Run entrypoint, google-adk for the agent, google-genai for the typed message parts, google-cloud-firestore for the KNN search. ADC works for all of it — no API keys anywhere. apps/agent is split into four files, each with one job: | File | Job | |---|---| src/agent/main.py | uvicorn entrypoint; binds $PORT Cloud Run injects | src/agent/agent definition.py | Builds the ADK Agent + InMemoryRunner , wraps in RunnerProtocol | src/agent/retrieval.py | Firestore find nearest + text-embedding-005 via Vertex AI | src/agent/api/app.py | FastAPI factory: POST /run , GET /healthz | The main.py is six lines of orchestration: php apps/agent/src/agent/main.py def main - None: runner = build agent builds the ADK-backed adapter app = create app runner=runner wires the runner into FastAPI port = int os.environ.get "PORT", "8080" uvicorn.run app, host="0.0.0.0", port=port, log level="info" build agent is where ADK gets wired up. Here's the whole thing: apps/agent/src/agent/agent definition.py MODEL ID = "gemini-2.5-flash" SYSTEM INSTRUCTION = "You are LogiChat's support assistant. Answer the user's question using " "ONLY the context retrieved from the company's knowledge base. If the " "retrieved context does not contain the answer, say you don't know and " "suggest the user rephrase or contact a human. Never invent facts, " "product names, or policies that are not present in the retrieved " "context. Each retrieved block is prefixed with an internal " " doc: