Insights · 2026-10-18 · 6 min read
The graph stays deterministic. The model works at its edges.
We read thirteen papers on putting language models into knowledge graphs, and the most useful thing they did was tell us what not to build. Then one day of changes: edges that know when a tie ended, a graph that can see how buyers pay, and an answer that says "did you mean".
Our trade knowledge graph has one property we guard more carefully than any other: every edge in it traces to a row. A supplier-to-buyer relationship comes from a relationship record. A director comes from a registry report. An invoice comes from an invoice. Nothing in the graph was guessed, and when a related-party pattern surfaces, an analyst can follow it back to the document that put it there. The obvious question in 2026 is whether a language model should be building that graph instead. We spent a day finding out, and the answer shaped what we built the same afternoon.
What thirteen papers settled
We read the two overview papers that make the case for "synergistic" language-model and graph development, then the thirteen primary papers behind them, in full. The most useful thing the reading did was close doors.
Letting a model induce the schema from text is a real research line, and it is wrong for a financial graph: our schema is the domain, and schema drift in a structure that feeds credit decisions is a liability dressed up as adaptability. Building a model-extracted graph over documents is also a real line, and the one careful empirical comparison we found, twelve methods on eleven datasets under one codebase, says plainly that for factual questions keeping the original text beats an expensive extracted graph, and that the one graph method which wins on abstract questions costs fifty-seven times the time and two hundred and ten times the tokens of ordinary retrieval. Interactive agents that walk the graph step by step are accurate and cost around a dozen model calls and seven thousand tokens per question. The one technique that guarantees a model's reasoning path exists in the graph needs control of the model's decoding and a fine-tune, neither of which an API gives us.
What survived was narrower and better. A production system at a large consumer company extracts facts from narrative text into a fixed ontology, checks each fact with a second, cheaper model, and holds itself to a weekly human audit that it must pass to stay in production. A graph memory engine stamps every edge with when a fact became true, when it stopped, and when the system learned each. And the entity-matching literature agrees on one shape for the cases a deterministic matcher cannot settle: show the model a short list of real candidates and a none-of-the-above option, never ask it yes or no per pair, and let it abstain.
Those three ideas became a design rule, a schema change and a held proposal.
The rule
A fact a model derives from narrative text lives in its own family of relationships, carrying its source, the passage it came from, the time it was extracted and a confidence. It never merges onto the deterministic projection, and every screen that shows it says so. We already had this rule for agency data, which never merges onto a company's own record; the graph now has the same rule in writing. The reason is simple: the day a news-sourced directorship lands in the same family as a registry-sourced one, nothing distinguishes them, and the whole family stops being evidence.
The defect the reading exposed
Reading the memory-engine paper against our own schema turned up a gap we had been carrying since July. A director who resigned stayed an open edge in the graph forever. The agency report that recorded the resignation updated the company's record, but the graph kept the old tie, so a related-party chain could run through an appointment that had ended, and nothing could reconstruct a chain as it stood on the day a credit decision was taken. Our data lake already insists that every feature used in a decision be computed as of the decision date. The graph could not honour that.
Every time-varying edge now carries four timestamps: the interval the tie held in the world, and the interval the graph asserted it. A later report that omits a director closes the edge at the report's date; it never deletes it. A report that lists a person again after a gap opens a new tenure. A report that carries no list at all asserts nothing, because an absent list is a gap, not an empty board. The related-parties read takes an as-of date and reconstructs the chain for that day, and the current view shows only open ties, which means a resigned director no longer appears, which is the correct reading and is also a change.
The statements that do this live in one place and are shared by the live consumer and the rebuild script, so a rebuild replays the reports in order and arrives at the same history the live path produced.
The change no paper suggested
The richest signal in a supply-chain-finance graph is how buyers actually pay, and our graph could not see it. The finance domain had been emitting three events for months, a receipt from a debtor, a disbursement to a client, and a disbursement that bounced, and the graph consumed none of them. No paper pointed at this. The founder did, in one sentence, while we were discussing something else.
The graph now takes all three onto the invoice: amount received, the number of receipts, days from issue to payment computed from the bank's own timestamp rather than ours, the moment an advance was financed, and every failed disbursement with its reason. The three origination queries carry the new figures. A buyer that anchors a payables programme now shows its observed days to pay beside its contractual terms; a factoring prospect shows how fast its debtors have settled; a growth candidate shows its settled history and any disbursement failures. An invoice nobody has paid shows no days-to-pay figure at all, rather than a zero, because a zero would read as a buyer who pays on the day.
The small one
A question to the graph that names a company nobody can find used to return an honest dead end. It now returns the nearest real names in the network, deterministically, so the answer can say "did you mean" instead of only "not in your network". One of the papers we read treats an empty result as its own state that deserves its own handling. It was right, and it cost nothing.
What the first run found
Everything above shipped with its end-to-end suites extended, run against the real stack. The first run caught two defects in the new code before anything reached the main branch. The statement that closes a superseded edge had been sent through a read-only database session, which the database refused, so every registry event was being recorded as poison after its upsert had already applied. And the identifier of the superseding report reached the edge as a decimal because of how the driver treats a plain number. Both were fixed in minutes. We mention them because a day's work that reports no defects is a day whose tests did not run.
What we held back
Three further changes came out of the same reading and are designed but not built: a check that extracts the claims in an analyst's recommendation and tests them against the graph; an adjudicator that settles the ambiguous residue of entity matching by choosing from a short list, with abstention; and the extractor for narrative sources, with its ontology, its grounding check and its audit gate. Each makes metered model calls. Each waits for a signature, because a path that spends money is wired on a decision, not on momentum.
And one conclusion stands above the rest. For a production funder the models that score risk will be conventional learners over the lake's decision records and as-of features, with the graph supplying features: concentration, shared controllers, payment behaviour, position in the buyer network. The language model's place is at the graph's edges, resolving what the rules cannot and reading what only prose contains. The structure itself stays deterministic, and today it also knows what time it is.
