Install
$ agentstack add skill-anthonyalcaraz-agentic-graph-rag-skills-three-graph-router ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Three-Graph Router
Overview
Real agentic systems integrate trusted structured data (CSVs, databases) with untrusted extracted data (documents, reviews). Merging them into one graph is the failure: unverified information pollutes trusted data, provenance is lost, extraction errors cascade, and validating agent reasoning becomes impossible.
The Three-Graph Architecture solves this by separating knowledge on origin, certainty, and semantic role:
- Domain graph — trusted, curated, entity-resolved. The canonical product
list, the definitive org hierarchy. High certainty, stable IDs, protected from contamination.
- Lexical graph — original unstructured text in structured form. Document
and Chunk nodes, immutable, complete provenance (every chunk links back to its source). This is the "retrieval" in RAG.
- Subject graph — entities/facts an LLM extracted from the lexical graph,
kept SEPARATE from domain until entity resolution establishes confident links. Extraction artifacts with explicit uncertainty (confidence, model version, timestamp).
The router refuses the boundary violations that quietly destroy the architecture: raw text without provenance is refused from the lexical graph, extractions without a confidence score are refused from the subject graph, and an extraction is never written straight into the domain graph. The critical operation is entity resolution: a subject entity links to a domain entity via CORRESPONDS_TO only when similarity clears a confidence threshold (default 0.85; 0.95 high-stakes, 0.75 exploratory). The worked example: a review mentions "the Stockholm chair", the system extracts a Subject_Product, finds Product(PROD_12345, "Stockholm Chair") in the domain graph, and links them if similar enough — enabling the cross-graph query domain -> CORRESPONDS_TO -> subject -> EXTRACTED_FROM -> lexical with full provenance.
When to Use
- Ingesting mixed structured + unstructured sources into one agent knowledge base
- Designing the trusted-vs-extracted separation for a graph RAG system
- Preventing LLM extraction errors from contaminating a system of record
- Implementing the CORRESPONDS_TO linkage between extracted and canonical entities
Phrases: "three-graph", "domain/lexical/subject graph", "CORRESPONDS_TO", "entity resolution linkage", "keep extractions separate", "provenance", "trusted vs extracted knowledge".
When NOT to Use
- Single trusted source. If all data is curated and entity-resolved, it all
lives in the domain graph; the separation buys nothing.
- The matching algorithm itself. This skill GATES the link with a threshold;
the actual embedding/Jaro-Winkler matcher is a swappable seam, not this skill's job.
- Graph storage/query engine selection. Use
graph-model-selectorfor the
model class; this routes records, it does not pick Neo4j-vs-RDF.
- Append-only logs with no notion of "trusted". No domain/subject split
applies.
Process
| Step | Input | Action | Output | Verification | |------|-------|--------|--------|--------------| | 1 | Record(payload, origin, entity_resolved, has_provenance, confidence) | lib.route(record) | {graph, label_suffix, reasons, requires_resolution} | structured+resolved->domain; rawtext+provenance->lexical; extraction+confidence->subject | | 2 | rawtext record without provenance | lib.route(record) | raises ValueError | lexical graph requires provenance — refuses silent insert | | 3 | extraction record without confidence | lib.route(record) | raises ValueError | subject graph requires uncertainty metadata | | 4 | extraction marked entity_resolved=True | lib.route(record) | raises ValueError | extractions never enter domain directly | | 5 | subject name + {domain_id: name} candidates + threshold | lib.link_subject_to_domain(...) | Correspondence(subject_id, domain_id, similarity, linked, threshold) | links only if best similarity >= threshold | | 6 | start graph + target graph | lib.cross_graph_query_path(start, target) | edge-type sequence | domain->lexical = [CORRESPONDSTO, EXTRACTEDFROM] |
Rationalizations
| Agent rationalization | Documented rebuttal | |------------------------|--------------------| | "Just merge everything into one graph — the separation is overhead." | The chapter names the exact failures: unverified info pollutes trusted data, provenance is lost, extraction errors cascade, validation becomes impossible. The separation is the entire value proposition; merging is the anti-pattern the architecture exists to prevent. | | "The LLM extracted it with 0.9 confidence, write it straight to the domain graph." | No. Confidence is not entity resolution. The extraction is a SUBJECT artifact until CORRESPONDSTO links it to a canonical domain entity. route raises if you try to mark an extraction entity_resolved and skip the subject graph. The domain graph is the single source of truth precisely because extractions cannot bypass resolution. | | "Provenance on lexical chunks is bookkeeping I can skip." | Provenance is what lets the agent cite the exact source passage and lets analysts audit retrieval quality. The chapter lists "complete provenance" as a defining lexical characteristic. route refuses provenance-less rawtext on purpose. | | "I'll set the CORRESPONDS_TO threshold to 0.5 so more links form." | 0.5 floods the domain graph with false links — the conflation error the chapter warns is as damaging as fragmentation. Defaults: 0.95 high-stakes, 0.85 standard, 0.75 exploratory. Lowering it is a deliberate, documented precision/recall trade-off, not a default. | | "Re-extracting the subject graph is dangerous — it'll change my data." | The opposite: because subject is separate from domain, you re-extract subject from the immutable lexical graph as models improve, and the domain graph is untouched. The separation is what makes re-extraction safe. |
Red Flags
- Many extractions routed with
requires_resolution=False. Bug — every
subject entity needs resolution before it can be trusted as domain.
link_subject_to_domainlinking almost everything. Threshold too low;
you are conflating distinct entities. Raise it and re-audit.
link_subject_to_domainlinking almost nothing. Threshold too high OR the
similarity stub is wrong for the domain — swap in the real matcher at the seam.
- Domain graph node count growing on every ingestion run. Extractions are
leaking into domain. Verify route is the only write path to domain and that it rejects extraction origins.
- Lexical chunks with no source-document edge. Provenance broken; agent
citations become unverifiable.
Non-Negotiable Verification
- Run the benchmark battery.
python cli.py benchmarkmust report 10/10:
- each origin routes to the correct graph
- provenance-less raw_text, confidence-less extraction, and
extraction-marked-resolved all RAISE (boundary enforcement)
- linkage respects the threshold and picks the best candidate
- cross-graph path is correct
- Run the scenario.
python cli.py scenario stockholm-chairroutes all
three record types and shows the CORRESPONDS_TO link forming.
- Verify CLI help.
python cli.py --helpexits 0 and prints this SKILL.md
description (so any harness can discover the skill from --help).
Security Posture
- Prompt injection. Records are untrusted by design - subject-graph
extractions come from adversarial documents. The router never executes payload content; its refusal rules ARE the defense that keeps injected extractions out of the trusted domain graph. The attack to resist is threshold-lowering or marking extractions entity_resolved to bypass the gate.
- Data exfiltration. No network calls, no file writes. Record payloads and
provenance metadata stay in-process; routing decisions go to stdout and the caller owns downstream piping.
- Privilege escalation. A CORRESPONDS_TO link is the escalation surface: it
promotes extracted data toward trusted status. The gate links only above the confidence threshold, and no code path writes an extraction directly to domain - keep those invariants when swapping in a real matcher at the seam.
Source Attribution
Distilled from Agentic GraphRAG (O'Reilly, by Anthony Alcaraz and Sam Julien) Ch3 — Knowledge Representation, section "The Three-Graph Architecture for Agent Knowledge" (domain / lexical / subject graphs, Figure 3-2) and "Entity Resolution and Linking Across Graphs" (the CORRESPONDS_TO three-stage linking pipeline and the 0.95 / 0.85 / 0.75 Jaro-Winkler thresholds). The Stockholm-chair worked example is the chapter's own illustration of subject-to-domain resolution.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: AnthonyAlcaraz
- Source: AnthonyAlcaraz/agentic-graph-rag-skills
- License: MIT
- Homepage: https://www.oreilly.com/library/view/agentic-graphrag/9798341623163/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.