Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

whiteboard

Give an agent a pencil.

We built models that genuinely see — with real spatial reasoning — and then hand them only linear text for the problems that are hard precisely because they are spatial. whiteboard is a small capability that closes that gap: it deterministically renders a tangled structured problem to an image, feeds that image back to the model, and lets the model perceive the answer the way an engineer reads it off a whiteboard.

  • A database with circular foreign keys → render the ER diagram and the cycle is a red loop you can point at, instead of 2,000 lines of DDL to trace by hand.
  • Six hours of logs → render a density timeline and the 3 a.m. burst is a bright smear, instead of 400k lines to scroll.
  • A distributed trace → render the span waterfall and the slow span is obvious, instead of arithmetic over timestamps.

The reflexive answer to "too much information" is usually bigger context or more retrieval — pump in more text. whiteboard is the opposite move: render it smaller and look at the shape.

The agent draws in order to reason. Perception, turned inward.


Why this works

Two ideas do all the work:

  1. A tool result can be a picture. When the model calls the render_whiteboard tool, the result handed back is an image content block (a base64 PNG), not text. A vision model then reasons over the rendered structure directly. That single mechanism — tool-result-as-image — is the whole trick.

  2. Renderers encode structure but never verbalize the conclusion. The schema renderer runs a real cycle-detection algorithm — and uses the result only to colour the cycle edges red. It does not return the sentence "there is a cycle between orders, invoices and payments." If it did, we would be back to text reasoning and the capability would be pointless. The algorithm makes the answer visible; the model's job is to see it.

See docs/HOW_IT_WORKS.md for the full mechanism.


Install

The rendering core needs matplotlib, numpy, and Graphviz. Graphviz has two parts: the Python bindings (pip) and the dot system binary.

# 1. The Graphviz system binary (provides `dot`)
#    macOS:           brew install graphviz
#    Debian/Ubuntu:   sudo apt-get install graphviz
#    Fedora:          sudo dnf install graphviz
#    Windows:         choco install graphviz   (or download from graphviz.org)

# 2. The Python package + its deps
pip install -e .

# 3. (optional) the agent loop needs the Anthropic SDK
pip install -e ".[agent]"

# 4. (optional) tests
pip install -e ".[dev]"

Verify the binary is visible: dot -V should print a version. If whiteboard render later complains it can't find dot, that system binary is missing.


Quickstart

1. Render an example — offline, no API key

This is the fastest way to see the idea. It calls the renderers directly:

python examples/run_examples.py
# then open the PNGs in examples/rendered/

or one at a time via the CLI:

whiteboard render --type schema --data examples/circular_schema.json -o schema.png
whiteboard render --type logs   --data examples/sample_logs.json     -o logs.png
whiteboard render --type trace  --data examples/sample_trace.json    -o trace.png

2. Let the agent solve a problem — needs ANTHROPIC_API_KEY

export ANTHROPIC_API_KEY=sk-ant-...

whiteboard solve \
  --data examples/circular_schema.json \
  --question "Are there any circular foreign-key dependencies? If so, name every table in each cycle."

The agent decides on its own to call render_whiteboard, looks at the returned image, and answers from what it sees. Every image it looked at is saved to whiteboard_session/ so you can see exactly what it saw.

3. Use it from Python

import whiteboard

# Offline rendering (deterministic, no network):
result = whiteboard.render("trace", trace_dict, "trace.png")
print(result.caption)          # neutral description — never the answer
print(result.legend)           # how to read the encoding

# The agent loop (needs the anthropic extra + an API key):
from whiteboard import WhiteboardAgent
agent = WhiteboardAgent()
out = agent.solve("What is the slowest span and why?", data=trace_dict)
print(out.answer)
print(out.images)              # paths to what the agent rendered

Build your own loop instead? whiteboard.whiteboard_tool_spec() gives you the Anthropic tool definition and whiteboard.run_tool(...) executes a call and returns the content blocks (image + legend) ready to drop into a tool_result.


The three built-in renderers

render_type Input shape What it draws The structure it surfaces
schema { "tables": [...] } ER diagram (Graphviz) foreign-key dependency cycles in red
logs { "events": [...] } service × time density heatmap + error strip activity bursts and error spikes
trace { "spans": [...] } span waterfall the dominant call chain (where the time went)

Full input formats and algorithms: docs/RENDERERS.md.

schema → ER diagram

schema example

Two cycles are visible at a glance: orders → invoices → payments → orders and customers ↔ addresses. Acyclic relationships stay grey.

logs → density timeline

logs example

A payments error burst at 03:00 is a bright vertical smear, with a matching spike in the error-rate strip below — found by looking, not scrolling.

trace → waterfall

trace example

The dominant chain (GET /feed → feed.build → recsys.rank → model.inference) is saturated and outlined; the 830 ms model.inference span is the obvious pole.


Project layout

whiteboard/
├── README.md
├── pyproject.toml / requirements.txt / LICENSE
├── docs/
│   ├── ARCHITECTURE.md      # components and data flow
│   ├── HOW_IT_WORKS.md      # the tool-result-as-image mechanism, in depth
│   ├── RENDERERS.md         # per-renderer input format + algorithm
│   └── EXTENDING.md         # how to add your own renderer
├── src/whiteboard/
│   ├── types.py             # validated input models (Schema / LogSet / Trace)
│   ├── analysis.py          # pure algorithms: SCC, cycle edges, dominant path
│   ├── renderers/           # base + schema / logs / trace
│   ├── registry.py          # render_type -> renderer
│   ├── tool.py              # Anthropic tool spec + dispatch (returns an image)
│   ├── agent.py             # the tool-use loop
│   └── cli.py               # `whiteboard list | render | solve`
├── examples/                # input files, a deterministic log generator, runner
└── tests/                   # 22 tests; the analysis tests are the important ones

Design principles (the short version)

  1. Don't expand the model — re-encode the problem. Match the input to the modality the model is already strong at.
  2. Encode, don't verbalize. Renderers make the answer visible; they never state it. (RenderResult carries the image, a neutral caption, and a visual legend — never the finding.)
  3. The deterministic core runs offline. No model, no network, identical pixels for identical input. The model is only in the loop to look.
  4. Adding a renderer is a one-file change. Implement Renderer, register it, and it appears in the CLI and the agent tool at once.

Testing

pip install -e ".[dev]"
pytest -q

The suite covers the deterministic pieces directly (cycle detection across self-loops, multiple independent cycles, a 5,000-node graph; the dominant-path selection; input validation) and asserts the renderers emit valid PNGs and that the tool result actually carries an image block.


License

MIT — see LICENSE.

About

Agent tool that deterministically renders a tangled structured problem to an image, feeds that image back to the model, and let's the model perceive the answer the way an engineer reads it off a whiteboard

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages