/graph-query
Query a pre-built knowledge graph using BFS or DFS traversal, explain individual nodes, or find shortest paths between concepts.
Prerequisite: A graph must already exist at
graphify-out/graph.json. If it doesn't, run/graph-build <path>first to build it.
Usage
/graph-query "<question>" # BFS traversal — broad context
/graph-query "<question>" --dfs # DFS — trace a specific path
/graph-query "<question>" --budget 1500 # cap answer at N tokens
/graph-query explain "<node name>" # plain-language explanation of a node
/graph-query path "<NodeA>" "<NodeB>" # shortest path between two concepts
For /graph-query (BFS/DFS traversal)
Two traversal modes — choose based on the question:
| Mode | Flag | Best for |
|---|---|---|
| BFS (default) | (none) | "What is X connected to?" - broad context, nearest neighbors first |
| DFS | --dfs |
"How does X reach Y?" - trace a specific chain or dependency path |
First check the graph exists:
python -c "
from pathlib import Path
if not Path('graphify-out/graph.json').exists():
print('ERROR: No graph found. Run /graphify <path> first to build the graph.')
raise SystemExit(1)
"
If it fails, stop and tell the user to run /graphify <path> first.
Load graphify-out/graph.json, then:
- Find the 1-3 nodes whose label best matches key terms in the question.
- Run the appropriate traversal from each starting node.
- Read the subgraph — node labels, edge relations, confidence tags, source locations.
- Answer using only what the graph contains. Quote
source_locationwhen citing a specific fact. - If the graph lacks enough information, say so — do not hallucinate edges.
python -c "
import sys, json
import networkx as nx
from graphify.build import build_from_json
from pathlib import Path
data = json.loads(Path('graphify-out/graph.json').read_text())
G = build_from_json(data)
question = 'QUESTION'
mode = 'MODE' # 'bfs' or 'dfs'
terms = [t.lower() for t in question.split() if len(t) > 3]
# Find best-matching start nodes
scored = []
for nid, ndata in G.nodes(data=True):
label = ndata.get('label', '').lower()
score = sum(1 for t in terms if t in label)
if score > 0:
scored.append((score, nid))
scored.sort(reverse=True)
start_nodes = [nid for _, nid in scored[:3]]
if not start_nodes:
print('No matching nodes found for query terms:', terms)
sys.exit(0)
subgraph_nodes = set()
subgraph_edges = []
if mode == 'dfs':
# DFS: follow one path as deep as possible before backtracking.
# Depth-limited to 6 to avoid traversing the whole graph.
visited = set()
stack = [(n, 0) for n in reversed(start_nodes)]
while stack:
node, depth = stack.pop()
if node in visited or depth > 6:
continue
visited.add(node)
subgraph_nodes.add(node)
for neighbor in G.neighbors(node):
if neighbor not in visited:
stack.append((neighbor, depth + 1))
subgraph_edges.append((node, neighbor))
else:
# BFS: explore all neighbors layer by layer up to depth 3.
frontier = set(start_nodes)
subgraph_nodes = set(start_nodes)
for _ in range(3):
next_frontier = set()
for n in frontier:
for neighbor in G.neighbors(n):
if neighbor not in subgraph_nodes:
next_frontier.add(neighbor)
subgraph_edges.append((n, neighbor))
subgraph_nodes.update(next_frontier)
frontier = next_frontier
# Token-budget aware output: rank by relevance, cut at budget (~4 chars/token)
token_budget = 2000 # default; override with --budget N
char_budget = token_budget * 4
def relevance(nid):
label = G.nodes[nid].get('label', '').lower()
return sum(1 for t in terms if t in label)
ranked_nodes = sorted(subgraph_nodes, key=relevance, reverse=True)
lines = [f'Traversal: {mode.upper()} | Start: {[G.nodes[n].get(\"label\",n) for n in start_nodes]} | {len(subgraph_nodes)} nodes']
for nid in ranked_nodes:
d = G.nodes[nid]
lines.append(f' NODE {d.get(\"label\", nid)} [src={d.get(\"source_file\",\"\")} loc={d.get(\"source_location\",\"\")}]')
for u, v in subgraph_edges:
if u in subgraph_nodes and v in subgraph_nodes:
d = G.edges[u, v]
lines.append(f' EDGE {G.nodes[u].get(\"label\",u)} --{d.get(\"relation\",\"\")} [{d.get(\"confidence\",\"\")}]--> {G.nodes[v].get(\"label\",v)}')
output = '\n'.join(lines)
if len(output) > char_budget:
output = output[:char_budget] + f'\n... (truncated at ~{token_budget} token budget - use --budget N for more)'
print(output)
"
Replace QUESTION with the user's actual question, MODE with bfs or dfs, and BUDGET with the token budget (default 2000, or whatever --budget N specifies). Then answer based on the subgraph output above.
After writing the answer, save it back into the graph so it improves future queries:
python -c "import subprocess; subprocess.run(['python', '-m', 'graphify', 'save-result', '--question', 'QUESTION', '--answer', 'ANSWER', '--type', 'query', '--nodes', 'NODE1', 'NODE2'])"
Each answer should end with a natural follow-up ("this connects to X — want to go deeper?") so the session feels like navigation, not a one-shot report.
For /graphify explain
Give a plain-language explanation of a single node — everything connected to it.
First check the graph exists:
python -c "
from pathlib import Path
if not Path('graphify-out/graph.json').exists():
print('ERROR: No graph found. Run /graphify <path> first to build the graph.')
raise SystemExit(1)
"
python -c "
import json, sys
import networkx as nx
from graphify.build import build_from_json
from pathlib import Path
data = json.loads(Path('graphify-out/graph.json').read_text())
G = build_from_json(data)
term = 'NODE_NAME'
term_lower = term.lower()
# Find best matching node
scored = sorted(
[(sum(1 for w in term_lower.split() if w in G.nodes[n].get('label','').lower()), n)
for n in G.nodes()],
reverse=True
)
if not scored or scored[0][0] == 0:
print(f'No node matching {term!r}')
sys.exit(0)
nid = scored[0][1]
data_n = G.nodes[nid]
print(f'NODE: {data_n.get(\"label\", nid)}')
print(f' source: {data_n.get(\"source_file\",\"unknown\")}')
print(f' type: {data_n.get(\"file_type\",\"unknown\")}')
print(f' degree: {G.degree(nid)}')
print()
print('CONNECTIONS:')
for neighbor in G.neighbors(nid):
edge = G.edges[nid, neighbor]
nlabel = G.nodes[neighbor].get('label', neighbor)
rel = edge.get('relation', '')
conf = edge.get('confidence', '')
src_file = G.nodes[neighbor].get('source_file', '')
print(f' --{rel}--> {nlabel} [{conf}] ({src_file})')
"
Replace NODE_NAME with the concept the user asked about. Then write a 3-5 sentence explanation of what this node is, what it connects to, and why those connections are significant. Use the source locations as citations.
After writing the explanation, save it back:
python -c "import subprocess; subprocess.run(['python', '-m', 'graphify', 'save-result', '--question', 'Explain NODE_NAME', '--answer', 'ANSWER', '--type', 'explain', '--nodes', 'NODE_NAME'])"
For /graphify path
Find the shortest path between two named concepts in the graph.
First check the graph exists:
python -c "
from pathlib import Path
if not Path('graphify-out/graph.json').exists():
print('ERROR: No graph found. Run /graphify <path> first to build the graph.')
raise SystemExit(1)
"
python -c "
import json, sys
import networkx as nx
from graphify.build import build_from_json
from pathlib import Path
data = json.loads(Path('graphify-out/graph.json').read_text())
G = build_from_json(data)
a_term = 'NODE_A'
b_term = 'NODE_B'
def find_node(term):
term = term.lower()
scored = sorted(
[(sum(1 for w in term.split() if w in G.nodes[n].get('label','').lower()), n)
for n in G.nodes()],
reverse=True
)
return scored[0][1] if scored and scored[0][0] > 0 else None
src = find_node(a_term)
tgt = find_node(b_term)
if not src or not tgt:
print(f'Could not find nodes matching: {a_term!r} or {b_term!r}')
sys.exit(0)
try:
path = nx.shortest_path(G, src, tgt)
print(f'Shortest path ({len(path)-1} hops):')
for i, nid in enumerate(path):
label = G.nodes[nid].get('label', nid)
if i < len(path) - 1:
edge = G.edges[nid, path[i+1]]
rel = edge.get('relation', '')
conf = edge.get('confidence', '')
print(f' {label} --{rel}--> [{conf}]')
else:
print(f' {label}')
except nx.NetworkXNoPath:
print(f'No path found between {a_term!r} and {b_term!r}')
except nx.NodeNotFound as e:
print(f'Node not found: {e}')
"
Replace NODE_A and NODE_B with the actual concept names from the user. Then explain the path in plain language — what each hop means, why it's significant.
After writing the explanation, save it back:
python -c "import subprocess; subprocess.run(['python', '-m', 'graphify', 'save-result', '--question', 'Path from NODE_A to NODE_B', '--answer', 'ANSWER', '--type', 'path_query', '--nodes', 'NODE_A', 'NODE_B'])"
Honesty Rules
- Never invent an edge. If unsure, use AMBIGUOUS.
- Answer using only what the graph contains — do not hallucinate connections.
- Always cite
source_locationwhen referencing a specific fact. - If the graph lacks enough information, say so explicitly.