WL.RSLV.0S1 resolve() · preview {{date}} · Rome · {{clock}}
WordLift / resolve()
// content analysis · entity resolution

resolve()

Turn mentions into identities.

Entity resolution in English, Italian, French, German, Spanish and Portuguese for knowledge graphs and AI systems. Give resolve() a mention, its context and candidate entities. Get back the supported entity, or NIL when the evidence isn't strong enough.

Try resolve() View the benchmarks
Resolver POST /v1/resolve
loading candidates
3/3
{
  "text": "I bought an Apple laptop in Rome.",
  "language": "en",
  "mentions": [{
    "text": "Apple", "start": 12, "end": 17,
    "candidates": [
      { "id": "wd:Q312" },      // Apple Inc.
      { "id": "wd:Q89" },       // apple, the fruit
      { "id": "wd:Q213710" }    // Apple Records
    ]
  }]
}
→ 200 OK
{
  "mentions": [{
    "text": "Apple", "start": 12, "end": 17,
    "status": "resolved",
    "entity": { "id": "wd:Q312", "label": "Apple Inc." },
    "score": 0.99
  }]
}
{
  "text": "Fresh bread daily at Apple Street Bakery.",
  "language": "en",
  "mentions": [{
    "text": "Apple Street Bakery", "start": 21, "end": 40,
    "candidates": [
      { "id": "wd:Q312" },      // Apple Inc.
      { "id": "wd:Q89" },       // apple, the fruit
      { "id": "wd:Q213710" }    // Apple Records
    ]
  }]
}
→ 200 OK
{
  "mentions": [{
    "text": "Apple Street Bakery", "start": 21, "end": 40,
    "status": "unresolved",
    "entity": null,
    "reason": "low_relevance"
  }]
}
// POST https://resolve.wordlift.io/v1/resolve · Authorization: Key <wordlift_key>WL.RSLV.0S1
// try it live
resolve.wordlift.io · live enginementions detected by the engine · six languages
{{liveStatus}}
{{r.text}} {{r.status}} {{r.entity}} {{r.score}}
// POST /demo/resolve · rate limited · 600 characters · for more, use your own key against /v1/resolve
Mention+Context+CandidatesEntity|NIL
the contract
01 / why

We took the other branch

Entity linkers have mostly been built to always answer: take the top-ranked candidate and move on. resolve() is built to answer when the evidence supports it.

lineage · entity linking approachesbranch: resolve()
string match surface forms NER types, not things entity linking argmax over candidates LLM linkers fluent, overconfident <always answers> resolve() calibrated · NIL-aware <answers when supported> + abstention + retrieval evidence
always answers answers when supported
02 / pipeline

The decision layer in the middle

resolve() sits after extraction and candidate retrieval. It reasons over the mention, context, candidates and retrieval evidence, then commits or abstains.

pipeline.tracedoc → graph
input
content
articles, pages, product copy
a
extract()
"Roma", "Apple", "Tim Cook"
b
retrieve()
candidates from your KG
c · WordLift engine
resolve()
mention context candidates rank · lexical · priors
disambiguation + NIL detection
wd:Q312
entity id
NIL
abstain
output
graph
KG links, JSON-LD, downstream AI
open-source pipeline WordLift engine
ner()what kind of thing
ner("Il sindaco di Roma ha presentato il piano.")
 Roma : LOCATION   // a type label
resolve()which thing
resolve("Roma", ctx, candidates)
 wd:Q220   // Rome, a stable identifier
03 / return NIL

Entity resolution isn't just knowing what to link. It's knowing when not to link.

In production a confident wrong link costs more than no link. It enters the graph, ships in JSON-LD and gets repeated by every system downstream. resolve() is built for precision, calibration and explicit NIL.

[b.64]
eyJtZW50aW9uIjoiQXBwbGUiLCJkZWNpc2lvbiI6Im5pbCIsImlkIjpudWxsfQ==
S25vd2luZyB3aGVuIG5vdCB0byBsaW5rLg==
evidence.view · mention "Apple"2 contexts · 3 candidates
ctx › {{k.ctx}}
{{r.id}}{{r.label}}
decision {{k.decision}}
support threshold accepted insufficient support illustrative · not released scores
correct link

The graph gains an edge you can trust.

wrong link

A false fact in your graph, markup and agent answers. Hard to find, costly to unwind.

NIL

The mention stays unlinked. It can be reviewed, or linked later when the right entity exists.

04 / architecture

Retrieval and resolution are separate on purpose

Your graph defines the world. Retrieval introduces your entities. resolve() decides whether a mention actually refers to them.

engine.specresolve()
separation
retrieval ⟂ resolution

Retrieval systems, vocabularies and knowledge graphs change without retraining a specialized classifier every time the entity universe grows.

scorer
private scorer · managed inference

Owned and operated by WordLift. No external classification or scoring service in the loop.

validation
EN · IT · FR · DE · ES · PT

