Skip to content

04 medium verbosity

Part I: Output & Parsing | Challenge §4

4. Verbosity & Token Cost

Severity: Medium | Frequency: Very Common | Detectability: Easy | Token Spend: High | Time: Low | Context: High

The Problem

Every byte of CLI output that reaches the agent consumes tokens from its context window. Verbose tools directly increase cost per operation and reduce how many operations fit in a context window.

Verbose output that wastes tokens:

$ tool list-files --dir /project
Scanning directory /project...
Found 1,247 files across 89 directories
Analyzing file types...
  JavaScript: 342 files
  TypeScript: 289 files
  CSS: 45 files
  HTML: 12 files
  JSON: 156 files
  Markdown: 23 files
  Other: 380 files
Summary:
  Total size: 45.2 MB
  Largest file: dist/bundle.js (2.1 MB)
  Newest file: src/components/Button.tsx (modified 2 hours ago)
  Oldest file: README.md (modified 3 years ago)
Scan completed in 0.34 seconds.

The agent probably just needed file paths. All the analysis text is noise.

Debug output leaking into normal runs:

$ tool deploy
[DEBUG] Loading config from /home/user/.config/tool/config.toml
[DEBUG] Resolving endpoint: api.example.com → 1.2.3.4
[DEBUG] Establishing connection...
[DEBUG] Sending request: POST /v1/deploy
[DEBUG] Response: 200 OK
Deployed successfully.

Redundant confirmation messages:

$ tool create-user --name Alice
User creation initiated.
Processing user Alice...
User Alice has been created successfully with ID 42.
Your new user Alice is ready to use.
Have a great day!

Impact

  • Directly increases $/operation
  • Reduces effective context window for reasoning
  • Signal-to-noise ratio forces agent to spend tokens filtering irrelevant content
  • Accumulated across many tool calls, verbose tools can exhaust context

Solutions

Tiered verbosity:

tool deploy --quiet          # only emit final JSON result, no prose
tool deploy                  # default: minimal human output + JSON
tool deploy --verbose        # progress to stderr, JSON to stdout
tool deploy --debug          # full debug trace to stderr

CI environment auto-detection:

# When CI=true, behave as --quiet automatically
if [ "$CI" = "true" ]; then
  VERBOSITY=quiet
fi

Minimal JSON output by default:

// Bad default: everything
{"id": 42, "name": "Alice", "email": "alice@example.com", "created_at": "...",
 "updated_at": "...", "role": "user", "preferences": {...}, "metadata": {...}}

// Good default: just what was asked for
{"id": 42, "name": "Alice"}

// With --full flag: everything

--fields selector:

tool list-users --fields id,name --output json
# Returns only requested fields

For framework design: - Default verbosity is --quiet when stdout is not a TTY - JSON output never includes prose, only structured data - Provide --fields filtering at framework level - Track and log token-approximate output sizes for monitoring

Evaluation

Score Condition
0 No verbosity control; debug output emitted unconditionally; CI=true not respected
1 --quiet flag exists on some commands; progress lines still leak into stdout in default mode
2 --quiet suppresses all prose; CI=true auto-activates quiet mode; JSON output never contains prose
3 Default is quiet when stdout is not a TTY; --fields selector at framework level; JSON never includes extraneous fields

Check: Run a command with CI=true set and no --quiet flag — verify stdout contains only structured data with no progress or summary lines.


Agent Workaround

Set CI=true and --quiet to suppress prose; use --fields to limit output size:

env = {**os.environ, "CI": "true", "NO_COLOR": "1"}
cmd = [
    "tool", "list-users",
    "--output", "json",
    "--quiet",                         # suppress all progress output
    "--fields", "id,name,status",      # request only needed fields
    "--limit", "50",                   # prevent unbounded output
]
result = subprocess.run(cmd, capture_output=True, text=True, env=env)

Estimate token cost before processing large output:

import sys
output_bytes = len(result.stdout.encode())
approx_tokens = output_bytes // 4  # rough estimate: ~4 bytes per token
if approx_tokens > 10_000:
    # Output is large — use --fields or --limit to reduce before re-running
    raise RuntimeError(f"Output too large (~{approx_tokens} tokens) — add --fields or --limit")

Limitation: If the tool has no --quiet or --fields flags and emits verbose output unconditionally, the only workaround is to post-process stdout — filter out non-JSON lines and extract only the fields needed, accepting that token cost is already paid