Instalación
pip install hypgraph # engine, CLI, JSON API, UI — standard library only pip install "hypgraph[mcp]" # + the MCP server (FastMCP)
Python 3.10+. Un archivo SQLite por registro (Graph("research.hypgraph")), o Graph() en memoria para pruebas y notebooks.
El modelo
Nodos tipados — pregunta, hipótesis, experimento, ejecución, resultado, afirmación, fuente, revisión, decisión — unidos por enlaces tipados cuyos extremos se hacen cumplir (supports, challenges, requires, tests, produced, replicates, based_on …). Todas las tablas son de solo anexión; cada escritura es un evento en una cadena SHA-256. Nada de la situación se almacena. Cada lectura deriva:
- revisión — comprobado / sin comprobar / impugnado / desactualizado, a partir de los veredictos de los revisores sobre la versión actual;
- riesgo — premisas caídas aguas arriba (
AT RISK), decisiones cuya base se movió (REVISIT), todo el apoyo caído (UNSUPPORTED); - líneas independientes — apoyos que no comparten ejecución, artefacto de datos ni fuente;
- evidencia — la línea más fuerte: notificada por un agente → registrada por el instrumento → reproducida → implementación independiente → datos nuevos → fuente externa.
Las reglas completas están en el formato del registro; hypgraph schema --json imprime el vocabulario.
Política y roles
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)author= al pie de la letra. Allí donde un agente llegue al grafo, fija su autor de tu lado y rechaza POLICY.reserved — eso es lo que hacen el servidor MCP incluido y la API HTTP.Un patrón que funciona para un equipo de agentes:
| rol en tu sistema | en HypGraph |
|---|---|
| agentes trabajadores (investigadores, analistas) | cada uno con su propio nombre de autor; sus afirmaciones se comprueban; sus revisiones son comentarios |
| agentes críticos / revisores | reviewers; sus veredictos fijan la situación; nunca sobre sus propios nodos |
| el ejecutor de trabajos / sandbox que ejecuta código | instrument: registra ejecuciones con hashes, así que los resultados cuentan como registrados |
| tú, o un orquestador que actúa en tu nombre | operator |
Conectar agentes
MCP
Un proceso de servidor por identidad de agente — el autor se fija al arrancar:
hypgraph --db research.hypgraph --as critic mcp # stdio hypgraph --db research.hypgraph --as worker-1 mcp --transport http --port 8766 # streamable HTTP
O constrúyelo dentro del proceso (política propia, tu propia autenticación): hypgraph.mcp.build(graph, author="worker-1") devuelve el servidor FastMCP.
Ejemplos ejecutables, la misma historia integrada de seis formas (LangChain, LangGraph, PydanticAI, CrewAI, OpenAI Agents SDK, Claude Agent SDK): examples/frameworks. Cada uno funciona sin conexión con un modelo guionizado, o con --real sobre uno real.
Agent Skill
hypgraph skill --install escribe .claude/skills/hypgraph/SKILL.md: el método, los rechazos y sus correcciones, a través de la CLI.
Tus propias herramientas
Cualquier framework que acepte funciones de Python como herramientas (Claude Agent SDK, LangChain, LangGraph, OpenAI Agents …) — devuelve el texto del rechazo para que el modelo pueda actuar en consecuencia:
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}"Pon g.report(inv)["markdown"] o el texto MCP overview en el contexto del agente al principio de una tarea, para que lea lo que está establecido antes de volver a derivarlo.
Registrar ejecuciones
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"]Los artefactos se direccionan por contenido (mismos bytes, un nodo), así que dos resultados calculados a partir de los mismos datos se funden automáticamente en una línea de evidencia. Una reejecución enlaza replicates con data.differs (new_seed, new_impl, new_data …) y eleva el perfil de evidencia.
Perfiles y flujos de trabajo
Fija un perfil en la investigación y el registro aplica las reglas de esa disciplina — y, con los perfiles @2, su flujo de trabajo: etapas con compuertas, qué invalida la evidencia, las formas tipadas que deben tener un diff, una instancia, una fuente o una posición. La etapa se deriva en cada lectura, como la situación.
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- Las compuertas son predicados.
hypothesis_stated,preregistered,diff_registered,tests_pass_current,backtested,positioned,still_valid,solved,result_reviewed… — utilizables también como eldone_whende una orden. - Qué invalida. Además de las premisas, un flujo de trabajo añade
uses(un archivo de datos corregido, un diff o una instancia sustituidos) ocites(una fuente retirada) a lo que deja EN RIESGO a los dependientes — por perfil, así que los registros bajo@1conservan exactamente la situación que tenían. - Vigencia. Un resultado cuya ejecución usó un artefacto ahora sustituido, o cuyo
valid_untilya pasó, está DESACTUALIZADO; una afirmación está desactualizada cuando lo están todos los resultados que la respaldan. Una comprobación sobre evidencia desactualizada no cumplereviewed. - Formas tipadas.
artifact.kind+meta(undiffnecesita base_sha, head_sha, files),source.kind,decision.kind=positioncon size, stop, review_date, paper — rechazados si están incompletos. Registrar de nuevo los mismos bytes tipa un artefacto sin tipo y añade los enlaces indicados. - Finales honestos. Una respuesta
inconclusivecomprobada, una decisiónno_position, un fallo verificado (un resultado negativo registrado por el instrumento, comprobado) completan cada uno una pregunta que las compuertas por sí solas nunca completarían.
El método del que sale cada flujo de trabajo, y la decisión detrás de cada compuerta, ocupan una página cada uno en docs/workflows. El planificador de HypSwarm emite la orden de etapa de cada pregunta exactamente a partir de esto.
Receta: informes de evaluación que se sostienen
- Prerregistra. Un experimento con enlaces
testsa cada hipótesis («el ajuste fino es mejor», «no hay diferencia»), cada una condata.expected, antes de ejecutar. - Registra la ejecución como arriba; pon los recuentos en los valores del resultado como
{k, n}, no solo porcentajes. - Afirma con mediciones. «1.624 de 2.000 frente a 1.568» queda ligado; «5 puntos mejor» sin un campo de resultado para el 5 se rechaza.
- «No hay diferencia» necesita potencia. Siembra un efecto conocido (inyecta una diferencia de 2 puntos en una copia de las predicciones), demuestra que la comparación lo detecta con este tamaño de muestra y marca ese resultado como
power: "demonstrated". Sin eso, registra la afirmación comoinconclusive— el registro insiste. - Revisión. Un agente crítico vuelve a ejecutar el evaluador sobre las predicciones guardadas; su
passofailqueda fijado a esta versión de la afirmación. - Informe.
g.report(inv)["markdown"]es la tabla de hechos para el principio del informe; no puede volver a enunciar una afirmación retirada.
Coste
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 movedHTTP y la interfaz
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
En tu propia aplicación (se muestra FastAPI; funciona cualquier framework):
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)La interfaz no usa frameworks ni paso de compilación: HypGraph.mount(el, {source: HypGraph.HttpSource('/api/graph')}), o HypGraph.BundleSource(bundle) sin ningún servidor. Sigue GET /api/graph/stream en directo, y la telemetría de una ejecución de HypSwarm (<record>.telemetry.jsonl) añade cada llamada al modelo y a herramientas. hypgraph export --telemetry la lleva en un bundle para reproducirla, fuera de la cadena de hashes; {play: true} abre en la reproducción. Dale tema con variables CSS --hg-*. Míralo en los ejemplos en directo.
Bundles en 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
Publica el hash de head del bundle en algún sitio que no controles (una etiqueta, un artículo, un ticket) cuando se publique un resultado: un registro posterior que pase por ese hash no puede haber reescrito nada anterior. Los revisores pueden comprobar un bundle en /verify sin instalar nada.
Revisar el informe
La mayor parte del hype vive en el informe final, no en el registro. hypgraph check lee markdown contra el registro y sale con 1 si hay errores, así que el resumen que entrega un agente no puede contradecir lo que de verdad se demostró:
hypgraph check summary.md # errors fail hypgraph check --strict summary.md # warnings (unsourced numbers …) fail too
| error | detecta |
|---|---|
REPORT_OVERSTATES_STANDING | «establecido» / «confirmado» sobre una afirmación que el registro tiene sin comprobar, impugnada o en riesgo |
REPORT_CITES_WITHDRAWN | citar una afirmación retirada como si siguiera en pie |
REPORT_UNKNOWN_CODE | una cita a una afirmación o un resultado que no existe |
REPORT_ABSOLUTE_UNBACKED · REPORT_ABSENCE_UNBACKED · REPORT_SIGNIFICANCE_UNBACKED | nunca / sin efecto / significativamente sin nada citado detrás |
Avisos: números que ningún resultado contiene ni implica, disputas omitidas, citas sustituidas, lenguaje causal sin citas. Los bloques de código, el código en línea, las URL, los pasajes citados, las negaciones («no se puede demostrar») y las instrucciones («nunca uses el conjunto de evaluación») no se tratan como afirmaciones — calibrado con dos docenas de informes reales comprobados contra el registro en vivo del que informan. En Python: g.check_text(text, inv); por HTTP: POST /api/graph/check; para agentes: la herramienta MCP check_report.
El registro de rechazos
Cada afirmación rechazada se registra — autor, enunciado, códigos — y se marca como corregida cuando el mismo autor vuelve con la versión honesta. Es el número que nadie más puede enseñarte: con qué frecuencia tus agentes intentaron sobreafirmar, qué reglas los pararon y cuáles abandonaron en silencio.
hypgraph refusals # per author and rule, fixed vs open hypgraph refusals --author analyst # one agent's refusals in full
De solo anexión con su propia cadena de hashes (g.verify_refusals()), aparte de la del registro. En Python: g.refusal_report(inv); HTTP GET /api/graph/refusals; MCP refusal_log; y un recuento de sobreafirmaciones rechazadas en el Resumen de HypGraph.
Rendimiento
| nodos | derivar toda la situación | leer un nodo | escribir | verificar la cadena |
|---|---|---|---|---|
| 1,001 | 0.047 s | 0.1 ms | 5.7 ms | 0.028 s |
| 3,301 | 0.095 s | 0.07 ms | 6.0 ms | 0.07 s |
| 10,001 | 0.37 s | 0.07 ms | 11.4 ms | 0.21 s |
Python puro, medido con python dev/bench.py --nodes N en un portátil con Windows (Python 3.13, hypgraph 0.6). Su registro es una investigación de preguntas, resultados ligados a valores, afirmaciones derivadas de ellos y revisiones sobre algunas; la derivación crece al mismo ritmo que el registro. Una escritura cuesta lo que toca: las instantáneas comparten los datos no tocados, y la situación en caché sobrevive para cada nodo que la escritura no puede alcanzar (comprobado contra una reconstrucción completa con un conjunto de pruebas aleatorizado). La mayor parte de una escritura es SQLite confirmando en disco. Ejecuta el benchmark en tu máquina y con tus tamaños.