I built the entity resolution pipeline at 2asy.ai against structured registry data. I benchmarked it against structured registry data. The match scores looked good. I moved on.
About a month later I started ingesting news articles. The first batch turned into two weeks of cleanup.
What I hadn't tested was what happens when the source text doesn't use formal company names. News doesn't. A Korean financial article calls "삼성전자 주식회사" just "삼성." A piece in English calls it "Samsung" without specifying which Samsung entity, because to a human reading with context, it's obvious which one. The pipeline had no reading context. It had a blocking key built on the first four characters of a normalized name.
"삼성" hits the same blocking bucket as Samsung Electronics, Samsung SDI, Samsung C&T, and a dozen other entities under the group umbrella. The EM scoring then gets candidate pairs with name similarity 1.0 across all of them and nothing else to work with.
ON LEFT(l.name_normalized, 4) = LEFT(r.name_normalized, 4)
This was the blocking rule. It was designed for formal names where "Samsung Electronics" and "삼성전자" both normalize to something starting with "sams." It's also what causes a short news mention to block with every Samsung subsidiary in the registry.
What I added was a post-scoring filter using signals from the article itself. The article a mention comes from has context: what other entities appear nearby, what sector terms show up in the paragraph. When the top match by name similarity has no overlap with those signals, I look at the second-best match.
def extract_context_signals(article: dict, mention_span: tuple) -> dict:
window = article["text"][max(0, mention_span[0]-200):mention_span[1]+200]
return {
"mentioned_with": extract_entity_mentions(window),
"sector_keywords": extract_sector_terms(window),
"article_category": article.get("category", "unknown"),
}
This helped with the Samsung group disambiguation specifically. It doesn't do much for companies that don't have siblings with similar names. And when the article context is thin — short breaking news items especially — the signals aren't reliable enough to use.
I also raised the confidence floor for what I'm calling informal mentions: short Korean-character forms, English names that don't include the entity's legal suffix. The threshold for a two-character Korean name merging to a registry entry is higher than for a full formal name match.
if mention_is_short_informal(mention_text, source_language):
threshold = max(threshold, 0.90)
The 0.90 came from watching the false-merge rate and adjusting until it dropped to something workable. It's not principled.
The benchmark I ran when building this is still the benchmark I'm running. It's all registry-vs-registry pairs. I don't have a labeled test set for news mentions, so I don't actually know what the precision-recall curve looks like for the domain I'm now running against daily. That's the part I'm not comfortable with.
I build er-api, a multilingual entity resolution service for Korean, Japanese, Chinese, and English corporate data, used in 2asy.ai for news-to-knowledge-graph ingestion. More at hannune.ai.
Top comments (0)