Module 4 of 6 · 35 minutes

Information extraction

Extraction turns free text into fields you can count, join and act on. Pick the simplest method that works — and validate everything.

Learning objectives

  • Use regular expressions to extract well-defined patterns
  • Explain named entity recognition
  • Extract structured JSON with an LLM
  • Validate and ground extracted values

4.1 Turning text into structured data

Information extraction pulls specific facts out of free text — order numbers, dates, amounts, product names, people and organisations — so they can be counted, joined to other data and acted on. It's often the step that makes text useful to the rest of the business.

Customer messageHi, the desk lamp from orderGFY-48213 arrived broken. I'dlike my money back.Structured recordorder_id:GFY-48213issue:productproduct:desk lampwants_refund:trueextractregex for fixed patterns · NER for names and places · LLM for flexible fields
Information extraction turns free text into fields other systems can use.

4.2 Regular expressions for well-defined patterns

When the pattern is precise, a regular expression is fast, free and fully predictable:

order_re = re.compile(r"\bGFY-\d{5}\b")
messages["order_id"] = messages["text"].str.extract(r"(GFY-\d{5})", expand=False)
print(messages["order_id"].notna().mean().round(2), "of messages mention an order")

text = "Order GFY-48213 was charged £24.99 twice on 03/10/2026. Email me at sam@example.com"
print(order_re.findall(text))
print(re.findall(r"£\d+(?:\.\d{2})?", text))
print(re.findall(r"\b\d{2}/\d{2}/\d{4}\b", text))

Test patterns against real examples, including awkward ones. "03/10/2026" is 3 October in the UK and 10 March in the US — extraction is only half the job; interpretation is the other half.

4.3 Named entity recognition

Named entity recognition (NER) finds names of people, organisations, places, products, dates and amounts — things too varied for a regex. Libraries such as spaCy ship pre-trained NER models, and cloud services offer it as an API (for example Amazon Comprehend and Azure AI Language). Pre-trained models know general entities; for your own product names or contract terms you'll usually need custom training or an LLM.

4.4 Extraction with an LLM

LLMs are excellent at flexible extraction: you describe the fields, and they return structured output — even from messy, multilingual text. Ask for JSON and validate it.

Python · LLM extraction (optional — needs an API key)import json
import anthropic

client = anthropic.Anthropic()   # reads ANTHROPIC_API_KEY from the environment
MODEL = "YOUR-MODEL-NAME"        # copy a current model name from the provider's docs

def extract(message):
    prompt = f"""Extract these fields from the customer message and reply with JSON only:
{{"order_id": string or null, "issue": "delivery" | "product" | "billing" | "other",
  "product": string or null, "wants_refund": true | false}}

<message>{message}</message>"""
    reply = client.messages.create(model=MODEL, max_tokens=200,
                                   messages=[{"role": "user", "content": prompt}])
    return json.loads(reply.content[0].text)

print(extract("Hi, the desk lamp from order GFY-48213 arrived broken. I'd like my money back."))

4.5 Validate everything

Whatever the method, check the output before it flows into other systems:

ALLOWED = {"delivery", "product", "billing", "other"}

def validate(record):
    problems = []
    if record.get("order_id") and not re.fullmatch(r"GFY-\d{5}", record["order_id"]):
        problems.append("bad order_id format")
    if record.get("issue") not in ALLOWED:
        problems.append("unknown issue type")
    if not isinstance(record.get("wants_refund"), bool):
        problems.append("wants_refund must be true/false")
    return problems

print(validate({"order_id": "GFY-48213", "issue": "product", "product": "desk lamp", "wants_refund": True}))
print(validate({"order_id": "48213", "issue": "complaint", "wants_refund": "yes"}))

Measure extraction quality on a hand-labelled sample: for each field, how often is it correct, missing, or invented? LLMs occasionally hallucinate a plausible order number that isn't in the text — a check that the value actually appears in the source catches this.

Exercise: catch invented values

Write grounded(record, message) that returns False if order_id or product is not null but does not appear (case-insensitively) in the original message.

Show a solution
def grounded(record, message):
    low = message.lower()
    for field in ("order_id", "product"):
        value = record.get(field)
        if value and value.lower() not in low:
            return False
    return True

msg = "The desk lamp from order GFY-48213 arrived broken"
print(grounded({"order_id": "GFY-48213", "product": "desk lamp"}, msg))   # True
print(grounded({"order_id": "GFY-99999", "product": "desk lamp"}, msg))   # False
ExtractedJSONValid format &allowed values?Values appearin source?Into thesystemHuman review / retryyesyesnono
Never trust extracted fields blindly: check format, allowed values and grounding before they reach other systems.

Key takeaways

  • Regex is fast and predictable for precise patterns.
  • NER finds names, places, organisations and dates.
  • LLMs handle messy, varied text — ask for JSON.
  • Validate formats and allowed values.
  • Check extracted values actually appear in the source.