Graph engineering: model the edges first
Most business questions that seem hard are relationship questions being asked of a schema that only stores things. Modelling the connections first is what makes them ordinary.
Vector search finds passages that sound like the question. Plenty of real questions are not answered by any single passage — and no amount of better embeddings fixes that.
· 6 min read· Closing Brackets
A support agent grounded in a company's documentation answers policy questions well and account questions badly, and the team's instinct is to blame the embeddings. They try a larger model, a smaller chunk size, a reranker. The numbers move a little. The failure mode does not change, because it was never a retrieval quality problem.
"Is this customer affected by the outage?" is not answered by any document. It is answered by walking from the customer to their subscribed services, from those services to the incident, and checking whether the intersection is non-empty. No passage in the corpus contains that sentence, so no similarity search can return it.
Before building any grounded system, it is worth sorting the real questions into two piles. The test is simple: could a single well-written document answer this?
The dangerous case is not the question retrieval cannot answer. It is the one it answers plausibly.
The pattern that works is not choosing between them. Retrieval is very good at one thing traversal is bad at — turning an ambiguous natural-language question into a specific entity to start from. Traversal is very good at the thing retrieval cannot do at all. So: retrieve to locate, traverse to gather, then generate over the gathered neighbourhood.
const seeds = await vectorSearch(question, { k: 5 });
const anchor = await resolveEntity(seeds); // passages → a node
const hood = await graph.traverse(anchor, {
edges: ["subscribes_to", "reported", "billed_for"],
depth: 2,
since: "90 days",
});
return generate(question, hood); // grounded in structure, not similarityThe context handed to the model is now a small, connected, explicable subgraph rather than five passages that scored well. It is also dramatically smaller, which matters more than it sounds — a tight, relevant context is worth more than a large one, and it costs less.
The first is anchor resolution. If the retrieval step lands on the wrong entity, everything after it is confidently wrong about a different customer. Anchors need a confidence threshold and a defined behaviour below it — asking which of two accounts was meant is a far better outcome than guessing.
The second is neighbourhood explosion. In any real graph some nodes are enormous — a shared service, a popular product, a support macro. A depth-two traversal through one of those returns half the database. Cap the fan-out per hop, order neighbours by edge weight or recency, and treat hub nodes as terminal rather than as things to expand.
Both are ordinary engineering problems with ordinary engineering answers, which is rather the point. Once the relationships are modelled, the hard part of grounding a system in your business stops being a research question and becomes a query you can read.
Most business questions that seem hard are relationship questions being asked of a schema that only stores things. Modelling the connections first is what makes them ordinary.
Describe it and we will tell you what we would do about it. One business day, no charge for the first answer.