>-
npx skills add https://github.com/agentsope/SkillAlchemy --skill agentsop-output-format-by-model
> 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 schema
Ten 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]10. "The task is fixing JSON, so format doesn't matter." Counter-edge case: when the *content itself is JSON* (e.g. "fix this malformed JSON"), output it as a fenced `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 YAML
Every 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.Integration with protocols.io API for managing scientific protocols. This skill should be used when working with protocols.io to search, create, update, or publish protocols; manage protocol steps and materials; handle discussions and comments; organize workspaces; upload and manage files; or integrate protocols.io functionality into workflows. Applicable for protocol discovery, collaborative protocol development, experiment tracking, lab protocol management, and scientific documentation.
Analyzes job descriptions and generates tailored resumes that highlight relevant experience, skills, and achievements to maximize interview chances
Generate Excalidraw diagrams from natural language descriptions. Use when asked to "create a diagram", "make a flowchart", "visualize a process", "draw a system architecture", "create a mind map", or "generate an Excalidraw file". Supports flowcharts, relationship diagrams, mind maps, and system architecture diagrams. Outputs .excalidraw JSON files that can be opened directly in Excalidraw.
Build and distribute Expo development clients locally or via TestFlight
Use when you have a written implementation plan to execute in a separate session with review checkpoints
Data structure for annotated matrices in single-cell analysis. Use when working with .h5ad files or integrating with the scverse ecosystem. This is the data format skill—for analysis workflows use scanpy; for probabilistic models use scvi-tools; for population-scale queries use cellxgene-census.
Benchling R&D platform integration. Access registry (DNA, proteins), inventory, ELN entries, workflows via API, build Benchling Apps, query Data Warehouse, for lab data management automation.
Comprehensive molecular biology toolkit. Use for sequence manipulation, file parsing (FASTA/GenBank/PDB), phylogenetics, and programmatic NCBI/PubMed access (Bio.Entrez). Best for batch processing, custom bioinformatics pipelines, BLAST automation. For quick lookups use gget; for multi-service integration use bioservices.
Take agentsope/agentsop-output-format-by-model from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.