Retrieval

We beat the frontier retrieval, on plain Postgres

Our engine comes out ahead of Milvus's Vector Graph RAG on all three public multi-part question-answering benchmarks, and posts the best average of the field, with no dedicated vector or graph database.

Charcoal dots joined by faint grey lines, illustrating search by meaning combined with the connections between related facts. meaning + connections

01The problem

We were building an agentic system to search across millions of financial documents: the filings, reports, and contracts that hold numbers people actually act on. Two things make that hard in a way ordinary search is not built for.

The answers have to be checkable. When an agent pulls a figure out of a sensitive filing, or works one out across several, you cannot take its word for it. You need a trace: exactly which documents, which facts, and which steps produced the answer, so a person can verify it before anyone acts on it. In finance, "the model said so" is not an answer.

And the questions are not lookups. They are comparisons, inferences, and temporal questions, and no single passage answers them:

comparisonwhich of these two issuers carries more leverage? inferencewho is the ultimate parent, three ownership hops up? temporalhow did this segment's revenue move across the last three filings?

They need search by meaning, reasoning across many documents, and an answer you can audit.

We looked at what exists, and at our scale no single system did all of it:

So we built the system this post is about: search by meaning and traceable, verifiable inference and comparison and temporal reasoning, over millions of documents, on plain Postgres. The rest is how it held up.

02The approach

We do two things at once. First, we search by meaning rather than by exact wording: the system recognizes what a passage is about, so a question phrased nothing like the source can still find it. Second, we keep track of how facts relate to one another, so the system can follow the connections from one document to the next instead of judging each page on its own.

The part we are proud of is that those connections are discovered automatically from your documents. Nobody has to sit down and design a data model for your subject first. You point the system at a new set of documents and it works. Exactly how the connections are discovered, and how the two kinds of search are combined, is the part we keep to ourselves. This post is about what that combination does for you, not how it is built.

A stateless engine on plain Postgres

Here is the part an operations team will appreciate: there is no dedicated vector database and no separate graph database anywhere in this design. The engine does both jobs directly on PostgreSQL, the same ordinary, widely used database that millions of teams already run, using pgvector, a standard, free add-on for meaning-based search, alongside everyday tables. The engine itself is just an application that talks to that one database, the way any service talks to its store. So there is one database to back up, secure, and monitor: no second data system to run, scale, or keep in sync, and no second copy of your data living somewhere else.

03Advantages

The system brings together two well-established strengths that normally live in two different products, plus one thing neither of them does. Here is what each buys you, in plain terms.

Domain induction. Unstructured documents pass through the engine and come out the other side as a typed graph, its nodes and relations read from the material itself, with no schema authored by hand.

The easiest way to see the difference is against the two kinds of product teams usually reach for. A meaning-based search product understands language but does not track how facts connect. A connection-mapping database tracks how facts connect but needs an expert to design its data model for every new subject, and it is a whole extra system to run. We take the genuinely useful half of each, and ask you to run neither.

How it compares
What it does Ours Meaning-based search
(e.g. Milvus)
Connection database
(e.g. Neo4j)
Finds information by meaning, not exact wordsYesYesAdded recently, not its focus
Follows connections between facts across documentsYesNoYes
Both of the above, in one systemYesNoOnly if you model it yourself
No data model to design per subjectYesYesNo
Nothing to run beyond an ordinary databaseYesNoNo

04Financial inference

From search to guarantee. The engine traces how it reached an answer and runs it through the verifier, so the result is checked, not asserted.

On financial data the contest stops being about recall and becomes about trust. A retrieve-and-summarise system finds the passages and hands them to a language model, which reads the figures off and does the arithmetic itself, so it can misread a number, miscount a total, or invent one outright. Because the graph is typed, it does something a retriever cannot: it infers and verifies the answer. Feed the engine a set of filings and it reads a financial vocabulary straight out of them, with nobody writing it down: types like company and segment, relations like owns and reports, figures like revenue and fiscal year. Over that vocabulary runs a small set of typed operations:

Take a concrete one. To answer "what is Segment A's share of company revenue?" the engine reads two figures off the filing, Segment A's revenue and the company's revenue, then divides them with the units checked. Neither number is generated, and the division is verified, not guessed.

Segment A revenue $2.1B · read from the filing Company revenue $8.4B · read from the filing divide units checked 25% share verified Both figures are read; the division is checked; the result verified.
A verified financial calculation. Every figure is read from the source, the arithmetic is checked, and the answer carries the steps that produced it, so it can be audited rather than trusted on faith.
Financial calculations a typed graph makes safe, and a retriever cannot
Ask a filing Retrieve, then let a model compute (e.g. Milvus) Read, then compute by rule (ours)
A single figure, like a segment's revenuefinds the passage, then a model reads the number off and can misread itreads the exact figure off the filing, grounded to its source
A ratio, like a segment's share of revenuethe model does the division itself and can get it wrongcomputed by rule with the units checked, or refused
A total across subsidiariesthe model has to find and add every one, and may miss somewalks the ownership links and totals them, every step audited
Can you trust the number?no guarantee it was not invented or miscalculatedevery figure is read, every step leaves a paper trail

