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, one distribution: the A2A mesh (agent2society) and the memory engine ship inside the wheel, so there is no sibling package to version-match. 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+), [api] (the operator dashboard behind grx.serve() and graxella show), [embed] (local sentence-transformers), [otel], [mcp] — or [all].

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.

What's new in 0.2

0.1 governed one agent's tools. 0.2 governs the organisation around the agent, and ships as one wheel. In full, with the reproduction behind each fix, in CHANGELOG.md; in one screen:

One distribution agent2society (the A2A mesh) now ships inside graxella. pip install graxella is the only install; no sibling to version-match.
Teams, consensus, self-awareness Teams nest into teams; six conflict resolvers; agents carry declared and ledger-earned limitations and a live status board. §
A governed org chart A panel that keeps agreeing becomes a gated proposal to collapse to one agent, filed by reconcile() unprompted, demoted the moment that agent fails.
Trust joined to routing trust_weight lets the ledger's record overrule a better capability match; unavailable agents are removed from routing outright. §
Token economy Usage from any gateway (Portkey, LiteLLM, raw SDKs), your own price book, cost ceilings that fire, and avoided() — model calls the ledger proves you no longer make. §
graxella.demo() and /lens Fifteen acts on a real ledger, then the page that explains what graxella did — on the demo, or on your own system. §
Un-learning, concurrency, security, migrations Rule-scoped health with a failure streak; single-flight healing and per-tenant WALs; an authenticated operator API that records the real principal; a versioned ledger schema migrated in place.
Pressure-tested 20k-outcome ledgers, 16 concurrent routers with statuses flipping underneath, hostile 10k-character tasks, cross-process reads during writes — and the bugs those found, fixed.

See it in one command

import graxella
graxella.demo()

or graxella demo. It builds a real ledger, runs a scripted incident through every governed surface, and opens the operator UI on what it produced. Fifteen acts:

1–3 an observed tool, a vendor renaming a field, one healer call
4–6 evidence accumulating, a promotion, a rule un-learned
7–9 a routed mesh, a three-level team hierarchy, a disagreement escalated to a human
10–11 a collapse proposal filed by reconcile() unprompted, a bounded trajectory
12 the ledger overruling a better capability match
13 an unavailable agent taken out of routing, and brought back
14 a declared cost ceiling firing on a real dispatch
15 what the run spent, and the model calls it never made

No model, no API key, no network: the healer, router and resolvers in the script are deterministic, so the story and the numbers are identical on every machine. What it prints:

healing       1 healer run(s), at most one model call each; 18 repairs applied with no model
rulebook      1 active, 1 un-learned
review queue  2 proposal(s) awaiting a human
routing       2 route(s) changed by evidence
spend         49,700 tokens over 11 metered call(s), $0.1445
avoided       17 model call(s) not made (~$0.0504)

Token reports in the script are staged in the OpenAI shape at illustrative rates; everything computed from them runs through production code. The UI needs pip install "graxella[api]".

The UI's /lens page answers the day-one question — what did graxella do to my system, and why should I trust it in production? — with the loop act by act, the repairs serving traffic, the repairs it took back out with the evidence that convicted them, what is waiting on you, and which agents are trusted versus what the ledger says they are bad at.

The same view on your own system

Nothing about those pages is demo-only. Open them on the ledger your agents actually write to, either from inside the running process or from outside over an existing workdir:

grx.serve(port=8321)          # in-process: the full view, live teams included
graxella show --workdir .graxella/support-desk     # any session's workdir

