POST /v1/gb/remember receives, stored as it came. It is the provenance of the graph: every entity and edge is anchored to the artifact(s) it was inferred from, so a fact can always be traced back to the original text.
Code: internal/memory/memory_remember/remember.go (Remember, Store, ResolveKind), remember_chunks.go (chunked texts), remember_websearch.go (web searches), internal/db/graph/model/model.go (types), internal/db/graph/{sqlite,postgres}/db_schema.go (tables).
Images are not artifacts in this sense: remember with an image stores an image artifact (a row in gb_image_artifacts tracking a file on disk) through a separate pipeline, and they are not linked to entities or edges yet (see Images).
Kinds
Every artifact has akind, set by the optional kind field of the remember request:
kind is trimmed and case-insensitive; an empty one means knowledge; any other value returns 400 and stores nothing.
Three more kinds exist but cannot be asked for: web_search, the result of a web search that remember made (with web_search) for an entity of another artifact, series_observation, a text derived from a timeseries (with timeseries.observations), and chunk_summary, the LLM-written summary of a chunk. The first two have a query and a parent (below); a summary has a parent and references the inference (model and reasoning level) that wrote it.
The kind is stored and returned, but it does not change how the text is extracted or reconciled yet. Both kinds get the same bi-temporal treatment (see Temporal facts). It exists so that behaviour can differ per kind later (for example, a knowledge artifact need not supersede older facts).
Fields
Tablegb_artifacts:
|
parent_id | The artifact this one hangs off; NULL for an artifact that is nobody’s child. Either the artifact it is a chunk of (see Chunks), or, for a web_search artifact, the artifact the searched entity came from (see Web searches), or, for a series_observation, the artifact that anchors the series (see Series observations), or, for a chunk_summary, the chunk it summarizes (see Chunk summaries) |
| chunk_index, start_offset, end_offset, tokens | Only for a chunk: its position among its siblings (from 0), the byte range of its text in the parent’s text, and its size in tokens |
| query | Only for a web_search artifact: the web search query that was issued for it; for a series_observation, the series and window it is of |
| series_id | Only for a series_observation artifact: the series it describes (gb_series.id; NULL if the series was deleted) |
| inference_id | Only for a chunk_summary artifact: the run (gb_inferences.id, see Inferences) of the model that wrote the summary; the run holds the model (gb_models.id) and the reasoning level it ran with (none, low, medium or high) |
Every remember call adds one artifact (plus, for a long text, its chunks), so sending the same text twice stores two artifacts. They add no duplicate entities or edges, but both are recorded as sources (below).
Chunks
Artifacts can be the children of other artifacts. A text larger than one chunk (1024 tokens by default) is stored as an artifact holding the whole text, the parent, and is split into overlapping chunks that are artifacts too, its children (parent_id set; same kind, session_id and times; text is the chunk). Each chunk is extracted, stored and embedded individually, and the entities and edges link to the chunk they came from. A text that fits one chunk has no children. See Chunking.
Chunk summaries
Each chunk of a chunked text is also summarized by an LLM, and the summary is stored as an artifact of its own, of kindchunk_summary: its parent (parent_id) is the chunk, so the chain is document, chunk, summary. It references the inference (inference_id: the model and the reasoning level) that wrote it, is embedded into the dataset’s summary vector index, and, like a web search, is derived and not a chunk (no chunk_index). No entity or edge links to it. Everything is in Chunking.
Web searches
Withweb_search, remember also searches the web for the latest world events about the key entities of the text, and stores each result as an artifact of its own, of kind web_search. It saves the query that was issued (query) and its parent (parent_id) is the artifact from which the entity came: the artifact the entity was extracted from, which for a long text is the chunk. A web search is therefore the second kind of child artifact; it is told apart from a chunk by its kind and by having no chunk_index. The entities and edges extracted from the result are linked to it like to any artifact (see below), so a fact learned on the web traces back to the query that found it and to the text whose entity was searched for. Everything is in Web search.
Series observations
Withtimeseries.observations, remember derives a text from the stats of each timeseries it stored (generated in Go, not by an LLM) and stores it as an artifact of its own, of kind series_observation: it records the series it describes (series_id) and a query saying which series and window, and its parent (parent_id) is the artifact that anchors the series. It is the third kind of child artifact and, like a web search, is not a chunk (no chunk_index). The entities and edges extracted from its text link to it like to any artifact, so a fact derived from a series traces back to the observation, the series, and the text it was anchored to. Everything is in Timeseries.
Provenance links
Entities and edges point back to their artifacts in three ways:
Why link tables:
- Entities are deduplicated per dataset on (normalized name, type), so “Alice” mentioned in artifacts 1, 5 and 9 is one row. A single
artifact_idcolumn could only say where she first appeared. The link table also answers “which artifacts ground this entity?” and “which entities does this artifact contribute to?”, and lets an artifact be removed without losing entities that other artifacts still support. - Edges are created by one artifact, hence
artifact_id. But when a later artifact states the same fact, the new edge is recognized as a duplicate (exactly, or by the LLM) and merged into the stored edge instead of being inserted. Withoutgb_edge_artifactsthe fact that this artifact also supports it would be lost. The stored edge keeps its ownartifact_id,created_atandvalid_at. - Contradicted edges are not linked to the artifact that contradicted them: a contradiction is not a restatement.
artifact_id to NULL on its edges and removes its link rows.
Example. “Alice works at Acme” is remembered twice as artifacts 1 and 2:
Sessions
An artifact carries thesession_id of the remember call that created it, which connects the artifacts of one user/agent run (and, through query’s session_id, the queries). The session is also recorded against the entities and edges the artifact yields, in gb_entity_sessions and gb_edge_sessions, including entities and facts that already existed, so a session that only repeats known facts still leaves a trace. Image artifacts carry it too (gb_image_artifacts.session_id). See Sessions.
Where artifacts show up
POST /v1/gb/rememberreturns the newartifact_id, itskind,session_id(if any),valid_atandcreated_at(Remember).- Edges in the graph export and in the query
evidence(Query) carryartifact_id(the creating artifact).
Limitations
- There is no endpoint to read, list or delete artifacts yet. The raw text can only be read from the database.
querydoes not use artifact text: it answers from the graph only.- The
gb_edge_artifactsandgb_entity_artifactslinks, and the session links (gb_entity_sessions,gb_edge_sessions), are stored but not exposed by the API yet. - Artifacts are not embedded, except chunks:
rememberembeds the entities and edges extracted from an artifact, and the text of each chunk of a long text (see Chunking and Vector storage); the text of an artifact that is not chunked is not embedded.