Skip to content
AgentEnsemble AgentEnsemble
Get Started

25 -- TOON Context Format: Token-Efficient LLM Serialization

This document specifies how AgentEnsemble integrates the TOON (Token-Oriented Object Notation) serialization format as a configurable alternative to JSON for structured data sent to LLMs, reducing token usage by 30-60%.

TOON is an optional, user-activated feature. JSON remains the default for full backward compatibility.


AgentEnsemble sends structured data to LLMs in several places:

  • Prompt building: Context from prior tasks, memory entries, and tool descriptions are serialized into prompts.
  • Tool output: Tool execution results are returned as strings that the LLM processes in subsequent reasoning steps.
  • Task hand-offs: In sequential/hierarchical workflows, one agent’s output becomes another agent’s context.
  • Trace export: Execution traces are serialized for analysis and debugging.

All of these currently use JSON. JSON is verbose — curly braces, quotes around every key, commas, colons, and brackets consume tokens that carry no semantic value for the LLM. In long multi-agent pipelines with rich context, this overhead compounds quickly.

TOON (spec v3.0.3) is a compact, human-readable serialization format designed specifically for LLM contexts. It combines YAML-like indentation with CSV-like tabular arrays and achieves 30-60% token reduction versus JSON while remaining fully parseable.

JSON:

{"items":[{"sku":"A1","qty":2,"price":9.99},{"sku":"B2","qty":1,"price":14.5}]}

TOON:

items[2]{sku,qty,price}:
A1,2,9.99
B2,1,14.5

JToon (dev.toonformat:jtoon) is the Java implementation. It is MIT-licensed, available on Maven Central, requires Java 17+, and supports Jackson annotations — which AgentEnsemble already uses.


  1. Opt-in: JSON remains the default. Users who want TOON add one dependency and one builder call.
  2. Fail-fast: If TOON is selected but JToon is not on the classpath, Ensemble.build() fails immediately with a clear error message.
  3. Single configuration point: One contextFormat field on Ensemble.builder() controls all serialization points. No per-task or per-tool configuration is needed (or exposed) in v1.
  4. Optional dependency: JToon is compileOnly in agentensemble-core. Users add the runtime dependency themselves when they choose TOON.
  5. Extensible: The ContextFormat enum and ContextFormatter interface allow future formats without changing the public API.

TermDescription
ContextFormatEnum (JSON, TOON) selecting the serialization format for LLM-facing structured data.
ContextFormatterInterface with format(Object) and formatJson(String) methods.
JsonContextFormatterBuilt-in implementation wrapping Jackson ObjectMapper. Always available.
ToonContextFormatterImplementation wrapping JToon.encode(). Loaded via reflection; requires JToon on classpath.
ContextFormattersFactory class that resolves the correct ContextFormatter for a given ContextFormat.

package net.agentensemble.format;
/**
* Serialization format for structured data included in LLM prompts.
*
* <p>JSON is the default and is always available. TOON provides 30-60%
* token reduction but requires the {@code dev.toonformat:jtoon} library
* on the classpath.
*/
public enum ContextFormat {
/** Standard JSON serialization (default). Always available. */
JSON,
/**
* TOON (Token-Oriented Object Notation) serialization.
* Requires {@code dev.toonformat:jtoon} on the runtime classpath.
*/
TOON
}

package net.agentensemble.format;
/**
* Serializes structured data for inclusion in LLM prompts.
*
* <p>Implementations are obtained via {@link ContextFormatters#forFormat(ContextFormat)}.
*/
public interface ContextFormatter {
/**
* Serialize a Java object to the target format.
*
* @param value any Java object (Map, List, record, POJO, etc.)
* @return formatted string; never null
*/
String format(Object value);
/**
* Convert a JSON string to the target format.
*
* <p>For the JSON formatter this is a no-op (returns the input).
* For TOON this parses the JSON and re-encodes it as TOON.
*
* @param json a valid JSON string
* @return formatted string; never null
*/
String formatJson(String json);
}

Always available. Uses the shared Jackson ObjectMapper for format(Object). formatJson(String) returns the input unchanged.

Available only when JToon is present on the classpath. On first use, ContextFormatters checks for dev.toonformat.jtoon.JToon and, if found, instantiates ToonContextFormatter directly; otherwise it throws IllegalStateException. JToon is declared as a compileOnly dependency so applications that do not use TOON are not required to include it.

