Skip to main content

Orquestador local de agentes autónomos: razonamiento ReAct, memoria episódica con utilidad y ejecución sandboxed.

Project description

Moyter

Moyter

Orquestador local de agentes autónomos con razonamiento ReAct verificable.

Moyter ejecuta tareas de análisis y cómputo mediante un bucle de razonamiento (plan → acción → observación → crítica) sobre modelos locales (Ollama) o cloud. Su rasgo distintivo: defensas anti-confabulación que verifican las afirmaciones numéricas del propio agente contra la salida real del código ejecutado, y marcan cualquier cifra que no esté fundamentada.

La idea de fondo: mover la inteligencia del modelo al código. Un modelo puede equivocarse razonando; el código ejecutado no miente sobre lo que imprime. Moyter ancla las conclusiones a esa evidencia.

Estado: alpha. Publicado en PyPI, API en evolución.


Por qué Moyter

La mayoría de los frameworks de agentes optimizan por hacer cosas —conectar servicios, ejecutar tareas—. Moyter optimiza por no mentirte con los números. Cuando un agente procesa datos y te da una conclusión con cifras, ¿quién garantiza que no confabuló? Moyter añade esa garantía:

  • Síntesis anclada: la respuesta final solo puede usar cifras que aparezcan en la salida real del código ejecutado en el sandbox.
  • Guardián de groundedness: un verificador sin-LLM extrae los números del informe y marca los que no estén respaldados por ninguna ejecución.
  • Autoconsistencia automática (versión dura): el agente declara sus relaciones numéricas (sumas, particiones, porcentajes) y Moyter las verifica con aritmética pura; si no cuadran, regenera la síntesis con el detalle del fallo antes de darla por buena, en vez de solo avisar.
  • Juicio de dominio marcado: cuando el informe afirma algo que es criterio experto y no un hecho verificado por código o datos (una opinión, una evaluación de gravedad), Moyter lo señala explícitamente como "no verificado, contrástalo" — no evalúa si el juicio es correcto (eso necesitaría un oráculo que no existe), pero no deja que se confunda con una cifra comprobada.

Estas defensas no hacen listo a un modelo flojo, pero impiden que te engañe sin avisar — que en tareas donde un número importa, es la diferencia que cuenta.


La garantía, medida

No es una promesa: hay un benchmark que la mide. Sobre el mismo objetivo y los mismos datos, se sintetiza un informe desnudo (prompt neutro, sin defensas — lo que hace un agente cualquiera) y otro con Moyter, y se cuentan las cifras que el informe afirma sin respaldo en la evidencia — en particular las que llegarían al usuario sin marcar, como si fueran hechos.

El resultado central no es una cifra que dependa del modelo del día, es estructural: el guardián de groundedness es determinista, así que marca toda cifra infundada que el modelo emita. En las corridas end-to-end, las confabulaciones que llegan sin aviso caen a cero con Moyter, mientras el informe desnudo —que no marca nada— las deja pasar todas. Como efecto secundario, anclar la síntesis al stdout suele hacer que el modelo confabule menos de entrada, pero eso sí varía con el modelo y la tarea; la garantía no descansa en ello, sino en que nada infundado pase sin aviso.

Y no depende de tener un modelo bueno. Se ha corrido la misma comparativa con tres modelos de calidad muy distinta —desde uno cloud potente hasta un 8B local flojo— y las confabulaciones que llegan sin marcar caen a cero en los tres. El guardián es determinista: caza toda cifra infundada, la confabule un modelo bueno o uno malo. (Detalle curioso y honesto: cuanto más flojo el modelo, menos cifras arriesga de entrada —informe más pobre—, así que no es que sea "más fiable"; simplemente inventa menos, y lo que inventa queda cazado igual.)

Es una medición estocástica —señal, no prueba estadística—, así que el README no fija un número: reprodúcelo tú mismo y mira los tuyos (necesita Ollama):

python benchmarks/reliability/run_e2e.py --model minimax-m3:cloud --repeats 3
python benchmarks/reliability/run_e2e.py --model llama3.1:8b     --repeats 3

El Nivel 1 del benchmark es determinista (sin LLM ni red) y corre en CI, así que además de medir, detecta regresiones de las defensas. Ver benchmarks/reliability/.


Características

  • Bucle ReAct con planificación (Plan-and-Solve), ejecución por sub-tareas y crítica (LLM-as-judge con rúbrica cerrada).
  • Local-first: Ollama por defecto (modelos locales y cloud), proveedor Anthropic opcional. Capa de proveedores agnóstica.
  • Memoria en dos niveles: memoria de trabajo compactada + memoria episódica vectorial (ChromaDB) con deduplicación, decay y refuerzo por utilidad.
  • Ejecución sandboxed en Docker; los datos entran por un canal aislado, no por disco compartido.
  • Agentes como configuración inmutable (Pydantic frozen). Presets: coder, analyst, researcher, solver, con herramientas de mínimo privilegio.
  • Defensas anti-confabulación integradas en el núcleo (ver arriba).
  • Interfaz web opcional (Chainlit) que muestra el razonamiento paso a paso.
  • TUI opcional (Rich) para el mismo razonamiento paso a paso, sin servidor web — moyter-tui.
  • Scheduling opcional (moyter-schedule) para correr un análisis de forma recurrente, con log de informes por timestamp.
  • Servidor MCP opcional que expone esas mismas defensas como servicio para otros agentes/harnesses (ver más abajo).

