Pi RAG is a web app for asking technical questions against imported PDF documentation. The agent does not just answer from memory: it can search the indexed documents, open exact chunks and pages, and return answers with the PDF sources that support them.
The first corpus is TRACE32 documentation, but the import format is generic enough for other manuals and reference docs.
Technical support questions often fail when the answer sounds right but is not traceable to the manual. This app gives the agent retrieval tools that let it move from a user question to the exact PDF page, then shows that page in the UI so the user can verify the answer directly.
What matters:
- The agent can combine semantic search, exact keyword search, chunk lookup, page lookup, and context expansion.
- Each documentation-based answer can point back to the PDF name and page.
- The UI keeps the cited PDF pages next to the conversation.
- Imported bundles keep source PDFs, chunks, metadata, and embeddings in sync.
The app has three parts:
web/is the Next.js app for chat, PDF preview, import, auth, and API routes.- Postgres stores threads, run state, document chunks, embeddings, and source metadata.
- MinIO/S3 stores the imported PDFs and bundle artifacts.
flowchart LR
User["User"] --> UI["Next.js chat UI"]
UI --> Stream["/api/pi-chat/stream"]
Stream --> Agent["Pi agent session"]
Agent --> Tools["Documentation tools"]
Tools --> Retrieval["Postgres + pgvector"]
Retrieval --> Chunks["Imported document chunks"]
Stream --> Events["Run and stream events"]
Events --> Postgres
UI --> Sources["Source panel"]
Sources --> S3["MinIO/S3 PDFs"]
flowchart TB
Agent["Pi agent"] --> Search["query/search/vsearch"]
Agent --> Get["get/page/context/multi-get"]
Search --> Chunks["Document chunks"]
Get --> Chunks
Chunks --> Sources["document_sources_json"]
Sources --> Panel["Source panel"]
Panel --> Pdfs["PDF pages in MinIO/S3"]
At runtime, a user message starts a Pi run through /api/pi-chat/stream. The server attaches the documentation retrieval tools, streams Pi events back to the browser, persists normalized events, and saves final messages plus source metadata. If the page reloads while a run is active, the client reconnects through the run endpoints and replays stored events before continuing the live stream.
Documentation is imported through /api/qmd-artifacts/import. The upload must be a ZIP containing manifest.json, qmd index/config artifacts, a compressed Markdown archive, PDF files, and chunk metadata. Import stores PDFs and artifacts in object storage, embeds chunks with the configured embedding model, and writes searchable rows to Postgres.
web/app/api/: chat, retrieval, import, PDF, auth, and workspace file routes.web/components/docs-chat/: main chat workspace and source/PDF panels.web/lib/agent/: Pi streaming, run persistence, workspace handling, and artifact parsing.web/lib/retrieval/: Postgres-backed documentation search.web/db/: Drizzle schema and database access.web/drizzle/migrations/: database migrations.scripts/: documentation bundle build/import helpers.k8s/: Kubernetes manifests for the deployed stack.docker/: supporting container build files.
Create a local env file:
cp .env.example .envSet at least:
PI_RAG_OPENAI_API_KEYBETTER_AUTH_SECRETDOCS_INTERNAL_API_TOKENDEFAULT_AUTH_EMAILDEFAULT_AUTH_PASSWORD
Start the full local stack:
pnpm install
pnpm compose:upRun database setup when needed:
pnpm db:migrate
docker compose run --rm seed-authFor app-only development against an existing database/object store:
pnpm web:devpnpm typecheck
pnpm --filter web test
pnpm build