Skip to content

Memory graphs

The memory graphs service gives your account typed knowledge graphs: the people, things and ideas your agents learn about, as entities, and how they connect, as relationships. Use it for what an agent should still know next time and be able to navigate: who works where, what a decision rests on, which topics a person cares about.

A graph holds three things. Entities each have a type, a short label, an optional summary, properties and raw data, and are named by their id. graph_get_entity also takes its slug; every other tool takes the id. An entity's slug is its type, a colon and its label in lower case, with every run of characters other than a to z and 0 to 9 made one hyphen and none at either end: a Person labelled Ada Lovelace is Person:ada-lovelace. A slug already taken in the graph gets -2, -3 and so on, and a new label gives the entity a new slug. Pass a slug exactly as an answer shows it. Relationships each have a type and join two different entities, from a source to a target, with optional properties of their own. The vocabulary declares every entity type and relationship type the graph may hold: each type's properties, which of them are required, and for a relationship type the entity types allowed at each end. Every entity and relationship written must be of a declared type and carry every required property.

The vocabulary is open: your agents extend it themselves as they learn what a graph needs, and an addition applies at once, with no one to approve it. What holds it together is deduplication: a proposed type too like one already declared is turned away and the similar types named, so the existing type is used instead of a near-copy of it.

Your account owns every one of its graphs, and every agent of the account shares them.

It offers a trial, and it can also be enabled in full and billed per use.

Every tool works on this system's own storage only; none reaches outside it.

Its tools

Each tool is charged under an operation of its own, named in brackets at the end of its entry: one unit per call, except the tools that take a list of entities, relationships or ids to write, which charge one unit per list member, up front, for every member sent, and graph_delete, which charges after the delete for what it removed (see its entry). The tools that only read are reached through toolsets_calltool_read, the ones that lose nothing through toolsets_calltool_write, and the ones that replace or remove content through toolsets_calltool.

Tools that only read

  • graph_list lists your account's graphs, oldest first, each with its id, name, description, the template it started from and when it was created and last changed (graph-list).
  • graph_list_templates lists the templates a graph can start from, each with its id, title and description (graph-list-templates).
  • graph_open returns a graph's vocabulary, its entity and relationship counts, its own usage instructions and guidance on choosing a query tool. Open a graph before writing to it (graph-open).
  • graph_find_entities finds a graph's entities by words in their label or summary (searchTerm), by type (entityTypes), by exact property values (properties), or any combination, one page at a time (graph-find-entities).
  • graph_get_entity returns one entity, by its id or its slug: by default everything about it, including its properties, raw data and who created and last changed it (graph-get-entity).
  • graph_get_relationships lists an entity's relationships, each with its id, type, both ends and properties, one page at a time — for example to find a relationship's id before deleting it (graph-get-relationships).
  • graph_explore shows what surrounds an entity: the entities up to depth relationships away, grouped by distance and by the relationship type that reached them (graph-explore).
  • graph_find_paths shows how two entities connect: the paths from sourceEntityId to targetEntityId, each with its entities and the relationships it crosses (graph-find-paths).
  • graph_traverse runs a multi-hop query: a start, a sequence of steps with type and property filters at each, and what to answer — the entities the last step reached, everything walked, the paths, or property values and counts (graph-traverse).
  • graph_search finds entities whose label or summary matches the words in query, across one, several (graphIds) or every graph your account holds, answered graph by graph, every graph searched present even with no hits; a graph found deleted while an all-graphs search runs is left out, and naming a deleted graph in graphIds answers NOT_FOUND. Within each graph, hits come in order of label: nothing is ranked by relevance, within a graph or across graphs. It is charged once however many graphs it searches (graph-search).
  • graph_timeline shows when an entity was created and last changed and which relationships were added to it, one page at a time (graph-timeline).
  • graph_validate checks a graph's existing entities or relationships against its current vocabulary and answers one page of the issues found — for example after a vocabulary change. Each page says how many it checked, the nextOffset to continue from and whether it is done. It changes nothing; fix an issue with the write tools (graph-validate).

Tools that change things and lose nothing

  • graph_create makes a new graph with a name and optionally a description, its vocabulary and usage instructions copied from the template named by templateId. It is refused when your account already holds as many graphs as it may. A repeated call creates a second graph (graph-create).
  • graph_create_entities creates entities in a graph, in order. Each needs a declared entityType and every required property. A repeated call creates them again: entities are not matched by label, and one whose slug is taken gets a numbered slug. Charged per member of entities (graph-create-entities).
  • graph_create_relationships creates relationships between existing entities, by id, in order. Each needs a declared relationshipType that allows the types of the two entities it joins. A repeated call creates them again, since two relationships of one type may join the same entities. Charged per member of relationships (graph-create-relationships).
  • graph_propose_vocabulary adds to a graph's vocabulary: new entity and relationship types, and new optional properties on types that already declare some. It is additive only: a proposal that would remove, narrow or rewrite anything — a type's description included — add a required property or delete a type is refused, naming it, and belongs to graph_revise_vocabulary. A property proposed for a type that declares none yet is answered rejected, since it would be the type's first (see Read before you write). It is charged once however many proposals it holds (graph-propose-vocabulary).

