Diese Seite ist eine Übersetzung. Maßgeblich ist die englische Fassung: English

Für Ingenieure und Wissenschaftler

Geben Sie Ihren Agenten ein Protokoll, dem sie nicht schmeicheln können.

Ein Python-Paket, keine Abhängigkeiten. Sie legen fest, wer prüft, welche Läufe als aufgezeichnet zählen und was die Arbeit kostet; HypGraph setzt das durch, prüft jede Behauptung beim Schreiben und leitet ab, was wirklich gezeigt wurde.

Installation

pip install hypgraph            # engine, CLI, JSON API, UI — standard library only
pip install "hypgraph[mcp]"     # + the MCP server (FastMCP)

Python 3.10+. Eine SQLite-Datei pro Protokoll (Graph("research.hypgraph")) oder Graph() im Speicher für Tests und Notebooks.

Das Modell

Typisierte Knoten — Frage, Hypothese, Experiment, Lauf, Ergebnis, Behauptung, Quelle, Prüfung, Entscheidung — verbunden durch typisierte Verknüpfungen, deren Endpunkte durchgesetzt werden (supports, challenges, requires, tests, produced, replicates, based_on …). Alle Tabellen sind nur anhängend; jeder Schreibvorgang ist ein Ereignis in einer SHA-256-Kette. Kein Stand wird gespeichert. Jedes Lesen leitet ab:

  • Prüfung — geprüft / ungeprüft / angefochten / veraltet, aus den Urteilen der Prüfer zur aktuellen Version;
  • Risiko — vorgelagert gefallene Prämissen (AT RISK), Entscheidungen, deren Grundlage sich bewegt hat (REVISIT), alle Stützung gefallen (UNSUPPORTED);
  • unabhängige Linien — Stützen, die weder Lauf noch Datenartefakt noch Quelle teilen;
  • Belege — die stärkste Linie: von einem Agenten gemeldet → vom Instrument aufgezeichnet → reproduziert → unabhängige Implementierung → neue Daten → externe Quelle.

Die vollständigen Regeln stehen im Protokollformat; hypgraph schema --json gibt das Vokabular aus.

Richtlinie und Rollen

from hypgraph import Graph, Policy

POLICY = Policy(
    reviewers={"critic", "stats-reviewer"},   # whose verdicts count (an investigation can add its own)
    instrument="runner",                      # the only author whose kind=lab runs are "recorded" evidence
    operator="operator",                      # revises anything, settles disputes
    importer="importer",                      # brings legacy records in, with their original dates
)
g = Graph("research.hypgraph", policy=POLICY)
Authentifizierung ist Ihre Sache. HypGraph nimmt author= beim Wort. Wo immer ein Agent den Graphen erreicht, setzen Sie seinen Autor auf Ihrer Seite fest und lehnen POLICY.reserved ab — genau das tun der mitgelieferte MCP-Server und die HTTP-API.

Ein Muster, das für ein Team von Agenten funktioniert:

Rolle in Ihrem Systemin HypGraph
arbeitende Agenten (Forscher, Analysten)jeder unter eigenem Autorennamen; ihre Behauptungen werden geprüft; ihre Prüfungen sind Kommentare
Kritiker- / Prüfer-Agentenreviewers; ihre Urteile setzen den Stand; nie über ihre eigenen Knoten
der Job-Ausführer / die Sandbox, die Code ausführtinstrument: zeichnet Läufe mit Hashes auf, damit Ergebnisse als aufgezeichnet zählen
Sie selbst oder ein Orchestrator, der für Sie handeltoperator

Agenten verbinden

MCP

Ein Serverprozess pro Agentenidentität — der Autor wird beim Start festgelegt:

hypgraph --db research.hypgraph --as critic mcp                                # stdio
hypgraph --db research.hypgraph --as worker-1 mcp --transport http --port 8766 # streamable HTTP

Oder bauen Sie ihn im Prozess (eigene Richtlinie, eigene Authentifizierung): hypgraph.mcp.build(graph, author="worker-1") gibt den FastMCP-Server zurück.

Ausführbare Beispiele, dieselbe Geschichte auf sechs Arten eingebunden (LangChain, LangGraph, PydanticAI, CrewAI, OpenAI Agents SDK, Claude Agent SDK): examples/frameworks. Jedes läuft offline mit einem geskripteten Modell oder mit --real auf einem echten.

Agent Skill

hypgraph skill --install schreibt .claude/skills/hypgraph/SKILL.md: die Methode, die Ablehnungen und ihre Korrekturen, über die CLI.

