Find schema properties that could be asking more than one question — see the competing readings side by side, and pin each property to a single meaning before the data is collected.
Entity Enricher treats LLMs as queryable knowledge bases, and a property name is the question you are asking. When the name admits several readings, each model quietly picks one — so size on a company comes back as a headcount from one model, a revenue figure from another, and a floor area from a third. The models did not disagree about a fact. They answered different questions, and your column now holds a mix of answers that no downstream consumer can tell apart.
Pinning the meaning is what makes an enrichment comparable across models and stable over time. It also cleans up everything downstream: multi-model fusion stops seeing conflicts that are really two questions, and benchmark comparisons stop punishing models for reading your schema differently than the reference did.
A fact that simply changes over time is not ambiguity. A schema is a durable contract, so a plainly-named ceo means “the CEO at enrichment time”, and re-running the schema next year should return the new one. The check never proposes freezing a date into a name — that would break every future run.
The whole check is one question, asked of every property: reading its name in the context of its parent object, how many distinct things could it be asking for? The count is the verdict.
| Readings | Verdict | What it means |
|---|---|---|
| Exactly one | Clear | Every model looks up the same thing. No chip, nothing to fix. |
| Two or more | Ambiguous | Each model settles on its own reading, so the column silently mixes answers to different questions. The check names the competing readings and proposes a wording that keeps one. |
| None | Unmappable | The name names nothing the parent object has, so the model cannot look a value up — it invents one. The listed readings are the ones the analyzer considered and rejected, and the remedy is a rename or a removal: no description can give an entity a property it does not have. |
A property that names exactly one thing can still be flagged when the value is unframed — the reader knows what is being asked, but not in what terms it comes back. These are the recurring shapes:
| Sub-case | Example | What is open |
|---|---|---|
| Unclear referent | Companysize | The name points at several distinct facts the parent really has — headcount, revenue, floor area. Nothing about the name chooses. |
| Unclear measure or unit | Companyannual_revenue | One fact, but no currency, no period and no gross/net frame — a plausible answer can be off by three orders of magnitude and still be "correct". |
| Unclear scale or direction | Supplierrisk_score | No stated range and no stated polarity: 0–10 or 0–100, and is a high number safer or riskier? Two models can invert each other. |
| Unclear scope or boundary | Companyemployees | Which subset, which level of aggregation, whose perspective — the whole group or this site, headcount or full-time equivalents, contractors in or out. |
| Unmappable | Authorrelease_year | An author has no release year — their books do. The model cannot look this up, so it fabricates. Rename it to something the parent owns, or move it to the object that has it. |
Prose properties — description, summary, notes, bio — are never flagged. Their wording obviously differs from one model to the next, but the question being asked is perfectly clear, and that is all this check judges. Ambiguity is about the question, never about how much the answers look alike.
A verdict on its own (“this is unclear”) leaves you to guess what the analyzer had in mind. So every finding carries its interpretations: two to four short, distinct readings the property admits, most likely first. That list is the finding — if the analyzer cannot name two readings, the finding is dropped as noise rather than shown to you.
annual_revenue on a CompanyAlongside them comes a suggested description that keeps exactly one — here, “total group revenue in USD for the most recent completed fiscal year, gross of returns”. Applying it costs nothing: the description reaches the enriching model exactly like the name does, but the property keeps its name, so no data contract moves. When the name itself is what misleads, the finding also carries suggested names.
Seeing the readings spelled out usually settles the property faster than any explanation would: you recognize the one you meant, and the rest are what you have been silently receiving.
After a sample is generated, the analyzer reviews its property names and returns an ambiguity report. Unambiguous renames are applied automatically to AI-invented keys (never to fields you named yourself), so the sample you review already reads better. It also flags over-specialized properties — traits that leaked in from the example instance and fit only a subtype (an athlete's medals on a generic Person) — and suggests a narrower entity type. The identity-scoping check runs here as its own call, right after: it settles the shape of related items before you review the sample. Samples grounded in attached documents are skipped — their values come from the source document, not from model recall.
Once a generated schema is saved, a post-pass annotates every property with its ambiguity verdict and suggests renames for the ones that are still open. Relationship sites are already annotated by then — generation judges their scoping itself, as one of its own steps — so the post-pass covers property names only. This step is best-effort: if it fails, the generation itself is unaffected.
The Re-check button runs both checks — property names and relationship sites — as two parallel calls. This is the one place that suggests a rewritten description rather than a rename. It analyzes only what has no annotation yet, and switches to a full re-analysis once everything is annotated.
Sample JSON you paste for schema creation can be analyzed statelessly — you get a report of ambiguous and unmappable property names, and of related items mixing entity facts with pairing facts, without anything being modified.
The same finding suggests a rename in one place and a description in another, and the reason is worth knowing. At generation time the description does not yet exist independently — it is written from the name, so it can only repeat the ambiguity. The name is the only thing that can be fixed, and nothing depends on it yet. That is why sample generation and the schema-generation post-pass both propose renames.
Once the schema is live, renaming a property moves columns, breaks queries and re-keys synced tables, while a sharper description reaches the model just as directly and changes nothing else. So the rule is simple: before anything depends on the schema, rename; once it is live, pin the description — and keep the rename in reserve for the cases where the name itself is the problem.
The check is advisory only. Nothing analyzes your saved schemas in the background: it runs at generation, and when you press Re-check. It never blocks generation, never rejects an enrichment, and its annotations are stripped from every prompt sent to the enriching models — it informs you, not the AI.
Flagged properties show an “ambiguous” chip in the Workflow Editor: amber when the readings mostly overlap and only edge cases differ, red when the competing readings would yield materially different data. Properties judged clear carry no chip. Hovering the chip shows the analyzer's note, the competing readings it found, and the suggested description or names — so the decision and the fix are in the same tooltip.
A verdict is rendered on a property's name and description together — so renaming a property, or editing its description, drops its annotation. The editor highlights such properties as stale and offers a re-check, which analyzes only what is missing. This is exactly what you want after applying a suggested fix: the re-check confirms whether the new wording really pins a single reading.
Identity scoping is a second check, run as its own model call alongside the ambiguity pass and reported with it. It reviews every relationship site — the items of a related array, and nested objects: when one mixes facts about the related entity itself (its name, its country) with facts about the pairing (a role held for this parent, a per-parent designation), both share one identity — and re-enrichments overwrite the pairing facts across parents. Such sites carry an amber “mixed facts” chip whose tooltip shows the recommended shape: the entity's own fields nested into a subobject, the pairing fields kept on the item. When the item already carries such a subobject, the fix is smaller — the misplaced fields move into the one that is already there.
The split is applied during sample generation, before you approve the sample: the shape is settled on the first sample, every restructured site is listed in the generation warnings, and the other samples of the batch are generated against the settled shape. So schemas generated from a fresh sample normally come out clean. Samples grounded in attached documents are left as their sources implied, and get the chip instead.
Schema generation itself never restructures the sample you approved — it judges the same sites and reports what it finds. On an existing or hand-written schema the chip is therefore where the fix lives: it offers a one-click split that restructures the sample and regenerates the schema from it. The structure is the contract, so it is changed by regenerating from a new sample, never patched in place. A site you choose not to split keeps working — it just keeps one shared identity, and the chip. The chip disappears on its own when the item's field set changes.
The check can be switched off per schema from the Workflow Editor's overflow menu. When disabled, the post-generation pass is skipped, the chips, Re-check button and stale warnings are hidden, and the analysis endpoints answer with a ambiguity_check_disabled error. Existing annotations are kept (just hidden), and re-enabling the check on a schema that was never analyzed runs it automatically.
Every generated schema starts with the check on, including schemas generated from attached documents. Ambiguity is a property of how the schema is worded, not of where one run's values came from: the document settled those values once, and the schema goes on to be reused against entities it never covered. What the document does change is the sample step — its property names come from the source document's own vocabulary, so they are never renamed in code, and the schema built from them carries the check instead.
“The annual revenue of the company” adds no information the name did not already carry, so the analyzer treats such a description as if it were absent and judges the name alone. A description earns its place by naming the unit, the period, the scale or the boundary.
Analyzer notes and interpretations are written in your interface language — a French user sees French readings, a Japanese user Japanese ones. Suggested property names stay in English, matching schema naming conventions.
Each analysis is a real (cheap) model call — two of them, run in parallel, when there are also relationship sites to scope. Each is recorded as its own prompt on the record, under the ambiguity_analysis type, and deducted from credits like any other AI usage. Incremental re-checks only pay for the properties and sites actually analyzed.
Sample and schema generation are themselves instructed to name one thing per property and to write descriptions that state the unit, the scale and the boundary — and, for debatable lists, an upper bound in the description rather than a count forced into the name. So most schemas come out clean and the analyzer only has to catch the stragglers.
The check is available programmatically:
| Surface | Description |
|---|---|
POST /api/schema/analyze-sample | Analyze pasted sample JSON — both checks in parallel behind one request, stateless report, nothing is modified |
POST /api/schema/saved/{id}/analyze | Analyze a saved schema and write the annotations of both checks — incremental by default, force=true re-analyzes all |
POST /api/schema/scoping-split | Apply one "mixed facts" split to a sample set — deterministic, free, nothing saved; feed the returned samples back into schema generation |
analyze_sample | MCP tool — the same stateless sample report, both checks, from Claude or any MCP client |
analyze_schema | MCP tool — annotate a saved schema; combine with update_schema to apply a suggested description or rename |
Findings come back with a kind (ambiguous or unmappable), a level, a note, the interpretations list and the suggested fix. On a saved schema they are stored on each property as ambiguity; sample generation returns them under ambiguity_report.
See the API Reference and the MCP Server guide for authentication and the full tool catalog.