Hindsight ohne Plugin: retain, recall und reflect mit curl
Nicht jedes Werkzeug hat ein Hindsight-Plugin. Ein Skript, ein Cron-Job oder ein CI-Schritt spricht die HTTP-API direkt an. Drei Aufrufe, und die Stellen, an denen sie sich anders verhalten als erwartet.

In zwei früheren Beiträgen ging es darum, warum ich das Gedächtnis meiner Agents in Hindsight ablege und wie das Ganze auf Coolify läuft. Für Claude Code gibt es ein Plugin, für Grok und Cursor einen kleinen Wrapper um denselben MCP-Server. Damit sind die Agents versorgt.
Was dann noch fehlt, sind die Dinge ohne Plugin: ein Shell-Skript, ein Cron-Job, ein Schritt in der Pipeline. Die reden direkt mit der HTTP-API. Das sind im Kern drei Aufrufe: retain schreibt, recall sucht, reflect antwortet. Jeder davon hat eine Eigenheit, die man kennen sollte, bevor man ihn in einer Schleife laufen lässt.
Vorbereitung
Zwei Variablen reichen:
export HINDSIGHT_URL="https://memory.example.com"
export HINDSIGHT_TOKEN="…"
Läuft der Server mit der API-Key-Tenant-Extension, gehört der Key als Bearer-Token in den Authorization-Header. Ohne ihn kommt ein 401 zurück. /health bleibt offen.
Alle Pfade für eine Bank beginnen mit /v1/default/banks/<bank>. Ein nacktes /banks gibt 404. Wenn du einen Pfad suchst, schau in /openapi.json, das geht schneller als Raten.
In den Beispielen heißt die Bank my_websites, so wie das Repo. Bei mir bekommt jedes Repo eine eigene Bank.
Schreiben mit retain
curl -s -X POST "$HINDSIGHT_URL/v1/default/banks/my_websites/memories" \
-H "Authorization: Bearer $HINDSIGHT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"items": [{
"content": "Die Portfolio-Seite wird aus apps/portfolio gebaut, nicht mehr aus dem alten Repo.",
"context": "deploy notes",
"document_id": "deploy-notes-portfolio",
"timestamp": "2026-10-01T10:00:00Z"
}]
}'
Hindsight sucht später nicht im Originaltext. Ein LLM zieht beim Schreiben Fakten, Entitäten und Zeitpunkte heraus, und mit diesen Fakten arbeitet recall. context geht dabei direkt in den Extraktions-Prompt und verändert, was herauskommt. Die Doku nennt einen konsistent gesetzten context einen der wirksamsten Hebel für die Qualität der Erinnerungen.
timestamp ist der Zeitpunkt, an dem das Beschriebene passiert ist, nicht der Zeitpunkt des Imports. Lässt du das Feld weg, nimmt Hindsight die aktuelle Zeit. Für Material ohne echtes Datum, etwa Referenzdokumentation, gibt es den Wert "unset".
Ohne document_id landet alles doppelt
Für ein Skript, das regelmäßig läuft, ist document_id das wichtigste Feld. Mit einer ID ersetzt Hindsight das vorhandene Dokument samt allen daraus gezogenen Erinnerungen und verarbeitet den neuen Inhalt von vorn. Ohne ID vergibt Hindsight eine zufällige UUID. Lässt du denselben Inhalt zweimal durchlaufen, stehen danach doppelte Erinnerungen in der Bank.
Ein Cron-Job ohne document_id füllt die Bank also bei jedem Lauf mit denselben Fakten, ohne Fehlermeldung. Gib jedem logischen Dokument eine feste ID, zum Beispiel den Dateipfad oder changelog-2026-10, und der Job darf beliebig oft laufen.
Für Inhalte, die nur wachsen, wie ein Log oder ein Journal, gibt es "update_mode": "append". Dann schickst du nur den neuen Teil, Hindsight hängt ihn an und lässt unveränderte Abschnitte bei der Extraktion aus.
Asynchron, wenn es mehr wird
Bei größeren Mengen gehört "async": true in den Request. Der Aufruf kommt sofort mit einer operation_id zurück, die Arbeit erledigt der Worker im Hintergrund. Den Stand siehst du unter /v1/default/banks/<bank>/operations.
Geht die Antwort unterwegs verloren, weißt du nicht, ob die Operation angelegt wurde. Ein blinder Retry würde dann womöglich alles doppelt extrahieren und doppelt bezahlen. Erzeugst du die operation_id vorher selbst als UUID und schickst sie bei jedem Versuch mit, ist der Retry harmlos. Dieselbe ID liefert die ursprüngliche Operation zurück und legt keine neue Arbeit an.
Suchen mit recall
curl -s -X POST "$HINDSIGHT_URL/v1/default/banks/my_websites/memories/recall" \
-H "Authorization: Bearer $HINDSIGHT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "Aus welchem Ordner wird die Portfolio-Seite gebaut?",
"max_tokens": 1000
}'
Die Antwort ist eine Liste von Fakten, keine Dokumente. Jeder Eintrag hat ein text-Feld mit dem extrahierten Fakt, einen type (world, experience oder observation), die document_id, aus der er stammt, und ein paar Zeitfelder. Mit jq '.results[].text' hast du in einem Skript schnell das, was du brauchst.
max_tokens begrenzt nicht die Zahl der Treffer, sondern wie viel Platz sie zusammen einnehmen dürfen. Der Standard liegt bei 4096. Hindsight füllt dieses Budget in der Reihenfolge der Relevanz. Für ein Skript, das die Treffer in einen Prompt kopiert, ist das praktischer als ein top-k, weil du direkt in der Einheit rechnest, die auch das Modell zählt.
budget (low, mid, high, Standard mid) regelt dagegen, wie breit und tief gesucht wird. Für eine schnelle Nachschlage-Abfrage reicht low.
Ein recall ruft kein LLM auf. Die Suche läuft parallel über Vektoren, Volltext, den Entitätsgraphen und Zeitangaben, danach sortiert ein Reranker die Kandidaten.
Abfragen mit mehr als 500 Tokens lehnt der Server ab. Ein ganzes Dokument als Suchtext zu schicken, funktioniert also nicht.
Wenn recall langsam ist
Hängt "trace": true an den Request, liefert die Antwort eine Aufschlüsselung nach Phasen mit. So habe ich damals gesehen, dass von 9,82 Sekunden Recall 8,87 Sekunden auf das Reranking entfielen. Bevor du am Server drehst, schau zuerst hier rein.
Tags innerhalb einer Bank
Banks sind die harte Grenze zwischen Projekten. Innerhalb einer Bank kannst du mit Tags filtern. Beim Schreiben hängst du sie an ein Item ("tags": ["topic:deploy"]), beim Suchen gibst du sie mit:
{
"query": "Wie wird deployt?",
"tags": ["topic:deploy"],
"tags_match": "any_strict"
}
Auf tags_match kommt es an. Der Standard ist any, und any liefert zusätzlich alle Erinnerungen ohne Tags. Wer erwartet, dass ein Tag-Filter wirklich nur getaggte Treffer zurückgibt, braucht any_strict oder all_strict.
Antworten mit reflect
curl -s -X POST "$HINDSIGHT_URL/v1/default/banks/my_websites/reflect" \
-H "Authorization: Bearer $HINDSIGHT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "Was muss ich beachten, bevor ich die Portfolio-Seite neu deploye?",
"budget": "mid",
"include": { "facts": {} }
}'
recall liefert Rohmaterial, reflect liefert eine fertige Antwort als Markdown im Feld text. Dahinter läuft eine Agenten-Schleife: Das Modell entscheidet selbst, was es nachschlägt, sucht in mehreren Runden und schreibt erst danach. Mit "include": { "facts": {} } enthält die Antwort ein based_on mit den Erinnerungen, auf die sie sich stützt. Zitiert werden kann nur, was in der Schleife wirklich abgerufen wurde.
Im Betrieb verhält sich reflect deutlich anders als recall. Der Aufruf braucht ein LLM, kostet also jedes Mal Tokens und dauert länger. Der Standard-Timeout für die LLM-Aufrufe von reflect liegt bei 30 Sekunden, solange du kein globales HINDSIGHT_API_LLM_TIMEOUT setzt. Und budget steht hier standardmäßig auf low, nicht auf mid wie bei recall.
Fällt in der Schleife ein Werkzeug aus, zum Beispiel die Datenbank oder der Reranker, antwortet reflect mit einem 500, statt eine Antwort aus dem Nichts zu bauen. Für ein Skript ist das angenehm. Ein Fehler kommt als Fehler an, und ein 200 mit „dazu weiß ich nichts“ heißt, dass die Bank wirklich nichts dazu hat.
Welcher Aufruf wofür
Wenn dein Code oder dein Agent selbst entscheidet, was er mit den Fakten macht, nimm recall. Das geht schnell, braucht kein LLM, und max_tokens passt die Menge an den Prompt an. reflect lohnt sich, wenn am Ende ein Mensch eine Antwort lesen soll, etwa als Zusammenfassung vor einem Deploy, und ein paar Sekunden und ein paar Tokens dafür in Ordnung sind.
Prüfen, was angekommen ist
Ob ein Skript wirklich schreibt, siehst du am schnellsten in der Dokumentenliste:
curl -s "$HINDSIGHT_URL/v1/default/banks/my_websites/documents?q=deploy-notes" \
-H "Authorization: Bearer $HINDSIGHT_TOKEN"
q filtert dabei auf einen Teil der document_id. Taucht dieselbe ID nach mehreren Läufen genau einmal auf, ersetzt dein Job sauber. Lass den Filter weg, und du siehst alle Dokumente der Bank. Stehen dort nach ein paar Läufen lauter zufällige UUIDs mit ähnlichem Inhalt, fehlt irgendwo die document_id.