Thirty-two data questions were put to the chat during the rig-loop spike (#548), against the eight-document AI-companies base, on DeepSeek-V3 and Qwen2.5-72B. Where the answer came from:
| question |
runs |
from the graph |
from text search |
from model memory |
stopped at a lookup ("which OpenAI?", or gave up) |
| the relation between OpenAI and Anthropic |
16 |
3 (four or more calls each) |
3 |
4 |
6 |
| OpenAI's timeline |
9 |
3 |
0 |
1 |
5 |
| the board at Altman's removal |
7 |
5 |
0 |
1 |
1 |
The relation question is the graph question, and the graph answered it three times in sixteen, each time after the model had pulled OpenAI's facts and Anthropic's facts and joined them in its own context. Qwen tried that route three times, six or seven graph calls each, and concluded the base lacks the information. The base has it, two hops long: OpenAI —employee→ Dario Amodei —founder→ Anthropic.
The graph tools the model has are a lookup and a dump:
find_entities returns every entity whose name contains the string, flat, with a fact count: fourteen for "OpenAI". Twelve of thirty-two turns ended right there, asking the user which one.
entity_facts returns every fact of one entity, with no filter except the time axes and no cap. "OpenAI's timeline" was one tool result of 433 lines and 26,384 characters, which the model then ordered by hand.
- Nothing walks an edge: no neighbourhood, no path, no ordered events.
What the model receives also costs more than it says. Half the facts open with attested 2026-09-06T12:23:58.175078Z, the ingest instant to the microsecond; the four board members are comprised … ended by 2026-09-06T12:23:58.175078Z, which reads as the whole board resigning at the second of ingest (it is what the store records for "ended, date unknown"); predicates arrive as extracted (can remove, interactAction, comprised, employee, works_for for one relation).
The interface
Typed operations, no query language. The model and MCP share tools_schema, so both get them.
paths_between(a, b, max_hops ≤ 3, at?, as_of?) — the one real multi-hop. Returns chains, not node sets, shortest and most specific first, at most ten; a hub (an entity over a fact-count threshold) is not traversed past the first hop. With at, every edge on the path must hold at that moment; with as_of, as the base had it then. This puts "the relation between A and B" on the graph in one call, and it is the query a plain graph cannot answer the same way: the two clocks are ours.
neighbors(entity, predicate?, type?, at?) — one hop, grouped by predicate. Further hops are the model's to take, one call each.
timeline(entity, since?, until?) — the entity's facts that have a world-time position, in order.
find_entities ranked by type and fact count, with the disambiguator, without untyped phrase entities; when one candidate clearly dominates, it says so.
entity_facts with predicate, object_type, since/until filters and a cap; output grouped by predicate; an ingest-time anchor rendered as attested / ended by without the instant, and the ended by <ingest> shape shown for what it is.
Not a k-hop neighbourhood expansion as a tool result: OpenAI is 555 facts at one hop.
Acceptance, end to end
The harness from #548 (ask.sh, the scratch base, twelve fresh data questions: relation ×3, relation-again ×3, timeline ×3, board ×3), on DeepSeek-V3 and Qwen2.5-72B:
- the relation question answered from the graph with the path in the answer, on at least five of six runs per model (today three of sixteen overall);
- the timeline answered from
timeline, with no tool result over a few KB;
- the board question answered with
at;
- turns that end on "which OpenAI?" halve;
- every stored answer keeps
steps/sources/tool_exchange; MCP lists the new tools; the record axis reaches every new tool.
The raw tool results go in the PR, as the model sees them.
Related: #546, #548 (the loop this was measured on), #547 (the mark for answers without sources); the extraction side is #559 and #560.
Thirty-two data questions were put to the chat during the rig-loop spike (#548), against the eight-document AI-companies base, on DeepSeek-V3 and Qwen2.5-72B. Where the answer came from:
The relation question is the graph question, and the graph answered it three times in sixteen, each time after the model had pulled OpenAI's facts and Anthropic's facts and joined them in its own context. Qwen tried that route three times, six or seven graph calls each, and concluded the base lacks the information. The base has it, two hops long: OpenAI —employee→ Dario Amodei —founder→ Anthropic.
The graph tools the model has are a lookup and a dump:
find_entitiesreturns every entity whose name contains the string, flat, with a fact count: fourteen for "OpenAI". Twelve of thirty-two turns ended right there, asking the user which one.entity_factsreturns every fact of one entity, with no filter except the time axes and no cap. "OpenAI's timeline" was one tool result of 433 lines and 26,384 characters, which the model then ordered by hand.What the model receives also costs more than it says. Half the facts open with
attested 2026-09-06T12:23:58.175078Z, the ingest instant to the microsecond; the four board members arecomprised … ended by 2026-09-06T12:23:58.175078Z, which reads as the whole board resigning at the second of ingest (it is what the store records for "ended, date unknown"); predicates arrive as extracted (can remove,interactAction,comprised,employee,works_forfor one relation).The interface
Typed operations, no query language. The model and MCP share
tools_schema, so both get them.paths_between(a, b, max_hops ≤ 3, at?, as_of?)— the one real multi-hop. Returns chains, not node sets, shortest and most specific first, at most ten; a hub (an entity over a fact-count threshold) is not traversed past the first hop. Withat, every edge on the path must hold at that moment; withas_of, as the base had it then. This puts "the relation between A and B" on the graph in one call, and it is the query a plain graph cannot answer the same way: the two clocks are ours.neighbors(entity, predicate?, type?, at?)— one hop, grouped by predicate. Further hops are the model's to take, one call each.timeline(entity, since?, until?)— the entity's facts that have a world-time position, in order.find_entitiesranked by type and fact count, with the disambiguator, without untyped phrase entities; when one candidate clearly dominates, it says so.entity_factswithpredicate,object_type,since/untilfilters and a cap; output grouped by predicate; an ingest-time anchor rendered asattested/ended bywithout the instant, and theended by <ingest>shape shown for what it is.Not a k-hop neighbourhood expansion as a tool result: OpenAI is 555 facts at one hop.
Acceptance, end to end
The harness from #548 (
ask.sh, the scratch base, twelve fresh data questions: relation ×3, relation-again ×3, timeline ×3, board ×3), on DeepSeek-V3 and Qwen2.5-72B:timeline, with no tool result over a few KB;at;steps/sources/tool_exchange; MCP lists the new tools; the record axis reaches every new tool.The raw tool results go in the PR, as the model sees them.
Related: #546, #548 (the loop this was measured on), #547 (the mark for answers without sources); the extraction side is #559 and #560.