Instalación

Requiere Python 3.11+ y Ollama para modelos locales.

pip install moyter              # núcleo
pip install "moyter[full]"      # con memoria, sandbox y SQL
pip install "moyter[ui]"        # con interfaz web Chainlit

Para desarrollo:

git clone https://github.com/rmoya81/moyter.git
cd moyter
pip install -e ".[dev]"
pytest

Uso rápido

from moyter import coder_agent

# Construye un agente y ejecuta una tarea verificable
agent = coder_agent(model="minimax-m3:cloud").build()
resultado = agent.run("Calcula 17 * 23 verificándolo con código")
print(resultado)

Selección de modelo por variable de entorno:

MOYTER_MODEL=llama3.1:8b python tu_script.py

Pasar datos al sandbox (aislado, sin acceso a tu disco):

csv = open("datos.csv", encoding="utf-8").read()
agent = coder_agent(
    model="minimax-m3:cloud",
    attachments={"datos.csv": csv},
).build()
agent.run("Lee /workspace/datos.csv, límpialo y reporta las métricas clave.")

Interfaz web

pip install "moyter[ui]"
moyter-ui

Abre localhost:8000 y verás el plan, los pensamientos, las herramientas (con código y salida) y la crítica como tarjetas desplegables; solo la respuesta final llega al chat.

TUI (terminal)

pip install "moyter[tui]"
moyter-tui "Calcula los 20 primeros números primos y su suma"

Sin objetivo como argumento entra en modo interactivo (pide tareas una a una, Ctrl-C para salir). Narra el mismo ciclo que la GUI —plan, sub-tareas, pensamientos, código resaltado, observaciones y la respuesta final— pero imprimiéndolo directamente en la terminal con Rich, sin servidor web.

moyter-tui --agent solver --model minimax-m3:cloud "¿Cuántas asignaciones cumplen X?"
moyter-tui --attach datos.csv "Limpia datos.csv y reporta las métricas clave"

Scheduling (análisis recurrente)

pip install "moyter[tui]"
moyter-schedule --every 1h "Resume las novedades de datos.csv"

Proceso persistente (loop interno con time.sleep, no depende de cron ni systemd): ejecuta la tarea, duerme el intervalo, repite — Ctrl-C para parar. Cada corrida anexa su informe a un log con timestamp (--log-file, default ./moyter_schedule.log). --attach se relee en cada corrida, así que un archivo que cambie entre corridas llega actualizado sin reiniciar el proceso.

moyter-schedule --once "..."                       # una corrida, para probar el setup
moyter-schedule --every 30m --agent analyst --attach datos.csv \
    "Detecta anomalías nuevas en datos.csv" --log-file analisis.log

Flags: --agent (preset: coder/analyst/researcher/solver), --model, --max-steps, --num-ctx, --attach RUTA (repetible).


Servidor MCP — defensas como servicio

Las mismas defensas anti-confabulación del núcleo se pueden exponer por MCP para que cualquier otro agente o harness (Claude Code, OpenClaw, Hermes, tu propio bucle...) verifique su propia salida antes de dártela por buena — sin ceder el control de su bucle de razonamiento a Moyter. No orquesta nada: solo confirma que las cifras no se inventaron.

Instalación:

pip install "moyter[mcp]"

Arranque (por stdio):

moyter-mcp
# equivalente: python -m moyter.mcp.server

Tools que expone, todas puras y sin Docker/ChromaDB/LLM:

Tool Para qué
check_consistency(sums, partitions, percentages) Verifica que sumas, particiones o porcentajes que afirmas cuadran entre sí.
check_grounding(report, evidence) Marca cifras de un informe que no aparezcan en la evidencia (stdout, resultados de otras tools) que las respalda.
verify_numeric(expression, claimed_result) Evalúa una expresión aritmética suelta ("17*23") y la compara con el resultado que afirmas.
check_units(sums, equations, quantities) Análisis dimensional: que no sumes magnitudes de distinta dimensión (kW+V) ni un producto dé una dimensión que no es (P=V·I → W). Con quantities verifica la ecuación física completa (valor + unidad), cazando desajustes de prefijo (0.4 kV·12 A = 4.8 W falla).

Apuntar un cliente MCP a él

Para Claude Code, añade en .mcp.json (raíz del proyecto donde quieras usarlo):

{
  "mcpServers": {
    "moyter-defenses": {
      "type": "stdio",
      "command": "moyter-mcp"
    }
  }
}

Si lo ejecutas desde un checkout con uv en vez de un pip install, usa "command": "uv", "args": ["run", "moyter-mcp"].

¿Integras Moyter en tu propio harness? La guía docs/mcp-integration.md lo cuenta en una pantalla, agnóstica de cliente (ejemplo con el SDK MCP de Python): contrato exacto de las tools, dónde enchufar cada una y qué NO hace Moyter.

