---
title: "Graph RAG that says when it does not know: context that is cited, grounded, or refused"
slug: knowledge-layer-4-graph-rag
date: 2026-10-12
author: Semurg
section: how-to
tags: [graph-rag, rag, memory, agents, citations]
summary: Ask the memory a question and get the facts and passages around it, found by name, keyword, meaning and the graph, each cited, with a verdict on whether your question's words are covered.
status: published
covers: [route:POST /v1/memory/remember, route:POST /v1/memory/context, mcp:memory_context]
---
Retrieval for an agent has two jobs: find what the store knows about a question, and say honestly when it does not know. Most retrieval
stacks do the first with a vector search and leave the second to the model, which then answers anyway. Semurg's `memory_context`
does both: it gathers the facts and passages around a question from several directions at once, cites each one, and says which
words of your question the store holds nothing about.

## Store something to ask about

```bash runnable kind=shell-node needs=token
export SEMURG_API_TOKEN=<your token>
curl -sS http://localhost:4000/v1/memory/remember \
  -H "Authorization: Bearer $SEMURG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"scope": "rag-intro", "source": "support ticket 4471",
       "facts": {"entities": [{"name": "Northwind Retail", "type": "Company"},
                              {"name": "Invoice export", "type": "Feature"}],
                 "relations": [{"source": "Northwind Retail", "target": "Invoice export", "type": "REPORTED_BUG_IN"}]},
       "passages": [{"text": "Northwind Retail reports that the invoice export drops the currency column for orders in euros.",
                     "mentions": ["Northwind Retail", "Invoice export"]}]}'
```

```expect
expect: the facts and the passage are stored
assert: json:.ok==true
```

## Ask a question

> Diagram graph-rag-flow: A question becomes cited items and a verdict: starting points are found by name, keyword and meaning, the graph is walked out from them, what was gathered is ranked, and every returned item is cited, with a verdict on whether the question is covered.

`memory_context` takes a question in plain words. It first finds where to start: the names the question mentions, exact and close
spellings, the kinds of thing it asks about, the keywords it shares with stored passages, and its meaning where an embedding model is
installed. Then it walks the graph out from those starting points, up to three steps. It ranks what it gathered and returns the best of
it, each item cited:

```bash runnable kind=shell-node needs=token
export SEMURG_API_TOKEN=<your token>
curl -sS http://localhost:4000/v1/memory/context \
  -H "Authorization: Bearer $SEMURG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"scope": "rag-intro", "query": "What does Northwind Retail report about the invoice export?", "k": 8, "hops": 2}'
```

```expect
expect: the passage about the currency column comes back, cited, and the verdict is grounded
assert: json:.ok==true and .grounding.state=="grounded" and ([..|strings|ascii_downcase|select(contains("currency column"))]|length)>0
```

## The verdict, and what it does and does not mean

Every answer carries `grounding.state`, so an agent can tell a covered question from an uncovered one before it writes a word:

- `grounded`: every content word of your question is covered by what came back, and at least one item can be cited.
- `related`: at least one content word of your question is covered by nothing that came back; the reply lists those words in `uncovered`, with the stored names that did match.
- `uncited`: something matched, but nothing it can cite.
- `nothing_stored`: nothing in this scope matches.
- `could_not_judge`: a stage could not run, and the answer names which.

Now ask about something the store holds nothing on, the export's deadline, and the verdict says so:

```bash runnable kind=shell-node needs=token
export SEMURG_API_TOKEN=<your token>
curl -sS http://localhost:4000/v1/memory/context \
  -H "Authorization: Bearer $SEMURG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"scope": "rag-intro", "query": "What does Northwind Retail report about the invoice export deadline?", "k": 8, "hops": 2}'
```

```expect
expect: the verdict is related, and it names "deadline" as the word nothing stored covers
assert: json:.ok==true and .grounding.state=="related" and (.grounding.uncovered|index("deadline")) != null
```

Nothing stored mentions a deadline, so the verdict says related and names the word, while the rest of the question is still covered. That is the point of the verdict: it tells an agent
which part of the question the store cannot speak to. It is a check on your question's words, not proof that an answer is right, so
read the cited items before you answer from them.

### For coding agents: Rules for an agent using Semurg's context (graph RAG)

- [ ] Before answering a question about stored knowledge, call memory_context with the question in plain words and the scope.
- [ ] Read grounding.state before the items.
- [ ] Answer from the items only when grounding.state is grounded, after reading the cited items; quote each item's citation for every fact you use.
- [ ] On related, say what the store holds, and name the words in uncovered that it holds nothing about.
- [ ] On uncited, nothing_stored or could_not_judge, say you do not know, and why; never fill the gap from your own knowledge as if it came from the store.

The check that proves it:

```bash
curl -sS http://localhost:4000/v1/memory/context -H "Authorization: Bearer $SEMURG_API_TOKEN" -H "Content-Type: application/json" -d '{"scope": "rag-intro", "query": "What does Northwind Retail report about the invoice export?"}'
```

Passes when: grounding.state is grounded, with cited items

```bash
curl -sS http://localhost:4000/v1/memory/context -H "Authorization: Bearer $SEMURG_API_TOKEN" -H "Content-Type: application/json" -d '{"scope": "rag-intro", "query": "What does Contoso say about payroll?"}'
```

Passes when: grounding.state is not grounded (nothing_stored, or related with the unknown words in uncovered)

## What it does not do yet

- Search by meaning on a node without an embedding model, for example a node run in Docker without one installed: it then finds by name, keyword and the graph only, and says so.
- Answer as of a past date.
- We do not yet publish a measured grounded-answer rate: it appears here, linked to its release gate row, once a published release measures it.

## Next

- [A knowledge graph your agents build as they work](/blog/knowledge-layer-1-the-knowledge-graph): how the graph this walks is built.
- [Give your agent persistent memory with MCP in five minutes](/blog/agent-memory-mcp-five-minutes): the same calls over MCP.
