Skip to main content
An artifact is a piece of data ingested to be remembered: the raw text that 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 a kind, 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

Table gb_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 kind chunk_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

With web_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

With timeseries.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. 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_id column 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. Without gb_edge_artifacts the fact that this artifact also supports it would be lost. The stored edge keeps its own artifact_id, created_at and valid_at.
  • Contradicted edges are not linked to the artifact that contradicted them: a contradiction is not a restatement.
An entity is linked to an artifact whether it was created by it or already existed, and also when an edge names a stored entity the extraction did not list. Links are idempotent (primary key on the pair), and deleting either side removes only the link: deleting an entity cascades to its edges, and deleting an artifact sets 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 the session_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/remember returns the new artifact_id, its kind, session_id (if any), valid_at and created_at (Remember).
  • Edges in the graph export and in the query evidence (Query) carry artifact_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.
  • query does not use artifact text: it answers from the graph only.
  • The gb_edge_artifacts and gb_entity_artifacts links, 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: remember embeds 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.