Teams want to extract structured data from text constantly: tag a million product reviews, match duplicate customer records, filter rows by a fuzzy condition, split a batch of scanned pages into separate documents. The data lives in free text; the analysis needs columns. This page explains where typed decisions fit in an extraction pipeline, where they do not, and how to design one with Jev.

The problem

Unstructured text is the largest data source most companies have and the least queryable. Analysts cannot GROUP BY the complaint in a review or JOIN two vendor names spelled differently. Extraction converts text into values that SQL, dashboards and rules understand. Most practical extraction asks one of a few question shapes:

  • Which category? Product area, document type, reason for contact.
  • How much? Severity, sentiment strength, relevance.
  • Yes or no? Mentions a competitor, is the same entity, satisfies the filter, starts a new document.

Those are decisions, not text generation, and treating them as decisions makes the output directly usable.

Common approaches and trade-offs

Regex and rules. Excellent for formats — dates, IDs, amounts, emails. Weak for meaning.

Named-entity recognition and span extractors. Pull names, places and organisations out of text. Good at "what is mentioned," not at "what does it mean for my schema."

Prompted chat models returning JSON. Flexible and capable, but per-row cost adds up on large tables, outputs sometimes break the schema or invent values, and scores vary run to run.

Trained classifiers per column. Fast and cheap once built, but you need labels for every column, and analysts change their minds about columns weekly.

How Jev's typed decisions fit

Jev, from TypeSafe AI, reads a state (one row, one record pair, one page boundary) and answers typed questions:

  • Choice for categorical columns — up to 255 options — with a probability per option.
  • Score for ordinal columns on an ordered scale of 2 to 10 levels described in words.
  • Noul for boolean columns as a 0–1 probability, which you can threshold or keep as a float.

The output never leaves your schema, so it loads straight into typed columns. Jev 1.13 is $0.042 per million input tokens on Flatkey with free output, and TypeSafe reports 70–500 ms per request, so row-by-row enrichment of large tables is practical with modest concurrency.

What Jev does not do is copy or write strings. If you need the customer's name or the invoice number as text, extract it with a parser or a span model and use Jev for the judgments around it.

Pipeline design walkthrough

  1. Design columns as questions. For each output column, decide its type and write option or level descriptions. Include unknown or not_mentioned options so missing information is not forced into a real value.
  2. Keep rows small. Send only the text that informs the columns. Long rows cost more input tokens and dilute attention.
  3. Batch columns, parallelise rows. All columns for one row go in one request; rows run concurrently within rate limits.
  4. Store probabilities alongside values. A sentiment column plus sentiment_p lets analysts filter to confident rows.
  5. Review a sample, then backfill. Validate on a labelled sample before running the full table.

The patterns are shown in text to columns, entity resolution, semantic SQL filters and document split.

Mapping column types to question types

Output column SQL type Jev question Notes
Product area enum / text Choice Add not_mentioned
Sentiment smallint Score Levels such as very_negative → very_positive
Mentions competitor boolean or float Noul Keep the probability as a float
Same entity as record B boolean Noul Ask only for blocked candidate pairs
Document boundary boolean Noul One request per page transition
Matches filter boolean Noul Used as a semantic WHERE clause

A useful habit is to keep both the value and its probability. A product_area column with a product_area_p column lets analysts run the same query at different confidence levels and see how sensitive a conclusion is to uncertain rows. For semantic SQL filters, evaluate cheap exact predicates first (date range, region) and run the Noul only on the rows that survive, which keeps input tokens — the main cost driver — down.

For document splitting, frame the question per boundary ("does page N+1 start a new document?") rather than asking for all split points at once. Each boundary becomes an independent, checkable decision.

Pitfalls and evaluation tips

  • Forced choices. Without an unknown option every row gets a value, and the column looks cleaner than the data is.
  • Pairwise blow-up. Entity resolution over n records has n² pairs. Use blocking (same postcode, similar name) in code first, then ask Jev only about candidate pairs.
  • Inconsistent definitions. If two analysts would label a row differently, the model will too. Tighten descriptions until humans agree.
  • Per-column metrics. Measure each column separately; one weak column can hide in an overall average.
  • Drift. New products and new slang change the data. Re-sample periodically.

When not to use Jev

For fixed formats (dates, amounts, IDs) use parsers. For copying spans of text, use NER or a generative model. For a column that is a pure function of other columns, compute it in SQL.

Glossary

  • Enrichment: adding derived columns to existing records.
  • Entity resolution: deciding whether two records refer to the same real-world thing.
  • Blocking: cheap pre-filtering that limits which pairs are compared.
  • Ordinal column: a column whose values have an order, like low < medium < high.
  • Semantic filter: a WHERE condition expressed in meaning rather than exact values.

Pick a use case below for a live demo and copyable code.

Structured Data Extraction use cases

Jev decision flow: Use Jev to turn text into columnsData extraction

Turn text into typed columns

Ask many bounded questions about the same text in one Jev call and store the answers as typed columns that SQL, dashboards and rules can use.

See the workflow →
Jev decision flow: Use Jev for entity resolutionEntity / Record matching

Match or reject candidate record pairs with typed same/different/review decisions

Decide whether two records or mentions refer to the same real-world entity—same_entity, different_entity, or review—with calibrated confidence, so dedupe and linking pipelines stop guessing from fuzzy string scores alone.

See the workflow →
Jev decision flow: Use Jev as semantic SQL filtersData / Semantic SQL

Turn free-text rows into typed SQL columns: a boolean filter, an ENUM routing value and a sortable score

