Structured Outputs & JSON 11 min read Updated Jul 14, 2026

Normalize Messy Inputs Before AI Processes Them

Paste a messy export and "classify these rows" and the AI splits one bug into three categories, mixes currencies, and counts three duplicate rows as three problems — silently. Normalize messy inputs before AI processes them: standardize the formats and flag the ambiguous cases.

Build the Normalization Contract

The messy table the AI classified as-is

You paste a messy export into the AI — support tickets, feedback rows, a copied spreadsheet — and ask it to classify the rows and summarize the issues. It does, confidently. Then you look at what it actually did with the mess. It read "1/2/26" and "02-01-2026" as two different dates when they might be the same one. It made "BUG," "issue," and "Defect" into three separate categories. It put "$1.2k" and "1.200,00 €" in the same column and averaged across currencies. Three rows that are almost certainly the same login bug got counted as three problems. And a Turkish-language row got quietly misfiled. The analysis looks clean and finished. It's built on input the model silently guessed its way through, and every guess is now baked into the result.

The mistake is asking the model to do two hard jobs in one step: make sense of messy input and analyze it. Real exports are full of variation the model has to resolve on the fly — mixed date and currency formats, label variants, duplicates, missing units, inconsistent casing, mixed languages, copy-paste artifacts — and when it resolves them silently, inside the analysis, you never see the calls it made. The fix is to split the work: normalize the input first, as its own reviewable pass, and only then run the classification, extraction, or summary on the cleaned result. This guide is how to normalize messy inputs before AI processes them: standardize the formats, flag the ambiguous cases instead of guessing them, and hand the downstream task input you've actually checked. NewPrompt's JSON Output Prompt Builder is where you build the normalization output contract — a row per value, with its normalized form, the rule applied, and a review flag. The boundary: NewPrompt isn't a data-cleaning engine — it doesn't validate, import, or transform your data, convert currencies, or merge duplicates for you; it helps you build the prompt, you run it on your own input, and the normalized result is a candidate you review before anything downstream touches it.

Why messy input produces confident wrong analysis

A model asked to analyze messy data doesn't refuse the mess; it resolves it, invisibly, and moves on. Every ambiguous date, every label variant, every duplicate is a decision it makes without showing you — and because the analysis on top reads fluently, the guesses underneath are invisible until a number is wrong. Here is what the model silently decides when you skip normalization:

  • It guesses ambiguous dates. "1/2/26" is January 2 or February 1 depending on locale; the model picks one, and a whole timeline can shift on that coin flip.
  • It splits label variants into separate categories. "BUG," "issue," and "Defect" become three buckets, so a count of bug reports is wrong before the analysis even starts.
  • It mixes currencies and number formats. "$1.2k," "1200 EUR," and "1.200,00 €" land in one column, and a total or an average silently spans currencies.
  • It treats duplicates as distinct records. Three rows describing the same login failure get counted as three issues, inflating whatever you're measuring.
  • It invents missing units. A bare "500" with no currency or unit gets assigned one the model finds plausible, and a guess becomes a data point.
  • It normalizes inconsistently, run to run. Ask twice and "New York," "NY," and "N.Y." might merge one time and split the next, so the same messy input gives you different clean-looking answers.

Step 1: Make normalization its own pass, before the task

The core move is refusing to normalize and analyze in the same breath. Tell the model, explicitly, that its first job is only to normalize the input — not to classify, extract, or summarize yet. That single instruction changes what you get back: instead of an analysis built on invisible cleanup, you get the cleanup itself, laid out where you can check it, and the analysis waits until the input is one you trust. The reason to separate them is that the decisions worth reviewing — which date reading, which label mapping, whether two rows are duplicates — are exactly the ones that vanish when they happen inside an analysis. Pull them into their own pass and they become a list you can scan.

This mirrors how a reliable data flow is built — the AI Data Extraction Workflow runs messy text through a sequence (bound the source, extract the fields, force a clean shape, validate before anything flows downstream), and a normalization pass sits right at the front of that idea: clean the input before it reaches the step that depends on it. It's a prompt sequence you run in your own AI tool; NewPrompt supplies the order and the prompts, not a pipeline that executes on your data.

Step 2: Set the target format for each field

