User problem
Generic chat systems often answer without showing which passages influenced the response. This project prioritizes traceable evidence and refusal when evidence is too weak.
Research Assistant Platform
This view explains the project as a working system: what it is for, how the web app, FastAPI service, ADK agent, Gemini clients, PostgreSQL, pgvector, ingestion pipeline, retrieval service, citations, evaluation, and security boundaries fit together.
The platform is a portfolio-grade research workspace inspired by the useful parts of NotebookLM and ChatGPT, but built to expose the mechanics: source parsing, retrieval, agent tool use, citations, traces, quality checks, and security boundaries.
Generic chat systems often answer without showing which passages influenced the response. This project prioritizes traceable evidence and refusal when evidence is too weak.
Developers, researchers, students, analysts, and specialists who need to reason over a private document collection and verify source claims.
Five uploaded PDFs can be queried, relevant chunks are retrieved, answers cite exact pages, and behavior is measurable through deterministic tests and later evals.
The system keeps UI, API, model calls, agent orchestration, ingestion, retrieval, and storage in explicit boundaries. That makes the stack easier to test and harder to blur with hidden framework behavior.
Browser
-> Next.js Web App
-> FastAPI Agent API
-> Gemini chat client
-> Google ADK Research Agent
-> read-only tools
-> retrieval-backed search_documents
-> Document Ingestion Service
-> PDF validation
-> PyMuPDF extraction
-> page-bounded chunking
-> embedding generation
-> Retrieval Service
-> query embedding
-> pgvector similarity search
-> metadata filters
-> evidence packaging
-> PostgreSQL + pgvector
Provides the user workflow: collection selection, PDF upload, question submission, answer rendering, citations, retrieved evidence, health checks, and later evaluation report views.
Owns request validation, dependency construction, stable error responses, model client calls, ADK runtime execution, session handling, document endpoints, retrieval diagnostics, and response schemas.
PostgreSQL stores documents, original PDF bytes, pages, chunks, hashes, index status, collection identity, and vector embeddings. pgvector performs local similarity search.
The roadmap is ordered by dependency. Each phase proves one layer before adding the next so the project avoids hiding fundamentals behind managed abstractions.
Monorepo, Next.js, FastAPI, Docker Compose, PostgreSQL, pgvector, linting, tests, and environment handling.
Simple Gemini request, structured output, streaming, timeout handling, token metadata, latency, and prompt versioning.
Explicit read-only document tools, validated tool input, safe tool failures, and inspectable tool traces.
One ADK research agent, session handling, runtime adapter, structured final answers, and tool invocation tests.
PDF upload, validation, SHA-256 duplicate detection, PyMuPDF extraction, page records, chunks, persisted sources, and re-indexing.
Embeddings, pgvector schema, vector retrieval, metadata filters, retrieval debug view, cited answers, and insufficient-evidence behavior. Embedding abstraction complete; pgvector integration and retrieval system in progress.
Deterministic quality metrics including retrieval recall@k, citation validity, fact coverage, and forbidden-claim detection. Full eval dataset and CLI scoring tool implemented.
Adversarial tests, threat model, reusable MCP server, Google Cloud deployment, Terraform, model cards, and portfolio artifacts.
The FastAPI service is the main integration boundary. It keeps request and response contracts typed with Pydantic models and maps expected failures to stable error codes.
| Endpoint | Purpose | Important response details |
|---|---|---|
GET /health |
Checks service health and database connectivity. |
Returns ok only when the API can connect to
PostgreSQL.
|
POST /chat |
Runs the plain Gemini chat path from Phase 1. | Returns answer, model name, prompt version, and latency. |
POST /chat/stream |
Streams Gemini response chunks as newline-delimited events. | Returns chunk, done, or error events with stable event types. |
POST /agent/chat |
Runs the ADK research agent with optional session continuation and corpus filters. | Returns answer, session ID, citations, retrieved chunks, refusal state, warnings, trace, model, prompt version, and latency. |
POST /documents |
Uploads a PDF into a collection and indexes source-derived records. | Returns document metadata, page count, status, and chunk count. |
GET /documents/{id} |
Fetches persisted document metadata. | Includes filename, MIME type, hash, page count, status, and timestamps. |
GET /documents/{id}/pages/{page} |
Fetches extracted page text for citation inspection. | Returns page number, text, and source identity. |
POST /documents/{id}/reindex |
Rebuilds page and chunk records from stored source bytes. | Returns refreshed document status and chunk count. |
POST /retrieval/search |
Debugs retrieval without invoking the agent. | Returns ranked chunks, scores, configured top-k, minimum score, and latency. |
Ingestion and answering are intentionally separate. Deterministic indexing happens before any agent reasoning. The agent later consumes retrieval output as evidence.
The browser sends a PDF and optional collection ID to the API.
The API checks media type, size, page count, PDF validity, and duplicate hash.
PyMuPDF extracts page text while preserving page numbers for citations.
Pages are split into bounded overlapping chunks with document and page metadata.
The embedding client generates fixed-dimension vectors for chunk text.
Document, source bytes, pages, chunks, hashes, status, and embeddings are stored.
Phase 5 connects persisted document chunks to the ADK agent through a local retrieval service. The design keeps retrieval inspectable instead of delegating it to a managed black box.
The embedding interface supports fake deterministic vectors in tests and Gemini embeddings in local runs. Document embeddings and query embeddings use different request purposes so provider formatting stays centralized.
PostgreSQL stores vectors through pgvector. Retrieval uses cosine distance, top-k limits, metadata filters, and a minimum score threshold before packaging evidence for the API or agent.
Retrieved text is untrusted. The answer prompt must frame chunks as evidence, never as instructions that can modify tools, policy, credentials, or authorization.
Structured citations must map to retrieved chunks and exact pages. When evidence is too weak or citations cannot be validated, the response should refuse or warn instead of presenting unsupported claims.
The project relies on stable typed contracts at module boundaries. These contracts make endpoint tests and later evaluations possible.
id, collection_id, filename, MIME
type, SHA-256 hash
The repository favors deterministic tests for default runs and explicit live smoke tests only when secrets and services are available.
pnpm format:check pnpm lint pnpm test pnpm --dir apps/web build uv run --directory apps/agent-api ruff check . uv run --directory apps/agent-api pytest
Live checks: Gemini smoke tests require
GEMINI_API_KEY and an explicit opt-in flag. Docker
Compose endpoint checks should be used when Docker is available to
verify PostgreSQL and pgvector behavior outside pure unit tests.
These items are the critical path for completing local RAG. They must be resolved to progress from embedding abstraction to full retrieval and citation capability.
Chunks table needs vector embedding column, pgvector type binding, and cosine-distance index. Schema migration must support both fresh and migrated Phase 4 databases without hidden side effects.
Chunk embedding generation at ingestion time, query embedding at retrieval time, and proper request-purpose separation for Gemini embeddings API. Deterministic fake embeddings in tests.
Cosine similarity search with top-k limits, configurable minimum score thresholds, and enforced collection/document filters for both debug endpoints and agent tool calls.
Structured citations mapping to exact chunks and pages, citation validation against retrieved evidence, and refusal behavior when evidence is insufficient or citations cannot be validated.
Complete local RAG by implementing vector storage, retrieval, and citations before moving into security and cloud phases.