WHERE clauses can't read meaning. Ask Jev plain-English questions per row—"is this customer angry?", "which team owns this ticket?", "how urgent is it?"—and get typed Noul, Choice and Score answers you can store as a boolean predicate, an ENUM column and a sortable value in DuckDB or any SQL engine.

See the workflow →
Jev decision flow: Use Jev to classify and split multi-doc PDFsDocuments

Classify inbox documents and decide multi-doc PDF split boundaries

After LiteParse (or similar) extracts text from an inbox PDF, let Jev classify the document and decide keep_whole / split_at_page / needs_review at candidate boundaries—plus a needs_human flag—so routing and multi-doc splits stay typed and gated in code.

See the workflow →
Jev decision flow: Use Jev for crash report screeningData extraction · also fits

Screen crash narratives against a schema

Screen free-text crash and incident narratives against a fixed question schema, write the answers as columns and flag narratives that contradict the coded fields.

See the workflow →
Jev decision flow: Use Jev for research paper triageResearch · also fits

Triage research papers by topic and relevance

Sort a flood of research papers by topic and relevance to your brief with typed questions, so people only read what matters.

See the workflow →
Jev decision flow: Use Jev for resume screeningRecruiting · also fits

Screen resumes against a role

Screen resumes against a role with typed Choice, Score and Noul questions, shortlist only confident matches and keep a human on every rejection.

See the workflow →
Jev decision flow: Use Jev to rank product needsProduct discovery / Needs ranking · also fits

Classify every product need from complaint data so a weighted ranking shows which problems are worth customer interviews

Turn a pile of user complaints into a ranked list of product needs. Jev labels each need's type, severity and whether recent launches already solve it; your code applies user-tunable weights and sorts.

See the workflow →
Jev decision flow: Use Jev to scan competitor adsAd intelligence · also fits

Classify every live ad in a competitor's ad library (creative angle, funnel stage, whether it carries an offer, how strong it is) cheaply and fast enough to scan thousands of ads across many brands, then let code count angles per brand and pull the standouts into a swipe file

Scan a competitor's whole ad library instead of scrolling it. Jev tags every live ad with its creative angle, funnel stage, offer and strength in one parallel call, so code can map what each brand is testing.

See the workflow →
Jev decision flow: Use Jev for edge intent extractionRouting · also fits

Extract typed intent fields at the edge

Turn a user request into four bounded intent fields in one Jev call, then validate, admit and schedule it in ordinary code instead of waiting on an LLM parser.

See the workflow →
Jev decision flow: Use Jev as a semantic PII linterCI / Semantic lint · also fits

Decide pass / warn / fail / needs review for each changed line against meaning-level lint rules like "never log PII"

ESLint catches syntax, not meaning. Write lint rules as English yes/no questions—"does this change log PII?"—and let Jev return calibrated probabilities per file, line and rule: pass / warn / fail / needs_review, plus a PII severity Score, so CI blocks real leaks and stays quiet on safe logging.

See the workflow →

Copy the code

The request behind Use Jev to turn text into columns, calling Jev 1.13 on Flatkey. Set FLATKEY_API_KEY and run it as is, then swap in your own state and questions.

curl -sS https://router.flatkey.ai/api/alpha/decisions \
  -H "Authorization: Bearer $FLATKEY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "typesafe/jev-1.13",
  "state": "I'\''ve been charged for the Team plan twice this month and support hasn'\''t answered in 5 days. Honestly we'\''re evaluating other tools now. Fix the charge or I'\''m filing a chargeback.",
  "questions": {
    "topic": {
      "type": "choice",
      "instructions": "What is this message mainly about?",
      "criteria": {
        "billing_dispute": "Disputed charges, refunds or double billing",
        "bug_report": "Something in the product is broken",
        "feature_request": "Asking for something the product doesn'\''t do",
        "cancellation": "Wants to cancel or downgrade",
        "other": "None of these"
      }
    },
    "urgency": {
      "type": "score",
      "instructions": "How time-sensitive is this message?",
      "criteria": [
        "low",
        "normal",
        "high",
        "critical"
      ]
    },
    "churn_risk": {
      "type": "score",
      "instructions": "How likely is this customer to leave, even if they don'\''t say so directly?",
      "criteria": [
        "none",
        "low",
        "medium",
        "high"
      ]
    },
    "threat": {
      "type": "noul",
      "instructions": "The message threatens legal action, a chargeback or public escalation, beyond ordinary frustration."
    },
    "needs_human": {
      "type": "noul",
      "instructions": "A person should read this message before any automated action."
    }
  }
}'

FAQ

Can Jev extract arbitrary strings like names or addresses?

No. Jev returns typed decisions — an option, a level on a scale or a probability — not free text. It is a fit for extraction tasks that map text to a known set of values. For copying spans of text, use a parser, a NER model or a generative model.

What kinds of extraction does Jev handle well?

Turning text into categorical or ordinal columns (sentiment, product area, severity), yes/no attributes, matching two records as the same entity or not, choosing where a document splits, and evaluating a semantic filter row by row.

How do I turn a text column into several columns at once?

Define one question per output column — Choice for categorical, Score for ordinal, Noul for boolean probability — and send them in one request per row. Each answer becomes a typed column.

Is it cheaper than using a chat model for extraction?

Jev 1.13 on Flatkey costs $0.042 per million input tokens with free output. For high-volume row-by-row enrichment, the cost is dominated by the input text you send.

How do I validate the extracted columns?

Label a random sample of rows by hand, compare per column, and keep the probability so you can filter low-confidence rows or send them for review.

Can I use the output directly in SQL?

Yes. Because every answer is one of your options, a level or a number, it maps to an enum, an integer or a float column with no parsing.

Further reading

Other categories