Normalization needs a target, so for each field say what "normalized" means. Dates: one format, stated (ISO YYYY-MM-DD is the safe default), and the locale assumption written down, because "01/02/26" only has a meaning once you say whether the day or the month comes first. Numbers and currency: keep the amount and the currency as separate fields, and — this is the important one — do not convert between currencies unless you provide an exchange rate, because a converted number that looks clean is a fabricated one. Units: state the expected unit and what to do when it's missing. Labels and categories: give the allowed set and the mappings ("BUG," "issue," "defect" all map to "bug"). Casing, locations, abbreviations: the same — a canonical form and the variants that map to it. The tighter the target, the less the model improvises.

Pinning those target formats is exactly what the JSON Output Prompt Builder is for — it builds the output contract where each field's type and format are stated, so "date" becomes "YYYY-MM-DD" and "amount" and "currency" become two pinned fields instead of one loose string. The Create Reliable JSON Responses resource is a prompt template built on that discipline: it pins every field's format in its description — catching the drift where a date is a string all week and a number on run 500 — which is the same move normalization makes on every field at once. Both are prompts you run in your own AI tool; they pin what the target should be, and whether the model hit it is yours to check.

Step 3: Flag ambiguity instead of resolving it silently

The single most valuable rule in a normalization pass is that the model must flag what it can't resolve, not guess it. An ambiguous date whose locale you haven't pinned gets a null normalized value and review_required set true — not the model's best guess dressed as a fact. A bare number with no currency gets flagged, not assigned one. Two rows that might be the same record get marked as possible duplicates with the reason, not merged automatically and not silently kept apart. A row in another language keeps its original and gets a language tag. The principle is the one that separates a normalization you can trust from one you can't: ambiguity is surfaced for a human, not resolved by the model, because the model's resolution is a guess, and a guess that looks normalized is more dangerous than one that looks messy.

The label side of normalization — mapping "BUG"/"issue"/"Defect" to one canonical "bug" — is a closed-set classification, and the Data Classification Prompt is built for exactly that: labels from a defined set, with edge-case rules and an escape hatch for the value that doesn't fit any bucket instead of a forced misfit. Used for normalization, that escape hatch is the flag — a label the model can't confidently map goes to "needs review," not to whichever bucket was closest. It's a prompt you run in your own tool, and the mappings and the ambiguity rule are yours to define.

Step 4: Keep the original next to the normalized value

A normalization you can't audit is one you have to take on faith, so make the model show both values: for each field it changed, return the original_value and the normalized_value, the rule it applied, and a note. "original: '1.200,00 €' | normalized: amount 1200, currency EUR | rule: EU decimal format, currency split" is a row you can verify at a glance; the normalized value alone is a row you have to trust. Keeping the original is what makes the pass reversible and reviewable — if the model mis-read a European decimal as a thousands separator, or mapped a label wrongly, you can see it against the source instead of discovering it three steps later when a total is off. The original is the receipt for the transformation.

The pairing also protects you from the failure that's hardest to catch: a normalization that's plausible but wrong. "1.2k" normalized to 1200 is probably right; a cell reading "1200" normalized to 1200, when the column was actually denominated in thousands, so the real figure is 1,200,000, is the kind of error that survives every downstream step because 1200 is a perfectly reasonable-looking number. You only catch it by seeing "1.2k" sitting next to "1200" and asking whether the rule was right for that field. Without the original, the reasonable-looking wrong value is indistinguishable from the reasonable-looking right one.

Step 5: Review the flagged rows, then run the real task

A normalization pass produces a candidate, not clean data — so the last step is yours: read the flagged rows and decide them. Resolve the ambiguous dates by supplying the locale; confirm or split the possible duplicates; assign the missing currencies; correct any mapping the model got wrong. This is where the decisions the model was told not to make get made by someone who can — you, or whoever owns the data — with the original in front of you. Only the reviewed, resolved input goes on to the classification, extraction, or summary. Feeding an unreviewed normalization straight into the downstream task just moves the guessing one step later and hides it better; the point of the separate pass is to spend your attention on the handful of rows that actually need a human before the analysis locks them in.

And it's worth being exact about the split here. NewPrompt gives you the prompts and the structure for the normalization pass, not the cleaning itself — the model can mis-normalize (read the wrong date, split a currency wrong, over- or under-merge duplicates), and it's your review that catches that. The currency conversions, the duplicate-merge calls, the date interpretations, and the decision that the input is clean enough to analyze all stay with you and your team, and for anything high-impact, with the owner of the data. Normalization makes the messy input reviewable and the model's guesses visible; deciding they're right, and using the result, is your work.

Common mistakes