Ihre eigenen Werkzeuge

Jedes Framework, das Python-Funktionen als Werkzeuge annimmt (Claude Agent SDK, LangChain, LangGraph, OpenAI Agents …) — geben Sie den Ablehnungstext zurück, damit das Modell darauf reagieren kann:

from hypgraph import GraphError

def add_claim(statement: str, scope: str, result: str, measurements: list[dict], why: str) -> str:
    """Add a claim supported by a result; every number in the statement must be bound in measurements."""
    try:
        n = g.create(author=AGENT_NAME, type="claim", inv=INV,
                     fields={"statement": statement, "scope": scope, "measurements": measurements},
                     links=[{"rel": "supports", "from": result, "why": why}])
        return f"Added {n['code']}: {n['state']['sentence']}"
    except GraphError as e:
        return f"Refused — fix this and retry:\n{e}"

Legen Sie g.report(inv)["markdown"] oder den MCP-Text overview zu Beginn einer Aufgabe in den Kontext des Agenten, damit er liest, was feststeht, bevor er es neu herleitet.

Läufe aufzeichnen

from hypgraph import file_sha256

data, code = file_sha256("evals/heldout.jsonl"), file_sha256("eval.py")
a_data = g.create(author="runner", type="artifact", inv=INV, fields={**data, "format": "jsonl"})["id"]
a_code = g.create(author="runner", type="artifact", inv=INV, fields={**code, "format": "py"})["id"]
run = g.create(author="runner", type="run", inv=INV, fields={"kind": "lab", "state": "succeeded", "seed": 7},
               links=[{"rel": "uses", "to": a_data, "data": {"role": "data"}},
                      {"rel": "uses", "to": a_code, "data": {"role": "code"}}])["id"]
res = g.create(author="runner", type="result", inv=INV, links=[{"rel": "produced", "from": run}],
               fields={"summary": "Held-out accuracy, fine-tune vs baseline.",
                       "values": {"finetune": {"k": 1624, "n": 2000}, "baseline": {"k": 1568, "n": 2000}}})["id"]

Artefakte werden über ihren Inhalt adressiert (gleiche Bytes, ein Knoten), daher verschmelzen zwei Ergebnisse aus denselben Daten automatisch zu einer Beleglinie. Ein erneuter Lauf verknüpft replicates mit data.differs (new_seed, new_impl, new_data …) und hebt das Belegprofil an.

Profile und Workflows

Setzen Sie ein Profil auf die Untersuchung, und das Protokoll setzt die Regeln dieser Disziplin durch — und mit den Profilen @2 ihren Workflow: Phasen mit Schranken, was Belege ungültig macht, die typisierten Formen, die ein Diff, eine Instanz, eine Quelle oder eine Position haben müssen. Die Phase wird bei jedem Lesen abgeleitet, wie der Stand.

inv = g.create(author="operator", type="investigation",
               fields={"title": "Issue 412", "objective": "Fix the importer and prove it.", "profile": "software@2"})["id"]
q = g.create(author="operator", type="question", inv=inv, fields={"text": "Does the importer keep quoted commas?"})["id"]

g.stage(q)        # {"stage": "implemented", "index": 2, "of": 6, "complete": False,
                  #  "next": "no live artifact kind=diff (meta: base_sha, head_sha, files) part_of Q1", "gates": [...]}
g.stages(inv)     # one row per live question;  GET /api/graph/stages?inv=…  ·  MCP tool: stage
g.completion(inv) # the profile's criteria for the whole investigation
  • Schranken sind Prädikate. hypothesis_stated, preregistered, diff_registered, tests_pass_current, backtested, positioned, still_valid, solved, result_reviewed … — auch als done_when eines Auftrags verwendbar.
  • Was ungültig macht. Zusätzlich zu den Prämissen fügt ein Workflow uses (eine korrigierte Datendatei, ein ersetzter Diff oder eine ersetzte Instanz) oder cites (eine zurückgezogene Quelle) zu dem hinzu, was Abhängige GEFÄHRDET — pro Profil, daher behalten Protokolle unter @1 genau den Stand, den sie hatten.
  • Aktualität. Ein Ergebnis, dessen Lauf ein inzwischen ersetztes Artefakt verwendet hat oder dessen valid_until verstrichen ist, ist VERALTET; eine Behauptung ist veraltet, wenn alle sie stützenden Ergebnisse es sind. Eine Prüfung auf veralteten Belegen erfüllt reviewed nicht.
  • Typisierte Formen. artifact.kind + meta (ein diff braucht base_sha, head_sha, files), source.kind, decision.kind=position mit size, stop, review_date, paper — abgelehnt, wenn unvollständig. Dieselben Bytes erneut aufzuzeichnen typisiert ein untypisiertes Artefakt und fügt die genannten Verknüpfungen hinzu.
  • Ehrliche Abschlüsse. Eine geprüfte inconclusive-Antwort, eine no_position-Entscheidung, ein verifizierter Fehlschlag (ein vom Instrument aufgezeichnetes negatives Ergebnis, geprüft) schließen jeweils eine Frage ab, die die Schranken allein nie abschließen würden.

