Which Connected Records Can the Assistant Use?
Build bounded graph retrieval with access and time boundaries, then preserve evidence and measure whether the added context actually helps.
14 sections · 4 questions · 1 coding challenge
The arrow keys move between sections too.
Some Questions Needed Connections Across Retrieved Passages
BOTIE
Our maintenance example uses a smaller local retrieval mechanism: start from a known asset and follow approved dependency types to related manuals. It is not a reproduction of that paper’s community-summary pipeline. The common motivation is that useful evidence may be connected across records.
Sources for this history
A Relevant Manual Can Still Be the Wrong Evidence
The assistant has found P7, but its own manual does not explain a supply fault. Botie could follow P7’s dependency to S1 and retrieve the supply’s manual too. That connected context might explain what the isolated document misses.
There are limits. One route passes through another tenant’s asset, and one manual was uploaded after the decision we are evaluating. Relevance alone does not make either route acceptable. We need an explicit retrieval policy before handing records to a model.
Invented maintenance records. Python exercises and workshop components expose the mechanism locally. Source-linked Cypher or SPARQL examples require the stated external database setup.
Step 1 of 1
A route has to remain usable all the way through
The request belongs to tenant red and is evaluated at hour six.
Nodes and properties (6)
- P7 · red asset
- S1 · red supply
- D1 · red, available 2
- D2 · red, available 8
- X · blue asset
- D3 · red, available 1
- P7 to S1: DEPENDS_ON
- P7 to D1: HAS_MANUAL
- S1 to D2: HAS_MANUAL
- P7 to X: DEPENDS_ON
- X to D3: HAS_MANUAL
Collect evidence through permitted routes
Scope is applied before dependency expansion. Document availability then excludes future evidence while the permitted route structure excludes cross-tenant detours.
Collect evidence through permitted routes
Future documents and routes through another tenant must not enter the context.
BOTIE
Result for these records
- 0D1
Follow the operations
1. Invented graph snapshot
Nodes and properties (6)
- P7 · Asset
- tenant: red
- S1 · Asset
- tenant: red
- X · Asset
- tenant: blue
- D1 · Document
- tenant: red
- available_at: 2
- D2 · Document
- tenant: red
- available_at: 8
- D3 · Document
- tenant: red
- available_at: 1
- P7 to S1: DEPENDS_ON
- P7 to X: DEPENDS_ON
- P7 to D1: HAS_MANUAL
- S1 to D2: HAS_MANUAL
- X to D3: HAS_MANUAL
Inspect the node and relationship records
{ "nodes": [ { "id": "P7", "kind": "Asset", "tenant": "red" }, { "id": "S1", "kind": "Asset", "tenant": "red" }, { "id": "X", "kind": "Asset", "tenant": "blue" }, { "id": "D1", "kind": "Document", "tenant": "red", "available_at": 2 }, { "id": "D2", "kind": "Document", "tenant": "red", "available_at": 8 }, { "id": "D3", "kind": "Document", "tenant": "red", "available_at": 1 } ], "edges": [ { "id": "d", "source": "P7", "target": "S1", "type": "DEPENDS_ON" }, { "id": "cross", "source": "P7", "target": "X", "type": "DEPENDS_ON" }, { "id": "m1", "source": "P7", "target": "D1", "type": "HAS_MANUAL" }, { "id": "m2", "source": "S1", "target": "D2", "type": "HAS_MANUAL" }, { "id": "m3", "source": "X", "target": "D3", "type": "HAS_MANUAL" } ] }- P7 · Asset
2. Permitted tenant
red
3. Restrict the graph before expansion
Nodes and properties (5)
- P7 · Asset
- tenant: red
- S1 · Asset
- tenant: red
- D1 · Document
- tenant: red
- available_at: 2
- D2 · Document
- tenant: red
- available_at: 8
- D3 · Document
- tenant: red
- available_at: 1
- P7 to S1: DEPENDS_ON
- P7 to D1: HAS_MANUAL
- S1 to D2: HAS_MANUAL
Inspect the node and relationship records
{ "nodes": [ { "id": "P7", "kind": "Asset", "tenant": "red" }, { "id": "S1", "kind": "Asset", "tenant": "red" }, { "id": "D1", "kind": "Document", "tenant": "red", "available_at": 2 }, { "id": "D2", "kind": "Document", "tenant": "red", "available_at": 8 }, { "id": "D3", "kind": "Document", "tenant": "red", "available_at": 1 } ], "edges": [ { "id": "d", "source": "P7", "target": "S1", "type": "DEPENDS_ON" }, { "id": "m1", "source": "P7", "target": "D1", "type": "HAS_MANUAL" }, { "id": "m2", "source": "S1", "target": "D2", "type": "HAS_MANUAL" } ] }- P7 · Asset
4. Starting asset ID
P7
5. Maximum dependency hops
2
6. Traverse within one entity category
[ { "id": "P7", "depth": 0 }, { "id": "S1", "depth": 1 } ]7. Collect distinct identifiers
- 0P7
- 1S1
8. Start a binding at each identifier
[ { "origin": "P7", "current": "P7", "path": [ "P7" ], "edge_ids": [] }, { "origin": "S1", "current": "S1", "path": [ "S1" ], "edge_ids": [] } ]9. Read graph relationships
[ { "id": "d", "source": "P7", "target": "S1", "type": "DEPENDS_ON" }, { "id": "m1", "source": "P7", "target": "D1", "type": "HAS_MANUAL" }, { "id": "m2", "source": "S1", "target": "D2", "type": "HAS_MANUAL" } ]10. Choose a relationship type
[ { "id": "m1", "source": "P7", "target": "D1", "type": "HAS_MANUAL" }, { "id": "m2", "source": "S1", "target": "D2", "type": "HAS_MANUAL" } ]11. Extend each binding by one relationship
[ { "origin": "P7", "current": "D1", "path": [ "P7", "D1" ], "edge_ids": [ "m1" ] }, { "origin": "S1", "current": "D2", "path": [ "S1", "D2" ], "edge_ids": [ "m2" ] } ]12. Collect distinct identifiers
- 0D1
- 1D2
13. Look up nodes by stable ID
[ { "id": "D1", "kind": "Document", "tenant": "red", "available_at": 2 }, { "id": "D2", "kind": "Document", "tenant": "red", "available_at": 8 } ]14. Text comparison value
Document
15. Keep rows passing a comparison
[ { "id": "D1", "kind": "Document", "tenant": "red", "available_at": 2 }, { "id": "D2", "kind": "Document", "tenant": "red", "available_at": 8 } ]16. Decision hour
6
17. Keep rows passing a comparison
[ { "id": "D1", "kind": "Document", "tenant": "red", "available_at": 2 } ]18. Collect distinct identifiers
- 0D1
19. Result
- 0D1
These components operate on the supplied records in your browser. They do not run a graph database or interpret Cypher.
1. Choose the starting entity before collecting neighbours
The request concerns P7. Entity resolution must connect that mention to a stable permitted asset, rather than treating a similar display name as sufficient. Vector search can suggest candidates, but an approximate similarity score does not establish identity or access.
For this lesson the starting ID is already supplied. We also receive the tenant, decision hour and maximum dependency depth. Those inputs define the retrieval question; the model does not get to silently widen them.
A known start keeps this exercise focused on connected evidence rather than language-based entity resolution.
2. Apply the scope to intermediate nodes and relationships
Construct the permitted graph before expanding. Keep nodes in the requested tenant and only relationships whose endpoints both remain. This prevents a route through an excluded asset from revealing an otherwise permitted endpoint.
Real policies can also restrict relationship types, individual records or properties within a tenant. The browser receives invented multi-tenant fixtures so we can see the failure. In a deployed system, enforce the policy on the server before forbidden records are exposed to the client or model.
A traversal depth limit controls work. It does not grant permission.
3. Keep dependency expansion separate from document lookup
Require the start and every intermediate dependency node to have kind Asset. Starting at P7, follow DEPENDS_ON up to the chosen depth and retain each reached asset once. Include P7 at depth zero so its own manual remains eligible. Then follow HAS_MANUAL from those assets to document records.
This separation prevents a general “follow every edge” search from wandering through technicians, unrelated claims or other relationship types. Inspect the reached asset IDs before inspecting document content.
Each stage has a different row meaning: assets reached, document IDs attached, then eligible evidence.
Inspect what remains after applying the tenant boundary
Only edges whose two endpoints are in red remain.
g={"nodes": [{"id": "P7", "kind": "Asset", "tenant": "red"}, {"id": "S1", "kind": "Asset", "tenant": "red"}, {"id": "X", "kind": "Asset", "tenant": "blue"}, {"id": "D1", "kind": "Document", "tenant": "red", "available_at": 2}, {"id": "D2", "kind": "Document", "tenant": "red", "available_at": 8}, {"id": "D3", "kind": "Document", "tenant": "red", "available_at": 1}], "edges": [{"id": "d", "source": "P7", "target": "S1", "type": "DEPENDS_ON"}, {"id": "cross", "source": "P7", "target": "X", "type": "DEPENDS_ON"}, {"id": "m1", "source": "P7", "target": "D1", "type": "HAS_MANUAL"}, {"id": "m2", "source": "S1", "target": "D2", "type": "HAS_MANUAL"}, {"id": "m3", "source": "X", "target": "D3", "type": "HAS_MANUAL"}]}
allowed={n['id'] for n in g['nodes'] if n['tenant']=='red'}
print(sorted(e['id'] for e in g['edges'] if e['source'] in allowed and e['target'] in allowed))['d', 'm1', 'm2']The cross edge and m3 disappear because X is excluded, even though D3 itself belongs to red.
4. Use only evidence available by the decision time
The supply manual D2 becomes available at hour eight. It cannot support a response evaluated as of hour six. Check availability after resolving document records, and retain version information so a later revision does not leak backward into an old evaluation.
For training features, apply the same point-in-time discipline to graph edges and node properties. A future relationship can leak an outcome even when the final label column is absent. This exercise assumes the graph topology is the authorised snapshot for the decision and varies document availability only.
Real historical evaluation needs the graph snapshot and evidence versions that existed at the time.
Check yourself
Questions on Parts 1 to 4
Question 1 of 2
Can a final-document tenant check alone enforce this route policy?
Use the information below.
P7 and document D3 belong to red. The only route between them passes through blue asset X. The request is scoped to red.5. Express the same narrow retrieval policy in a database query
The bounded path begins at the asset, permits zero dependency hops and then matches a manual. all over nodes(p) checks the tenant of every asset along that path. The document has its own tenant and availability predicates.
These query conditions illustrate the policy. A server must derive the permitted tenant from authenticated context and enforce its full access rules. Never accept an arbitrary user-selected tenant as authorisation.
The example fixes a two-hop query template and parameterises data values.
Run in your own environment
Retrieve manuals through a scoped dependency route
This reads a disposable property graph; it does not run in the Python challenge.
In an empty test database run CREATE (a:Asset {id:'P7',tenant:'red'}),(s:Asset {id:'S1',tenant:'red'}),(x:Asset {id:'X',tenant:'blue'}),(d:Document {id:'D1',tenant:'red',available_at:2}),(e:Document {id:'D2',tenant:'red',available_at:8}),(f:Document {id:'D3',tenant:'red',available_at:1}),(a)-[:DEPENDS_ON]->(s),(a)-[:DEPENDS_ON]->(x),(a)-[:HAS_MANUAL]->(d),(s)-[:HAS_MANUAL]->(e),(x)-[:HAS_MANUAL]->(f).
Bind asset P7, tenant red and at 6 through the driver. Use server-enforced permissions appropriate to every traversed record.
MATCH p=(a:Asset {id:$asset,tenant:$tenant})-[:DEPENDS_ON*0..2]->(x:Asset)
MATCH (x)-[:HAS_MANUAL]->(d:Document)
WHERE all(n IN nodes(p) WHERE n:Asset AND n.tenant=$tenant)
AND d.tenant=$tenant AND d.available_at <= $at
RETURN DISTINCT d.id AS document
ORDER BY documentOnly D1 is returned. At hour eight, D2 also qualifies. D3 remains unreachable through the permitted graph.
Official documentation. Documentation checked .
6. Give the model evidence with its limits attached
Return source IDs and relevant passages, rather than an unlabelled bundle of graph text. The answer should cite support for its claims and acknowledge missing or conflicting evidence. Documents can contain hostile instructions; retrieved content remains untrusted data rather than instructions to the application.
Bound expansion, document count and context size using documented policies. If a bound truncates potentially useful evidence, expose that limitation rather than claiming a complete search. A graph makes relationships available; it does not guarantee a factual model response.
Graph retrieval and graph neural network training are separate uses of graph data.
7. Evaluate the retrieval decision as well as the generated answer
Use held-out maintenance questions with known supporting records. Check whether required evidence is retrieved, whether forbidden or future evidence is excluded and whether citations support the answer actually given. Include an unresolved question where the correct behaviour is to ask for more information.
Compare this retrieval policy with a simpler document search on the same questions. Measure latency and failure cases alongside answer quality. More connected context can add irrelevant material or repeated claims, so improvement needs evidence.
The workshop tests the evidence-selection mechanism. It makes no claim to evaluate a live language model.
Check yourself
Questions on Parts 1 to 7
Question 1 of 2
What should the assistant’s policy allow?
Use the information below.
A permitted bounded search finds no adequate supporting manual.Try it with Botie
Build with Botie
Write and run Python