Both print the /lens and trust-center URLs with the operator token. graxella show is honest about what an outside process cannot know: the ledger-backed parts are exact, and the parts only a live process holds (a team's shape right now, the status board) show as absent, never as stale. Details, auth and the PostgreSQL case: docs/OPERATOR_UI.md.

Teams, consensus, and a governed org chart

Routing one task to one agent is the easy case. When a decision needs several agents, graxella makes the shape of that group reviewable:

reviewers = grx.team("reviewers", [risk, legal],          # a panel
                     pattern="consensus", resolver="unanimous")
pricing   = grx.team("pricing", [analyst, reviewers])      # routed, over a panel
app       = grx.mesh([triage, pricing])                    # teams nest into meshes
  • Hierarchy. A team is itself a member, so patterns compose and differ per level in the same run. Nothing above a team knows its shape.
  • Consensus with a stated policy. majority, trust_weighted (votes weighted by each member's cited record), specialist, unanimous, escalate. Every verdict names who dissented and what they said; a tie escalates instead of picking quietly.
  • Agents that know their limits. Peers see each other's capabilities, the limitations the ledger earned for them (the error classes they actually fail with), and a live ready / busy / degraded / unavailable status. An unavailable member is never dispatched, and its absence is a recorded abstention, not a gap.
  • The org chart is governed too. adaptive=True and a panel whose answer one member reproduces accrues evidence for collapsing to that member — through the same Evidence Gate, promoted by reconcile(), and demoted the moment that member starts failing. A demoted collapse is the panel coming back.

Three LLM calls become one when the evidence says the panel stopped buying anything, and three again when it stops being true.

Routing that knows who actually works

Your capability graph answers who matches this task. Your ledger answers who actually succeeds at it. graxella joins them:

app = grx.mesh([billing_v1, billing_v2], trust_weight=0.5)
app.route("refund this invoice")

print(app.last_routing_diff.render())
# trust routing picked billing_v2 over billing_v1: billing_v1 matched
# better (fit 0.68 vs 0.62) but its record here is trust 0.27 over 11
# call(s) against trust 0.89 over 42 call(s)
  • fit multiplies, it never adds. Evidence re-orders the agents that already fit; a flawless record at something else can't win a task the agent doesn't match.
  • No model on this path. It's arithmetic over ledger rows plus a shortest path, so the same ledger routes the same way every time — which is the only reason a route can be replayed or audited.
  • Failover is a shortest path. Edge cost -log(p_success), so two 0.9 agents in sequence beat one 0.75 agent.
  • trust_weight=0.0 is the default and reproduces every earlier release exactly. graxella doesn't change dispatch behaviour silently.
  • Exploration is built in. Near-ties go to the agent with less evidence — otherwise trust routing is a rich-get-richer trap where one bad afternoon strands an agent forever.

grx.report("billing_v1", "unavailable") removes an agent from routing entirely, at any weight. Capacity isn't quality, so it's removed rather than down-weighted.

What it cost, and what it didn't

graxella routes no model calls and owns no tokenizer — your gateway (Portkey, LiteLLM, your own proxy) picks the model and reports the usage. What graxella adds is the accounting, and one number nobody else can produce:

grx = graxella.Session("desk", domain="support",
                       prices={"gpt-4o": (2.50, 10.00)})   # your rates, $/Mtok in, out
...
print(grx.spend().render())
# spend    1,500 tokens (1,000 in / 500 out) over 1 metered call(s) · $0.0075
print(grx.avoided().render())
# avoided  18 model call(s) not made (18 deterministic repair)
#          ≈ 22,500 tokens ≈ $0.1125 — median metered call here is 1,250 tokens

Rates can also come from GRAXELLA_PRICES (a JSON map), so they stay out of source control.

avoided() only counts calls the ledger proves used to happen. A repair after the first qualifies — the one persisted transform proposal is the receipt for the one healer call that was ever paid for. Deterministic routing does not: a hand-written if/elif is free too, so billing that against an LLM router you never wrote would be a fiction.

Two things it refuses to do, because both would be lies: it ships no price list (an unpriced model costs None, never $0) and it ships no tokenizer (token counts come from your provider's own usage report, the only authority on what you were billed).

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 721 passed, 3 skipped (skips need a PostgreSQL URL) 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.2.x, alpha. The API surface is small and tested, but it will move. CHANGELOG.md names every defect each release fixed and the ones it knowingly leaves open.
  • 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

  • docs/FIRST_CUT_SCOPE.md — the problem, and what this first cut does and doesn't claim
  • docs/OPERATOR_UI.md/lens, the trust center and the topology map on your own ledger: grx.serve(), graxella show, auth, PostgreSQL
  • docs/HEALING.md — the heal ladder, drift taxonomy, recipe capabilities
  • docs/specs/ — the binding Promotion, Disclosure, Orchestration and Routing specs
  • CONTRIBUTING.md — setup, and the honesty contract for changes

License

Apache-2.0 — see LICENSE.

Release files for graxella 0.2.0

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.2.0
File Size Uploaded
graxella-0.2.0.tar.gz 363.7 kB Details

Built distribution (wheel)

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

Total release size: 788.6 kB

Release files / graxella-0.2.0.tar.gz

Download URL graxella-0.2.0.tar.gz
Size 363.7 kB
Tags Source
SHA-256 checksum
How to use checksums
f8af3148fa8d058fbf4315d64089978c8dc07b22e0b22478259e940b0f746d9e
BLAKE2b-256 checksum
How to use checksums
2ecf6e1ea7b18e6ad50e2f853cf355e47bf9cb218271fa1a916e78b4cc647692
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.

Transparency log

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

Download URL graxella-0.2.0-py3-none-any.whl
Size 425.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
896578ebe89f2cf7eb9b428f773f2bf95cedb10c9fc2bc73df45f57c9a54f18c
BLAKE2b-256 checksum
How to use checksums
da11a5bec099d3653fd121b4058d2d3816570da00f0fc2e6cb188743cad144d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.1

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