Die Methode, aus der jeder Workflow stammt, und die Entscheidung hinter jeder Schranke haben jeweils eine Seite in docs/workflows. Der Planer von HypSwarm erstellt den Phasenauftrag jeder Frage genau daraus.

Rezept: Evaluationsberichte, die halten

  1. Präregistrieren. Ein Experiment mit tests-Verknüpfungen zu jeder Hypothese („das Fine-Tuning ist besser“, „kein Unterschied“), jede mit data.expected, vor dem Lauf.
  2. Zeichnen Sie den Lauf auf wie oben; tragen Sie die Zählungen als {k, n} in die Werte des Ergebnisses ein, nicht nur Prozente.
  3. Behaupten Sie mit Messungen. „1.624 von 2.000 gegenüber 1.568“ ist gebunden; „5 Punkte besser“ ohne Ergebnisfeld für die 5 wird abgelehnt.
  4. „Kein Unterschied“ braucht Teststärke. Pflanzen Sie einen bekannten Effekt ein (fügen Sie einer Kopie der Vorhersagen einen Unterschied von 2 Punkten hinzu), zeigen Sie, dass der Vergleich ihn bei dieser Stichprobengröße erkennt, und markieren Sie dieses Ergebnis als power: "demonstrated". Ohne das zeichnen Sie die Behauptung als inconclusive auf — das Protokoll besteht darauf.
  5. Prüfung. Ein Kritiker-Agent führt den Bewerter erneut auf den gespeicherten Vorhersagen aus; sein pass oder fail wird an diese Version der Behauptung geheftet.
  6. Bericht. g.report(inv)["markdown"] ist die Faktentabelle für den Anfang des Berichts; sie kann keine zurückgezogene Behauptung erneut aufstellen.

Kosten

def spend(inv, tasks, since=None):            # your meter: model calls per task id
    rows = ledger.calls(task_ids=tasks, since=since)
    return {"usd": sum(r.usd for r in rows), "calls": len(rows), "input_tokens": ..., "cached_tokens": ...,
            "output_tokens": ..., "by_task": [{"task_id": t, "usd": ...} for t in tasks]}

g = Graph("research.hypgraph", policy=POLICY, spend=spend)
with g.writing_for(task_id):                    # events carry the task
    g.create(...)
g.cost(inv)        # $ for the investigation, per node, per verdict, and since the record last moved

HTTP und die Oberfläche

hypgraph serve --db research.hypgraph            # HypGraph UI + JSON API on 127.0.0.1:8765, read-only
hypgraph serve --db research.hypgraph --writable # + POST /api/graph/write

In Ihrer eigenen App (gezeigt mit FastAPI; jedes Framework geht):

from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from hypgraph.server import dispatch
from hypgraph.ui import static_dir

app = FastAPI()
app.mount("/hypgraph", StaticFiles(directory=static_dir(), html=True))

@app.get("/api/graph/{path:path}")
def graph_api(path: str, request: Request):
    status, body = dispatch(g, "GET", "/" + path, dict(request.query_params))
    return body          # (set the status code too)

Die Oberfläche braucht kein Framework und keinen Build-Schritt: HypGraph.mount(el, {source: HypGraph.HttpSource('/api/graph')}) oder HypGraph.BundleSource(bundle) ganz ohne Server. Sie verfolgt GET /api/graph/stream live, und die Telemetrie eines HypSwarm-Laufs (<record>.telemetry.jsonl) ergänzt jeden Modell- und Werkzeugaufruf. hypgraph export --telemetry trägt sie zum Abspielen in einem Bundle, außerhalb der Hash-Kette; {play: true} öffnet im Abspielmodus. Gestalten Sie sie mit den CSS-Variablen --hg-*. Sehen Sie sie in den Live-Beispielen.