Tools that replace or remove content, with no undo

Nothing in this service is versioned, so what these tools replace or remove cannot be recovered.

  • graph_update changes a graph's name, description or usageInstructions; give at least one. What it names is overwritten, the usage instructions replaced whole; null clears the description or the instructions. The graph's contents are untouched (graph-update).
  • graph_delete deletes a graph and everything in it: its entities, relationships and vocabulary, and answers how many entities and relationships it removed. A delete cut short — on a timeout, say — may leave the graph partly removed and no longer openable; send it again to finish it. It is charged after the delete, for what it removed: one unit per entity (graph-delete-entities) and one per relationship (graph-delete-relationships), the same as graph_delete_entities and graph_delete_relationships charge; nothing for an empty graph or a graph already gone. Its size is not known until it has run, so before it deletes anything it is refused only when your account could not pay for one unit; open the graph with graph_open first to see its entity and relationship counts. The charge cannot be refused once the graph is gone, so it is taken in full even past your wallet's balance: your account may then owe, and every paid call is refused until a top-up covers it. On a trial it takes only what the trial has left. A delete cut short charges nothing for what it removed; the repeat that finishes it charges what it removes.
  • graph_update_entities changes existing entities, each named by entityId: its label, summary, properties or data. properties is a merge patch: a key it names overwrites the value the entity held, a key set to null is deleted, and a key it leaves out is kept. What a change leaves out is kept. Charged per member of updates (graph-update-entities).
  • graph_delete_entities deletes entities, by id, and every relationship to or from each of them. Charged per member of entityIds (graph-delete-entities).
  • graph_delete_relationships deletes relationships, by id; the entities they joined are untouched. Charged per member of relationshipIds (graph-delete-relationships).
  • graph_revise_vocabulary changes a graph's vocabulary in any way: it replaces a type's description, removes or rewrites properties, adds required ones or a type's first property, replaces the entity types a relationship type allows or whether it is bidirectional, and deletes types. Deleting an entity type deletes every entity of that type and their relationships; deleting a relationship type deletes every relationship of that type; each result says how many. A type deletion sent again finishes it: it removes whatever of the type's data is left and is answered approved, or answered rejected as not found when nothing of the type is left. Run graph_validate afterwards to find existing data the change left invalid. It is charged once however many proposals it holds (graph-revise-vocabulary).

Read before you write

Open a graph with graph_open before writing to it, and read its vocabulary and its usage instructions. The vocabulary is enforced: an entity or relationship of an undeclared type, a relationship between entity types its type does not allow, or an entity missing a required property is refused.

When no declared type fits what you want to record, propose one with graph_propose_vocabulary, then write. Each proposal is answered approved or rejected, with the reason. A rejected answer that lists duplicates is the deduplication catch, not a failure: the vocabulary already has a type like the one proposed, and the answer names it, so use that type. A property the type already declares is left as it is; to change it, use graph_revise_vocabulary's updateProperties. Resending the whole call is safe: it adds nothing new — a type already added is answered rejected as a duplicate, a property already declared is answered approved and left as it is, and any other change already applied leaves the vocabulary as it was — though vocabularyVersion may advance.

A type that declares no properties takes any keys. Its first declared property closes it: from then on, every key of its entities that the type does not declare is refused, and the entities already written with other keys become invalid. So graph_propose_vocabulary answers a type's first property rejected; declare it with graph_revise_vocabulary if that is intended, then run graph_validate.

The write tools that take a list write its members one at a time, in order, and answer each: those written, and those refused with why, each named by its 0-based member index. Resend only the failed members, corrected where the refusal asks for it; the others are written. A member refused with MEMORY_GRAPH_ENTITY_CONFLICT lost a race for its slug and is simply sent again as it is. An outage or a timeout ends the call at that member, answered as endedAt, and no member after it is tried. A refusal never undoes the members already written, so after an endedAt resend the failed members and those from endedAt.member on. An endedAt member with code MEMORY_GRAPH_QUERY_TIMEOUT was not written — the store rolled it back — so it can be resent as it is; after any other code it may or may not have been written, so look for it before resending it. A connection to the store lost while the member was being written is one such other ending. A graph deleted while a call writes to it keeps nothing the call wrote, and a call that meets the graph gone part-way answers NOT_FOUND for the graph.

Choosing a query tool

Every graph_open answer carries this guidance with the vocabulary it is about.

  • To see what surrounds an entity you know, use graph_explore; its answer is grouped by relationship type, which also shows what types exist around it.
  • To learn how two entities you know connect, use graph_find_paths.
  • To look an entity up by label words, type or exact property values, use graph_find_entities.
  • To look up by label or summary words across one, several or all of your graphs, use graph_search. It is text matching, in label order, with no ranking by relevance.
  • For multi-hop navigation with filters at each step, use graph_traverse.
  • For everything about one entity — its properties, data and who wrote it — use graph_get_entity; for an entity's relationships with their ids, use graph_get_relationships; for when it was created and changed, use graph_timeline.

