Repository navigation
query retrieval quality degrades as the graph grows, because the output budget is a fixed 2000 tokens #2530
Description
As a graph grows, query finds proportionally more nodes but prints the same number, so the
share of the answer you actually see falls steadily. Nothing about the invocation changes — the
same question, on a better graph, returns a worse answer. Measured on 0.9.34 (PyPI package
graphifyy), Windows, Python 3.13; the relevant code is unchanged in 0.9.35 (version note at
the end).
The mechanism
budget = 2000 is a constant in cli.py, and _query_graph_text / _subgraph_to_text default
to token_budget = 2000 with char_budget = token_budget * 3. It does not vary with the size
of the graph or of the found set.
Self-contained repro
Generate graphs of increasing size with an identical shape — one seed, K mid nodes, 45 leaves
each. Every non-seed node keeps degree 46, deliberately below the hub threshold (see below), so
nothing is pruned as a hub and the growth is purely "a larger relevant neighbourhood":
import json, os
LEAVES = 45
def build(k, path):
os.makedirs(os.path.join(path, "graphify-out"), exist_ok=True)
nodes = [{"id": "core", "label": "CoreService", "source_file": "core.py",
"source_location": "L1", "community": 0}]
links = []
for i in range(k):
mid = "mid%03d" % i
nodes.append({"id": mid, "label": "MidModule%03d" % i,
"source_file": "mid%03d.py" % i, "source_location": "L1", "community": 0})
links.append({"source": "core", "target": mid, "relation": "uses"})
for j in range(LEAVES):
leaf = "leaf%03d_%02d" % (i, j)
nodes.append({"id": leaf, "label": "Leaf%03d_%02d" % (i, j),
"source_file": "leaf%03d_%02d.py" % (i, j),
"source_location": "L1", "community": 0})
links.append({"source": mid, "target": leaf, "relation": "uses"})
json.dump({"directed": False, "multigraph": False, "graph": {},
"nodes": nodes, "links": links},
open(os.path.join(path, "graphify-out", "graph.json"), "w"))Then run graphify query "CoreService" in each:
| graph nodes | nodes found | printed at default | share printed | printed at --budget 8000 |
|---|---|---|---|---|
| 93 | 93 | 93 | 100.0 % | 93 |
| 231 | 231 | 109 | 47.2 % | 231 |
| 461 | 461 | 109 | 23.6 % | 436 |
| 921 | 921 | 109 | 11.8 % | 437 |
| 1841 | 1841 | 110 | 6.0 % | 437 |
The found set grows 20x; the printed set stays flat at ~109.
The same thing on a real graph
Our prose graph over two weeks, taken from git history of graphify-out/graph.json:
| date | nodes | edges |
|---|---|---|
| 2026-07-24 | 585 | 1167 |
| 2026-07-31 | 1478 | 3324 |
| 2026-08-04 | 2588 | 5701 |
| 2026-08-06 | 4139 | 10043 |
7x node growth in 13 days, while the default budget stayed 2000 tokens. Replaying 121 real
questions from our own query log against today's graph, at the default budget:
- 110 of 121 are truncated
- median share of found nodes actually printed: 28.8 %
- for the 11 questions that find 300+ nodes, median share printed: 11.6 % (worst 5.2 %)
- median printed count: 44 at the default budget, 152 at
--budget 8000
The printed count is essentially constant at ~40-50 regardless of whether the question found
60 nodes or 763.
Hub blocking is already there and is not the missing piece
I want to be explicit about this, because it is the obvious first hypothesis and it is wrong.
_bfs already declines to expand through high-degree nodes:
degrees_sorted = sorted(degrees)
p99_idx = int(len(degrees_sorted) * 0.99)
hub_threshold = max(50, degrees_sorted[p99_idx])
...
if n not in seed_set and G.degree(n) >= hub_threshold:
continueOn our graph p99 degree is 35, so the threshold floors to 50, and 29 nodes (0.7 %) are excluded
as transit. I tested the hypothesis directly by ablating the single highest-degree node — degree
966, about 10 % of all edges — and re-asking 25 questions that each find 100+ nodes:
- mean share printed, full graph: 27.5 %
- mean share printed, top hub removed: 27.9 %
Effectively no change. The flood is not coming from hubs; it is the ordinary depth-2
neighbourhood in a graph that has simply got denser. So this is not a traversal-pruning bug —
it is that the output budget does not scale with what the traversal legitimately found.
What the banner already does, and why it is not enough
Credit where due, the truncation banner is informative:
[!] TRUNCATED: showing 46 of 524 nodes (~2000-token budget). The answer may be among the
478 cut nodes - raise the token budget (CLI: --budget) or narrow the query
(e.g. context_filter=['call'], or get_node for a specific symbol).
It already says the answer may be in the cut portion. Three reasons that does not close the gap:
- It is advice, not a default. The default stays wrong, and the overwhelmingly common case is
a caller who never passes--budget. On our corpus the default is wrong for 110 of 121 real
questions. - The recommended narrowing does not work on LLM-extracted graphs. Following
context_filter=['call']on our graph takes 524 found nodes down to 8, because almost no
edge carries acontextattribute. I have filed that separately. - The banner also prints when nothing was cut —
showing 30 of 30 nodes ... The answer may be among the 0 cut nodes— which trains the reader to ignore it.
Request
- Scale the default budget with the size of the found set or the graph — for example a
floor of 2000 with growth in the number of nodes found, capped at something sane. The
information needed is already available at the point the banner is printed. - Failing that, at minimum suppress the banner when nothing was cut, and drop the
context_filtersuggestion unless the graph actually hascontextattributes on its edges.
Version note
Measured on 0.9.34. The 0.9.35 diff does not touch this path: cli.py is byte-identical
between the two tags, so the budget = 2000 default is unchanged, and serve.py's only changes
are to context hints and seed selection, leaving _subgraph_to_text, _cut_lines_to_budget
and the BFS/hub logic untouched. The MCP tool schema still declares "token_budget": {"default": 2000}.
Happy to test a branch. We have a 4139-node LLM-extracted graph and a log of 336 real queries I
can replay to give you a before/after distribution of "share of found nodes printed".
Activity