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.
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.
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)) # FalseKey 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.