Day 25: GraphRAG for Call Graphs and Service Maps

Day 25: GraphRAG for Call Graphs and Service Maps

Flat chunk RAG struggles with “what calls what?” and “which service owns this queue?” Day 25 introduces GraphRAG patterns for engineering systems: nodes for services, endpoints, tables, and topics; edges for calls, publishes, and owns — retrieved as subgraphs that beat disconnected paragraphs for dependency questions.

⚡ TL;DR: Build a service/call graph from runtime telemetry, IaC, and CODEOWNERS. For dependency questions, retrieve a neighborhood subgraph + linked runbooks. Keep text RAG for prose how-tos. Validate edges; hallucinated edges are worse than missing chunks.

When edges win

Question type Prefer
How do I failover RDS? Text RAG
What services publish to payments.events? Graph
Trace blast radius of auth change Graph + diff
Explain error X in runbook tone Text / hybrid
# ✅ Neighborhood retrieval
def graph_context(entity: str, hops: int = 2) -> dict:
    nodes, edges = graph.ego(entity, hops=hops, limit=40)
    docs = [n.runbook_chunk_id for n in nodes if n.runbook_chunk_id]
    return {"nodes": nodes, "edges": edges, "docs": retrieve_ids(docs)}

Building the graph without fantasy

Sources of truth:

  • Service catalog / Backstage
  • Mesh or tracing edges (sampled)
  • Terraform module dependencies
  • Async: SNS/SQS/Kafka topic bindings from IaC
✅ Edge: checkout → payments via HTTP /v1/charge (from mesh)
❌ Edge: inferred by an LLM reading a README once

Store provenance on edges (source=terraform|mesh|manual, confidence). Agents may propose edges; humans or CI confirm before they become retrieveable fact.

Combining with chunk RAG

GraphRAG is not a full replacement. Pattern: classify intent → if dependency, graph neighborhood → expand node descriptions via chunk IDs → generate with citations to both edge IDs and chunk IDs. Cite-or-refuse still applies (Day 5).

Field notes from production

Graphs rot. Add CI checks that fail when a service is deployed without catalog registration. Cap hops and node counts — 5-hop explosions recreate the lost-in-the-middle problem inside a prettier structure.

Implementation sketch

def answer(q):
    if intent(q) == "dependency":
        ent = extract_entity(q)
        ctx = graph_context(ent, hops=2)
        return generate_with_citations(q, ctx)
    return text_rag(q)

Closing checklist

  • [ ] Graph built from catalog + IaC + mesh, not LLM invention
  • [ ] Edge provenance stored
  • [ ] Hop and node caps
  • [ ] Intent router between text RAG and graph
  • [ ] Citations include edge and doc IDs
  • [ ] CI freshness checks for service registration

Graph freshness SLO

Define maximum age for mesh-derived edges and catalog nodes. If freshness breaches, fall back to text RAG with a warning rather than answering from a stale graph. Visualize ego graphs in the debug UI for on-call so they can see what the model saw. Remember: pretty wrong graphs create confident outages.

Extended discussion

Return to the core angle for Day 25: When edges beat flat chunks for microservices. That sentence is the acceptance lens for every design review this week. If a proposed change does not make this angle easier to measure or enforce, it is a distraction.

Write down three metrics you will look at after shipping Day 25 ideas, schedule a 45-minute readout, and archive the notes next to the eval artifacts. Architecture without a readout becomes slideshow archaeology.

Pair this day with the adjacent lessons in the series navigation below. Forward links exist so you can keep momentum; backward links exist so you can repair foundations when a later lab fails for boring earlier reasons.

Practically, allocate half a day to implement the smallest vertical slice, half a day to wire measurement, and refuse to polish UI until both are done. This ordering is how bootcamp projects stay honest under time pressure.

Revisit assumptions whenever the model ID, embedding ID, or index alias changes — treat those as breaking changes for Day 25 behaviors, with the same seriousness as a database migration. Canary first, then promote.

Revisit assumptions whenever the model ID, embedding ID, or index alias changes — treat those as breaking changes for Day 25 behaviors, with the same seriousness as a database migration. Canary first, then promote.

Revisit assumptions whenever the model ID, embedding ID, or index alias changes — treat those as breaking changes for Day 25 behaviors, with the same seriousness as a database migration. Canary first, then promote.

Revisit assumptions whenever the model ID, embedding ID, or index alias changes — treat those as breaking changes for Day 25 behaviors, with the same seriousness as a database migration. Canary first, then promote.

Revisit assumptions whenever the model ID, embedding ID, or index alias changes — treat those as breaking changes for Day 25 behaviors, with the same seriousness as a database migration. Canary first, then promote.

Revisit assumptions whenever the model ID, embedding ID, or index alias changes — treat those as breaking changes for Day 25 behaviors, with the same seriousness as a database migration. Canary first, then promote.

Revisit assumptions whenever the model ID, embedding ID, or index alias changes — treat those as breaking changes for Day 25 behaviors, with the same seriousness as a database migration. Canary first, then promote.

Revisit assumptions whenever the model ID, embedding ID, or index alias changes — treat those as breaking changes for Day 25 behaviors, with the same seriousness as a database migration. Canary first, then promote.

Series navigation

← Day 24 · Day 26 →

Last updated September 11, 2026


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply