Skip to content

Latest commit

 

History

History
277 lines (194 loc) · 5.91 KB

File metadata and controls

277 lines (194 loc) · 5.91 KB

ARCHITECTURE.md

Purpose

This document describes the implemented architecture of RAGU.

It is intended for:

  • maintainers
  • contributors
  • AI agents preparing non-trivial changes

For setup and runtime usage, see README.md. For contributor guidance, see CONTRIBUTING.md. For operations, see OPERATIONS.md.

System summary

RAGU is a local-first, Streamlit-based retrieval-augmented generation application for structural biology PDFs.

The architecture has two major halves:

  • application/runtime logic in app.py
  • Docker Compose services that provide inference, vector storage, and observability

High-level architecture

flowchart LR
    User[User]
    App[Streamlit app]
    PDFs[Uploaded PDFs]
    Ollama[Ollama]
    Chroma[Chroma]
    Reranker[Cross-encoder]
    MolStar[Mol* viewer]
    Piper[Piper]

    User --> App
    PDFs --> App
    App --> Chroma
    App --> Ollama
    App --> Reranker
    App --> MolStar
    App --> Piper
Loading

Request/answer pipeline

flowchart TD
    Upload[Upload PDFs] --> Load[PyMuPDFLoader]
    Load --> Split[RecursiveCharacterTextSplitter]
    Split --> Embed[Ollama embeddings]
    Embed --> Store[Chroma collection]
    Question[User question] --> Retrieve[Retrieve candidate chunks]
    Retrieve --> Rerank[Rerank with CrossEncoder]
    Rerank --> Prompt[Build context prompt]
    Prompt --> Generate[Ollama chat generation]
    Generate --> Render[Streamlit streamed answer]
    Render --> PDB[Search PDB code]
    PDB --> Viewer[Mol* structure viewer]
    Render --> TTS[Piper synthesis if available]
Loading

Current module reality

The implemented code is still mostly monolithic.

Current practical boundary:

  • app.py contains UI orchestration, service clients, document ingestion, retrieval, reranking, generation, PDB extraction, and TTS logic

This is important:

  • the architecture is operationally split across services
  • the Python code is not yet structurally split into clean modules

Main architectural decisions

1. Streamlit as the application shell

Why:

  • fast iteration
  • simple local UX
  • easy to deploy as a single Python process

2. Ollama for both chat and embeddings

Why:

  • local-first inference
  • self-hosted model control
  • simple deployment story

3. Dedicated Chroma service over HTTP

Why:

  • avoids embedded SQLite permission issues in the app container
  • better separation between application and vector-store runtime

4. Cross-encoder reranking

Why:

  • improves retrieval quality with a small, understandable step

5. Optional Piper TTS

Why:

  • audio output is useful but not essential

6. Separate observability paths

Why:

  • Chroma has a documented OTEL path
  • Ollama monitoring is best handled through an exporter + Prometheus stack

Application internals

Key helpers in app.py

  • process_document
  • get_ollama_client
  • get_chroma_client
  • get_vector_collection
  • add_to_vector_collection
  • query_collection
  • re_rank_cross_encoders
  • call_llm
  • search_pdb_code
  • get_piper_status
  • synthesize_speech

State and caching

The app uses Streamlit resource caching for long-lived expensive objects such as:

  • Ollama client
  • Chroma client/collection
  • cross-encoder reranker

Service architecture

flowchart LR
    App[app]
    Ollama[ollama]
    Chroma[chroma]
    Redis[redis]
    OTel[otel-collector]
    Zipkin[zipkin]
    Exporter[ollama-exporter]
    Prometheus[prometheus]
    Grafana[grafana]
    CAdvisor[cadvisor]

    App --> Ollama
    App --> Chroma
    App -. future .-> Redis
    Chroma --> OTel
    OTel --> Zipkin
    OTel --> Prometheus
    Ollama --> Exporter
    Exporter --> Prometheus
    CAdvisor --> Prometheus
    Prometheus --> Grafana
Loading

Data flows

Document ingestion flow

  • uploaded PDF enters the Streamlit app
  • file is temporarily written
  • PyMuPDF extracts text
  • chunks are generated
  • embeddings are requested from Ollama
  • chunk vectors are stored in Chroma

Query flow

  • user asks a question
  • relevant chunks are retrieved from Chroma
  • candidate chunks are reranked locally
  • best context is sent to Ollama chat generation
  • streamed answer is rendered in Streamlit

Structure visualization flow

  • generated answer text is scanned for a PDB identifier
  • if a valid code is found, Mol* is embedded in the page
  • structure data is loaded from the remote RCSB asset URL

TTS flow

  • answer text is checked against Piper availability
  • if Piper is available, synthesis runs inside the app container
  • resulting audio is played in the browser

Boundaries and responsibilities

app

Owns:

  • Streamlit UI
  • document ingestion
  • retrieval orchestration
  • reranking
  • chat request construction
  • PDB detection
  • TTS availability and playback preparation

ollama

Owns:

  • model serving
  • chat inference
  • embedding inference
  • model storage

chroma

Owns:

  • vector persistence
  • collection management
  • retrieval API

redis

Currently reserved for:

  • future cache or session features

Observability services

Own:

  • trace collection and visualization
  • metrics scraping
  • dashboards

Known architectural debt

  • app.py is still too large
  • UI code and runtime logic are mixed
  • no automated tests yet protect the core helpers
  • Redis exists in the topology but not in the feature set

Intended near-term evolution

The next healthy structural change is not a rewrite.

It is:

  • extract non-UI helpers into focused modules
  • keep app.py as the Streamlit orchestration layer
  • preserve env-driven runtime behavior
  • preserve current Docker service boundaries

Guidance for AI agents

Before making non-trivial architectural changes:

  1. Read AGENT.md.
  2. Read README.md.
  3. Read this file.
  4. Read OPERATIONS.md if your change affects runtime behavior.
  5. Update related docs in the same change if service topology, env vars, or operational behavior changes.