resolve() answers in six languages today. The NIL-safe decision layer is released one language at a time, each against immutable held-out fixtures and a gate whose criteria are fixed before the run.

knowledge graphs GraphRAG semantic search AI agents content intelligence structured-data pipelines
05 / evidence

Measured at the shipping gate

Single frozen runs on held-out sets, 2026-09-23. The criteria were fixed before each run: accepted precision ≥ 99%, resolution coverage ≥ 90%, false resolution under forced NIL ≤ 2%. No language has cleared all three yet, so the decision layer stays on hold and these sets are now regression sets. We publish the misses too.

gate.report · de ×3 / it / en · frozen 2026-09-23decision layer on hold
languageaccepted precision
≥ 99%
resolution coverage
≥ 90%
false resolution · forced NIL
≤ 2%
false resolution · natural NIL
reported
verdict
DE36/36 · 100%36/40 · 90%2/40 · 5.0%0/1hold
DE · revised rule, fresh holdout49/50 · 98%50/57 · 88%2/57 · 3.5%0/1hold
DE · third gate, vector-only rule45/45 · 100%45/48 · 94%1/48 · 2.1%0/1hold
IT37/37 · 100%37/37 · 100%3/37 · 8.1%1/2hold
EN41/41 · 100%41/57 · 72%1/57 · 1.8%0/6hold
Gold present: does adding NONE preserve resolution? Gold removed: does the resolver say NONE instead of the least-wrong candidate? DE and IT fail the second question on closely related identities (a subsidiary for its group, a festival edition for the festival). EN passes it and fails coverage: the strict gate, frozen on Italian development data, rejects correct English choices. German went through two more iterations the same day, each with its own development evidence, frozen rule and fresh holdout. The second gate failed all three criteria. The third, after a structural rule (the primary gate refuses candidates the dictionary does not know under that surface form), passed precision and coverage and missed NIL safety by one case out of 48: the city Essen, removed, was linked to the noun Essen. The criterion stays where it was frozen. All three German holdouts are now regression sets.
candidate recall ≠ resolution accuracy

If the correct entity never reaches resolve(), that is a retrieval problem. If it is present and the wrong identity is selected, that is a resolution problem. We report them separately so every error has an owner.

error.attributionper mention
gold ∈ candidates ?
├── no  → retrieval miss       # candidate recall
└── yes → resolve() returns gold ?
          ├── yes → correct resolution
          └── no  → resolution error  # accuracy
06 / one mental model

resolve() is the decision layer between detecting an entity mention and committing an identity to the graph.

Graph of Life · entities are born, persist, or return NIL gen {{gen}} · live {{alive}}
new link resolved nil Conway, B3/S23 · toroidal
07 / get started

Start open. Add resolve() when identity matters.

The Content Analysis pipeline is open source. Use it to extract mentions, connect your own candidate retrieval and knowledge graph, inspect the data flow, and build on it.

resolve() adds WordLift's proprietary entity-resolution engine at the decision point: given a mention, its context and your candidate entities, it returns the supported identity, or NIL.

boundary.mapopen where you build · proprietary where we resolve
open sourceyou run it
extract(document)
        │
        ▼
candidate retrieval  ──────►
        │
        │
your KG / vocabulary
WordLift enginewe run it
 resolve(
     mention,
     context,
     candidates
  )
     │
     ├── entity
     └── NIL
option a · open source
Run the open-source pipeline

Inspect the code, run the pipeline locally, bring your own entities and candidate retrieval, and plug in any resolver. Apache-2.0.

View on GitHub →
option b · WordLift engine
Use the WordLift resolver

Point the pipeline's WordLiftResolver at POST /v1/resolve for multilingual entity disambiguation, explicit NIL and managed inference. Same shape, no code change.

Get API access →
terminalbash
$ git clone https://github.com/wordlift/content-analysis.git
$ cd content-analysis
$ pip install -e ".[ner]"
pipeline.pyresolve_pipeline · Apache-2.0
from resolve_pipeline import run, WordLiftResolver

results = run(
    document,
    retrieve=my_retriever,       # candidates from your KG
    resolver=WordLiftResolver(api_key=WL_KEY),
)
# → per mention: resolved entity, or unresolved
Open where you build. Proprietary where we resolve.

Content Analysis, the pipeline and the integration layer are open source. WordLift operates the resolution models, scoring infrastructure and production inference service.

You keep control of your content, your candidate universe and your knowledge graph.

08 / get access

Tell us what you're resolving

Four short questions. We read every answer and reply with a key, a plan or a straight "not yet" for your case, usually within a working day.

get API access
Thanks. We'll get back to you at {{leadEmail}}.

Meanwhile, the open pipeline and the clients are on GitHub, and the live tester above runs against the same engine.

Step {{leadStep}} of 4
{{leadTitle}}

{{leadHint}}

{{leadKeyHint}} {{leadError}}

We'll use these details to follow up about resolve(). Read our Privacy Policy.

Build with resolve()

View source on GitHub Get API access