Verificado con el MCP Inspector oficial como cliente independiente:

npx @modelcontextprotocol/inspector moyter-mcp

Dogfooding real

Este mismo repo se autoverifica: .mcp.json en la raíz conecta Claude Code a moyter-mcp, y el propio Claude Code ha usado las tools por decisión propia (no dirigido a mano) antes de reportar una cifra. Ejemplo real: al contar los tests del repo desglosados por archivo, verificó la suma antes de darla por buena —

check_consistency(sums=[{
  "label": "tests_totales", "total": 70,
  "parts": [5, 10, 16, 10, 29]   # uno por archivo de test
}])
→ "TODAS COHERENTES\n[OK] tests_totales: suma([5, 10, 16, 10, 29])=70 == 70"

— y ancló el informe completo (recuento + passed/skipped) contra la salida real de pytest con check_grounding antes de presentarlo. Es la prueba de que un harness externo puede usar las defensas por su cuenta, dentro de una tarea normal, sin que Moyter orqueste nada.


Arquitectura

Objetivo
   │
   ▼
Planner ──► descompone en sub-tareas
   │
   ▼
Orchestrator ──► bucle ReAct por sub-tarea
   │              (pensamiento → acción → observación)
   │              usa herramientas: execute_python (sandbox), check_consistency…
   ▼
Critic ──► evalúa progreso (rúbrica cerrada), decide continuar o cerrar
   │
   ▼
Síntesis ──► anclada al stdout real del sandbox
   │          + guardián de groundedness numérico
   │          + verificación de autoconsistencia
   ▼
Informe verificado (con avisos si alguna cifra no está fundamentada)

Memoria: de trabajo (compactada por sub-tarea) + episódica (vectorial, con refuerzo por utilidad de las lecciones que funcionaron).


Modelos y hardware

Moyter funciona con modelos locales y cloud vía Ollama. La calidad del resultado sigue a la capacidad del modelo:

Modelo Comportamiento típico
Modelos capaces (cloud o grandes) Resuelven y se auto-verifican bien
Modelos medianos Capaces; las defensas cazan sus errores sutiles
Modelos pequeños (~8B) Limitados en tareas complejas; las defensas impiden que confabulen sin avisar

Las defensas de Moyter no sustituyen la capacidad del modelo — la complementan, garantizando que un error sea visible en vez de silencioso.


Alcance honesto

Moyter destaca en tareas computables y de varios pasos: limpiar datos, calcular, enumerar, encadenar herramientas, cualquier cosa donde el sandbox verifique y las defensas anclen las cifras a ejecuciones reales.

En tareas de puro juicio experto de un solo paso (interpretar, opinar sin código que ejecutar), el bucle no añade capacidad sobre el modelo base — el conocimiento vive en el modelo, no en el andamiaje. Las defensas marcan cuándo el informe está haciendo un juicio de dominio en vez de reportar un hecho verificado, pero no pueden comprobar si ese juicio es correcto —eso necesitaría un oráculo externo—. Conocer este límite es parte de usar la herramienta bien.


Contribuir

Moyter es un proyecto joven y las contribuciones son bienvenidas. Abre un issue para discutir cambios grandes antes de un PR. Todo cambio debe pasar ruff check src/ y pytest.

Autor

Moyter fue creado por Rubén Moya Morata (moyter) — GitHub @rmoya81, moyter.com.

Licencia

MIT © Rubén Moya Morata (moyter)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

moyter-0.1.17.tar.gz (85.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

moyter-0.1.17-py3-none-any.whl (88.2 kB view details)

Uploaded Python 3

File details

Details for the file moyter-0.1.17.tar.gz.

File metadata

  • Download URL: moyter-0.1.17.tar.gz
  • Upload date:
  • Size: 85.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for moyter-0.1.17.tar.gz
Algorithm Hash digest
SHA256 2bbc54d4e019e5464d1c0c8d2a2b61cf86c9471e7bb17e4763358d357b2ca0b3
MD5 69015b314c8e8b9bdb90ee099ba99400
BLAKE2b-256 bb6cd06d2046de7808e33c82452f5e7586821a48247da5c7e4a16d71a3c0654b

See more details on using hashes here.

Provenance

The following attestation bundles were made for moyter-0.1.17.tar.gz:

Publisher: publish.yml on rmoya81/moyter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file moyter-0.1.17-py3-none-any.whl.

File metadata

  • Download URL: moyter-0.1.17-py3-none-any.whl
  • Upload date:
  • Size: 88.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for moyter-0.1.17-py3-none-any.whl
Algorithm Hash digest
SHA256 6d54b8056da883b669d724abd5e305b6283eebd4f63edac54820f02bbc8b2c3f
MD5 06de922d3de195f0e83719c349a46c8a
BLAKE2b-256 29a9f0ca1cfe47ad7aa9b6b3052d7c94ae7b6bfbcdae41e7806cd342ab984a07

See more details on using hashes here.

Provenance

The following attestation bundles were made for moyter-0.1.17-py3-none-any.whl:

Publisher: publish.yml on rmoya81/moyter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page