YAML vs JSON for Prompt and Agent Configuration

Every LLM project accumulates configuration — prompts, model settings, tool definitions, agent behaviour — and picking the wrong format for it produces a specific, recognisable misery: prompts crammed onto one line with escaped newlines, invisible in code review, edited nervously. The choice is not arbitrary, and it is not the same choice you would make for data.

The short version

YAML for config humans edit — prompts, agent definitions, model settings. Multiline text is the deciding factor and YAML handles it properly.

JSON for data machines exchange — API payloads, tool schemas, model output, anything crossing a trust boundary.

Validate both against a JSON Schema. The format is a serialisation choice; the contract is separate.

The deciding factor: multiline text

A prompt is a paragraph. Sometimes several. Watch what each format does with one.

// JSON — technically fine, practically hostile { "system": "You are a support assistant.\n\nRules:\n- Never guess at order numbers.\n- If the customer says \"refund\", route to billing.\n- Reply in under 100 words." } // YAML block scalar — the text is just the text system: | You are a support assistant. Rules: - Never guess at order numbers. - If the customer says "refund", route to billing. - Reply in under 100 words.

The JSON version is not merely uglier. It is unreviewable: a one-character prompt change shows in a diff as a modification to a 400-character line, so nobody can see what changed. Since prompts are the part of an LLM system most likely to be edited and most consequential when edited badly, that is a real operational cost rather than an aesthetic complaint.

💡 The two block scalar styles

| preserves newlines — use it for prompts, where line breaks are meaningful.

> folds lines into one paragraph — use it for long descriptions you want wrapped in the file but joined in the value.

Both accept a - suffix (|-) to strip the trailing newline, which is usually what you want for a prompt that is followed by other content.

YAML's traps are real

YAML's readability comes from inferring types from unquoted text, and that inference is where it bites.

# What you wrote # What YAML 1.1 parsers give you country: NO # false — the Norway problem enabled: yes # true version: 1.20 # 1.2 — trailing zero gone port: 08080 # error, or 0 — leading zero time: 12:30 # 750 — parsed as base-60 key: null # null, not the string "null" model: gpt-4 # string, as intended

These are not hypothetical. The Norway problem has broken real deployments, and the version-number truncation is common in model configuration, where 1.20 and 1.2 may be entirely different releases.

🚨 Quote every value that is not obviously a number

The reliable rule: if a value is meant to be a string, quote it. Version numbers, country codes, model identifiers, anything containing a colon, and anything that could be read as a boolean.

country: "NO" version: "1.20" model: "claude-opus-4" temperature: 0.7 # genuinely a number — leave it

Two more worth knowing. Indentation must be spaces — a tab character is a parse error, and it is invisible. And YAML is a superset of JSON, so any valid JSON is valid YAML, which is occasionally useful during a migration.

The security difference

This one is categorical rather than a matter of taste.

# Full YAML can construct arbitrary objects in some bindings value: !!python/object/apply:os.system ["rm -rf /"] # A safe loader refuses this. A full loader may not.

JSON cannot express this. It has six types and no mechanism for instantiating anything, which makes parsing untrusted JSON a bounded operation by construction.

The practical rules that follow:

  • Always use the safe-load variant of your YAML library. There is essentially never a reason to use the full loader on config.
  • Never load YAML from a user, an upload or a network response. Use JSON at trust boundaries.
  • YAML for files in your repository, written by your team, reviewed in pull requests. That is a different trust category and YAML is fine there.

A note on token cost

If your config is sent to a model — as an agent definition, a tool list, a set of examples — the format affects your bill. YAML is modestly cheaper than JSON for nested structures because it drops braces, brackets and most quotes, but it is not a large saving and both lose badly to CSV for repeated records. The full comparison is in token-efficient data formats.

For config specifically, this rarely decides anything. Config is small and sent once; the readability difference matters far more than a handful of tokens.

Validate either one

The most common mistake in this area is treating format choice as a substitute for a contract. It is not — a typo in YAML fails just as silently as a typo in JSON.

// Load YAML, validate against JSON Schema, fail loudly const config = yaml.load(text, { schema: yaml.SAFE_SCHEMA }); const valid = validate(config, { type: 'object', required: ['system', 'model'], properties: { system: { type: 'string', minLength: 10 }, model: { type: 'string' }, temperature: { type: 'number', minimum: 0, maximum: 2 }, tools: { type: 'array' } }, additionalProperties: false // catches typos in keys });

additionalProperties: false is the line that earns its place. Without it, temperatue: 0.9 parses cleanly, validates cleanly, and silently uses the default temperature forever. With it, the config fails at load with a message naming the unexpected key.

⚠️ Prompts in config are still code

Because config files feel like settings, prompts stored in them often escape the review discipline applied to code. That is backwards — a prompt change can alter system behaviour more dramatically than most code changes.

Version prompts alongside your code, review changes to them properly, and keep enough structure to know which prompt version produced which output. YAML's readable diffs are what make that review possible, which is the strongest argument for using it here.

A layout that works

config/ agents/ support.yaml # prompt + model + tools, human-edited triage.yaml schemas/ agent.schema.json # the contract, machine-consumed tools/*.json # tool definitions, sent to the API

The split follows the audience. Files people write and review are YAML. Files that are contracts or travel to an API are JSON, because that is what the API expects and there is no readability requirement to trade against. If you need to move between the two during a migration, our YAML to JSON converter does it in the browser with nothing uploaded.

Converting between YAML and JSON?

Convert and validate config files entirely in your browser — nothing is uploaded to a server.

Open YAML to JSON →

Summary

  • YAML for human-edited config, JSON for machine exchange and trust boundaries.
  • Multiline handling decides it. Escaped prompts in JSON are unreviewable in a diff.
  • Use | for prompts, > for folded descriptions.
  • Quote every string value. NO becomes false; 1.20 becomes 1.2.
  • Always safe-load YAML. Full loaders can instantiate objects.
  • Never parse untrusted YAML — that is what JSON is for.
  • Validate against a JSON Schema either way, with additionalProperties: false.
  • Prompts in config are code. Review them like code.

Frequently Asked Questions

Should I use YAML or JSON for prompt configuration?

YAML, in most cases. Prompts are multiline text with quotes and special characters, which YAML block scalars handle cleanly and JSON forces you to escape into an unreadable single line. For machine-generated config or data moving between services, JSON remains the better choice.

What is the Norway problem in YAML?

In the widely used YAML 1.1 behaviour, the unquoted value 'no' is parsed as the boolean false — so a country code list containing NO for Norway silently becomes false. The same applies to yes, on, off and several other words, which is why value quoting matters in YAML far more than people expect.

How do I put a multiline prompt in JSON?

You escape the newlines as backslash-n and put everything on one line, which works but is unreadable and unreviewable in a diff. If your prompts live in JSON, the usual workaround is to keep them in separate text files and reference the paths from the config instead.

Is YAML safe to load from untrusted sources?

Only with a safe loader. Full YAML can instantiate arbitrary objects in some language bindings, which makes loading untrusted YAML a code execution risk. Always use the safe-load variant, and prefer JSON for anything crossing a trust boundary.

Can I validate YAML with JSON Schema?

Yes. YAML parses to the same basic data structures as JSON, so the standard approach is to load the YAML and validate the resulting object against a JSON Schema. This gives you readable config files with the same validation guarantees as JSON.

P

Written by Paras

We build free, browser-based file tools and write the reference material we wish existed when we were looking things up. Spotted an error? Tell us and we will fix it.