A jaguar swam through the Amazon rainforest. In one of our demos, a
search-based agent files that story under automotive news — because the
first hit for "jaguar" is Jaguar Cars. Nothing crashes. The wrong fact
just moves into the graph, the markup and the agent's memory, and every
system downstream repeats it.
I'm Andrea, co-founder of WordLift. We have spent years linking text to
knowledge graphs, and that one decision — which thing is this? — is
where it goes wrong. So we made it an API call. It's called resolve(),
and this is the five-minute version of using it.
Step 1 — try it with no key. There is an open demo endpoint, 600
characters, 10 calls a minute:
curl -s -X POST https://resolve.wordlift.io/demo/resolve \
-H "Content-Type: application/json" \
-d '{"text": "Apple opened a new store in Rome.", "language": "en"}'
You get Apple → Q312 (Apple Inc.) and Rome → Q220, each with a score.
Now try an invented company name. You should get unresolved, with a
reason, instead of a plausible-looking link. That refusal is the part I
care about most: a resolver that cannot say "I don't know" will
eventually hand your agent a confident wrong answer.
Step 2 — a free key. Request one at wordlift.io/resolve/#access:
name, email, use case, it arrives by email. 1,000 requests a month; past
that you get a 429, nothing is charged.
Step 3 — the open pipeline. The pipeline and clients are Apache-2.0
(github.com/wordlift/content-analysis). The hosted decision engine is
ours and stays closed — that's the business model, stated plainly.
pip install "resolve-pipeline @ git+https://github.com/wordlift/content-analysis.git"
export WL_KEY=...
python -m resolve_pipeline "Apple opened a new store in Rome." --language en
Apple resolved Q312 Apple Inc. score 0.99
Rome resolved Q220 Rome score 0.98
Step 4 — resolve against your own world. Wikidata is the default,
not the limit. Send your own vocabulary (up to 5,000 entities) and every
answer is one of your ids, or unresolved:
from resolve_pipeline import WordLiftResolver
resolver = WordLiftResolver(api_key="...", dataset=[
{"id": "https://acme.example/kg/acme", "name": "Acme",
"aliases": ["ACME Corp"], "types": ["Organization"]},
])
A name that matches none of yours returns no_candidates. A match whose
context does not fit returns low_relevance. Same gate as Wikidata mode.
Step 5 — handle the "no" in your agent loop. Treat unresolved as a
signal: ask a clarifying question, skip the action, flag it for review.
Never replace it with a guess, and never treat a transport error as an
unresolved — they are different problems.
The numbers, including where we lose. We ran resolve() on VoxEL, a
public news benchmark (ISWC 2018; 1,005 mentions, five languages) that
nobody tuned on. With the mentions given, F1 is 91.8 and it makes 37
wrong links; a first-hit Wikidata lookup makes 238. End to end, where
each system finds its own mentions, Google Cloud NL is ahead, 89.9 to
our 87.0 — it detects more mentions than we do, mostly short second
references. On our own development sets the first-hit lookup got 61 of
351 names wrong; those sets are ours, so read them as favourable to us.
The harness and the raw results are in the repo (benchmarks/2026-10-19,
tools/compare.py) if you want to rerun any of it. Full write-up:
wordlift.io/blog/en/entity-linking-apis-compared/
Where it breaks. People named by surname alone (the "Jordan scored
45 points" problem — pass the type and it gets Michael Jordan, don't and
it may not). Japanese and Chinese trail Google. A stricter NIL decision
layer exists but has not passed its own published gates in any language,
so it is not in production and I am not going to pretend otherwise.
There is an experimental MCP adapter in the repo (clients/mcp) that
exposes resolve_text as a tool, and an llms.txt at
resolve.wordlift.io/llms.txt written for agents.
If you try it, the useful feedback is not "nice" — it is the sentence
where it picks the wrong entity, or refuses one it should have taken.
Those cases are what improve a resolution engine.
Top comments (0)