A knowledge graph your agents build as they work, with every fact traced to its source
12 October 2026 · Semurg · 4 min read · Markdown
Verified: every command below ran on a fresh node of r11.78.20261012.bc0d94f (how)
knowledge-graphmemoryagentsprovenance
An agent learns things in pieces: a customer's name in one conversation, their company in another, a contract clause in a document. A knowledge graph keeps those pieces as entities and the relationships between them, so the next question can follow the connections instead of searching the whole history again. In Semurg the graph is built from what you store, kept on your own disk, and every fact it gives back says where it came from.
This post stores a few facts with the passage they came from, recalls them by name, and adds a relationship later.
Store facts with their source
One call can carry entities, the relationships between them, and the passages of text they came from, with a label for the source and the date it was true. The entities and relationships are the graph; the passages are the evidence.
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": "kg-intro", "date": "2026-10-01", "source": "kickoff call notes",
"facts": {"entities": [{"name": "Priya Raman", "type": "Person"},
{"name": "Northwind Retail", "type": "Company"},
{"name": "Invoice export", "type": "Feature"}],
"relations": [{"source": "Priya Raman", "target": "Northwind Retail", "type": "WORKS_AT"},
{"source": "Northwind Retail", "target": "Invoice export", "type": "USES"}]},
"passages": [{"text": "Priya Raman from Northwind Retail said their finance team runs the invoice export every Monday.",
"mentions": ["Priya Raman", "Northwind Retail", "Invoice export"]}]}'Expected: the facts and the passage are stored
Each entity is stored once, whatever number of calls name it later, and each relationship is its own record, linked to the entities at its two ends. A relationship may also name an entity an earlier call stored; one that names an entity nobody stored is refused, by name, and nothing is stored, so a misspelt name never slips into the graph.
Recall by name, with every item cited
export SEMURG_API_TOKEN=<your token>
curl -sS http://localhost:4000/v1/memory/recall \
-H "Authorization: Bearer $SEMURG_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"scope": "kg-intro", "subject": "Priya Raman"}'Expected: an answer about Priya Raman, facts first, each item carrying its citation
Recall starts from the name you give it and returns its own facts first (Priya Raman works at Northwind Retail), then what is one step away: the entities it connects to and the passages that mention it. To follow connections further, memory_context walks up to three steps out; the graph RAG post covers it. Every item carries a citation: a fact or a passage cites the document it came from, when that was stored and the exact span it quotes, and an entity cites itself, its node in the graph. The answer also says what it is: answered, nothing_stored when nothing matched (an empty answer that says so, never an invented one), or could_not_judge with the stage that could not run.
Add a relationship later
Relationships learned later join the same graph. Here, the agent learns that Priya reported an issue with the feature:
export SEMURG_API_TOKEN=<your token>
curl -sS http://localhost:4000/v1/memory/relate \
-H "Authorization: Bearer $SEMURG_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"scope": "kg-intro", "from": "Priya Raman", "to": "Invoice export", "type": "REPORTED_ISSUE_WITH"}'Expected: the relationship is stored (edge_present true)
memory_relate stores either end that is not there yet, as an entity without a type, so it never fails for a missing name; the reply says the relationship is present.
The same three calls are MCP tools, memory_remember, memory_recall and memory_relate, so an agent connected over MCP builds and reads the same graph as your code over HTTP.
Raw text, read into entities by a model
Facts you send are stored as they are, with no model involved. Semurg can also read raw text into entities with an extraction model running on your own node. How quickly that model reads a passage, and that it installs as the default on a fresh node, are not yet measured on a published release, so this post does not show it; a later post will, with the measurement.
Agent block
What it does not do yet
- Read raw text into entities on a node you install: the extraction model does it when it is installed and serving, and the reply says which, but how it does on a fresh node has not been measured from outside yet.
- Answer as of a past date, or let a fact expire: both are coming.
Next
- One memory per end user: the same graph, kept apart per user.
How these commands were checked
Our test farm ran each of the 4 command blocks marked runnable, exactly as written, with the answer checked against the expectation printed under it, against a fresh node of the published release r11.78.20261012.bc0d94f. A post is published only when every one of them passed there.