- The real risk of connecting an AI assistant to fund data is not that it invents a number. It is that it returns a real number for a question nobody asked, with no error to warn you.
- It happens when the system quietly ignores part of the request — a fund, a date range, an approval status. The total is correctly computed. It is just the total for the wrong set of records.
- A smarter model does not fix this, because the assistant was never told anything went wrong. Nor does a human reviewing the answer: it looks exactly like the right one.
- The fix is to write down what your data means — every field, every measure, every rule — and generate the AI’s interface from that, so an unanswerable question is refused rather than guessed.
- Those definitions are re-tested against the live database on every release, so they cannot quietly go stale and start giving confident instructions to do the wrong thing.
- Nothing changes about access. Read-only, inside the asking person’s own permissions, enabled per role, every query logged.
The bottom line: your team can put fund questions to the assistant they already use, and the answers carry the same definitions your finance team would apply by hand — or the assistant tells them it cannot answer.
Every fund platform that shipped an AI feature in the last two years solved the same first problem: how to let a language model reach the data. That part is now standard. A protocol, an OAuth flow, a set of read-only tools, and the assistant your team already uses can ask about your funds.
The second problem is much harder, gets much less attention, and is the one that will eventually produce a wrong number in front of a limited partner.
It is this: a query layer does not refuse a filter it does not recognise. It discards it and returns everything else.
No exception. No warning. HTTP 200, a well-formed result set, and an assistant that reports it as a filtered answer in a fluent, specific sentence.
The failure that produces no error
Here is the shape of it, at the wire level. Someone asks what one fund paid in management fees. The assistant composes a request with two conditions — the fund, and the type of ledger entry.
The fund condition was dropped because the attribute was not on the list of things that entity accepts as a filter. Everything else worked. The total is real, correctly computed, and returned without complaint — it is simply the total across every fund the user can see.
The assistant has no way to know. It asked for three conditions and got a successful response. It says: “Fund II paid $18.41m in management fees in 2025.”
Why this is worse than an error. An error is a bad afternoon. A silently discarded filter is a number that gets pasted into a board pack, because it looked exactly like the right answer and arrived faster than asking an analyst.
Three ways the same failure shows up
The dropped filter is the most consequential variant, but it is not the only one. All three below share a property: the request succeeds, so nothing surfaces.
| What the assistant sends | What actually happens | What the person is told |
|---|---|---|
A filter on an attribute that isn’t queryable — status on a capital call, when approval is held in a different field |
Condition discarded. Every row returns. | A total for “active calls” that is the total for all calls. |
A sort on a key the layer won’t honour — the physical column call_amount_cents rather than the queryable call_amount |
Sort discarded. Rows return in arbitrary order. | “The five largest capital calls” — which are five calls, not the five largest. |
| A request with no limit against a large table | Server caps the result at 500 rows. | “There are 500 remittances outstanding.” There are 2,300. |
Notice that a smarter model does not fix any of these. The assistant behaved correctly at every step. It asked a reasonable question, received a successful answer, and reported it. The information needed to know better was never in the response.
The model isn’t guessing. It’s being told, correctly, the answer to a question nobody asked.
This is not hallucination — which is why a better model won’t fix it
The industry has spent three years learning to worry about fabrication: a model inventing a figure that was never in the data. That is a real problem, and it is not this one.
Here the model fabricates nothing. It is perfectly faithful to what it was handed. The retrieval was wrong; the generation was an accurate report of a wrong retrieval. Every metric the AI industry uses to measure grounding would score this answer as correct.
Retrieval-augmented generation — the pattern behind essentially every “AI on your data” product since Lewis et al. formalised it in 2020 — rests on an assumption almost no one states out loud: that the retrieval step either returns what was asked for, or fails loudly. Ground a model in your documents and it stops making things up. Ground it in a query layer that discards conditions it doesn’t recognise and you have grounded it in something worse than nothing.
Grounding a model in a retrieval layer that silently drops your filters isn’t grounding. It’s a confident falsehood with a database behind it.
The research on querying databases in natural language points at the same gap from the other direction. The field’s first serious benchmark, Spider, used clean schemas and questions answerable from column names alone. Its successor, BIRD, was built on large, messy, real-world databases — and accuracy fell sharply. The telling detail is what BIRD had to add to make the task tractable at all: external knowledge evidence, shipped alongside each question, because the schema by itself did not carry enough meaning to answer correctly.
That external evidence is what a semantic layer is. A fund ledger is a BIRD database, not a Spider one — multi-currency, temporally qualified, full of columns whose names understate what they mean and fields whose correctness depends on the state of another field entirely.
And “a human checks it” is the weakest control you have
The instinctive answer is that someone reviews the number before it goes anywhere. Four decades of human-factors research says that is the control most likely to fail.
Lisanne Bainbridge set out the paradox in Ironies of Automation (1983): the more reliable an automated system becomes, the less practised its supervisor is at catching the failures it does have — and the more the supervisor’s attention drifts from the task they are meant to be checking. Parasuraman and Riley’s work on automation bias sharpened it: people accept a confident automated answer more readily than an equally confident human one, and they verify it less, precisely when it looks well-formed.
A fee total that arrives in eleven seconds, correctly formatted, with a plausible breakdown and a citation to your own system, is exactly the artefact this literature predicts will not be checked. The control has to sit before the answer is produced, not after.
You cannot review your way out of this. The answer that is wrong looks exactly like the answer that is right.
Where this idea comes from
A semantic layer is not a new invention, and that is a point in its favour — this is a proven architecture being pointed at a new consumer.
Analytics has had one for over a decade. LookML established the pattern in 2012: define dimensions, measures and joins once, above the warehouse, and let every chart inherit them so two dashboards cannot disagree about revenue. The metrics layer generation that followed — dbt’s Semantic Layer, Cube, Malloy — pulled those definitions out of the BI tool entirely so any consumer could use them. In enterprise software the same idea is sold as an ontology: a governed model of objects, properties and relationships sitting between raw tables and the applications — now the agents — that act on them. It is the central concept in Palantir’s platform, and it is why they talk about the ontology rather than the database.
What changes when the consumer is a model rather than a person is not the definitions. It is what has to happen when the request does not fit them. A dashboard has a human in front of it who notices an implausible chart. An agent has no such reader, so the layer needs three properties BI never had to provide:
- Refusal instead of best effort. Jon Postel’s robustness principle — be liberal in what you accept — built the internet and is exactly wrong here. A query layer that liberally ignores what it cannot parse is the entire failure described above. For an agent, a precise refusal is more useful than a permissive success, because the refusal is information it can act on.
- Illegal states made unrepresentable. Yaron Minsky’s formulation from typed programming — the best way to prevent an invalid value is to make it impossible to construct. Enumerating every valid sort key and aggregate in the tool’s JSON Schema does exactly that: modern assistants constrain their output to the schema, so the invalid option is never generated, not merely rejected afterwards.
- Contracts enforced by the build. The data contracts movement made the case that an interface between a producer and a consumer of data should be explicit, versioned and tested in CI rather than assumed. A semantic layer is a data contract whose consumer happens to be a language model — so the same discipline applies, and the same thing goes wrong without it.
Those three are what separate this from a document describing your data, and they are what the rest of this piece is about.
From theory to implementation
None of the ideas above are ours. What follows in the rest of this piece is each of them made concrete in a fund platform — and this is the map.
| Concept | What it holds | Where it lives in CapHive |
|---|---|---|
| Semantic layer LookML, 2012; dbt, Cube, Malloy |
Define entities, dimensions and measures once, above the store, so no two consumers disagree. | Sixteen definition files, one per entity, holding every field an assistant may touch and every measure it may quote. |
| Ontology The enterprise framing; central to Palantir’s platform |
A governed model of objects, properties and links that applications — and now agents — act through rather than around. | The relationship map in each definition: cardinality, the route from fund to folio to payment, and which tool resolves each hop. |
| External knowledge evidence BIRD text-to-SQL benchmark, 2023 |
Real databases cannot be queried correctly from the schema alone; the model needs the domain knowledge the columns omit. | The description and caveat on every field — that a status is meaningless until verified, that an amount excludes fees. |
| Grounding Retrieval-augmented generation, Lewis et al., 2020 |
Condition the answer on retrieved facts rather than model memory. | Every figure in an answer comes from a tool call against your data. Nothing is recalled from training. |
| Faithfulness is not correctness The grounded-generation literature |
An answer can be perfectly faithful to what was retrieved and still wrong, if the retrieval was. | Why the validation sits on the request, not the answer. Checking the output cannot catch this class at all. |
| Make illegal states unrepresentable Minsky; typed functional programming |
The strongest way to prevent an invalid value is to make it impossible to construct. | Sort keys, aggregate fields and grouping dimensions are enumerated in the tool’s JSON Schema, so an invalid one is never generated. |
| Parse, don’t validate Alexis King, 2019 |
Turn loose input into a known-good value once, at the boundary, and carry the guarantee inward. | The validator runs at the tool boundary. Past it, the query is known to be expressible — nothing downstream re-checks or silently repairs it. |
| Design by contract Meyer; preconditions as errors |
A caller must satisfy stated preconditions, and a violation is an error rather than undefined behaviour. | Tools over very large tables declare their preconditions — at least one filter, or aggregation only — and refuse a call that does not meet them. |
| Fail fast, not liberal Inverting Postel’s robustness principle |
Accepting what you cannot honour produces silent wrongness rather than resilience. | An unrecognised attribute returns an error naming the correct one. It is never dropped. |
| Data contracts in CI The data-contracts movement |
An interface between a data producer and consumer should be explicit, versioned and tested, not assumed. | 1,728 rules executed against the live schema on every build. A definition that has drifted stops the release. |
| Automation bias Parasuraman & Riley, 1997 |
People verify confident automated output less than equally confident human output. | Every control sits before the answer is produced. None of them depends on someone catching it afterwards. |
| Ironies of automation Bainbridge, 1983 |
The more reliable the system, the less able its supervisor is to catch the failures it still has. | Refusals are surfaced to the user, not swallowed. The boundary of what the system can answer stays visible instead of eroding. |
| Least privilege Saltzer & Schroeder, 1975 |
Every access runs with the minimum authority needed, authorised in its own right. | Each request executes inside the asking user’s own scope, gated again by role and client application, against a tool set with no write path. |
What we built instead
A semantic layer is a single, machine-readable definition of what your data means: every entity, every field, every measure, and every rule about how they may legitimately be combined. It is authored once, by people who know the difference between gross and net distributions, and it is the source that everything else is generated from.
Not documentation that sits beside the system. The system.
The map
Which entities exist and how they relate — a fund has commitments, a commitment has remittances, a remittance has payments — with the cardinality and the route between them recorded. The assistant no longer infers a join from column names, and it knows which tool resolves a name into an identifier before it starts.
The fields
Every attribute that may be filtered, sorted or totalled, with its type, its unit, its currency basis, the comparisons it accepts, and the values it can hold. Critically, it also records the fields that look available and are not, and why — the physical column behind a formatted amount, the value computed after the query that can never be filtered, the attribute that exists on a related record rather than this one.
The measures
What called capital, outstanding, DPI and total distributed mean at your firm, written as formulas over those fields, with the caveats attached. The assistant uses your definitions rather than inventing a plausible one from the column names — and a plausible one is exactly what it will invent, every time, if you don’t give it yours.
Ask a model what “total distributed” means and it will give you a plausible answer. Plausible is the whole problem.
What a definition actually says
This is not an abstraction. It is a text file your team can read, and here are three fragments of the one that governs capital remittances.
Three pieces of finance knowledge that no model could infer from a schema, and that every answer about payment status now depends on. A firm that reconciles differently edits the file; the change is reviewed like any other control, and it applies to every assistant, every question, from that moment on.
Three questions, walked through
The same three requests, before and after.
“What did Fund II pay in management fees last year?”
Ungoverned
$18,412,000The fund condition is discarded. The figure is the ledger total across all six funds, reported as one fund’s.
With the semantic layer
$3,940,000The fund filter is part of the published contract for that entity, and the build check proves it still works against the live schema before the code ships.
“Show me the five largest capital calls.”
Ungoverned
Five callsThe assistant sorts by the column name it can see. That key isn’t sortable, the sort is dropped, and five rows come back in whatever order the database chose. None of them is necessarily large.
With the semantic layer
The five largestEvery valid sort is enumerated in the input schema, so the invalid key cannot be expressed. If one arrives anyway, it is refused with the correct key named.
“What’s our total committed capital?”
Ungoverned
One numberUS dollars, euros and rupees added together, then labelled with whichever currency the first fund happened to use. Feeder and master funds both counted, so the capital that flows between them appears twice.
With the semantic layer
One per currencyTotals come back grouped by currency. The feeder-to-master relationship is declared, so the assistant is told to choose feeders or masters before totalling — and says which it chose.
The traps a definition closes
None of these are model failures. They are the questions a careful analyst asks before answering, and that an assistant reading raw column names has no way to know it should ask.
| The trap | What an ungoverned answer does | What the definition enforces |
|---|---|---|
| Mixed currencies | Adds USD, EUR and INR into one “total committed capital”. | Totals are grouped by currency and returned separately, or the assistant is told to pick one fund first. |
| Gross versus net | Reports gross distributions — before carry and fees — as what investors received. | Both are defined, DPI is pinned to the net figure, and adding the two is flagged as double counting. |
| Feeder and master funds | Sums every fund row, counting capital that flows through a feeder into its master twice. | The relationship is declared and totals require choosing feeders or masters. |
| Unapproved records | Includes calls and distributions that were never issued in “called to date”. | Approval is a first-class field, and the definition names the questions that need it filtered. |
| Unverified payments | Quotes an unreconciled remittance status as settled fact. | The field records that its status is meaningless until verified, so the assistant filters first. |
| Running totals | Sums cumulative ledger lines alongside period lines, inflating every figure. | The two are named and a ledger total requires choosing one. |
| Stale valuations | Quotes a fair value without noting it was struck eleven months ago. | The valuation date travels with the value, and system-derived valuations are excluded unless asked for. |
| Silent truncation | Reads the first 500 rows, counts them, reports “500”. | Row caps are declared, and counting questions are routed to a database count instead. |
Every line in that table is a question your finance team already knows to ask. The assistant doesn’t — until somebody writes it down.
Five gates every request passes
Correctness is one layer of this. The others were already there, and the semantic layer sits inside them rather than around them.
-
The input schema
Sort keys, aggregate fields and grouping dimensions are enumerated, not free text. A client that respects the schema — all the major ones do — cannot compose an invalid option in the first place.
Stops the request being formed -
The request validator
Anything that arrives anyway is checked against the definition before it reaches the database. The refusal names the attribute that was wrong and the one to use instead, so the assistant corrects itself on the next call rather than exploring.
Stops the request being served -
The asker’s permission scope
Every request runs as the person who asked it, inside the same authorisation scope that governs the web application. An analyst’s assistant sees exactly what the analyst sees. Access is separately gated at organisation, role and client-application level.
Stops data they could not otherwise reach -
The row cap
Results are capped server-side when no limit is supplied — prompt instructions are not a reliable guardrail. The cap is declared in the definition, so the assistant knows a count must come from a database count rather than the length of a list.
Stops an unbounded read -
Read-only by construction
There is no write path to disable, because none was built. All 22 tools read. Every call is logged and attributable to a user.
Stops any modification
A definition that drifts is worse than none
Written guidance about data always rots. Someone renames a field, and the document that says otherwise becomes a set of confident instructions to do the wrong thing — now with the authority of having been written down.
So the definitions are not trusted. They are tested.
On every build, every filter, every sort key, every aggregate and every grouping dimension the definitions advertise is executed against the live database. Anything that no longer resolves — or that the database would quietly ignore — fails the build. It cannot reach a customer’s assistant, because it cannot reach production.
| What is verified | Count | How |
|---|---|---|
| Filter conditions | 1,414 | Each is executed; the generated SQL is compared before and after to prove the condition was actually applied and not discarded. |
| Sort options | 314 | Each must produce an ordering clause. A key that is silently ignored fails. |
| Aggregate and grouping fields | Every one | Must resolve to a real column, since a total is computed in the database rather than in memory. |
| Predefined filters | Every one | Read from the code that implements them, so the published list cannot drift from the real one. |
| Cross-references | Every one | A definition that tells the assistant to call another tool must name a tool that exists. |
What that check prevents
A filter that no longer binds to the column it names. A sort key the database will accept and ignore. A total that no longer resolves. Each of those is a wrong answer with nothing to warn you, and each is caught the moment it appears — because the definitions are not read, they are executed.
That is a stronger guarantee than review can offer. Nobody reading a definition file can tell whether a field still filters; the database can, and it is asked before every release. A definition that has drifted does not reach a customer’s assistant, because it does not reach production.
Ambiguity gets found by a build, or it gets found by a limited partner reading a wrong number. Those are the only two options.
For your technical team
The semantic layer is a set of versioned, human-readable definition files held in the CapHive codebase — one per entity — reviewed through the same process as application code. Each generates three artefacts at load time: the assistant-facing description of the entity, the JSON Schema for the tool’s arguments, and the validator every request passes through. Because all three come from one file, they cannot diverge from one another; because the build check executes them against the live schema, they cannot diverge from the database.
Connection is over the Model Context Protocol with OAuth 2.0. The AI client registers itself dynamically (RFC 7591) and authorises with PKCE, so no client secret ever exists to be transmitted, stored or leaked. Tokens are scoped to the individual user; disabling an account ends AI access at the same moment it ends web access. Access is gated at four levels — platform, organisation, role, and named client application — and every tool call is logged individually.
Queries reach the database through the same authorisation policies as the web application. There are no write tools. Result sets are capped server-side. Aggregations are computed in the database rather than by loading rows, and money aggregates are grouped by currency wherever the entity has an unambiguous one — where it does not, the result is explicitly marked as unresolved rather than presented as a trustworthy total.
Questions
Isn’t this just a better system prompt?
No, and the difference is the whole point. A prompt is advice the model may follow. The definition also generates the tool’s input schema — so an invalid option cannot be composed — and the validator every request passes through — so an invalid request is refused before it reaches the database.
Two of the three enforcement points do not depend on the model cooperating. A prompt-only approach has none.
Isn’t this the hallucination problem? Doesn’t better grounding solve it?
It is the opposite of hallucination, which is what makes it dangerous. The model invents nothing — it reports faithfully on what it retrieved. Every measure of grounding would score the answer as correct, because the answer is correct with respect to the data it was given.
Grounding tells you the answer came from your data. It says nothing about whether the retrieval understood the question. That second gap is the one a semantic layer closes, and no amount of better grounding closes it.
Does this make answers slower or more expensive?
Faster, in practice. Validation is an in-memory check against a definition and costs nothing measurable.
The saving comes from removing round trips. An assistant that guesses a field name and gets an empty list will try three more variations. An assistant that gets told “that attribute doesn’t exist; use this one” corrects on the next call.
Who writes the definitions — do we have to?
CapHive ships definitions covering the whole platform, so nothing is required of you to start.
Where your firm’s conventions differ — what you mean by outstanding, whether a recallable distribution counts toward DPI, which fee categories belong in a management fee total — those are yours to set. It is a text edit, reviewed like any other policy change, and it applies to every assistant from that moment on.
That conversation is worth having regardless of AI. Most firms discover that two teams have been answering the same question two different ways.
What happens when you add a field or rename one?
The build check fails, and the release stops. A renamed field whose definition still names the old one is exactly the drift this is designed to catch, and it is caught before the code ships rather than by a customer.
Adding a queryable field to the platform does not automatically expose it to assistants. It appears when the definition says it does — which is deliberate, so the AI surface is a decision rather than a side effect.
Can the assistant still get an answer wrong?
It can still reason badly about numbers it was given correctly — no vendor can honestly claim otherwise, and be sceptical of one who does.
What it can no longer do is receive a confidently wrong dataset. The class of error where the filter was ignored, the sort was dropped, or two currencies were added together is closed. That is the class that produces answers no one can catch by reading them.
Does this work with ChatGPT and Gemini, or only Claude?
All three, and anything else that speaks the Model Context Protocol. The definitions generate a standard interface, so one definition serves every assistant your firm uses today and any it adopts later, with no work on your side when that changes.
Can an AI assistant change anything in our data?
No. Every one of the 22 tools reads. There is no create, update or delete path to disable, because none was ever built — which is a stronger property than a write path that is switched off.
Can an assistant see data the person asking cannot?
No. Every request runs inside the requesting user’s own authorisation scope — the same one that governs what they see in the web application. An analyst’s assistant sees an analyst’s data.
Beyond that, an administrator enables AI access per role and, if they choose, restricts it to named client applications. Every call is logged and attributable.
What happens when someone asks something the definitions don’t cover?
The assistant is told, specifically, that the thing it reached for is not available and what the nearest available field is. It then either answers a narrower question honestly or says it cannot answer.
A refusal is a good outcome here. The alternative — the one this replaces — is a plausible number with nothing behind it.
How is this different from a BI semantic layer, or giving the AI SQL access?
A BI semantic layer defines measures for a dashboard a person reads. It assumes a human chose the filters and will notice if a chart looks wrong. Neither assumption holds when the consumer is a model composing its own queries.
Direct SQL is worse still: it hands the model the entire schema with none of the meaning, so every trap on this page is live at once, and a mistake is a query against your production database rather than a refused tool call.
This is a semantic layer built for a caller that will not notice it was misunderstood — which is why refusal, and a build-time proof that the definitions are still true, matter more here than they do in BI.
See it against your own structure
The fastest way to judge this is to ask your own hard question — the cross-fund, multi-currency one that usually needs an analyst and a workbook — and watch what comes back, including what the assistant refuses to do.
Worth doing even if you go no further: it makes the AI governance conversation concrete in a way a policy document never manages.