format(Object) delegates to JToon.encode(value). formatJson(String) delegates to JToon.encodeJson(json).


Ensemble.builder() gains a contextFormat field:

Ensemble.builder()
.chatLanguageModel(model)
.contextFormat(ContextFormat.TOON) // opt-in to TOON
.task(task1)
.task(task2)
.build()
.run();
FieldTypeRequiredDefaultDescription
contextFormatContextFormatNoJSONSerialization format for structured data in LLM prompts. TOON requires dev.toonformat:jtoon on classpath.

At execution time (inside run() / runWithInputs()), if contextFormat is TOON, the framework verifies that the JToon class is loadable via ContextFormatters.forFormat(TOON). If not, it throws an IllegalStateException with a clear message including the Maven/Gradle coordinates to add.

The resolved ContextFormatter is stored in ExecutionContext and passed to all components that serialize data for the LLM.


The prompt builder receives the ContextFormatter from ExecutionContext. When building the user prompt:

  • Context from previous tasks: Task output strings that contain structured data are formatted via contextFormatter.format(...) before inclusion.
  • Memory entries: Structured content from memory entries is formatted via the configured formatter.
  • Structured output schema: JSON schema descriptions remain in JSON (the LLM needs to produce JSON for parsing), but context data around them uses the configured format.

When a tool returns structured data (JSON), the executor can optionally reformat it via contextFormatter.formatJson(toolResultText) before appending it to the conversation as a ToolExecutionResultMessage.

This is the highest-impact integration point — tool results often contain large JSON payloads that consume significant context window space over multiple iterations of the ReAct loop.

8.3 SequentialWorkflowExecutor (Task Hand-offs)

Section titled “8.3 SequentialWorkflowExecutor (Task Hand-offs)”

Task outputs passed as context to subsequent tasks flow through the prompt builder (8.1), so no additional changes are needed in the workflow executor itself.

ExecutionTrace gains companion export methods:

/** Serialize this trace to TOON format. */
public String toToon() { ... }
/** Write this trace to a file in TOON format. */
public void toToon(Path outputPath) { ... }

These methods use JToon directly and are only available when JToon is on the classpath. They throw IllegalStateException with dependency instructions if JToon is missing.


Version Catalog (gradle/libs.versions.toml)

Section titled “Version Catalog (gradle/libs.versions.toml)”
[versions]
jtoon = "1.0.9"
[libraries]
jtoon = { group = "dev.toonformat", name = "jtoon", version.ref = "jtoon" }

Core Module (agentensemble-core/build.gradle.kts)

Section titled “Core Module (agentensemble-core/build.gradle.kts)”
compileOnly(libs.jtoon) // available at compile time, optional at runtime
// Add to your project when using ContextFormat.TOON
implementation("dev.toonformat:jtoon:1.0.9")

LevelWhat
UnitJsonContextFormatter serializes objects to JSON.
UnitToonContextFormatter serializes objects to TOON (with JToon on test classpath).
UnitContextFormatters.forFormat(TOON) fails with clear message when JToon absent.
UnitContextFormatters.forFormat(JSON) always succeeds.
UnitAgentPromptBuilder uses configured formatter for context sections.
UnitExecutionTrace.toToon() produces valid TOON output.
UnitExecutionTrace.toToon() throws clear error when JToon absent.
IntegrationEnsemble.builder().contextFormat(TOON) with JToon present: full run succeeds.
IntegrationEnsemble.builder().contextFormat(TOON) without JToon: fails at build time.
IntegrationSequential workflow with TOON: context from task 1 formatted as TOON in task 2 prompt.

  • Per-task format override: Allow individual tasks to use a different format than the ensemble default (e.g., JSON for a task that needs exact JSON in its output, TOON for everything else).
  • TOON delimiter configuration: Expose EncodeOptions (tab vs comma vs pipe delimiter) on the ensemble builder for users who want maximum token savings with tab delimiters.
  • Bidirectional support: If LLMs improve at producing TOON output, structured output parsing could accept TOON in addition to JSON.
  • Token savings metrics: Track and report actual token savings from TOON vs JSON in ExecutionMetrics.