Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Knowledge Ingestion

Recipes for loading data into Quipu — from single triples to batch imports.

Turtle Files (Bulk Load)

The fastest way to load structured data:

quipu knot infrastructure.ttl --db knowledge.db

With SHACL validation:

quipu knot infrastructure.ttl --db knowledge.db --shapes shapes/infra.shapes.ttl

With a specific timestamp (for valid-time):

quipu knot infrastructure.ttl --db knowledge.db --timestamp 2026-04-01

Via REST:

curl -s localhost:3030/knot -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "turtle": "@prefix hw: <http://example.org/homelab/> .\nhw:koror a hw:Host ; hw:hostname \"koror.example\" .",
    "timestamp": "2026-04-01",
    "actor": "bulk-import"
  }'

Episodes (Agent Observations)

Episodes are the structured write path for agents. Each episode is a transaction with nodes, edges, and provenance:

curl -s localhost:3030/episode -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "name": "discovery-run-42",
    "source": "prometheus-sd",
    "group_id": "monitoring",
    "episode_body": "Periodic service discovery sweep",
    "nodes": [
      {"name": "redis", "type": "Service", "description": "Cache layer"},
      {"name": "memcached", "type": "Service"}
    ],
    "edges": [
      {"source": "redis", "target": "koror", "relation": "runsOn"},
      {"source": "memcached", "target": "palau", "relation": "runsOn"}
    ]
  }'

Episode Fields

FieldRequiredDescription
nameYesEpisode identifier (becomes rdfs:label)
nodesYesArray of entities to create
edgesYesArray of relationships
sourceNoAgent/system that produced this
group_idNoLogical grouping (e.g., “monitoring”)
episode_bodyNoHuman-readable description
shapesNoInline SHACL shapes for validation
replace_snapshotNoAtomically replace prior facts from this episode name; use for complete inventories

Node Fields

FieldRequiredDescription
nameYesEntity name (used to generate IRI)
typeNoRDF type (e.g., “Service”, “Host”)
descriptionNoHuman-readable description (rdfs:comment)
propertiesNoKey-value map of additional properties

Edge Fields

FieldRequiredDescription
sourceYesSource entity name
targetYesTarget entity name
relationYesPredicate: a bare name lands in aegis: (e.g. "runsOn"); a declared prefix (owl:sameAs, rdfs:seeAlso, rdf:, skos:, prov:, quipu:, xsd:, sh:) or a full <http://…> IRI is emitted verbatim; anything else is a 400, never a silent rewrite. See REST API → edge relation.

Aliases / entity dedup. owl:sameAs is the convention, and it is writable from /episode. It was not always: it used to land as the inert aegis:owl_sameAs behind a 200. Reuse existing node names byte-for-byte, and check retrievability with the reader’s own query rather than trusting count > 0; the full recipe is in the REST API reference.

Graphiti-Compatible Ingestion

For systems already using the Graphiti API format:

curl -s localhost:3030/episodes/complete -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "name": "flat-episode",
    "episode_body": "Ingested via Graphiti compat endpoint",
    "entity_nodes": [
      {"name": "svc-1", "entity_type": "Service", "summary": "A service"}
    ],
    "episodic_edges": [
      {"source_node_name": "svc-1", "target_node_name": "host-1", "relation_type": "runsOn"}
    ]
  }'

Batch Patterns

Multiple Turtle files

for f in data/*.ttl; do
  echo "Loading $f..."
  quipu knot "$f" --db knowledge.db
done

Episodes from a JSON array

# episodes.json contains an array of episode objects
cat episodes.json | jq -c '.[]' | while read -r episode; do
  curl -s localhost:3030/episode -X POST \
    -H "Content-Type: application/json" \
    -d "$episode"
done

Idempotent ingestion

Episodes with the same entity names update existing entities rather than creating duplicates. The entity IRI is derived from the name, so repeated ingestion is safe.

Validated Ingestion Pipeline

For production use, always validate:

  1. Load shapes first:

    quipu shapes load --name infra --file infra.shapes.ttl --db knowledge.db
    
  2. Dry-run validate:

    quipu validate --shapes infra.shapes.ttl --data new-data.ttl
    
  3. Ingest with shapes:

    quipu knot new-data.ttl --db knowledge.db --shapes infra.shapes.ttl
    

If validation fails, the write is rejected and no facts enter the log.

MCP Tool Ingestion

For agents using MCP tools:

Assert triples

{
  "tool": "quipu_knot",
  "input": {
    "turtle": "@prefix hw: <http://example.org/homelab/> .\nhw:koror a hw:Host .",
    "shapes": "@prefix sh: ... optional validation ..."
  }
}

Ingest episode

{
  "tool": "quipu_episode",
  "input": {
    "name": "agent-observation",
    "source": "my-agent",
    "nodes": [{"name": "x", "type": "Thing"}],
    "edges": []
  }
}

Retraction (Removing Facts)

Retract all facts about an entity:

quipu retract "http://example.org/homelab/old-host" --db knowledge.db

Retract a specific predicate:

quipu retract "http://example.org/homelab/koror" \
  --predicate "http://example.org/homelab/cpuCores" --db knowledge.db

Via REST:

curl -s localhost:3030/retract -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "entity": "http://example.org/homelab/old-host",
    "timestamp": "2026-04-04",
    "actor": "cleanup-agent"
  }'

Retractions don’t delete data — they close the valid-time window. The original facts remain in the log for audit and time-travel.