Discover before you traverse: use graph_find_entities or graph_explore first. In a traversal, leave relationshipTypes out to follow every type, and use one multi-step traversal rather than a chain of single-hop calls or a query per relationship type. returnMode all shows the entities in between and path the relationships walked. detailLevel brief cuts an answer's size: brief is an entity's id, slug, type, label and summary, summary adds its properties, and full adds its raw data and who wrote it and when. truncated with truncationReason result_limit and hasMore marks a page boundary: page on with offset up to the offset limit, and narrow the traversal past it. Any other truncated answer was cut short: narrow the traversal.

Templates

A new graph starts from a template, which gives it a vocabulary and usage instructions. empty, the default, declares no types: your agents propose them as they learn what the graph needs. personal is a private memory of people, places, organisations, preferences, goals and decisions, with supersession, so a changed preference or decision is recorded as a new entity that supersedes the old one.

A template's usage instructions are copied into the graph when it is created and become the graph's own: graph_open returns them, and graph_update replaces them. A later change to a template changes no existing graph.

Paging and bounds

graph_find_entities, graph_get_relationships, graph_timeline and graph_traverse answer one page at a time. When there is more, call again with the next offset and every other argument unchanged. graph_validate pages by its own nextOffset until it answers done.

The most a call answers:

  • graph_find_entities: up to 50 entities to a page, 20 unless limit says otherwise, filtering on at most 20 properties.
  • graph_get_relationships: up to 50 relationships to a page, 50 by default.
  • graph_timeline: up to 50 events to a page, 20 by default.
  • graph_search: up to 50 hits from each graph, 10 by default, from at most 1,000 graphs named in graphIds. A graph with more matches than the limit answers its first by label, and says how many match in total; to reach the rest, narrow the words or entityTypes, or use graph_find_entities with searchTerm and offset on one graph.
  • graph_explore: from 1 to 3 relationships away, 1 by default, and up to 50 entities for each relationship type at each distance, 10 by default.
  • graph_find_paths: paths crossing from 1 to 5 relationships, 3 by default, and up to 50 paths, 5 by default.
  • graph_traverse: up to 200 results, 50 by default, over 1 to 10 steps; a repeated step walks again at most 10 times, and a traversal reaches at most 10 hops in all: each step counts one, a repeated step counts its maxDepth. Each filter list holds at most 20 conditions, and a projection names at most 20 properties. Its offset skips at most 1,000 results. Paging stops at the offset limit; to reach results beyond it, narrow the start, the types or the filters.
  • graph_validate: up to 200 issues to a page, 200 by default.

The most a write call carries: 100 entities to graph_create_entities, graph_update_entities or graph_delete_entities; 500 relationships to graph_create_relationships or graph_delete_relationships; and 50 proposals to graph_propose_vocabulary or graph_revise_vocabulary.

The longest text each field takes: a graph's name 200 characters, its description 4,000 and its usage instructions 16,000; an entity's label 1 to 200 and its summary 4,000; a type name 100, a type's description 1,000 and a proposal's justification 1,000; a property key 100; a search term 1 to 2,000; and an entity reference, an id or a slug, 400. A type declares at most 50 properties, each named once in a list, and an enum property at most 100 values. A property's defaultValue is a value of its own type, and as text at most 1,000 characters. A revision's removeProperties names at most 50 distinct properties.

A type name starts with a letter or an underscore and holds only letters, digits and underscores. A relationship type is written as the vocabulary answers it: upper-case words joined by single underscores, each word letters optionally followed by digits, or digits alone — WORKS_AT, LED_TO, HAS_2_FA.

How large a graph's vocabulary may grow is under Its limits.

Its limits

The most graphs your account may hold is the platform's limit, one value for every account. It is a setting and can change, so it is not written here. A graph_create that would take your account past it is refused, naming how many graphs your account holds and the most it may, and changes nothing. Deleting a graph frees its place. Lowering the limit deletes nothing: graphs already over it stay readable, writable and deletable.

The size of a graph's vocabulary is fixed: a graph's vocabulary holds at most 200 entity types and 200 relationship types, and at most 262,144 bytes as JSON. A proposal that would grow it past either is answered rejected and changes nothing; reuse a declared type, shorten descriptions or enum lists, or remove what is unused with graph_revise_vocabulary. A change that makes the vocabulary smaller — deleting a type, removing a property, shortening a description — is never refused for the size.

Changes sent at the same time. Vocabulary changes sent to one graph at the same time are each applied against what the others wrote; one that keeps colliding with them ends the call at that proposal, answered as endedAt (MEMORY_GRAPH_VOCABULARY_CONFLICT) — open the graph and send again whatever is still missing. The proposals approved before it were written and stay.

What it does not do yet

  • Semantic search. graph_search and graph_find_entities match words in labels and summaries, regardless of letter case; there is no search by meaning and no query syntax.
  • Import and export. A graph cannot be loaded from a file or written out to one.
  • A history of prior values. graph_timeline records that an entity was created and changed and which relationships were added, not what it held before a change. Nothing is versioned, so an update or a delete cannot be undone.