A compact, lossless wire format for agent-to-agent handoffs — cuts 36% of real cl100k_base tokens on held-out ToolBench trajectories, with every byte of overhead counted against it.
Multi-agent pipelines (planner → builder → reviewer, tool loops, LangGraph / CrewAI / AutoGen-style orchestration) burn most of their context window re-sending structure: JSON keys, quoting, restated identifiers, tool catalogs, and outputs quoted verbatim two handoffs later. If you control both sides of a handoff, that structure is free to compress — as long as the compression is exactly reversible and you are honest about what decoding costs.
a2acompress
is that codec, plus the benchmark harness that keeps it honest.
raw canonical JSON ████████████████████████████████████ 140,661 tokens
a2c/1 full cost ███████████████████████ 89,235 tokens −36.6%
└── payload 79,439 + decode instructions 9,796 + dictionary 0
Held-out split of 196 real ToolBench trajectories (3,013 handoffs), actual cl100k_base token counts, byte-exact round trip on every record. Reproduce with one command below.
Most compression demos compare payload bytes and skip the part where the receiver needs to be told how to read the payload. Here, full cost is the only headline number:
full cost = compact payload
+ the reconstruction instructions a model needs to decode it
+ any learned dictionary shipped alongside
Three more rules, enforced by the test suite and CI:
Byte-exact or rejected. Every configuration must decode back to identical canonical JSON for every held-out record, or it cannot win.Train/test split by complete trajectory. Anything learned (dictionaries, tuning choices) sees training tasks only; the reported number comes from tasks the optimizer never touched, re-verified across three more split seeds.No invented tokens. GPT and Claude APIs cannot consume custom token IDs. Everything here is ordinary text counted with the realcl100k_base
encoder. Transport size (gzip) is reported separately and never sold as token savings.
pip install -e .
a2acompress optimize --adapter synthetic --limit 40 --output release
That runs the full bounded optimize loop on a synthetic corpus and writes a
release folder: report, results JSON, the selected protocol, charts, and a
practical
/ research-only
verdict.
The real benchmark on public ToolBench trajectories:
a2acompress fetch --dataset toolbench --limit 240 --output data/toolbench
a2acompress optimize --adapter toolbench --source data/toolbench --seed 7 --output release
Or use the codec directly:
from a2acompress.protocol import ProtocolOptions, encode_session, decode_session
options = ProtocolOptions(
positional_body=True, packed_header=True,
minimal_instructions=True, inline_backrefs=True, token_aligned_refs=True,
)
encoded = encode_session(records, options) # one task trajectory in
restored = decode_session(encoded.payload, options)
assert restored == records # always, or it's a bug
CLI: fetch
· normalize
· train
· encode
· decode
· verify
·
benchmark
· optimize
.
Every handoff (plan, message, tool call/result, status, artifact, review, error, final) is first normalized to one canonical record shape. Records then encode to one line each:
#task|agents
c13|tool_catalog|image_search;{"count":5,"q":"web design"};c1
r31||c1;The search returned 5 results: ${2f:8a} …
f10||done — ${0:2q:1d4}
The savings stack, all reversible:
| Mechanism | What it does |
|---|---|
| compact schema | one-char keys, defaults omitted, positional fields, packed headers |
| value references | $3 repeats an earlier value; the first occurrence is the definition |
| inline backreferences | |
${o:l} splices a character span from earlier in the session — LZ-style, but the payload stays readable text and no dictionary ships |
|
| token-aligned splices | ref boundaries shift a few chars when the measured token cost improves; refs that don't beat their own literal are dropped |
| adaptive instructions | the decode instructions ship per session, compressed, and only describe features that session actually uses |
The inline backreference is the workhorse: final answers quote tool outputs verbatim, tool catalogs repeat their own envelope per tool — one 6-token marker replaces spans of any length, and the decoder rebuilds everything by replaying the session. On ToolBench it fully displaced the learned dictionary (the optimizer kept 0 entries: shipping a dictionary per session costs more than it saves — measured, not assumed).
Every constant in the format — separator characters, marker syntax, span
thresholds — was chosen by measuring real cl100k_base
costs, not guessed. The alternatives that lost (tab separators, longer n-grams, lazy dictionary bootstrap) are documented in the release report with their numbers.
a2acompress optimize
is a bounded search, not a demo script:
- evaluate the baseline protocol across a grid of configurations
- verify losslessness for every configuration; failures cannot win
- enable one improvement per cycle, chosen by a probe on training data - stop after two stale cycles or 10 cycles; select the cheapest verified winner
- re-test the winner on three other split seeds and report the spread
- write a release folder with a manifest (seed, corpus fingerprint, search space) so any result is reproducible bit-for-bit
Verdict thresholds: practical
needs ≥15% full-cost saving and break-even within 10 handoffs. The current winner saves 36.6% and breaks even on the first handoff of the median session.
Good fit: you run a multi-agent system where the same process (or two processes you control) encodes and decodes handoffs, you pay per token, and your handoffs carry repeated structure — tool catalogs, quoted outputs, schema-shaped messages.
Not a fit:
- You want a drop-in proxy that shrinks arbitrary prompts. This is a protocol; both sides must speak it.
- Your receiving model must act on the compact form directly with zero decode errors at any stakes. A model reads the format from the shipped instructions; that path is honest about its token cost but is still a model reading a spec — test on your own traffic before trusting it.
- Your messages are mostly unique prose. The ~63% that survives compression here isthe unique content; a corpus without structural repetition will save less.
Related work: TOON shrinks tabular
JSON for prompts by declaring shape once (~40% on uniform arrays);
LLMLingua-style prompt compression is lossy. a2acompress
differs by being lossless on heterogeneous agent trajectories, adding session-replay references and LZ-style splices, and charging itself for every byte of decode overhead.
| Held-out metric (seed 7) | Value |
|---|---|
| Raw canonical JSON | 140,661 tokens |
| Compact payload | 79,439 |
| Reconstruction instructions | 9,796 |
| Learned dictionary | 0 |
| Full cost | |
| 89,235 (−36.6%) | |
| Payload-only (not the claim) | −43.5% |
| Median break-even | 1 handoff |
| Sessions never breaking even | 0 of 39 |
| Stability on seeds 11/23/47 | −36.1% / −36.0% / −35.2% |
| Round trip | byte-exact, every record |
Full detail: release/optimization_report.md, every evaluated configuration in release/optimization_results.json.
Benchmarked on one public corpus (ToolBench) so far. The jsonl
adapter takes
your own traces — a role
/content
JSONL is enough — and the loop will tell you your number:
a2acompress optimize --adapter jsonl --source your_traces.jsonl --output my_release
If you run it on your own corpus, an issue with your numbers (good or bad) is the most useful contribution you can make.
pip install -e ".[dev]"
python -m pytest tests -q
CI runs the unit tests, lossless round-trip fixtures against adversarial inputs, a synthetic benchmark, the full optimize loop, and a release checker that fails the build if a report's arithmetic, verdict, or stability re-tests don't hold up.
A TypeScript port of the codec (byte-parity with Python, verified by cross-tests) powers a browser lab that encodes, decodes, and verifies the round trip live; it lives in a companion project and will be published separately.
MIT — see LICENSE.