The habits that let messy input turn into confident wrong output:

  • Normalizing and analyzing in one step. Ask for classification or a summary on raw input and the cleanup happens invisibly inside it; make normalization its own pass first.
  • Leaving the target format unstated. "Fix the dates" lets the model pick a format and a locale; state the target (YYYY-MM-DD) and the locale assumption explicitly.
  • Converting currencies without a rate. A converted amount that looks clean is fabricated; keep amount and currency as separate fields and don't convert unless you supply the exchange rate.
  • Letting the model merge duplicates on its own. Auto-merged records hide a judgment you should make; have it flag possible duplicates with a reason, and decide them yourself.
  • Guessing ambiguous values. An ambiguous date or a missing unit should be flagged for review, not filled with a plausible guess that looks like data.
  • Dropping the original value. Without the original next to the normalized one, a plausible-but-wrong normalization is invisible; keep both so the transformation stays auditable and the review is possible at all.

A worked example: normalizing messy support rows

Watch "classify these rows and summarize" split one bug into three categories and mix currencies, then a normalization-first prompt standardize the formats, flag the ambiguous dates and the possible duplicates, and leave the calls to you.

"Classify these rows and summarize" splits one login bug into three categories, mixes three currencies into one total, and counts three duplicate rows as three problems; a normalization-first prompt maps the labels, splits amount from currency without converting, nulls the ambiguous dates with review flags, and flags the duplicates without merging — a candidate you resolve before the real task runs
THE MESSY INPUT (support export):
  1 | 1/2/26      | BUG    | EU     | $1.2k       | login fails
  2 | 02-01-2026  | issue  | Europe | 1.200,00 €  | cannot login
  3 | Jan 2       | Defect | eu     | 1200 EUR    | login failure
  4 | 13/02/26    | refund | TR     | 500         | iade istiyor

