All Posts
Guide

Integrating wb with LLM Agents

Use the wb CLI as a structured spreadsheet tool inside LLM agents and agent frameworks.

Integrating wb with LLM Agents

The safest way to put spreadsheets in an agent loop is to treat wb as the spreadsheet tool boundary. Let the model decide what to inspect or change, and let wb handle workbook parsing, patch validation, recalculation, and dependency tracing.

This is usually more reliable than asking a model to reason over raw OOXML, screenshots, or copied formula text.

Why wb Works Well for Agents

  • wb --mode agent forces JSON envelopes and structured help on stdout.
  • wb capabilities --format json lets the agent discover commands, flags, and formats without scraping prose docs.
  • wb read, wb edit, wb calc, wb dep, and wb info cover the common inspect/change/verify loop.
  • wb edit --validate-only --plan gives you a dry-run plan before anything is written back to disk.
  • wb read --headers, --where, and --range help you keep the prompt small and focused.

Start by discovering the tool shape once:

wb capabilities --format json
wb --mode agent help read

Inspect workbook structure before reasoning about specific cells:

wb info budget.xlsx
wb read --sheet Budget --range A1:F20 --headers --format json budget.xlsx

Have the model propose a JSON patch, then validate it before saving:

wb edit --format json --validate-only --plan \
  --patch '[{"cell":"F18","formula":"SUM(F2:F17)"}]' \
  budget.xlsx

Apply the patch only after validation succeeds:

wb edit --format json \
  --patch '[{"cell":"F18","formula":"SUM(F2:F17)"}]' \
  budget.xlsx

Recalculate and inspect downstream impact:

wb calc --format json --range F18 budget.xlsx
wb dep --format json --cell F18 --direction dependents budget.xlsx

wb read returns stored or cached values. After formula edits, call wb calc when the agent needs fresh evaluated results.

Use --format markdown when you want to paste a small table directly into the model. Use --mode agent or --format json when another tool will consume the response.

Minimal Wrapper Example

This Python wrapper gives an agent runtime one structured entry point for wb:

import json
import subprocess


def wb(*args, stdin=None):
    proc = subprocess.run(
        ["wb", "--mode", "agent", *args],
        input=stdin,
        text=True,
        capture_output=True,
        check=False,
    )
    if proc.returncode != 0:
        raise RuntimeError(proc.stderr.strip() or proc.stdout.strip())

    payload = json.loads(proc.stdout)
    if not payload.get("ok"):
        err = payload.get("error", {})
        raise RuntimeError(err.get("message", "wb command failed"))

    return payload["data"]


caps = wb("capabilities")
sheet = wb("read", "--sheet", "Budget", "--range", "A1:F12", "--headers", "budget.xlsx")

patch = json.dumps([
    {"cell": "F13", "value": "Variance"},
    {"cell": "G13", "formula": "F11-F12"},
])

plan = wb("edit", "--validate-only", "--plan", "--patch", patch, "budget.xlsx")
result = wb("edit", "--patch", patch, "budget.xlsx")
recalc = wb("calc", "--range", "G13", "budget.xlsx")

If your framework supports named tools, exposing read, edit, calc, and dep as separate tools is usually better than hiding everything behind one generic shell command.

Prompting Guidelines

  • Ask the model for exact sheet and cell references in every finding.
  • Limit reads to the smallest useful range before asking for analysis.
  • Prefer JSON patches over free-form edit instructions.
  • Run wb edit --validate-only --plan before any write.
  • Run wb calc after edits that change formulas or formula inputs.
  • Use wb dep when the user asks where a number came from or what will break downstream.

In-Process Go Services

If your agent already runs inside a Go service, you can skip the CLI and use the library directly:

wb, err := werkbook.Open("budget.xlsx")
if err != nil {
    log.Fatal(err)
}

sheet := wb.Sheet("Budget")
if err := sheet.SetFormula("G13", "F11-F12"); err != nil {
    log.Fatal(err)
}

wb.Recalculate()

var buf bytes.Buffer
if err := wb.WriteTo(&buf); err != nil {
    log.Fatal(err)
}

Use the CLI when you want a language-agnostic tool boundary. Use the Go API when the agent backend already lives inside a Go process.