Shirube

Documentation

Graph memory

A property graph of people, projects, and typed edges. Reads are cheap and sync; writes are queued to three background workers.

What it is

The graph is a property graph: nodes (Person, Project, Technology, Preference, …) and typed edges (WORKS_ON, USES, PREFERS, …). It is not a chat log and not vector search.

On each run with .graph(true):

  1. Read (sync, cheap): graph.contextFor(prompt, userId) is appended to the system prompt so the model can use known facts.
  2. Write (async): the user + assistant text is queued. Three timers process the queue. Your run() returns without waiting for extraction.

Use when you want durable, queryable structure: who works on what, which stack, what they prefer. Typical products: support, CRM-like assistants, internal “who owns this service?” bots.

Use memory instead when you only need similar past sentences. Use sessions when you only need the current thread.

Start it

graph.ts
import { Agent, graph, createFileGraph } from "shirube-ai";

graph.start();

const agent = Agent.builder()
  .name("ops")
  .instructions(
    "Use the knowledge graph about this user. If a fact is missing, ask — do not invent employers or repos.",
  )
  .apiKey(process.env.OPENAI_API_KEY!)
  .graph(true)
  .build();

await agent.run("I'm Ada. I am working on Project Shirube using TypeScript. I prefer sci-fi.", {
  userId: "ada",
});

await graph.flush();
console.log(graph.contextFor("Shirube TypeScript", "ada"));

Persist across restarts:

file-graph.ts
const runtime = await createFileGraph("./.shirube-graph.json");
runtime.start();

Agent.builder().graph(runtime);

.graph(false) or omitting .graph() means this agent neither reads nor ingests.

The three background processes

They are separate intervals, not steps inside run(). One failing does not stop the others. Each item is retried up to three times; counters live on graph.stats.

1. Memory extraction

Reads queued conversations. Upserts nodes for people, projects, technologies, preferences, and similar entities (names, “working on X”, “using Y”, “I prefer Z”).

Problem it solves: transcripts are messy; the graph needs canonical nodes.

2. Relationship builder

Looks at the same text (and new nodes) and creates edges. The same (from, type, to) updates confidence instead of inserting a duplicate.

Problem it solves: “Ada uses TypeScript” mentioned twice should not be two edges.

3. Graph maintenance

Merges nodes that normalize to the same id, decays stale edges, prunes very low-confidence isolates, boosts frequently seen links.

Problem it solves: graphs rot — duplicates, dead links, contradictions as noise.

Production uses extractMs / relateMs / maintainMs (defaults 250 / 400 / 2000). Tests call graph.flush().

workers.ts
graph.start({ extractMs: 250, relateMs: 400, maintainMs: 2000 });
graph.stats;

Retrieval

contextFor scores nodes against the query (token overlap + confidence + mention count) and prints neighbors:

context
Knowledge graph:
- (Project) Shirube
    USES → (Technology) TypeScript
- (Person) Ada
    WORKS_ON → (Project) Shirube

Scope with userId so tenants do not see each other’s graph.

Opt out / inspect

graph.reset() clears store and workers (used in tests). graph.stop() pauses timers without wiping data.