Context as Code: A 150-line IPython agent reduces context cost to 30% with unchanged accuracy.

· a0gent

This harness is only 150 lines, has no tool-calling protocol, and the execution backend is a real IPython inside Docker. With the same task and same model, it uses 2062 tokens of context per task on average, while a traditional CLI agent needs 6855 — with no change in answer quality. Why the threefold gap? We used 336 controlled trials (total API cost ~$0.2) to break down the cost. The most counterintuitive result is that representing data as code does not make the model understand better. Under controlled comparison, this layer's benefit is exactly zero. Experiment code and all trajectories are in agentic-playground.

What "context as code" means

First, look at the artifact. Below is the context that already "exists" before the agent starts working in the experiment (excerpt):

In [1]: import json, os
   ...: sorted(os.listdir('data'))
Out[1]: ['events.jsonl', 'projects.jsonl', 'tasks.jsonl', 'teams.jsonl', 'users.jsonl']

In [2]: def load(name):
   ...:     return [json.loads(l) for l in open(f'data/{name}.jsonl')]
   ...: teams, users, projects, tasks, events = (load(n) for n in
   ...:     ['teams', 'users', 'projects', 'tasks', 'events'])
   ...: len(teams), len(users), len(projects), len(tasks), len(events)
Out[2]: (30, 300, 80, 5000, 20000)

In [3]: sorted({t['status'] for t in tasks}), sorted({e['action'] for e in events})
Out[3]: (['blocked', 'done', 'in_progress', 'review', 'todo'],
         ['commented', 'created', 'reassigned', 'status_changed'])

The key point is that this session history is fabricated. The model hasn't said a word yet, but every Out is real. The harness hands these cells to a real kernel for execution, then concatenates the outputs into the history. The variables tasks, events remain in the runtime namespace from then on. The agent's first message can directly write [t for t in tasks if ...]. For the model, this is not a document describing the data, but a behavioral prefix it can directly continue.

If you do agent development, you probably already know work like CodeAct, smolagents, and Cloudflare Code Mode on "code as action space." This article examines context. Actions can be written as code, and context can also be a continuable, executable session. We break down its cost.

Experiment design: a ladder of degrees of freedom

All groups use the same programmatically generated project-management dataset: the xl tier has 300 users, 5000 tasks, 20000 events, ~3.5MB of JSONL written to disk. Context only provides file line counts and a line or two from the end of each file; all tasks require multi-step interaction. We also fixed 8 query task templates (filtering, aggregation, multi-hop, graded against gold programs) and the model (deepseek-v4-flash via OpenRouter). The only thing that changes is how many layers of protocol sit between the agent and the execution environment.

Ordered by decreasing degrees of freedom:

Round 1 first ran through a round on two smaller data tiers with the same direction of conclusions; this article only reports Round 2 numbers.

Main result: the cost ladder

Group ctx tokens ctx median out tokens turns wall clock cost/trial
A Traditional CLI 6855 7032 506 3.62 16.2s $0.000484
B0 Session without bound vars 7263 6926 438 3.62 15.8s $0.000470
B Fabricated session 4803 3540 298 2.62 9.9s $0.000358
D Bare IPython 2062 2012 203 2.04 8.1s $0.000184
C Single-shot 854 850 363 1 8.5s $0.000113

All five groups hit 100% accuracy, already near ceiling. The following conclusions only discuss token cost, not capability ceilings.

The ladder itself is not surprising; what is surprising is the cause at each level.

Attribution layer by layer

Representation: 0% value

B0 ties A: identical turn counts (3.62), ctx/out differences within noise, and both experimental rounds reproduced this null result. With the same data and the same IPython session appearance, as long as the demonstration doesn't bind variables, cost is indistinguishable from a traditional CLI agent. The layer of "representing as code makes the model understand better" has zero benefit under controlled comparison.

The gap comes from behavior. On the same task (t6), B's first eval directly writes [p for p in projects if ...], referencing variables already loaded in the fabricated history, and completes in 3 calls. B0's every call redoes a full reload with [json.loads(l) for l in open(...)], using 4 calls total.

Another finding explains why B0≈A. Across A's 24 trials there were 63 tool calls, all multi-line python3 -c scripts in bash; the built-in read tool was never used once. Given primitive tools, the model still chooses a programming language. A effectively became a Python condition without persistent state.

Working habits: ctx −30%, out −41%