Bundles in der CI

hypgraph export -o record.bundle.json     # every event as the exact string that was hashed
hypgraph verify record.bundle.json        # exit 1 if anything was edited

Veröffentlichen Sie den head-Hash des Bundles an einer Stelle, die Sie nicht kontrollieren (ein Tag, ein Paper, ein Ticket), wenn ein Ergebnis veröffentlicht wird: Ein späteres Protokoll, das durch diesen Hash läuft, kann nichts Früheres umgeschrieben haben. Prüfer können ein Bundle unter /verify prüfen, ohne etwas zu installieren.

Den Bericht prüfen

Der meiste Hype steckt im Abschlussbericht, nicht im Protokoll. hypgraph check liest Markdown gegen das Protokoll und endet bei Fehlern mit 1, sodass die Zusammenfassung, die ein Agent abgibt, dem nicht widersprechen kann, was tatsächlich gezeigt wurde:

hypgraph check summary.md              # errors fail
hypgraph check --strict summary.md     # warnings (unsourced numbers …) fail too
Fehlererkennt
REPORT_OVERSTATES_STANDING„gesichert“ / „bestätigt“ für eine Behauptung, die das Protokoll als ungeprüft, angefochten oder gefährdet führt
REPORT_CITES_WITHDRAWNeine zurückgezogene Behauptung zitieren, als hätte sie noch Bestand
REPORT_UNKNOWN_CODEein Zitat einer Behauptung oder eines Ergebnisses, das es nicht gibt
REPORT_ABSOLUTE_UNBACKED · REPORT_ABSENCE_UNBACKED · REPORT_SIGNIFICANCE_UNBACKEDnie / kein Effekt / signifikant ohne etwas Zitiertes dahinter

Warnungen: Zahlen, die kein Ergebnis enthält oder impliziert, ausgelassene Streitpunkte, ersetzte Zitate, kausale Sprache ohne Zitate. Codeblöcke, Inline-Code, URLs, zitierte Passagen, Verneinungen („lässt sich nicht zeigen“) und Anweisungen („verwende nie den Evaluationsdatensatz“) werden nicht als Behauptungen behandelt — kalibriert an zwei Dutzend echten Berichten, geprüft gegen das lebende Protokoll, über das sie berichten. In Python: g.check_text(text, inv); über HTTP: POST /api/graph/check; für Agenten: das MCP-Werkzeug check_report.

Das Ablehnungs-Log

Jede abgelehnte Behauptung wird aufgezeichnet — Autor, Aussage, Codes — und als korrigiert markiert, wenn derselbe Autor mit der ehrlichen Fassung zurückkommt. Das ist die Zahl, die Ihnen sonst niemand zeigen kann: wie oft Ihre Agenten versucht haben zu übertreiben, welche Regeln sie gestoppt haben und welche sie stillschweigend aufgegeben haben.

hypgraph refusals                      # per author and rule, fixed vs open
hypgraph refusals --author analyst     # one agent's refusals in full

Nur anhängend, mit eigener Hash-Kette (g.verify_refusals()), getrennt von der des Protokolls. In Python: g.refusal_report(inv); HTTP GET /api/graph/refusals; MCP refusal_log; und eine Zählung abgelehnter Überbehauptungen in der HypGraph-Übersicht.

Leistung

Knotenden gesamten Stand ableiteneinen Knoten lesenschreibendie Kette verifizieren
1,0010.047 s0.1 ms5.7 ms0.028 s
3,3010.095 s0.07 ms6.0 ms0.07 s
10,0010.37 s0.07 ms11.4 ms0.21 s

Reines Python, gemessen mit python dev/bench.py --nodes N auf einem Windows-Laptop (Python 3.13, hypgraph 0.6). Sein Protokoll ist eine Untersuchung aus Fragen, an Werte gebundenen Ergebnissen, daraus abgeleiteten Behauptungen und Prüfungen einiger davon; die Ableitung wächst im selben Tempo wie das Protokoll. Ein Schreibvorgang kostet, was er berührt: Snapshots teilen die unberührten Daten, und der zwischengespeicherte Stand bleibt für jeden Knoten erhalten, den der Schreibvorgang nicht erreichen kann (geprüft gegen einen vollständigen Neuaufbau mit einer randomisierten Testsuite). Das meiste eines Schreibvorgangs ist SQLite, das auf die Platte schreibt. Führen Sie den Benchmark auf Ihrem Rechner und in Ihren Größen aus.