Two things make this trustworthy for numbers. Every figure in a calculation is read from a filing, never generated, so a model cannot slip in a value that was not on the page. And the arithmetic is checked, not guessed, so "segment revenue over total revenue" is either computed exactly or refused, never approximated. The result is an auditable number with a paper trail to its source, which is the bar financial work actually holds. A system that only fetches passages and asks a language model to read the figures off cannot promise any of that. Paired with a strong frontier model it will often be right, and such models are capable calculators, but it rests on the model reading every figure correctly and never slipping a digit, with no guarantee that it did. On a financial number, usually right is not the same as right.

05Typed queries

The connections the system builds are not just links, they are typed links: it learns that one page is about a film, another a person, and that a relationship like "directed by" joins a film to a person. That is what lets it answer a precise, multi-step question by walking the right kind of link at each step, rather than hoping the wording lines up.

Take a real one: "What nationality is the director of Dark River?" The film's page names its director but says nothing about nationality. The director's page gives the nationality but never mentions the film. Neither page answers the question on its own. The typed links do: the system knows a film connects to a person by "directed by," and a person to a country by "nationality," so it walks the two hops and reads off the answer.

Q: what nationality is the director of Dark River? Dark River type: film directed by Clio Barnard type: person nationality British type: country The director's page never mentions the film; the typed links reach it anyway.
A typed path. The types (film, person, country) and the relations (directed by, nationality) are induced from your documents, not designed by you, so the system can walk from a film to its director to their nationality even when no single page states the whole answer.

The typing also keeps the search efficient. Following a specific kind of link at each step, rather than fanning out to every neighbour and sifting the pile afterwards, keeps the walk small and precise even as the graph grows large. And none of it changes how you ask: you type an ordinary question, and the typed structure does the walking.

06Results

We beat the frontier

On plain Postgres, our engine beats Milvus's Vector-Graph RAG, the top-scoring vector-graph retrieval system in this field, on all three public benchmarks, and posts the best average of the group (88.6 vs 87.8). No dedicated vector or graph database.

We measured on three public question-answering benchmarks: standard, openly available collections of hard, multi-part questions that researchers use to compare systems fairly. Recall@5 = the share of a question's supporting passages found in the top 5 results (standard passage recall), averaged over questions. Higher is better. The headline: on plain Postgres we beat the leading vector-graph system (Milvus) on all three benchmarks, and post the best average of the group. Competitor figures are published numbers, cited below.

Recall@5 (higher is better)
Recall@5 MuSiQue HotpotQA 2Wiki Average
Ours (on Postgres)73.297.095.688.6
Vector Graph RAG (Milvus)73.096.394.187.8
HippoRAG 274.796.390.487.1
NV-Embed-v269.794.576.580.2
Naive RAG55.690.873.773.4

Underlines mark where our Postgres result is the top figure among the systems shown: HotpotQA (97.0), 2Wiki (95.6), and the three-benchmark average (88.6), the best average of any system here. We beat Milvus on all three benchmarks; on MuSiQue (73.2) we edge Milvus (73.0), with only HippoRAG 2 higher (74.7). All three are measured on one binary, n=1000, standard Recall@5, on nothing but Postgres.

0255075100 88.6 87.8 87.1 80.2 73.4 Ours Milvus HippoRAG 2 NV-Embed Naive RAG
Average Recall@5, ours (verified, n=1000) and published competitor figures
Average Recall@5 across MuSiQue, HotpotQA and 2Wiki. Ours (88.6) is the highest of the systems shown, on plain Postgres.

Competitor figures: Milvus "Vector Graph RAG" evaluation (zilliztech.github.io/vector-graph-rag/evaluation).

07Trade-offs

Being honest about where this helps least matters as much as showing where it helps most.

08Memory and performance footprint

The same small engine, scaled out. Because it holds no state, it runs as many identical copies behind an autoscaler, every one sharing the same Postgres, sharded underneath.

The whole benchmark above ran on a single Mac. MuSiQue is not a toy set: 11,656 passages, roughly 92,000 distinct entities, and 103,000 relationships between them. From one binary of about 80 MB, against an ordinary Postgres, the engine built the graph, ran the multi-step retrieval, and scored all 1,000 questions on that one machine. No cluster, no message queue, no object store, nothing to stand up.

That is a deliberately different design point from a dedicated vector database. Milvus is a fully distributed, Kubernetes-native system written in Go and C++, built to scale horizontally to billions of vectors across a cluster; even its single-machine image bundles its own storage and coordination services. That engineering buys enormous scale, and at billions of vectors it is the right tool for the job. But it is a system to deploy and operate. Ours is a small application on the database you already run, light enough to build, test, and run the entire pipeline, benchmark and all, on a single machine.

And small does not mean it cannot grow. Because the engine holds no state of its own, scaling it up is simply running more copies of it: put it in Kubernetes behind an autoscaler and it adds and sheds instances with load, every one of them sharing the same Postgres. The same build that runs the whole pipeline on one machine serves production traffic across a fleet, with nothing to re-architect in between.

Competitor numbers are published figures, shown for orientation. Our numbers are from a verified end-to-end run (n=1000 per benchmark, standard Recall@5, gpt-oss-120b listwise rerank on plain Postgres).