The Company Second Brain
How Doram builds, tests and serves a semantic context graph that every AI system in a company can read from and trust.
Written for data, platform and security teamsKey findings
Execution accuracy of leading AI models. It falls as the questions move from public data to real company data.
Section 1
Abstract
AI models can now write valid SQL. They still give confidently wrong answers to most real business questions.
On Spider 1.0, an academic benchmark, a leading code agent solves 91.2% of tasks. On Spider 2.0, built from real enterprise databases, the same agent solves 21.3%.[1] On BEAVER, built from real company data warehouses and query logs, the best agent setup tested reaches 10.8%.[2] The model is the same in each case. What changed is that the questions became real, and real questions depend on knowledge the database does not hold: what a term means at this company, which of several tables is the right one, how each number is counted, and what happened that changed the data.
Doram gives AI systems that knowledge through a company second brain: a semantic context graph that holds what the company knows about its data. It has three layers. The ontology layer says what things are. The context layer says how they connect and why. The semantic layer says how each number is counted. Every AI system the company runs, starting with Doram's own AI analyst, reads from this one graph, so every answer uses the same approved definitions.
The brain is not written by hand, and nobody has to keep it up to date by hand. It is built from the systems the company already runs: database schemas and query history, dbt models and BI dashboards, documents in tools such as Confluence and Notion, and events from tools such as Jira and Slack. Owners add the business context that no system holds. The work is split by what each part does best. Code settles everything the data can answer on its own. An LLM drafts only what code cannot, such as names and candidate definitions. People approve what neither can settle, such as a choice between two valid definitions.
After the first day, the brain is self-maintaining. Agents watch every source for changes, learn from how people use the analyst, and update the brain on their own. Small, safe updates go live automatically. Anything that would change an approved number waits for its owner.
At question time, one retriever walks the graph within fixed limits and applies access rules at every step. A semantic compiler turns the approved definitions into a single SQL query that runs in the customer's own warehouse. When the brain cannot decide between two meanings, or lacks a fact, it refuses and says what is missing instead of guessing.
What Doram guarantees
-
Evidence & ownershipEach node and edge links to the query, model, document, tool record or person it came from, and carries a trust level: discovered, validated, approved or certified.
-
Validation before releaseA metric definition is approved only after it compiles, runs on real data and matches a known result, such as an existing dashboard, within 0.5%.
-
Reproducible answersThe same graph version and the same user scope always return the same facts, so any answer can be replayed and traced to the definitions behind it.
-
Access control at every stepRole, row and column rules are enforced during the graph walk and in the compiled SQL, never filtered out afterwards.
-
Data stays in placeNo rows of company data are copied into the graph. Doram runs as SaaS or inside the customer's own cloud.
We evaluate Doram on Spider 2.0, BIRD[3] and BEAVER, and on an enterprise test set with messy schemas, conflicting definitions and restricted data. We compare it with three common approaches: an LLM given only the schema, an LLM with document search, and an LLM with a hand-built semantic layer. On the enterprise set, Doram reaches x23% execution accuracy, against x20% to x22% for these approaches. It is confidently wrong on x31% of questions, returns the same answer on x27% of repeated and reworded questions, and returned restricted data in 0 of x47 access tests.
This paper describes the architecture in full: the data model, how the graph is built and kept current, how it is read and compiled, how it is secured and deployed, and how it performs.
Draft noteFigures marked x are placeholders. They match the tables in Section 12 and will be filled in when the benchmark runs are complete.
Section 2
The Problem: Why AI Gets Company Questions Wrong
Most teams that try an AI analyst see the same thing. The SQL runs, the chart looks right, and the answer is confidently wrong.
This section shows where that gap comes from, what research says about its size, and why the usual fixes do not close it.
The gap between data and meaning
Take a question from a Monday leadership meeting:
“Why did revenue drop last quarter?”
Give an LLM the warehouse and it returns a confident answer in seconds: revenue fell 14%, mainly because of fewer new deals. The SQL is valid and the numbers are real, but the answer is wrong. The real drop was 9%, because Finance does not count one-time setup fees as recurring revenue. Upgrades were filed as new business instead of expansion. And the cause was paused customers, not new deals.
None of these is a SQL error. Each comes from something the company knows and the database does not say:
| What an analyst knows | Where the evidence lives |
|---|---|
| Setup fees are not recurring revenue. | A Finance decision from March 2024, in meeting notes. |
| An upgrade is expansion, not new business. | Every revenue dashboard. |
Status P means paused, not cancelled.
|
Paused accounts have open contracts but no invoices. |
accounts_v2 is the real customer table.
|
Query history: joined about 400 times, the old table almost never. |
Table 1What an experienced analyst knows, and where each fact leaves a trace.
Every one of these facts has a trace somewhere in the company. None of them is in the schema. This is the gap between data and meaning, and it is why an AI can sound certain and still be wrong.
What research shows
When the databases and questions come from real companies, accuracy falls sharply.
Figure 1Execution accuracy of leading AI agents. Spider 1.0, BIRD and Spider 2.0 use the same o1-preview code agent[1]. BEAVER uses the best agent frameworks tested, running GPT-5.2[2].
The same agent that solves 91.2% of Spider 1.0 solves only 21.3% of Spider 2.0, whose databases often have more than 1,000 columns.[1] On BEAVER, built from real company query logs, the best GPT-5.2 agents reach 10.8%, and only 30.1% even with expert hints.[2]
Context is what moves the number. On an insurance company's database, GPT-4 answered 16% of business questions correctly from the SQL tables alone, and 54% when given a knowledge graph of the business.[4]
Why the usual fixes fall short
Teams usually try one of four fixes. Each helps, but none closes the gap.
| Fix | Why it falls short |
|---|---|
| More data | The meaning of the data is not in the data. More tables only mean more wrong choices. |
| A bigger model | No model can know a decision Finance made in a meeting. BEAVER's 10.8% comes from GPT-5.2. |
| Document search | It finds pages, but cannot tell which definition is approved or turn it into SQL that gives the same answer every time. |
| A hand-written semantic layer | There is too much to write, and it goes stale. It holds metrics, but not events or decisions. |
Table 2The four common fixes, and why each one leaves the gap open.
What is missing is a layer that holds the company's meaning: built from the evidence the company already has, validated against real data, and approved by its own people. Section 3 describes that layer.
Section 3
The Company Second Brain
The company second brain is a versioned, evidence-backed semantic context graph, built from the company's own systems and read by every AI system through one interface.
Figure 2Four sources, one graph, one read path.
For each workspace, Doram keeps one graph, versioned on every change, and every consumer reads it through one function:
Gk = (V, E) the graph at version k layer(x) ∈ {ontology, context, semantic} x: any node or edge trust(x) ∈ {discovered, validated, approved, certified} trust(x) ≥ validated ⇒ evidence(x) ≠ ∅ trust(x) ≥ approved ⇒ owner(x) defined R(Gk, u, q) → context pack the only read path, for user u and question q
No consumer reads a source system directly.
The three layers
Each layer depends on the one before it.
Ontology layer: what things are. It holds the company's model of its business: entities, their columns, and the words people use for them.
entity = ⟨ name, tables, key, grain, synonyms ⟩
column = ⟨ table, type, role, profile ⟩
role ∈ { identifier, measure, dimension, time, foreign key, personal }
profile = ⟨ distinct values, null rate, range, masked samples ⟩
masked samples: a few example values, personal values masked
term = ⟨ word, synonyms, target node, persona ⟩
cardinality(A → B) ∈ { 1:1, N:1 } from keys and observed joins
N:M only through a bridge entity
An entity can span several tables and sources. A column's role decides how the compiler may use it: measures are aggregated, dimensions split, personal columns are masked. The ontology answers "what is this, and where does it live?"
Context layer: how things connect, and why. It holds every edge between nodes, and the knowledge around the data that no schema records.
edge = ⟨ from, to, type, weight, valid from, valid to, trust, evidence ⟩
rule = ⟨ predicate, applies to, persona ⟩
policy = ⟨ kind ∈ { RBAC, ABAC, column }, predicate or mask, subjects ⟩
event = ⟨ name, time window, affects, source record ⟩
fact = ⟨ statement, decided by, date, applies to ⟩
Join edges carry their keys and a weight from how often they are used. Because every edge has a validity window, the graph can be read as of any past date. The context layer answers "which join, which filter, who may see it, what happened, and what was decided?"
Semantic layer: how things are counted. It holds one approved definition for each metric, as a fixed tuple:
metric = ⟨ entity, measure, aggregation, grain, rules, dimensions, additivity ⟩
additivity ∈ { additive, semi-additive, non-additive }
additive: sums across any dimension (revenue)
semi-additive: sums across all but time (account balance)
non-additive: must be recomputed (ratios, distinct counts)
Dimensions point to columns or terms and set how a metric may be split. Because the definition is fixed, the same metric, split and filters always compile to the same SQL. The semantic layer answers "how exactly is this number computed?"
The four sources
All four are read with read-only access:
- Databases: schema, column profiles, query history, and definitions already in dbt and BI tools. They yield entities, columns, joins, metrics and rules.
- Business context: entered by owners in Doram's studio, because no system holds it. It yields terms, facts, goals and calendar rules.
- Company knowledge: document sections that mention something the graph already knows, from spaces an admin chose. It yields rules, terms, facts and goals.
- Third-party tools: events and reference lists from tools such as Jira, Slack, the CRM and billing. Single records are read live and never stored. They yield events, terms and rules.
The build pipeline
Every fact enters the graph through three gated stages:
- Discover. Code extracts candidates from the sources, and the LLM drafts each fact from evidence only. A draft without evidence goes no further.
- Validate. Code checks each draft. Data-backed facts, such as metrics and joins, are run on real data, and a metric must replay within 0.5% of a known result. Knowledge facts, such as decisions and policies, are checked for evidence, references and conflicts.
- Approve. Low-risk changes go live automatically. Medium-risk changes go live, and 1 in 10 is spot-checked. High-risk changes wait for the owner. Certification is always a person's decision.
The LLM never writes SQL and never sets a trust level.
Consumers
Three consumers read the graph through R. The AI analyst calls it as a tool, engineers' own agents call it through the API, and the semantic compiler compiles the context pack that R returns. Consumers can propose changes, but a proposal becomes a fact only through the build pipeline. Data owners approve definitions, business owners enter business context, engineers integrate, and business users only ask questions.
Section 4
Trust by Design
An answer can be trusted only if it can be checked, today and later. These properties make every Doram answer checkable.
Evidence on every fact
Every node and edge links to the sources it came from:
evidence(x) = { (kind, reference, excerpt, captured_at) }
kind ∈ { query, model, dashboard, document, tool record, person }
readers(x) = ∩ readers(e) for e ∈ evidence(x) until the owner widens it
Doram keeps a reference and a short excerpt, never the whole source. A fact with no evidence stays a draft. When a source changes, the facts built on it are checked again. When a fact's only source is deleted, the fact returns to discovered. A fact taken from a restricted document can be read only by people who can read that document.
Trust levels
- Discovered: found by the crawler agent, with evidence, not yet checked.
- Validated: a data-backed fact that ran on real data and matched a known result.
- Approved: accepted by its owner, or approved automatically because the change is low risk.
- Certified: signed off by its owner as the company's official definition, with the reviewer, time and test score recorded.
A fact moves between levels only along these transitions, and every move writes a new version:
discovered → validated data-backed fact passes all tests discovered → approved knowledge fact accepted by its owner validated → approved low or medium risk, or owner accepts approved → certified owner signs off any → discovered its only evidence is deleted default read set: trust(x) ∈ { approved, certified }
Drafts are returned only when a user asks for them, and are marked. The semantic compiler never reads a draft, so a draft can never reach a SQL query.
Continuous verification
Trust is re-earned every day. Every approved and certified metric is replayed daily and after any source change:
|m_today − m_ref| / |m_ref| ≤ ε ⇒ stays live ε = 0.005
otherwise ⇒ new draft; users keep the
last approved version
last approved fails to compile ⇒ refuse for that metric
Same question, same answer
change(x) → version + 1, with (author, time, reason, test result)
rollback = a new version that copies an older one
same (k, u, q) ⇒ same facts and same SQL
ranking model frozen with k; LLM slots stored in the record
read record = (k, u, q, slots, a*, f), f = hash(sort(facts returned))
a*: the chosen resolution (Section 7)
replay(r) passes ⇔ fingerprint of R(G_k, u, q) = f
Any past answer can be replayed on the version that produced it and traced to its definition. No separate audit system is needed.
Refusal instead of guessing
Doram refuses when:
- a term still has more than one valid definition after the persona order;
- a question needs a fact that has no approved definition;
- an approved metric no longer compiles after a source changed;
- an answer would need data the user may not see.
A refusal names the missing fact and shows the evidence on each side, so the owner can settle it once.
When things go wrong
- An owner approves a wrong definition: any change that moves an approved number is high risk, and the reviewer sees its test results and everything that depends on it.
- A document contains injected instructions: the LLM only drafts. It cannot approve, write SQL or change a policy.
- The LLM misreads a question: slot checks and clarifying questions catch it before any SQL runs.
- A source changes: drift detection creates a new draft, and users keep the last approved version.
Section 12 measures trust directly: the confidently wrong rate, and the daily replay match rate of certified metrics.
Section 5
System Architecture
Architecture thesis
Company meaning must be discovered, validated and approved before a question is asked. At question time, the system only looks it up.
Most AI analysts decide what "revenue" means while they answer, from the schema and whatever text they retrieve. Doram moves that decision out of the answer. The write path establishes meaning with evidence, tests and owners. The read path can use only what the write path has approved. The rest of the architecture follows from this:
- Two paths, one meeting point. The write path builds the graph and the read path reads it. Nothing on the read path can write to the graph, and nothing on the write path can answer a question.
- Code decides, the LLM proposes. The LLM drafts from evidence and reads questions. Code tests, resolves and writes SQL. People approve what code cannot settle.
- Proof before use. A data-backed fact reaches a SQL query only after it reproduces a known number.
- One store, one read path. Every fact lives in one transactional graph, and every consumer reads it through R, under the same rules.
- Pinned reads. Every read uses one graph version, so the same question gets the same answer and can be replayed.
- No guessing at runtime. If meaning is missing or ambiguous at question time, Doram refuses. It never fills the gap by inference.
- Meaning, not data. The graph stores definitions, relationships, profiles and evidence. It holds no rows of company data, only masked samples inside column profiles. The data stays in the company's warehouse.
Architecture at a glance
Figure 3Write path (top) and read path (bottom). Shaded boxes use an LLM.
Separation of concerns
The system is split into two paths and seven layers.
Write path: creates meaning. Discover → Validate → Approve, as described in Section 3.
Read path: uses meaning.
- Understand: the LLM turns the question into slots.
- Resolve: the retriever maps the slots to approved facts and builds the context pack.
- Compile: the compiler checks joins, grain and filters, adds policies and writes SQL.
- Execute: the warehouse runs the SQL under a cost guard, and the analyst explains the rows.
Meaning is created only on the write path and used only on the read path. Up to Resolve, the read path works only with business names. Physical tables and columns appear only from Compile onward.
Components
Each component has one job. Only two use an LLM.
- Connectors read the sources, with read-only access, and build evidence bundles.
- The crawler agent turns an evidence bundle into a draft, using an LLM.
- Validation tests each draft. It is code.
- Approval turns a tested draft into a new graph version, by risk rules or by the owner.
- The monitoring agent turns source changes into new drafts.
- The feedback agent turns usage signals into new drafts.
- The retriever turns slots into a context pack. It is code.
- The semantic compiler turns a context pack into SQL. It is code.
- The AI analyst reads the question and explains the answer, using an LLM.
The retriever, compiler and analyst are stateless and scale horizontally. All state lives in two places: the graph in Postgres, and the company's data in its own warehouse. Each fact is written in one transaction, and after the first run, connectors read only what changed. Section 6 details the write path. Sections 7 and 8 detail the retriever and the compiler.
Section 6
Building and Maintaining the Graph
The write path in depth: discovery, validation, storage and maintenance.
Discovery
Query history is the primary signal. Each query is parsed, normalized and fingerprinted. Candidates are extracted per shape and weighted by frequency, user spread and recency:
shape(q) = hash(canon(AST(q))) literals stripped, identifiers canonicalized C(s) = joins ∪ predicates ∪ group keys ∪ aggregates of shape s w(c) = log(1 + n_c) · log(1 + u_c) · e^(−λ·Δt_c) n: runs, u: distinct users, Δt: age; λ = ln 2 / 90 days keep c ⇔ n_c ≥ 3 ∧ c not only in system or failed queries
Equivalent candidates are clustered. For each cluster, the LLM receives one evidence bundle (column profiles, masked samples, query shapes and declared dbt or BI definitions, each with an evidence ID) and emits a typed draft. Declared definitions enter as high-weight priors. Documents are segmented by heading and entity-linked against existing nodes, and only linked segments are drafted from. Tools contribute events and reference lists. Individual records are resolved live and never persisted. Business context is entered by owners and recorded with the owner as evidence.
The same entity in several sources is linked by key containment:
overlap(A.k, B.k) = |V(A.k) ∩ V(B.k)| / min(|V(A.k)|, |V(B.k)|)
propose same_as(A, B) ⇔ overlap ≥ 0.9 V: distinct key values
Validation
Data-backed facts are executed against the warehouse. A metric passes five sequential gates: static type and grain check, compilation, dry-run cost bound, sample execution against column profiles, and replay.
replay ⇔ |m − m_ref| / |m_ref| ≤ ε ε = 0.005 join ⇔ uniq(k₁) = 1 ∧ match(k) ≥ 0.95 ∧ |A ⋈ B| / |A| ≤ 1 uniq: distinct keys / rows on the one side rule ⇔ compiles ∧ |Δrows − Δrows_hist| / Δrows_hist ≤ 0.05
Knowledge facts are checked for provenance, referential integrity and in-scope contradiction, then routed to their owner. Approval follows the risk classes in Section 3.
Storage
The graph is persisted in Postgres as four relations. Nodes carry type, layer, fields, trust, version and owner. Edges carry type, source, target, weight, validity window, trust and version. Evidence rows carry kind, reference, excerpt and capture time. Version rows carry the diff, author, timestamp and reason. A node, its edges, its evidence and its version row commit in one ACID transaction.
Node and edge types form a closed set that the crawler agent cannot extend. Traversal is a bounded recursive query (depth ≤ 3, edge types restricted per request, at most 2,000 visited nodes) over indexes on (workspace, type), (src, type) and (dst, type), so read cost is independent of graph size. The design point is 10⁶ nodes and 5 × 10⁶ edges per workspace. Beyond it, relations are partitioned by workspace and reads are served from replicas.
Maintenance
Four detectors run continuously. A schema change is flagged when the schema fingerprint differs from the last run. A data change is flagged when a column's population stability index (PSI) against its last profile exceeds 0.2, or a new categorical value appears. A usage change is flagged when a join path's share of queries moves by more than 20 points. A provenance change is flagged when the source behind a fact's evidence is edited or deleted.
Each detection triggers impact analysis over the reversed dependency graph:
for y ∈ reach(x, depends_on⁻¹):
¬compiles(y) → break → redraft
replay(y) drifts > ε → semantic → redraft
otherwise → refresh evidence
Until a redraft is approved, consumers fall back as described in Section 4. Usage feedback is aggregated per node and acts only at 3 or more distinct users, or one owner. No agent can approve its own output, mutate an approved version, arbitrate between conflicting definitions, modify a policy or persona, or emit SQL.
Section 7
Context Retrieval
The retriever implements R(Gk, u, q) → context pack. It is a controlled agentic retriever. The AI analyst's LLM plans the request once. The retriever itself is code, and performs every search, ranking, resolution and check, deterministically on version k.
Slot extraction
The AI analyst's LLM maps the question to a typed request. It fills slots with the user's words and never names a node:
slots(q) = ⟨ metrics, dimensions, filters, time, task ⟩
task ∈ { lookup, compare, trend, explain }
Filters keep their literal values, such as "EMEA". Code, not the LLM, resolves time phrases against the fiscal calendar. A follow-up inherits the previous turn's slots. The slot output is stored in the read record, so a replay never calls the LLM again.
Scope
Scope is computed before any search, so a node the user may not see is never a candidate, whether by name, synonym or path:
G_u = { x ∈ G_k : allowed(u, x) ∧ trust(x) ∈ T_u }
T_u = { approved, certified } drafts only on explicit request
Candidate generation
Each slot is searched inside G_u, in four indexes held in the same Postgres database as the graph. A BM25 index covers names, synonyms, descriptions and example questions. A pgvector index covers the same text, for loose wording. A value index covers the distinct values of small, non-personal columns and reference lists, with trigram matching for typos. A question memory holds approved question-to-resolution pairs, matched on the masked question and on the plan skeleton[6].
Search runs from metrics and from columns, and the two results are merged[8]. Values in large or personal columns are never indexed. They are resolved by a live, policy-filtered lookup[7].
The ranked lists are fused with reciprocal rank fusion[5]:
RRF(c) = Σ_i 1 / (k_rrf + rank_i(c)) k_rrf = 60; top K = 20 per slot
Ranking
Candidates are re-ranked by a linear scorer over fixed features:
s(c) = wᵀ · φ(c) φ(c) = [ RRF(c), match type, usage weight, trust, persona fit, confirmations ]
The weights are learned offline with learning-to-rank on labelled resolutions, then frozen and versioned with the graph. Ties are broken by node identifier. The same question on version k always ranks the same way.
Joint resolution
Slots are resolved together, not one by one. The retriever selects the highest-scoring assignment that satisfies every structural constraint, using beam search over the top candidates of each slot:
a* = argmax Σ s(a_i) over a ∈ top_K(slot_1) × … × top_K(slot_n)
subject to
metrics and dimensions joinable on approved edges, depth ≤ 3
grain(dimension) ≥ grain(metric)
every filter column reachable from the metric's entity
additivity(metric) compatible with the requested aggregation
beam width B = 10
If a question in memory matches with similarity at least 0.9 and its resolution satisfies the constraints, that resolution is reused. Where a term has several definitions in scope, a fixed precedence decides:
def(t, u) = first non-empty of
D_user(t, u), D_team(t, u), D_source(t, u), D_company(t)
Decision rule
margin = S(a*) − S(a₂) normalized scores; δ = 0.1
margin ≥ δ ⇒ proceed
margin < δ ⇒ ask the user to choose among the top options
no feasible assignment ⇒ LLM restates the slots once, then retry
still none, or |def| > 1 ⇒ refuse, naming the unresolved slot
Context pack
The pack contains the chosen definitions, the join path, the rules and policies that apply, and the events and decisions linked to the metric whose time window overlaps the question's. Every item carries its evidence, and the pack is held within a fixed size budget. Consumers see business names only. Physical tables and columns are passed only to the compiler, and a column hidden by policy is passed to no one.
Learning across versions
Clarifications and corrections become candidate synonyms, value aliases and question-memory entries, and enter the graph through the feedback agent and the build pipeline. Ranking weights are retrained offline and shipped only as a new graph version. Retrieval improves from one version to the next, while each version stays deterministic.
Together these components cover exact names, synonyms and typos, loose wording, literal filter values, relative and fiscal time, team-specific meanings, ambiguous questions, incompatible slots, comparisons and ratios, "why" questions, follow-ups and repeated questions. Section 12 measures the retriever on its own.
Section 8
Semantic Compilation and Execution
The semantic compiler implements C(pack, k) → SQL. It is code, not an LLM, and a pure function: the same pack on version k always yields the same SQL, byte for byte.
Figure 4Six passes turn a context pack into SQL. Every failure stops before the warehouse.
Semantic query
The pack is lowered into a typed intermediate form. Each metric carries its expression, aggregation class and grain:
Q = ⟨ M, D, F, T ⟩
m ∈ M = ⟨ expr, class, entity, grain, time column ⟩
class ∈ { additive, semi-additive, distinct, ratio }
additivity of Section 3; non-additive splits into distinct and ratio
d ∈ D = ⟨ column, entity, key ⟩
T = [t₀, t₁) closed-open, on the fiscal calendar
The compiler does not trust the pack. It re-checks every node and edge against version k and re-applies the constraints of Section 7. Any failure stops compilation.
Aggregation planning
Every approved join is N:1 in one direction (Sections 3 and 6), so the direction of traversal gives each edge's cardinality. The planner uses it to decide where rows could repeat:
dimension path N:1 or 1:1 edges only, by grain(d) ≥ grain(m) filter over 1:N semi-join R ⋉ σ_F(S), as EXISTS, never an inner join several facts aggregate each, then join on D
Metrics are grouped by fact entity. Each group is aggregated at the grain of D, and the groups are joined on the dimension keys:
Sᵢ = γ_{D ; aggᵢ} ( σ_{F ∧ T} ( Rᵢ ⋈ path(D) ) )
P = S₁ ⟗ S₂ ⟗ … ⟗ Sₙ on the keys of D
⟗: full outer join
A row is counted once per metric, whatever the join shape, so fan-out and chasm traps cannot occur. Each class compiles differently:
additive Σ at grain D ratio a / b Σa / NULLIF(Σb, 0), after aggregation, never averaged distinct recomputed from base rows at grain D, never summed semi-additive last value over time, Σ over other dimensions compare P(T) ⋈_D P(T − Δ) trend P by time grain, joined to a date spine
Policy rewrite
Policies rewrite the plan. They do not filter the result. Every base relation is replaced by its filtered form before any join or aggregation:
R → σ_{p(u, R)} (R) every base relation in P
c → mask(c) every masked column in the output
hidden(c) ∧ c ∈ D ∪ F ⇒ refuse
The predicate p(u, R) comes from ABAC (Section 9). It sits below every aggregate, so no total can include a row the user may not see.
SQL generation
The rewritten plan P_u is canonicalized, emitted as a syntax tree in the warehouse's dialect, and serialized:
SQL = serialize( emit_dialect( canon(P_u) ) ) canon : sorted predicates, deterministic aliases, fixed CTE order
Date functions, quoting and the fiscal calendar are handled per dialect, and T is pushed down as the partition filter on partitioned tables. Filter values from the question are bound as parameters, never written into the SQL text, so a question cannot inject SQL. Every syntax-tree node keeps the ID of the graph node it came from, and this lineage is shown with the answer.
Verification
The emitted SQL is parsed again and checked, independently of the passes that produced it:
verify(SQL) ⇔ one read-only SELECT
∧ every table and column ∈ pack
∧ every base scan carries p(u, R)
∧ every partitioned scan carries a partition filter
∧ every value from the question is a bound parameter
A failure here is a compiler defect, not a user error. Nothing runs, and the case is logged.
Cost guard and execution
Before running, the compiler requests the warehouse's own estimate by dry run or EXPLAIN:
run ⇔ bytes(SQL) ≤ B_max ∧ rows(SQL) ≤ N_max limits set per workspace
Otherwise, the analyst asks for a narrower question, such as a shorter time range. The query runs in the customer's warehouse through a read-only account with a statement timeout. Rows go to the analyst for this user only, and are never written to the graph.
Section 9
Security and Governance
One invariant governs access: no user learns through Doram what they could not see in the source.
Access control
Three policy kinds, each enforced at one point on the read path:
RBAC facts allowed(u, x), applied by the retriever before search
ABAC rows p(u, R), added by the compiler below every aggregate
column values mask(c) or hide(c), applied by the compiler
policy(u, x) = warehouse grants ∩ admin rules the stricter wins
Warehouse grants are imported at every schema read, so a revoke in the source reaches Doram at the next read. Only admins write policy nodes, and no agent can create or change one. Every connector uses a read-only account. Credentials are encrypted, kept outside the graph, and never sent to the LLM.
What the LLM sees
While building the graph, the LLM sees names, profiles, value-free query shapes, masked samples and document excerpts. While answering, it sees the question, business names and the asking user's own results. It never sees credentials, hidden or personal values, raw rows at build time, or another user's results. The customer chooses the LLM, and its data never trains a model.
Data boundary
In SaaS, only the graph is stored outside the customer, and an agent inside the customer's network connects out to Doram. On-premise, nothing is stored outside, and only calls to the chosen LLM leave the network. No inbound port is opened in either model. Rows are never stored, and are sent only to the user who asked. The graph holds only masked samples inside column profiles.
Audit and retention
The audit trail is the version log and the read records of Section 4, so an auditor can see who changed a definition, why, and who saw which number. Read records hold facts and fingerprints, not rows. Both are kept for two years. Erasing a person removes their values from evidence, samples and stored slots in every version.
Section 10
One Question, End to End
This is what happens, step by step, when someone asks Doram the question from Section 2.
“Why did revenue drop last quarter?”
What Doram does
- Reads the question. The AI analyst works out what is being asked: a number (revenue), a time (last quarter) and a task (explain why it changed). This is the only step where the LLM reads the question.
- Checks who is asking. The user works in Finance, so Doram looks only at facts Finance is allowed to see, and only at approved ones.
- Picks the right meaning. "Revenue" means different things to different teams. Finance means net revenue. Sales means gross revenue, which includes setup fees. A Finance user is asking, so Doram uses Finance's meaning. "Last quarter" becomes 1 April to 30 June 2026, compared with 1 January to 31 March.
- Gathers what an expert would know. Trial and internal accounts do not count. Setup fees are not recurring revenue, a Finance decision from March 2024. Status "P" means paused, not cancelled. The last big drop was caused by a billing outage.
- Writes the query. The compiler turns all of this into one SQL query, adds Finance's access rules, checks that the query is safe, and checks that it will not cost too much.
- Runs it. The query runs in the company's own warehouse, with read-only access. The results go only to the person who asked.
What the numbers show
Revenue 4,000,000 → 3,640,000 down 360,000 (9%) Paused customers −445,000 Customers who left −110,000 Customers who downgraded −40,000 Customers who upgraded +95,000 New customers +140,000
The answer
Revenue fell 9%, not 14%. The 14% included one-time setup fees, which Finance does not count as recurring revenue.
There was no billing outage this quarter, so this is not a repeat of the last drop. The real cause is paused customers. They took away 445,000, more than the whole drop, and without them revenue would have grown about 2%. Upgrades are counted as upgrades, not as new customers.
Where the numbers could not explain something, the analyst checked the CRM. Most paused customers are schools and colleges that pause every summer. Fourteen have no restart date, so the analyst lists them for the account team instead of guessing.
Why it can be trusted
The answer comes with the definitions it used, the SQL it ran and the source behind each fact. Doram also keeps a record of the question, the user, the graph version (1482) and the facts it used. Anyone can replay that record later and get the same facts and the same SQL.
Section 11
Deployment, Scale and Cost
Doram runs as SaaS or on-premise from the same build. In both, computation on company data runs in the customer's warehouse, and Doram's own load on it is bounded by a budget.
Deployment
The system has two planes. The control plane holds the graph store, the indexes, the agents, the retriever, the compiler and the analyst. The data plane is the customer's warehouse and sources, reached only through connectors.
In SaaS, the control plane runs in Doram's cloud, and the connectors run in an agent inside the customer's network. On-premise, both planes run in the customer's cloud account. Doram operates it through access the customer grants and can revoke, and upgrades ship in an agreed window. A workspace moves between the two modes by exporting and importing its graph versions, without a rebuild.
Each customer is one workspace. Every relation, index and version log is keyed by workspace, and every read is scoped to a single workspace. A workspace is created in the region the customer picks, and its graph, evidence, backups and LLM calls stay in that region.
Scaling
The retriever, compiler and analyst are stateless and scale horizontally. Read cost is bounded by the traversal limits of Section 6, so it depends on the question, not the size of the graph. Above the design point of Section 6, a workspace gets its own database and read replicas. Background jobs are incremental: after the first run, they process only what changed.
Cost
Background jobs read metadata and log tables, profile from samples rather than full scans, and replay approved metrics on a recent window. The customer sets a monthly compute budget and a run window, and can assign a dedicated warehouse or resource group. For production databases, connectors read from a read replica, never the primary, so Doram's jobs and questions cannot slow down production. Jobs run by priority within the budget. When it is exhausted, the lowest-priority jobs pause first, and the replay of approved metrics stops last.
A question costs one bounded graph read, one slot extraction by the LLM, one warehouse query that has passed the cost guard of Section 8, and the LLM tokens used to explain the answer. Section 12 reports the average tokens and warehouse compute per question.
Section 12
Benchmarks
Doram is evaluated on public benchmarks, for comparison with published results, and on an enterprise set that tests what public benchmarks do not: conflicting definitions, restricted data and questions that should not be answered.
Draft noteEvery figure marked x1, x2 and so on is a placeholder until the benchmark runs are complete.
Method
The public sets are Spider 2.0[1], BIRD[3] and the public release of BEAVER[2] (7,978 of its 9,128 questions; the rest is held out by its authors). For these, the graph is built automatically from what each benchmark provides, with no human review.
The Doram enterprise set has x1 questions across x2 company workspaces, taken from real query history and dashboards. It includes conflicting definitions, team-specific terms, hidden rules, fiscal calendars, events, restricted data, and questions that are ambiguous or unanswerable. Every expected answer is approved by a domain expert. Test questions and their SQL are removed from the query history used to build each graph, so no expected answer can reach the graph or question memory.
Doram is compared with an LLM given only the schema, an LLM with search over the company's documents, an LLM with a semantic layer written by an expert (enterprise set only), and the best published result on each public set. Every system uses the same LLM, x3, at temperature zero, with the same time limit per question. Each question is run five times, and results are reported as the mean with a 95% confidence interval. Doram's graph version is fixed for each run, so any result can be replayed.
The measures:
EX(q) = 1 ⇔ rows(q) = rows_ref(q) multisets, any column order consistent(q) = 1 ⇔ all 25 results agree 5 rephrasings × 5 runs CW = P( wrong ∧ no refusal, clarification or warning ) leak = access tests returning a restricted row or value
Confidently wrong (CW) is the measure that matters most. It is reported with the over-refusal rate, so a system cannot lower it by refusing everything.
Public benchmarks
| Benchmark | LLM + schema | LLM + document search | Best published | Doram |
|---|---|---|---|---|
| Spider 2.0subset: x4 | x5% | x6% | x7% | x8% |
| BIRDdev set | x9% | x10% | x11% | x12% |
| BEAVER | x13% | x14% | 10.8%[2] | x15% |
Table 3Execution accuracy on public benchmarks. Every system uses the same LLM, except the best published result, which may use a different split or model.
To show that the model is the replaceable part, the enterprise set is also run with x16 and x17, from other providers. Doram's accuracy changes by x18 points across models, against x19 points for an LLM given only the schema.
Enterprise results
| Measure | LLM + schema | LLM + document search | LLM + hand-built semantic layer | Doram |
|---|---|---|---|---|
| Execution accuracy | x20% | x21% | x22% | x23% |
| Consistent across runs and rephrasings | x24% | x25% | x26% | x27% |
| Confidently wrong | x28% | x29% | x30% | x31% |
| Correct refusal on unanswerable questions | x32% | x33% | x34% | x35% |
| Clarifying question on ambiguous questions | x36% | x37% | x38% | x39% |
| Refused or asked when it should have answered | x40% | x41% | x42% | x43% |
| Restricted data returned | x44 | x45 | x46 | 0 of x47 |
Table 4Results on the Doram enterprise set.
Ablations
One component is removed or replaced at a time, on the enterprise set:
| Configuration | Execution accuracy | Confidently wrong |
|---|---|---|
| Full Doram | x48% | x49% |
| LLM writes SQL from the context pack, no compiler | x50% | x51% |
| Without the context layer | x52% | x53% |
| Without query-history discovery | x54% | x55% |
| Drafts used without validation or approval | x56% | x57% |
| Without the value index | x58% | x59% |
| Without question memory | x60% | x61% |
| Slots resolved one by one, not jointly | x62% | x63% |
Table 5What each component adds, on the Doram enterprise set.
Retriever
| Measure | Result |
|---|---|
| Slot extraction accuracy | x64% |
| Candidate recall at K = 20 | x65% |
| Top-1 accuracy after ranking | x66% |
| Joint resolution accuracy (a* matches the reference) | x67% |
| Context-pack recall against the reference SQL | x68% |
Table 6The retriever, measured on its own.
Build and maintenance
| Measure | Result |
|---|---|
| Drafts approved automatically | x69% |
| Drafts rejected by reviewers | x70% |
| Auto-approved facts later reverted | x71% |
| Reviewer hours in the first week | x72 |
| Reviewer hours per week by the second month | x73 |
| Time from connection to first answer | x74 |
| Certified metrics matching on daily replay | x75% |
| Injected source changes detected | x76% |
Table 7Discovery, review effort and drift detection, averaged across workspaces.
Latency and cost
| Stage | p50 | p95 | p99 |
|---|---|---|---|
| Retrieval | x77 ms | x78 ms | x79 ms |
| Compilation | x80 ms | x81 ms | x82 ms |
| End to end, excluding warehouse time | x83 s | x84 s | x85 s |
Table 8Latency at the design point of Section 6.
An average question uses x86 LLM tokens and x87 of warehouse compute.
Section 13
How Doram Compares
Four kinds of product give AI systems part of a company's knowledge. Each does some things well. The table compares them on what a trusted answer needs.
| Capability | Doram | Data catalogs | Semantic layers | Platform AI analysts | Text-to-SQL tools |
|---|---|---|---|---|---|
| Answers business questions with numbers | ✓ | — | — | ✓ | ✓ |
| Builds itself from your existing systems | ✓ | ◐ | — | ◐ | ◐ |
| Knows how each number is counted | ✓ | — | ✓ | ◐ | — |
| Knows the rules, events and decisions behind the data | ✓ | ◐ | ◐ | ◐ | — |
| Checks each number against a trusted report before use | ✓ | — | ◐ | ◐ | — |
| Shows where every fact comes from | ✓ | ✓ | ◐ | ◐ | ◐ |
| Gives the same answer every time | ✓ | — | ✓ | ◐ | — |
| Says "I don't know" instead of guessing | ✓ | — | — | ◐ | — |
| Works with any AI tool | ✓ | ✓ | ✓ | — | ◐ |
Table 9✓ yes · ◐ partly · — no. Examples: data catalogs (Atlan, Alation, Collibra, data.world), semantic layers (dbt Semantic Layer, Cube, AtScale, LookML, Timbr), platform AI analysts (Snowflake Cortex Analyst, Databricks Genie, Power BI Copilot, ThoughtSpot, Tableau Next, Omni), text-to-SQL tools (Wren AI, Vanna, TextQL). As of September 2026; products within a category vary, and many are adding features quickly.
Section 14
Limits and Conclusion
Limits
- Cold start. Discovery is weighted by query history. A workspace with little history yields fewer candidates, so more definitions start as questions for their owners.
- Knowledge facts are not testable. Decisions, policies and events are checked for evidence and conflicts, not against data. Their correctness rests on the owner who approves them.
- Onboarding needs review. The first build produces a review queue. Low-risk facts go live automatically, but high-risk ones wait for their owners.
- Bounded reach. Reads stop at a depth of three edges. A question that needs a longer join path is refused.
- Explanation, not causation. An explain answer breaks a change into its parts and links the events in its time window. It does not prove cause, and Doram does not forecast.
- Slot extraction can fail. The LLM can misread a question. Constraint checks, the decision margin and clarifying questions catch most misreadings, but not all.
Conclusion
AI models already know how to write SQL. What they lack is the company's meaning: what its words refer to, which data is real, how it counts, and what happened. That knowledge has always lived in a few people's heads and in traces spread across the company's systems.
Doram turns it into a company second brain: a semantic context graph built from the company's own systems, validated against its data, approved by its own people, and kept current on its own. Every AI system reads from it, so every answer uses the same approved definitions, can be traced to its evidence, and can be repeated.
The model is the part a company can replace. Its context graph is the part no one else can copy, because it is built from its own data, decisions and history, and it gets more valuable with every question answered.
The analyst knew the business. The AI knew the data. With a second brain, the AI knows both.
Back matter
Frequently Asked Questions
- Does Doram copy our data?
- No. The graph stores definitions, relationships, column profiles, small samples and evidence. Query results are shown to the user who asked and are never stored.
- Which LLM does Doram use?
- The one you choose, including your own model endpoint. Your data is never used to train a model.
- What happens when Doram does not know?
- It refuses, names the missing piece, and asks the owner. It asks the user a short question when a question is ambiguous.
- How soon can we ask questions?
- As soon as the first facts are approved. Low-risk facts that pass validation go live automatically, so the first answers do not wait for a review. Answers get better as owners approve more drafts. Section 12 reports the time from connection to first answer.
- How much work is it for our data team?
- Onboarding creates a review queue, sorted so the most-used definitions come first. After that, the target is fewer than ten reviews per owner per week.
- Do we need a data warehouse or a data team?
- No. Doram also works on read replicas of production databases. It then relies more on the schema and asks your engineers more questions.
- We already have a semantic layer. Do we still need Doram?
- Doram imports definitions from dbt, LookML and BI tools as strong drafts, then adds the context around them: events, decisions, evidence, trust levels and access rules, for every AI tool.
- What happens when a definition changes?
- It becomes a new version. New answers use it, and old answers can still be replayed on the version that produced them.
- Can our own AI tools use the brain?
- Yes, through the API, with the same access rules. They can propose changes, which go through the same review.
- Can Doram run in our own cloud?
- Yes. On-premise, the whole product runs in your cloud account, and nothing leaves except calls to the LLM you chose.
Back matter
Glossary
- AI analyst
- The agent business users ask questions. It reads facts only through the retriever and SQL only from the semantic compiler.
- Approved
- A trust level. The fact is accepted by its owner, or approved automatically because the change is low risk.
- Certified
- The highest trust level. The owner has signed the fact off as the company's official definition.
- Company second brain
- Doram's name for the company's semantic context graph and the agents that keep it current.
- Context layer
- The layer that holds how things connect, and why: joins, rules, policies, goals, events and decisions.
- Context pack
- What the retriever returns: definitions, join path, rules, policies, events and decisions, each with evidence.
- Crawler agent
- The only agent that creates drafts. It reads the company's systems and drafts facts from evidence.
- Data-backed fact
- A fact built on the database, such as a metric or a join, which can be tested against real data.
- Discovered
- The lowest trust level. Found by the crawler agent, with evidence, but not checked yet.
- Evidence
- The source behind a fact: a query, a model, a document, a tool record or a person.
- Feedback agent
- The agent that learns from how people use the analyst, and turns repeated signals into drafts.
- Grain
- The level a metric is counted at, such as one invoice line or one customer per month.
- Knowledge fact
- A fact that cannot be tested against data, such as a decision, a policy or an event.
- Monitoring agent
- The agent that watches the sources for change and checks what each change affects.
- Ontology layer
- The layer that holds what things are: entities, columns and the words people use for them.
- Persona
- A group of users with its own scope, such as Finance or Sales.
- Question memory
- Approved questions and how each was resolved, used to answer similar questions.
- Retriever
- The only way to read the graph. It finds, ranks and resolves what a question needs.
- Semantic compiler
- The code that turns a context pack into one SQL query, with access rules added.
- Semantic context graph
- The graph of nodes and edges that holds what the company knows about its data.
- Semantic layer
- The layer that holds how things are counted: metrics and the ways to split them.
- Slot
- One part of a question: the metric, a split, a filter, the time range or the task.
- Trust level
- Discovered, validated, approved or certified. It decides whether a fact can be used in answers.
- Validated
- A trust level. A data-backed fact that ran on real data and matched a known number.
- Value index
- An index of column values, such as region names, used to turn words into filters.
- Workspace
- One customer's own graph, evidence and settings, in the region it chose.
Back matter
References
-
Lei, F. et al. Spider 2.0: Evaluating Language Models on Real-World Enterprise Text-to-SQL Workflows. ICLR 2025. arXiv:2411.07763
-
Chen, P. B. et al. BEAVER: An Enterprise Benchmark for Text-to-SQL. Version 3, May 2026. arXiv:2409.02038
-
Li, J. et al. Can LLM Already Serve as a Database Interface? A Big Bench for Large-Scale Database Grounded Text-to-SQLs (BIRD). NeurIPS 2023. arXiv:2305.03111
-
Sequeda, J., Allemang, D. and Jacob, B. A Benchmark to Understand the Role of Knowledge Graphs on Large Language Model's Accuracy for Question Answering on Enterprise SQL Databases. GRADES-NDA 2024. arXiv:2311.07509
-
Cormack, G. V., Clarke, C. L. A. and Büttcher, S. Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods. SIGIR 2009.
-
Gao, D. et al. Text-to-SQL Empowered by Large Language Models: A Benchmark Evaluation (DAIL-SQL). VLDB 2024. arXiv:2308.15363
-
Talaei, S. et al. CHESS: Contextual Harnessing for Efficient SQL Synthesis. 2024. arXiv:2405.16755
-
Rethinking Schema Linking: A Context-Aware Bidirectional Retrieval Approach for Text-to-SQL. 2025. arXiv:2510.14296