Relative to A, B's ctx drops 30% (median drops 50%), output drops 41%, one fewer turn, and wall-clock time drops 39%. The fabricated history makes the model reuse variables and accomplish more steps in a single cell. A and B0 have no persistent state, so the agent pastes the entire set of task IDs computed in the previous step as a literal into the next command. This directly causes B's output to be 40% lower. Documentation can explain these habits; a historical demonstration makes the model follow them directly.

Protocol layer: −57%

D strips out the tool-calling protocol, tool schemas, JSON argument wrapping, and harness encapsulation, dropping ctx from 4803 to 2062. B needs fabrication to maintain the form that "every assistant message is an eval call"; D's message format is already that natively. The demonstration prefix can therefore serve directly as a legitimate dialogue prefix.

Loop: −59%

C removes even the agent loop, generating code only once, and ctx drops to 854. D's ctx lower bound is roughly turns times prefix length. This is the cost of retaining multi-turn error correction. When a task can be hit in one shot, the loop is pure overhead. We still insist on executing code rather than letting the model answer directly. In Round 1, when the full data was visible, C's model directly counted distinct commenters in a long context and answered 2, when the true answer was 3. Models miscount; interpreters don't.

The boundary between demonstration and instruction

Before finalizing D, we ran one round of prompt ablation (n=23–24 per cell):

Variant System prompt Prefix Accuracy ctx out turns
v1 Rule lecture Full rules, no comment channel Exploration 95.8% 2080 248 2.12
v2 Comment instruction (final) Full rules + "write thoughts as # comments" Exploration 100% 2062 203 2.04
v3 Pure demonstration One sentence Exploration + warm-up Q&A demo 39.1% 9747 1045 8.26
v4 Demonstration + termination sentence One sentence + ANSWER explanation Exploration + demonstration 87.0% 2115 280 2.17

v3 tried to replace all rules with demonstration, and accuracy collapsed to 39.1%. After computing 16 correctly, the model treated displaying that value as the end of the answer, then repeatedly output 16, and finally output prose and triggered a SyntaxError. This closely resembles a human's habit of inspecting intermediate results in IPython. The termination protocol is a meta-rule of the session, not in the distribution of session content, and a single demonstration cannot reliably convey it. v4 added back one sentence explaining ANSWER, and accuracy returned to 87%. The remaining 3 failures were all guessed values without execution, showing that the constraint "answers must come from executed output" is indispensable.

The boundary is clear. Demonstration is suited for conveying habits like comments, batch processing, and variable reuse — in v3 these styles remained strong. Rules are suited for conveying meta-rules like the termination protocol and the anti-guessing constraint. Dropping either category causes accuracy to fall.

Same modality: the true role of programming language

The role of a programming language is not to make the model understand the representation better — that layer's benefit is zero. It puts demonstration and action in the same modality: every line in the demonstration can execute, and variables bound in the demonstration genuinely exist in the runtime, so the model can directly continue the demonstration. Plain-text skill documents can only describe behavior; they cannot serve as a behavioral prefix.

Adjacent work mostly addresses the "action" side. CodeAct and smolagents discuss whether code actions can reduce steps; Cloudflare Code Mode and Anthropic's code execution with MCP wrap tools as code APIs. Recent NVIDIA-labs OO Agents (NOOA) pulls roles, state, and capabilities back into Python objects from the other side: the model faces object methods and live runtime state rather than another framework-specific protocol. This is adjacent to this article's direction, but with a different emphasis: NOOA proposes a programming model for agents; this article implements context as an executable session and separately measures the costs of representation, working habits, protocol layer, and loop. Once broken apart, the most-discussed representation is the only layer with zero benefit.

If you're building agents

  1. No need to change data representation. Under controlled comparison, this layer has zero benefit.
  2. To convey habits like variable reuse, batch processing, and commenting thoughts, provide a fabricated history rather than explaining in documentation.
  3. The protocol layer is the biggest saver in this experiment, reducing ctx by 57%. Consider a bare completion loop where messages directly become cells.
  4. When a task can be hit in one shot, don't enable a loop by default. The loop pays for multi-turn correction; in this experiment it added another 59% to ctx.

Limitations

Next steps

Round 3 is scheduled. It will expose MCP tools to the same agent both via tool-calling and as a Python API, compare A vs D under N+1 combinatorial stress, and measure the cost of Code Mode–style claims.