Model Watch

GLiNER turned entity extraction into a small-model job

7 min read

GLiNER is the kind of model that makes a task feel smaller in the right way.

Entity extraction is often treated like a reason to reach for a general LLM. Give the model a paragraph, ask it to return JSON, repair the JSON when it drifts, argue with the prompt when it misses a label, and eventually build enough scaffolding that the “simple extraction” step is now a tiny application.

Sometimes that is the right move. Plenty of extraction problems need reasoning, context, or normalization beyond spans. But a lot of named entity recognition is narrower than that. Find these spans. Label them. Return offsets and confidence.

That is where GLiNER is interesting. It is a lightweight generalist NER model that can extract arbitrary entity types from text using labels you provide at inference time. The output is a list of spans with start, end, text, label, and score.

That shape is boring. Boring is exactly what extraction needs.

spans are better than vibes

For extraction, the span matters.

If the system says a support ticket contains a customer ID, I want the exact characters. If it says a security report contains an API key, I want the start and end offsets. If it says an incident note mentions a vendor, I want the text it matched and the confidence that led to the label.

A decoder LLM can return structured JSON, but it often wants to explain, normalize, infer, or be helpful. That is dangerous when the downstream system needs evidence. Entity extraction should preserve the link between the prediction and the source text.

GLiNER’s output shape is useful because it keeps that link:

{
    "start": 18,
    "end": 24,
    "text": "SpaceX",
    "label": "organization",
    "score": 0.97,
}

Now the product can highlight the span, redact it, review it, or pass it to another stage. If the prediction is wrong, a reviewer can point at the exact error. That is much better than a generated answer that says “the organization is probably SpaceX” without anchoring the claim.

labels are an interface

GLiNER lets you pass labels at inference time:

from gliner import GLiNER

model = GLiNER.from_pretrained("urchade/gliner_medium-v2.1")

text = "Maya renewed the Acme contract on April 12 after the SOC 2 review."
labels = ["person", "company", "date", "compliance report"]

entities = model.predict_entities(text, labels, threshold=0.5)

That looks simple, but the labels are doing real product work.

company and customer may not mean the same thing. incident and security event may overlap. credential may be too broad if the product needs to distinguish passwords, API keys, private keys, and session tokens. A label name is part of the extraction contract.

I would treat labels like API fields. Name them carefully. Document them. Add examples. Decide whether labels are mutually exclusive. Decide what should happen when spans overlap. Decide whether the system should allow an unknown bucket or simply return no span.

The model can accept arbitrary labels. The product still has to own the taxonomy.

thresholds are product choices

The confidence threshold is not a technical afterthought.

A high threshold reduces false positives and misses more real entities. A low threshold catches more entities and creates more review noise. The right threshold depends on what the extraction drives.

For a highlight feature in a note-taking app, false positives may be acceptable. For secret redaction, false negatives are expensive. For CRM enrichment, a wrong entity can pollute records. For incident review, missing a vendor name might be less severe than incorrectly tagging a person as an owner.

I would tune thresholds by label, not only globally:

label              threshold   reason
private_key          0.35      prefer review noise over leaks
person               0.70      avoid tagging ordinary words
company              0.60      moderate review cost
date                 0.80      formats are usually clear

That table might live in code, config, or an eval note. The important part is that the threshold expresses risk.

extraction is not normalization

Named entity recognition finds spans. It does not solve every downstream problem.

If GLiNER extracts Acme, the system still has to decide whether that means Acme Corp, ACME Inc., a test fixture, or a customer alias. If it extracts April 12, the system still has to resolve the year and timezone. If it extracts SOC 2, the system still has to decide whether that is a report type, a compliance requirement, or part of a document title.

That distinction matters because teams often overload extraction. They want the model to find, classify, normalize, deduplicate, and decide. Those are separate jobs.

A cleaner pipeline might be:

text
  -> span extraction
  -> type-specific normalization
  -> entity resolution
  -> confidence and review policy
  -> write to product state

GLiNER can own the first step. The rest of the pipeline should remain explicit.

where this fits in ai systems

Entity extraction shows up everywhere around AI products.

Before retrieval, extraction can identify product names, document IDs, customer names, dates, or policy references. Before logging, extraction can catch secrets or PII. During evals, extraction can compare whether required facts appeared in the answer. In agent workflows, extraction can turn messy tool output into structured candidates for a safer deterministic step.

The small-model angle is practical. If extraction runs cheaply and locally, it can sit closer to the data. It can process logs before storage. It can scan documents before they leave a workspace. It can run as a batch job over historical data without turning the bill into a meeting.

That does not mean every extraction task belongs to GLiNER. Long documents, tables, nested schemas, cross-sentence reasoning, and domain-specific normalization may need additional machinery. But the first pass does not always need a large decoder model.

eval the span, not the sentence

Entity extraction evals should be picky.

A generated summary can be directionally useful. A span extractor is either aligned with the source text or it is not. The eval should measure exact matches, partial overlaps, label correctness, false positives, false negatives, and performance by label.

For example:

label          precision   recall   notes
person           0.94      0.91     misses initials
company          0.88      0.84     aliases need resolver
api_key          0.97      0.72     threshold too strict
date             0.96      0.95     strong

Partial overlaps deserve review. If the gold span is Austin, Texas and the model returns Austin, maybe that is acceptable for some products and wrong for others. If the gold span is April 12 and the model returns April, that is probably wrong. The eval has to match the product use.

I would also keep adversarial examples:

  • names that are also common words
  • fake keys that look real
  • companies with punctuation
  • dates without years
  • copied logs with mixed formatting
  • multilingual names
  • overlapping spans

Small extraction models can be excellent, but they should be tested where span boundaries get annoying.

the useful reminder

GLiNER is useful because it makes entity extraction feel like its own job again.

That is the reminder I like. Not every language task needs a chat model pretending to be a classifier, a parser, and a database writer. Sometimes the right tool is a smaller model with a narrower output shape and a better relationship to the source text.

When the job is “find the spans and label them,” use a tool that respects spans. Then build the rest of the system around review, normalization, resolution, and product risk.

That is less dramatic than asking a giant model to do everything. It is also much easier to operate.

Jeremy London

About Jeremy London

Engineering leader and builder in Denver. I write about AI platforms, agents, security, reliability, homelab infrastructure, and the parts of engineering work that have to survive production.