Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Pick an LM output format per (task x consumer x model) rather than by reflex: different formats carry different cognitive load (e.g. code-in-JSON makes the same model write worse code than plain-text+diff, while asking for prose when you need a typed object fails the other way). Use when designing or debugging an LM's output schema, choosing between plain text / diff / JSON / tool-call / grammar-constrained output, or when a model's quality drops after wrapping its output in a structured format.
.claude/skills/agentsope-agentsop-output-format-by-model/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-22 | ✗→✓ | ▲ Improved | 345% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 253% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 266% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 284% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 333% | 0% |
> One-liner: Different output formats carry different cognitive load for the model. Code-in-JSON is the canonical proof: the same model writes worse code when wrapped in a JSON tool-call than when emitted as plain text + diff. The reverse failure (asking for prose when you need a typed object) is just as common. Pick format per (task × consumer), not by reflex.
Activate this skill before committing to an output schema in any of these situations:
| Trigger | Signal | |---|---| | Designing a coder-agent | "should the model return a apply_patch tool call or plain-text diff?" | | Adding a tool to an existing agent | "tool input has a code / query / sql / regex field — should I nest it in JSON or leave it as a string?" | | Building extraction / classification | "should I use dspy.Predict typed fields, Pydantic + response_format=json_schema, or just markdown?" | | Wiring an evaluator | "the metric needs a number — but the model also has to reason to produce it" | | Migrating a working prompt to "structured outputs" | someone said "let's make it safer with JSON schema" | | Tool-call harness adds latency / errors | repeated json.JSONDecodeError, escaping bugs, truncated outputs |
Anti-triggers (skip this skill):
┌──────────────────────────────────────────────────┐
│ FORMAT FOLLOWS FUNCTION │
│ │
│ Some formats add cognitive load to the model │
│ and measurably degrade quality on the │
│ *content* the format is supposed to wrap. │
└──────────────────────────────────────────────────┘
▲ ▲
│ │
What's being consumed? Who consumes it?
(code? prose? entities? (human reader? parser?
number? action selection?) downstream LM? compiler?)
│ │
└──────────┬───────────────┘
▼
FORMAT SELECTION
(text+diff | markdown | JSON | tool_use
| grammar-constrained | typed field)Aider's code-in-json benchmark is the load-bearing empirical anchor:
SyntaxError / IndentationError. Sonnet kept syntax clean but still scored lower overall. aider.chat/2024/08/14/code-in-json.html]Academic generalization, same year:
| Reflex | When it's wrong | |---|---| | "Structured output is always safer." | False for code, multi-step reasoning, free-form prose. Strictness ≠ quality of contents. | | "Markdown is only for humans." | False — markdown is also the highest-fidelity wire format for many LM-to-LM hand-offs (Aider uses it; DSPy's default chat adaptor uses field-marked markdown over JSON for many signatures). |
Every formatting requirement consumes some of the model's attention budget. The tax is:
\n becomes \\n; each quote becomes \"; the model has to track this while also solving the actual problem.⇒ Heuristic: the more semantically dense the content, the cheaper the format must be.
Three questions, in order:
| Content type | Default format | Why | |---|---|---| | Source code edits | plain text + diff format (SEARCH/REPLACE, unified diff, or str_replace tool with code as a single string field) | Aider 20%→61% on GPT-4 Turbo; same direction across all models. aider.chat/2024/08/14/code-in-json.html] | | Source code (full file rewrite) | plain text or markdown fenced block | Same reason. JSON-wrap adds escaping tax. | | Structured data extraction (entities, dates, IDs, classifications) | JSON / Pydantic / typed OutputField | Schema helps here — fields are the task. | | Action selection (which tool to call) | JSON tool_use | Tool name + scalar args. The decision is structured by definition. | | Action body (SQL, regex, code, file contents) | single string field inside tool_use — do not sub-structure | Same code-in-JSON penalty applies to any code-shaped payload. | | Reasoning / intermediate steps | markdown or Python-style scratchpad | CoT in JSON measurably degrades; see "Let Me Speak Freely?" arxiv.org/abs/2408.02442] | | Numeric answer with reasoning | markdown reasoning + final answer in fenced block or final-line convention | Don't force the reasoning into a JSON reasoning field — it shortens and stiffens. | | Free-form prose (summary, explanation, customer reply) | markdown | Native to instruction-tuned models. | | Mixed (e.g., extract entities AND rewrite the document) | split into two calls or two passes — see §5 Case B | One format can't serve two contents well. |
| Consumer | Constraint | Implication | |---|---|---| | Human in a chat UI | Render-friendly | Markdown wins. JSON is hostile. | | json.loads / Pydantic parser | Must be valid | JSON with schema, or a known-safe envelope (<result>...</result>) with markdown body. | | Downstream LM (LM-to-LM pipeline) | Reads what was written | Markdown is more robust than JSON when content includes code/math; the next LM ingests it natively. | | Compiler / interpreter (PoT, code-exec sandbox) | Must be a valid program | Output as code in a fenced block, not as a JSON program field. | | Tool dispatcher (function calling) | Needs tool name + args | JSON tool_use with scalar args; multi-line bodies go in a single string field. | | Diff applier (Aider, git apply, str_replace) | Must apply cleanly | Format dictated by the applier: SEARCH/REPLACE for Aider diff, unified diff for git, str_replace for Anthropic editor tool. |
Format support is model-specific. Aider maintains a per-model edit-format default precisely because of this. aider.chat/docs/more/edit-formats.html]
| Model family | Best code-edit format | Notes | |---|---|---| | GPT-4 Turbo / GPT-4o | udiff or SEARCH/REPLACE diff | 20%→61% with udiff on refactor. aider.chat/2023/12/21/unified-diffs.html] | | Claude 3.5/3.7 Sonnet | SEARCH/REPLACE diff | Tendency to write too much — instruct minimal blocks. aider.chat/2024/07/01/sonnet-not-lazy.html] | | Gemini family | diff-fenced | Path inside the fence. aider.chat/docs/more/edit-formats.html] | | GPT-4.1 / OpenAI patch tool | patch protocol | OpenAI-specific, multi-action robust. | | Weak / local models (Llama-3-8B class, GPT-3.5) | whole file rewrite | Diff parsing failures dominate; whole-file is dumb-but-stable. | | Reasoning models (o1, o3) as architect | architect mode: reasoner emits prose plan → editor model emits diff | o1-preview alone: 79.7%; o1-preview + Sonnet editor: 82.7%. aider.chat/2024/09/26/architect.html] |
Combined decision tree:
What's being emitted?
│
┌───────────────────┼───────────────────┐
│ │ │
code structured free-form
edits data fields prose/reasoning
│ │ │
▼ ▼ ▼
diff/SEARCH- JSON / Pydantic / markdown
REPLACE/ tool_use scalar
str_replace, args
code as string
│ │ │
▼ ▼ ▼
pick per-model wrap any code- do NOT
edit format shaped payload in force into
(Aider table) a single string field JSON schemaTen concrete operations. Format choice is not arbitrary — each citation is the empirical anchor.
| # | Task | Recommended format | Rationale / Evidence | |---|---|---|---| | 1 | "Edit auth.py to use JWT" (coder-agent core loop) | Plain-text SEARCH/REPLACE diff in markdown fence | Aider's measured 3× on GPT-4 Turbo, generalizes across models. aider.chat/2024/08/14/code-in-json.html] | | 2 | "Apply this patch to a file via Claude's text-editor tool" | Anthropic str_replace tool — old_str / new_str as string fields, no JSON sub-structure inside the code | Matches Anthropic's published tool-design guidance: fields should be high-signal scalars, not low-level identifiers. docs.anthropic.com text-editor-tool] | | 3 | "Extract invoice fields (vendor, amount, date, line items)" | JSON / Pydantic / DSPy typed OutputField | Schema is the task. Structured-output benchmarks show high accuracy here. arxiv.org/html/2505.20139v1] | | 4 | "Classify ticket priority (P0/P1/P2/P3)" | Single-token output or JSON {"priority": "..."} | Trivial; structure helps determinism. | | 5 | "Answer a math word problem" | Markdown reasoning + final answer in \boxed{} or fenced final line | "Let Me Speak Freely?" — 10–15% degradation when locked into JSON reasoning. arxiv.org/abs/2408.02442] | | 6 | "Decide which tool to call next" | JSON tool_use with tool name + scalar args | Action selection is intrinsically structured. | | 7 | "Generate the SQL query for that tool" | Tool args contain {"sql": "SELECT ..."} — SQL as a raw single string, no further JSON sub-structure | Same family as code-in-JSON; SQL is code-shaped. | | 8 | "Write a customer-support reply" | Markdown | Native to instruction tuning; JSON-wrap costs nothing useful. | | 9 | "Summarize a paper and return key claims as a list" | Markdown prose + a fenced claims: YAML or JSON block at the end (two-zone output) | Best of both: prose flows freely, downstream parser reads the trailing block. | | 10 | "Rewrite a long document AND extract entities" | Two passes: pass 1 rewrite (markdown), pass 2 extract (JSON, fed the pass-1 output) | One call cannot serve both contents at peak quality. |
Trigger: New extraction pipeline. Fields = vendor_name, total_amount, invoice_date, line_items[].
Constraints:
Decision steps:
response_format=json_schema or DSPy typed OutputFields. Field names carry the semantic load (DSPy's "Signatures carry semantic load" principle dspy.ai/learn/programming/signatures]).line_items[].description containing free-text: still inside JSON — descriptions are prose data, not code. Escaping cost is real but bounded.Outcome: JSON wins. Schema validation catches missing fields cheaply; no measurable degradation expected on this content shape.
Trigger: PM adds requirement — "and produce a cleaned-up version of the invoice document body."
Constraints:
cleaned_text: "..." inside the same JSON forces multi-paragraph escaping and competes with the extraction reasoning for attention.Decision steps:
{vendor, amount, date, line_items} as JSON. Call 2 receives the original doc + extracted JSON, returns markdown rewrite.===EXTRACTION=== separator, then a fenced JSON block. Parser splits on the separator. Cheaper but more fragile.Outcome: Plain text + JSON split, not unified JSON. Format follows content, even within one logical task.
Trigger: Building a data-analyst agent. One tool is run_query(sql: str).
Constraints:
Decision steps:
run_query vs read_file vs list_tables.sql a single top-level string field. Don't sub-structure it ({from: ..., where: ...}). Let the model write idiomatic SQL.dry_run: bool).sql block), then a thin downstream agent wraps it into the tool call. This is the Aider "architect" pattern transplanted to data tools. aider.chat/2024/09/26/architect.html]Outcome: JSON for the decision; single string for the SQL body; consider a two-step generate-then-dispatch if quality matters.
Trigger: Eng-org-wide push to add response_format=json_schema to every LM call.
Constraints:
json.loads failures.Decision steps:
json_schema. This is where structured output earns its keep.Outcome: Reject the blanket policy; replace it with a content-type-driven one. This is the inverse of the "JSON everywhere" reflex.
{language, lines: [...], imports: [...]}. Every level of nesting compounds the escape tax.reasoning JSON field. Models compress and stiffen when reasoning is inside JSON — they treat it as a label rather than as thinking. Prefer free markdown reasoning followed by a structured tail.name, file_type) over technical identifiers (uuid, mime_type). The model reads your schema as part of its prompt. anthropic.com/engineering/writing-tools-for-agents]json block in markdown — not as a JSON tool-call wrapping JSON-as-string. Even here, the rule holds: code-shaped content goes in a plain string surface, not nested escapes. arxiv.org/html/2510.04717v1 — JSON Whisperer]How major frameworks implement format selection. Use this to translate the principles into the stack you're already in.
| Format | Wire shape | When | |---|---|---| | whole | Full file in markdown fence | Weak models, fallback | | diff (SEARCH/REPLACE) | Two fences per edit, byte-exact match | GPT-4o, Sonnet, most strong models — default | | diff-fenced | Path inside fence | Gemini | | udiff | GNU unified-diff style | GPT-4 Turbo — the empirical anchor: 20%→61% | | patch | OpenAI patch protocol | GPT-4.1 | | editor-diff / editor-whole | Slim prompt for sub-model | Architect mode editor |
No JSON-wrapped edit format exists — it was tested and rejected. aider.chat/2024/08/14/code-in-json.html]
DSPy's Signature → Module pipeline ships with multiple adaptors that materialize the same logical I/O contract into different wire formats:
ChatAdapter (default): field-marked markdown with [[ ## field_name ## ]] headers. Used even for typed outputs because models reliably emit it.JSONAdapter: schema-enforced JSON. Used when the consumer must parse strictly (e.g., feeding another typed pipeline).TwoStepAdapter: free-form generation, then a second cheaper call reformats into JSON. Direct application of the "Let Me Speak Freely?" finding — generate freely, format separately. arxiv.org/abs/2408.02442]DSPy's recommendation: stay on ChatAdapter unless a downstream consumer demands JSON. The framework itself encodes the principle of this skill.
response_format=json_schema for typed extraction: yes. For code generation: avoid; if forced, single string field with code as the type.strict: true) guarantees schema validity but does not fix content degradation — Aider tested this explicitly. aider.chat/2024/08/14/code-in-json.html]text_editor tool: commands view, str_replace, create, insert, undo_edit. str_replace takes old_str and new_str as single string fields — explicitly avoiding the JSON-nests-code anti-pattern. This is the principle of this skill made into a first-party API.Layer | Format mechanism
-----------------------|-----------------------------------
Generation grammar | Outlines / Guidance — enforce
Per-task adaptor | DSPy ChatAdapter vs JSONAdapter
Per-model edit format | Aider's edit-format table
Tool envelope | OpenAI / Anthropic tool_use
Wire serialization | markdown vs JSON vs YAMLEvery layer can independently make a wrong format choice. This skill operates at the per-task layer: decide first what format the content wants, then let each lower layer enforce it.
┌──────────────────────────────────────────────────────────────────────┐
│ FORMAT DECISION CARD │
├──────────────────────────────────────────────────────────────────────┤
│ Code edits → text + diff (per-model: udiff/SEARCH/whole) │
│ [Aider: 20% → 61% on GPT-4 Turbo] │
│ Code generation → markdown fenced block │
│ SQL / regex / shell → string field inside tool_use, NOT sub-JSON │
│ Entity extraction → JSON / Pydantic / typed OutputField │
│ Classification → single token or JSON {label} │
│ Action selection → JSON tool_use │
│ Reasoning + answer → markdown CoT + fenced final answer │
│ [Format-restricted reasoning: -10 to -15%] │
│ Prose / explanation → markdown │
│ Mixed content → two passes OR two-zone output │
├──────────────────────────────────────────────────────────────────────┤
│ NEVER: │
│ • Nest code inside JSON sub-structure │
│ • Force CoT reasoning into a JSON "reasoning" field │
│ • Use schema as substitute for prompt engineering │
│ • Assume strict-mode JSON fixes content quality │
└──────────────────────────────────────────────────────────────────────┘Primary empirical anchors:
Academic generalization:
Framework / API docs:
Companion skills in this collection:
aider-sop-skill/SKILL.md — full edit-format treatment in coder-agent context.dspy-sop-skill/SKILL.md — adaptor selection within compiled programs.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-17 | pass→pass | 16,196 | 11,489 | -29% | 1 | 1 | 0% | 2,277 | 8,745 | +284% | 0 | 0 | — |
case-22 | fail→pass | 12,986 | 11,982 | -8% | 1 | 1 | 0% | 2,007 | 8,939 | +345% | 0 | 0 | — |
case-01 | fail→pass | 15,758 | 12,528 | -20% | 1 | 1 | 0% | 2,578 | 9,088 | +253% | 0 | 0 | — |
case-02 | fail→pass | 15,496 | 13,040 | -16% | 1 | 1 | 0% | 2,532 | 9,267 | +266% | 0 | 0 | — |
case-15 | pass→pass | 14,743 | 12,110 | -18% | 1 | 1 | 0% | 2,110 | 9,144 | +333% | 0 | 0 | — |
case-03 | pass→pass | 15,044 | 13,775 | -8% | 1 | 1 | 0% | 2,533 | 9,186 | +263% | 0 | 0 | — |
case-04 | pass→pass | 8,395 | 7,498 | -11% | 1 | 1 | 0% | 1,351 | 8,249 | +511% | 0 | 0 | — |
case-05 | fail→pass | 14,626 | 13,919 | -5% | 1 | 1 | 0% | 2,400 | 9,221 | +284% | 0 | 0 | — |
case-06 | pass→pass | 18,433 | 13,021 | -29% | 1 | 1 | 0% | 2,680 | 9,099 | +240% | 0 | 0 | — |
case-16 | pass→pass | 16,586 | 9,073 | -45% | 1 | 1 | 0% | 2,583 | 8,556 | +231% | 0 | 0 | — |
case-07 | pass→pass | 16,444 | 12,541 | -24% | 1 | 1 | 0% | 2,706 | 9,225 | +241% | 0 | 0 | — |
case-08 | pass→pass | 14,273 | 12,314 | -14% | 1 | 1 | 0% | 2,223 | 9,034 | +306% | 0 | 0 | — |
case-09 | pass→pass | 16,085 | 14,688 | -9% | 1 | 1 | 0% | 2,484 | 9,465 | +281% | 0 | 0 | — |
case-10 | pass→pass | 13,208 | 14,969 | +13% | 1 | 1 | 0% | 2,163 | 9,499 | +339% | 0 | 0 | — |
case-11 | pass→pass | 13,132 | 11,425 | -13% | 1 | 1 | 0% | 2,230 | 8,895 | +299% | 0 | 0 | — |
case-12 | fail→pass | 11,087 | 11,272 | +2% | 1 | 1 | 0% | 2,059 | 8,921 | +333% | 0 | 0 | — |
case-13 | fail→pass | 15,601 | 10,442 | -33% | 1 | 1 | 0% | 2,349 | 8,840 | +276% | 0 | 0 | — |
case-14 | pass→pass | 11,735 | 10,088 | -14% | 1 | 1 | 0% | 1,642 | 8,649 | +427% | 0 | 0 | — |
case-18 | pass→pass | 16,306 | 13,613 | -17% | 1 | 1 | 0% | 2,439 | 9,348 | +283% | 0 | 0 | — |
case-19 | fail→pass | 13,539 | 12,237 | -10% | 1 | 1 | 0% | 2,043 | 9,055 | +343% | 0 | 0 | — |
case-20 | pass→pass | 11,973 | 9,382 | -22% | 1 | 1 | 0% | 2,082 | 8,588 | +312% | 0 | 0 | — |
case-21 | fail→pass | 13,978 | 11,117 | -20% | 1 | 1 | 0% | 2,565 | 8,783 | +242% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +36 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.