Information Extraction
Information extraction turns prose into rows, finding the names, linking the pairs, and filling a record a database can answer questions from.
Why Does This Exist?
Most human knowledge sits in prose: news, contracts, medical notes, tickets. You cannot run a database query over a folder of PDFs to find every acquisition above a threshold, nor filter transcripts for adverse reactions without reading each one. Text stores meaning for readers, not rows for machines.
Information extraction bridges that gap. It takes unstructured text and returns typed pieces: the entities, the relations between them, and the events they participate in. Those pieces fill tables, knowledge graphs, and alerts. Without this step, every downstream analysis starts with someone reading.
This page covers the pipeline that does it: span finding, pair linking, and record filling. The span step is named entity recognition. The pair step is relation extraction. Mentions scattered across sentences get merged by coreference resolution before the record is filled. The one prerequisite is small: an entity is a typed span (person, organization, date), a relation is a labeled link between two spans, and an event adds roles like time and price. Everything else builds on those three.
Think of It Like This
A librarian filling out an index card
A librarian reads a dense biography not to enjoy it but to fill a card with fixed fields: name, birth date, birthplace, achievements. "Born in London on a rainy Tuesday in 1845" becomes birthplace London and birth year 1845. The rainy Tuesday is ignored because no field asks for weather.
Extraction works the same way at scale. The text is the biography, the schema is the card, and the pipeline keeps only what maps to a field. Anything without a slot is left on the page, which is why the schema decides the output as much as the model does.
Where it stops: a librarian who cannot find a date leaves the field blank and moves on. A careless extractor invents one. Blank tolerance has to be built in, or the database fills with confident guesses.
How It Actually Works
Extraction runs as stages, each adding one layer of structure. Modern systems sometimes run them jointly, but the logic stays the same.
Finding the spans
named entity recognition tags "Ada" as PERSON, "Oxfam" as ORG, and "2019" as DATE in "Ada joined Oxfam in 2019." That is three typed spans from five words, and every later stage reads these tags, never the raw text alone.
Linking the pairs
The linker scores candidate labels between span pairs. For (Ada, Oxfam) it compares joined, works_at, and none using the verb and the entity types. The winner becomes a triple (subject, relation, object) a graph can store. Cross-sentence pairs are skipped here; mentions are merged first by coreference resolution, then a document-level pass links across sentences.
Filling the record
The event frame collects the trigger ("joined") plus its roles: who (Ada), which organization (Oxfam), when (2019). Three spans, one trigger, one filled record. A template for acquisitions or hiring would add slots like price or title, and missing slots stay empty rather than guessed.
Worked example with real numbers
Score the pair (Ada, Oxfam) over labels with raw scores . Exponentiate: , , , summing to . Normalize:
The triple (Ada, joined, Oxfam) wins at , which is the number the diagram carries. Attach 2019 as the date role and the record is complete: one hire event, three arguments, no invented fields.
Code
This scores one pair with softmax and emits the winning triple, using only the standard library.
import mathdef softmax(scores: list[float]) -> list[float]: exps = [math.exp(s) for s in scores] total = sum(exps) return [e / total for e in exps]probs = softmax([1.5, -0.5, -1.0]) # [0.82, 0.11, 0.07]print([f"{p:.2f}" for p in probs]) # -> ['0.82', '0.11', '0.07']print(("Ada", "joined", "Oxfam")) # -> ('Ada', 'joined', 'Oxfam')Watch Out For
Accepting model-written JSON at face value
Prompting a large model for JSON feels like extraction, but the output can contain entities reworded or invented from nothing. The symptom is clean JSON that fails span checks: names that never appear in the source. Fix it by grounding every emitted span to exact source offsets and rejecting anything ungrounded before it reaches the database.
Letting one bad stage poison the record
Pipelines cascade. A missed span kills its relations, and a wrong relation fills the wrong slot, so one early error becomes three downstream. The symptom is records that look mostly right but join the wrong people to the right events. Fix it with confidence cutoffs per stage plus a blank-tolerant schema, and route low-confidence records to review instead of storage.
The Quick Version
- Information extraction turns prose into typed rows: entities, relations between them, and events with roles.
- The stages are span finding, pair linking into triples, and record filling against a fixed schema.
- In the worked example, three spans yield the triple (Ada, joined, Oxfam) at confidence about 0.82.
- Mentions across sentences are merged by coreference before document-level linking runs.
- Missing slots stay empty; downstream tables must tolerate blanks and cutoffs rather than accept guesses.