← Presentation home · Part 11 of 13

Technology stack

Technology stack
← Previous Next →

Deep dive

Technology stack

Here's what's actually inside the system, named and in plain English. If you want to build something like this yourself, this is your parts list.

The short version

Six pieces and two AI models. That's the whole thing.

ComponentWhat it does
OpenWebUIThe chat window you see in the browser.
Perl + MojoliciousThe web framework the proxy is written in (Mojolicious is to Perl what Flask is to Python).
Brain.pmA single ~870-line Perl file that holds the entire retrieval pipeline (reading files, chunking them, searching them).
SQLiteA database that lives in one file (no server, no setup). Holds the text of the documents.
PDLThe Perl Data Language, a fast numerical library. Holds the meaning-vectors and does the math at search time.
OllamaThe thing that actually runs the AI models on your machine.

And the two models:

ModelWhat it does
bge-m3The embedding model (turns text into 1,024-number vectors so meanings can be compared).
Ministral 3 14BThe chat model (writes the actual answers).

A bit more on each piece

OpenWebUI

The browser interface, the part the user actually clicks on. Stock open-source, runs in Docker. Slide 07 covers it in detail.

Perl and Mojolicious

Mojolicious is a Perl web framework. If you've written Flask in Python or Sinatra in Ruby, the shape is familiar: routes, handlers, request and response.

The proxy itself is about 1,290 lines of Mojolicious code. It handles HTTP, async streaming, request forwarding, and both API protocols (OpenAI-style and Ollama-style).

Brain.pm, the retrieval engine

This is the core. One Perl file, about 870 lines, doing the entire retrieval job.

Specifically:

Everything slide 05 called "Retrieve" lives in here. There's no orchestration framework, no microservice mesh, no message queue. Just a module you can read end to end in an afternoon.

SQLite

SQLite is a database in a single file. You don't run a server. You point your code at the file and you have a database.

Brain.pm uses it to hold the text of each chunk, the source document, the page or section, and metadata like token counts. One file per brain. Easy to back up (cp brain.db brain.db.bak), easy to inspect (any SQLite browser will open it).

PDL, the vector math

PDL (Perl Data Language) is a fast numerical-array library. Think NumPy for Perl, and actually older than NumPy.

It does the part that looks scary on paper but is just arithmetic: take the question's vector, compare it to every chunk's vector, return the closest matches.

The comparison is cosine similarity, a number from -1 to 1 that measures how close two vectors point in the same direction. 1 means same direction (very similar meaning), 0 means unrelated, -1 means opposite. It runs in milliseconds at the sizes we work with.

Vectors live on disk as a binary matrix in PDL's native format. Loading them and searching them are both quick.

Ollama

Ollama runs the AI models on your machine. We use it for two things: turning text into vectors (the embedding model) and writing the answers (the chat model).

Under the hood it wraps llama.cpp (the C library that actually runs the models) and exposes a clean HTTP API on port 11434. We never touch it directly, we just hit the API.

Why this stack instead of the usual Python tutorial

A typical Python RAG tutorial reaches for four pieces before it indexes a single document:

Each is its own framework with its own versions, its own config, its own learning curve. Some of them are cloud services, which puts us back to the problem from slide 02 (your data leaves the building).

The Perl version replaces all four with:

That's it. The whole knowledge base for 94 dental policy documents is under 1 MB on disk. Small enough to email.

The reason this works isn't a clever new algorithm. We picked plain off-the-shelf parts and composed them carefully, so the system stays small enough to debug, back up, and explain in one afternoon.

Why Perl, if someone asks

Not nostalgia. Concrete reasons:

You could rebuild the same architecture in Python (the design is language-agnostic). The result would be heavier, that's all.

What this design rules out

This stack is right-sized for dozens to low hundreds of documents per brain, with thousands still comfortable. Past tens of thousands of chunks, PDL starts to feel the load, and you'd want a proper vector index. At millions of documents, Pinecone or Weaviate earn their keep and the trade-offs flip.

So if you're indexing the whole web, this isn't the tool. For one team's policies, manuals, or notes, it fits like a glove.

FAQ

Why SQLite instead of a vector database? At this scale, a vector database is solving a problem you don't have. SQLite holds the text, PDL holds the vectors, cosine similarity runs in milliseconds. A hosted vector DB would add a service, a dependency, and often a cloud round-trip.

Could I rebuild this in Python? Yes. The architecture is a proxy, a chunker, an embedder, and a vector store. Perl is the implementation, not the idea. The idea travels. You can even mix: nothing stops a Python proxy from calling Brain.pm via subprocess, or vice versa.

Is 870 lines really enough for a whole RAG engine? For this kind of RAG, yes. Read files, split them, embed them, store them, compute cosine similarity at query time. The algorithm is small. Most of the bulk you see in big RAG frameworks is the integration of optional features, not the core math.

Does this scale? For most knowledge bases, comfortably. For millions of documents you'd want different tooling. The honest framing: this is right-sized for the job, not infinitely scalable. Most knowledge bases never grow that big.

What to do next

If you're following along to build your own setup, the next deep-dive (slide 12) goes through the extras the proxy gives you on top of basic retrieval. If you've seen enough of the stack and just want the install commands, jump back to slide 07.