Skip to main content
graxella

Accountable change-control for agent behavior.

Your agents already work. Can you prove what they changed — and undo it if it was wrong?

ci python license


The problem

Traditional software governs behavior change through a whole discipline: version control, review, CI gates, deploys, audit logs. Every change is proposed, approved, recorded, and reversible.

Agents have none of that — yet their behavior mutates continuously:

  • A tool contract changes underneath them. The carrier renames order_id to tracking_ref. Your agent starts asking customers for a tracking number it was supposed to look up itself.
  • They loop. Two agents hand the same task back and forth until something runs out — usually your budget.
  • They "learn" invisibly. Something works once, gets reused forever, and nobody approved it or can point at why.
  • There is no paper trail. "Why did it do that?" has no answer that survives the chat scrollback.

The usual fix is a smarter prompt or a retry. That is expensive at runtime, non-deterministic, and unauditable.

What graxella does

It sits underneath your agents — you keep writing plain LangChain or LangGraph — and turns behavior change into something with a process:

tool breaks  →  repaired once  →  cited proposal  →  you approve  →  permanent rule
                                                                          ↓
                                                    evidence turns bad → auto-demoted

One rule explains the whole design: the LLM may propose; the evidence decides. Routing, promotion, demotion, and every verdict are deterministic and recorded. A model appears in exactly one place — the drift healer's proposal step — and even there its output is validated against the real fallback before it is ever trusted twice.

The code you actually write

import graxella
from pydantic import BaseModel

grx = graxella.Session("support-desk", domain="support")

class TrackRequest(BaseModel):                 # the carrier's NEW schema
    tracking_ref: str                          # ...it used to be order_id

def carrier_v2(args: dict) -> str:
    req = TrackRequest(**args)                 # a real, validating client
    return f"parcel {req.tracking_ref}: out for delivery"

@grx.tool(fallback=carrier_v2)                 # <- the only graxella line
def track_shipment(order_id: str) -> str:
    """track a shipment's delivery status by order id"""
    return carrier_v2({"order_id": order_id})  # drifts: the old field name

That's it. @grx.tool returns a real LangChain BaseTool, so it drops into create_agent(llm, [track_shipment]) unchanged.

The first time the drift happens, graxella repairs it, caches the repair as a deterministic recipe, and files a cited proposal for you:

track_shipment.invoke({"order_id": "A-1042"})
# -> 'parcel A-1042: out for delivery'      the customer never saw a failure

grx.healer_calls          # 1  — repaired once, never again
grx.pending()             # 1  — nothing was promoted silently
print(grx.why(grx.pending()[0]))   # the cited reasoning behind the verdict

Approve it and it becomes a permanent rule. Later, if the evidence turns against that rule, grx.reconcile() demotes it on its own — no human, no LLM, just the posterior:

reconcile(): promoted=1  demoted=1
  demoted apr_581870be...: status=rolled_back
  reason='posterior 0.29 < 0.5 over 5 uses (1 ok / 4 failed)'

That last part — un-learning — is the piece most agent-memory systems don't have. Anything can accumulate rules. Removing one on evidence is what makes it change-control rather than a cache.

Install

pip install graxella            # everything above works
pip install "graxella[heal]"    # + the built-in drift healer (DSPy/Ollama)

One command, no extras needed. Extras are only for things you might genuinely not want: [heal] (a local model runtime for repairing ambiguous drift), [langgraph] (the graph runtime, for the mesh adapters and tutorials 08+), [embed] (local sentence-transformers), [otel], [mcp].

Nothing calls out to a hosted service — the healer runs against a local Ollama by default, and without one, drift fails loudly rather than faking a repair.

Measured, not asserted

Every number here comes from a script in this repo that you can run. The runs are on small local models (qwen2.5:7b, nomic-embed-text).

What Result Produced by
Routing across 15 paraphrased/slang tickets 15/15 vs 13/15 for a hand-written keyword router tutorial 11 §A
A runaway two-agent handoff loop stopped at 3 hops + escalated, vs 20 hops burned by a hand-rolled loop that never detects it tutorial 11 §B
Repairing a drifted tool 1 healer call, ever — then a cached deterministic recipe tutorial 02
Test suite 285 passed, 3 skipped uv run pytest
Load-bearing claims, checked in CI 5 probes benchmarks/eval_harness.py

What these numbers are not: single-run results on one small domain, not a statistically powered benchmark. The CI scorecard exists so they fail loudly when they stop being true.

Learn it

tutorials/ is a graded path — 01–06 need no LLM at all:

# Tutorial You learn
01–03 first toolself-healingreview queue a plain function becomes governed; a drift heals once; a human approves it into a permanent rule
04–06 mesh · recall · audit multi-agent routing with no routing-LLM, memory that recalls what worked, "why did it do that?" in one call
07–08 LangChain · LangGraph your real agents, unchanged, governed underneath
09–10 handoffs · supervisor team typed A2A handoffs, loops caught and escalated, a full org chart
11 capstone notebook every layer on one hierarchical org — then the same org rebuilt with zero graxella, compared on tokens, hops, and failure modes

Honest limits

This project's whole claim is accountability, so the limits are stated rather than buried:

  • It does not make your model smarter, and it does not claim a lower hallucination rate. Tutorial 11 contains a live probe where the governed agent hallucinated exactly as badly as the ungoverned one, and the built-in claim-detector missed it on both sides. That result is kept in the notebook. What differs is that the governed side's tool trail makes the false claim checkable afterwards.
  • Governance is detection-only. graxella flags reasoning/action mismatches and constitution violations; it does not silently block or rewrite your agent's output.
  • 0.1.0, alpha. The API surface is small and tested, but it will move.
  • The drift healer needs a local model (or your own @grx.healer). Without one, drift fails loudly — it never fakes a repair.

Where it sits

Not a competitor to your agent framework — a layer under it.

Guardrails.ai NeMo Guardrails LangGraph alone graxella
Validate a single output
Repair a broken tool contract
Evidence-gated promotion
Reverse a learned behavior
Cited audit trail per decision

Guardrails and NeMo answer "is this one output acceptable?". graxella answers "what changed in my agent's behavior, who approved it, and can I undo it?" — a different question, and they compose fine.

Docs

License

Apache-2.0 — see LICENSE.

Release files for graxella 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for graxella 0.1.1
File Size Uploaded
graxella-0.1.1.tar.gz 216.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for graxella 0.1.1
File Interpreter ABI Platform
graxella-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 471.5 kB

Release files / graxella-0.1.1.tar.gz

Download URL graxella-0.1.1.tar.gz
Size 216.8 kB
Tags Source
SHA-256 checksum
How to use checksums
c35eda229aab24a9da03ea3535de72db2ce34ad03eb5dd3d13fbda6fd80908e5
BLAKE2b-256 checksum
How to use checksums
33f6e20db9d50d9e88ce1f6c776a833fefd9b81f0a32e841daec6861cc0b781a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.4

Release files / graxella-0.1.1-py3-none-any.whl

Download URL graxella-0.1.1-py3-none-any.whl
Size 254.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5de60688369917521b345110272ed70300ffcda73ba59d1523de3a8b2f1e017f
BLAKE2b-256 checksum
How to use checksums
942c223014a3b8d3bf65cacb251f3e029aeaa229c8c52b2f7c9ad5f898ec22bd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.4

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page