THE WEAK ASK, AND WHAT IT GIVES BACK:
  ask:  "Classify these rows and summarize the issues."
  what goes wrong:
  - BUG / issue / Defect -> three categories (it's one)
  - 1/2/26 vs 02-01-2026 read as different dates (maybe the same)
  - $1.2k, 1.200,00 EUR, 1200 EUR mixed; a total spans currencies
  - rows 1-3 (same login bug) counted as three problems
  - row 4 (Turkish "iade istiyor" = wants a refund) misfiled
  - no flags -- every guess is silent

A NORMALIZATION-FIRST PROMPT:
  Normalize this input first. Do NOT classify or summarize yet.
  Per field return: original_value | normalized_value | rule_applied |
    review_required | note.
  Rules:
    - Dates -> YYYY-MM-DD. If ambiguous by locale, normalized = null
      and review_required = true.
    - Labels map: BUG, issue, defect -> bug (this set only).
    - Region map: EU, Europe, eu -> EU.
    - Amounts: keep amount and currency as SEPARATE fields; do NOT
      convert currencies; missing currency -> review_required.
    - Duplicates: do NOT merge; flag possible duplicates with a reason.
    - Non-English notes: keep original, add a language tag.

PART OF WHAT COMES BACK (a candidate you review):
  row 1 date  "1/2/26"     -> null   rule: ambiguous US/EU  review: yes
  row 2 date  "02-01-2026" -> null   rule: ambiguous        review: yes
  row 3 date  "Jan 2"      -> null   rule: no year in source review: yes
  rows 1-3 label  -> bug (BUG/issue/Defect mapped)
  rows 1-3 region -> EU
  amounts:  "$1.2k" -> {1200, USD}   "1.200,00 €" -> {1200, EUR}
            "1200 EUR" -> {1200, EUR}
  duplicates: rows 1-3 flagged POSSIBLE DUPLICATE (same login issue,
              region EU, ~same amount) -- NOT merged
  row 4 note  "iade istiyor" -> kept, language: tr; category maps to
              refund; amount 500, currency UNKNOWN, review: yes

NEXT: you resolve the flagged rows -- set the date locale, confirm the
  duplicates, assign row 4's currency -- then run the classification on
  the reviewed input. NewPrompt didn't clean the data; it made the mess
  reviewable.

Where this fits in NewPrompt

A normalization pass is input preparation, and NewPrompt gives you the prompts for it, not a data pipeline. The JSON Output Prompt Builder builds the output contract where the target formats live — a date as YYYY-MM-DD, amount and currency as separate pinned fields; the Create Reliable JSON Responses resource is a reusable template for that format-pinning discipline; and the Data Classification Prompt handles the label side, mapping variants to a canonical set with an escape hatch for what doesn't fit. The AI Data Extraction Workflow is the larger path where a normalization pass sits at the front, before the input reaches the step that depends on it. Each builds a prompt or shows a pattern you run in your own AI tool; none validates, imports, transforms, or cleans your data.

This guide sits among the structured-output guides as the one about the input, before the task. Handling unknown vs empty vs not-applicable is the same discipline pointed at the output — classifying what a value's absence means; this classifies what a value's messiness means before the model reads it. Classifying and tagging data consistently assumes the input is already clean enough to label; normalization is the pass that makes it so, so the labels aren't split across "BUG" and "bug" before you start. And stopping AI from inventing missing data is the never-guess rule this applies to formats and duplicates, not just missing fields. The through-line: clean the input in a pass you can see, before you ask the model to reason on top of it.

Normalizing before analyzing is mise en place — the cook's habit of preparing every ingredient before the heat is on. You don't dice the onion while the pan is smoking; you chop, measure, and lay everything out first, because once the cooking starts there's no time to notice the salt was actually sugar. Handing messy input straight to an analysis is cooking without the prep: the model dices, measures, and cooks all at once, and the mistakes — the wrong date, the mixed currency, the duplicate counted twice — get cooked in where you can't pick them back out. A normalization pass is the prep station: everything standardized and laid out, the questionable items set aside to check, before a single downstream step begins. The model can do the chopping faster than you can; whether the onion was really an onion, and whether the prep was right, is a look you take before the heat, not after.

Tools for this guide

Each generates the prompt described above — you run it in your own AI assistant.

Ready-made resources

Reusable prompts and templates for the exact steps in this guide.

Take it further

When this task is one step inside a larger workflow or build.

FAQ

Why not just ask the AI to clean the data and analyze it in one prompt?

Because the cleaning is where the consequential decisions are, and doing it inside the analysis hides them. When the model normalizes and classifies in one step, the calls that matter — which date reading, whether two rows are the same, how to map a label — happen invisibly, and all you see is a clean-looking result you can't audit. Splitting normalization into its own pass pulls those decisions into the open: you get a list of what was standardized, what was flagged, and what the model wasn't sure about, before any analysis depends on it. It's slightly more work and much more trustworthy, because the error you're trying to avoid isn't a messy output — it's a tidy output built on guesses you never got to check. Clean first, in a pass you can see; then analyze the input you've actually reviewed.

How should the prompt handle an ambiguous date like "1/2/26"?

It should refuse to guess it. The right rule is: if a date's format is ambiguous by locale and you haven't told the model which locale applies, it sets the normalized value to null and flags the row for review, rather than picking January 2 or February 1 and moving on. "1/2/26" is genuinely undecidable without context — the same string is two different dates in US and European conventions — so any single value the model returns is a coin flip presented as a fact. The fix on your side is to supply the missing context (state the source's locale, or the year if it's absent) and let it re-normalize, or resolve the flagged rows by hand. A flagged ambiguous date is a one-minute decision; a silently-guessed one is a timeline that's wrong in a way nothing downstream will reveal.

Should the AI merge duplicate rows during normalization?

No — it should flag them, not merge them. Whether two rows are really the same record is a judgment with real consequences (merge two distinct customers and you've lost one; keep two duplicates and you've double-counted), and it often depends on context the model doesn't have. So the safe rule is: the model marks possible duplicates, with the reason it suspects them ("same login issue, same region, near-identical amount"), and leaves them un-merged for you to decide. Auto-merging is the tempting shortcut that quietly changes your data — a merge you didn't see is indistinguishable from data that was never duplicated. Let the normalization surface the candidates; keep the merge decision, and the accountability for it, with you.

Does NewPrompt clean or validate the data itself?

No — NewPrompt only helps you build the prompt for a normalization pass; it isn't an ETL or data-cleaning engine, and it doesn't touch your data. It doesn't validate the input, import or export it, update a spreadsheet or database, convert currencies, merge duplicates, or infer a locale for you. The normalized output is a candidate the model produced, which you run in your own AI tool and then review — the model can mis-read a date, split a currency wrong, or over-merge, and catching that is the review step, not something NewPrompt does. What NewPrompt provides is the structure that makes the model's normalization decisions explicit and flaggable; the currency calls, the duplicate resolutions, and the decision that the input is clean enough to use stay with you and, for high-impact data, its owner.