Entity Disambiguation System Implementation
A memo for Sava, the YC trust-administration startup, confidently reported a $19M Series A led by Balderton — except that round belonged to Sava Technologies, a biosensor spinout. The data was real and well-cited, just about a different company.
Changelog - 2025-11-28 (#2)
Entity Disambiguation System Implementation
Type: Bug Fix / Feature Enhancement Priority: CRITICAL Triggered By: Sava memo generation confused target company with Sava Technologies (biosensor company)
Summary
Implemented entity disambiguation across all research and enrichment agents to prevent LLMs from confusing similarly-named companies. The Sava investment memo incorrectly included $19M Series A funding data from Sava Technologies (a European biosensor company) instead of Sava (the YC F25 trust administration platform at savahq.com).
Problem
When generating the investment memo for Sava (trust platform, savahq.com):
Section 07 (Funding & Terms) contained detailed, accurate information about a $19M Series A round led by Balderton Capital
This data was for Sava Technologies (Imperial College biosensor spinout), a completely different company
The confusion passed initial quality checks because the data was real and well-cited - just for the wrong entity
Fact-checker flagged inconsistencies but only after significant wrong-entity contamination
Root Cause: Perplexity Sonar Pro and other LLMs default to the most prominent entity with a given name. Without explicit disambiguation context, "Sava funding" returns the better-known biosensor company.
Changes Made
1. cli/improve_section.py
Lines Modified: 153-183, 225
What Changed:
Added extraction of
company_description,company_url, andresearch_notesfrom stateBuilt disambiguation context block when identifying info is available
Injected disambiguation context into Perplexity prompt
Before:
prompt = f"""You are improving the '{section_name}' section for an investment memo about {company_name}.
INVESTMENT TYPE: {investment_type.upper()}
MEMO MODE: {memo_mode.upper()}After:
# Build disambiguation block if we have identifying info
disambiguation_context = ""
if company_description or company_url:
disambiguation_context = f"""
CRITICAL - ENTITY DISAMBIGUATION:
There may be multiple companies named "{company_name}". You MUST research the CORRECT company:
...
DISAMBIGUATION RULES:
1. ONLY use sources that reference THIS specific company
2. If you find data for a DIFFERENT company with the same name, DISCARD IT
...
"""
prompt = f"""You are improving the '{section_name}' section for an investment memo about {company_name}.
INVESTMENT TYPE: {investment_type.upper()}
MEMO MODE: {memo_mode.upper()}
{disambiguation_context}2. src/agents/research_enhanced.py
Lines Modified: 134-173, 291-307, 403-412
What Changed:
A. PerplexityProvider.search() - New Parameter
Added disambiguation_context parameter to search method:
def search(
self,
query: str,
max_results: int = 10,
sources: Optional[List[str]] = None,
disambiguation_context: Optional[str] = None # NEW
) -> List[Dict[str, str]]:B. System Prompt Enhancement
When disambiguation context provided, append to system prompt:
system_prompt = "You are a research assistant providing detailed, cited information..."
if disambiguation_context:
system_prompt += f"""
CRITICAL DISAMBIGUATION:
{disambiguation_context}
ONLY return information about the CORRECT company. If search results contain
data about a DIFFERENT company with a similar name, DISCARD that data and
state that you could not find information for the correct entity."""C. Context Building in research_agent_enhanced()
Build disambiguation context from state before searches:
disambiguation_context = None
if company_description or company_url:
disambiguation_parts = [f"Target company: {company_name}"]
if company_description:
disambiguation_parts.append(f"Description: {company_description}")
if company_url:
parsed = urlparse(company_url)
domain = parsed.netloc if parsed.netloc else company_url
disambiguation_parts.append(f"Website: {company_url}")
disambiguation_parts.append(f"Domain: {domain}")
if research_notes:
disambiguation_parts.append(f"Notes: {research_notes}")
disambiguation_context = "\n".join(disambiguation_parts)D. Pass Context to Search Calls
Updated search call to include disambiguation:
if isinstance(search_provider, PerplexityProvider):
results = search_provider.search(
query,
max_results=5 if idx > 1 else max_results,
sources=research_sources,
disambiguation_context=disambiguation_context # NEW
)3. src/agents/perplexity_section_researcher.py
Lines Modified: 86-88, 137-155, 200-202, 286-288
What Changed:
A. Function Signature Update
Added company_url and research_notes parameters to build_section_research_query():
def build_section_research_query(
section_def: Any,
company_name: str,
company_description: str,
general_research: Dict[str, Any],
memo_mode: str,
deck_draft_content: str = "",
company_url: str = "", # NEW
research_notes: str = "" # NEW
) -> str:B. Disambiguation Block in Query
Added disambiguation block directly into research queries:
disambiguation_block = ""
if company_url or research_notes:
disambiguation_block = f"""
CRITICAL - ENTITY DISAMBIGUATION:
There may be multiple companies named "{company_name}". You MUST research the CORRECT company:
- Company website: {company_url or 'See description'}
- Description: {company_description}
"""
if research_notes:
disambiguation_block += f"- Research notes: {research_notes}\n"
disambiguation_block += """
DISAMBIGUATION RULES:
1. ONLY use sources that reference THIS specific company
2. If you find funding/revenue data for a DIFFERENT company with the same name, DISCARD IT
3. Cross-reference company website to verify you have the correct entity
4. If unsure, state "Data not verified for this entity" rather than include wrong data
"""C. State Extraction
Extract URL and notes from state in agent function:
company_url = state.get("company_url", "")
research_notes = state.get("research_notes", "")D. Pass to Query Builder
Updated call to include new parameters:
query = build_section_research_query(
section_def=section_def,
company_name=company_name,
company_description=company_description,
general_research=general_research,
memo_mode=memo_mode,
deck_draft_content=deck_draft_content,
company_url=company_url, # NEW
research_notes=research_notes # NEW
)4. context-vigilance/Disambiguation-Management-across-Agents.md
New File: Created comprehensive specification document
Contents:
Problem statement with Sava case study
Root cause analysis (4 causes identified)
Three-layer defense architecture
Implementation specifications with code examples
Standardized disambiguation rules
Testing protocol
Monitoring and alerting guidelines
Best practices for data file authors
Failure modes and mitigations
Pipeline flow diagram
Future enhancement roadmap
Verification
Ran improve-section.py on the problematic Funding & Terms section:
python -m cli.improve_section "Sava" "Funding & Terms"Result: The system correctly:
Identified the confusion: Output explicitly noted "The existing 'Funding & Terms' section references Sava Technologies, a European glucose biosensor company... However, the company specified for this investment memo is Sava (Sava Lakh, Inc.)"
Discarded wrong-entity data: Did not repeat the $19M Series A or Balderton Capital information
Used correct sources only: Citations reference savahq.com and YC profile only
Admitted data gaps honestly: "No Series A funding information is publicly disclosed in available sources"
Data File Best Practices
For companies with common/ambiguous names, ensure data/{Company}.json includes:
{
"description": "Specific description with unique identifiers",
"url": "https://official-website.com",
"notes": "Research X company only, NOT Y company (describe confusion risk)"
}Example (Sava):
{
"description": "Sava is a financial and legal technology platform specialized in quickly setting up and managing Trusts",
"url": "https://www.savahq.com",
"notes": "Ensure research is about SavaHQ (savahq.com), NOT other companies named Sava."
}Files Changed
| File | Type | Lines |
cli/improve_section.py | Modified | 153-183, 225 |
src/agents/research_enhanced.py | Modified | 134-173, 291-307, 403-412 |
src/agents/perplexity_section_researcher.py | Modified | 86-88, 137-155, 200-202, 286-288 |
context-vigilance/Disambiguation-Management-across-Agents.md | Created | Full document |
changelog/2025-11-28_02.md | Created | This file |
Related Documentation
Full Specification:
context-vigilance/Disambiguation-Management-across-Agents.mdHallucination Prevention:
context-vigilance/issue-resolution/Preventing-Hallucinations-in-Memo-Generation.mdFact Checker Design:
context-vigilance/Anti-Hallucination-Fact-Checker-Agent.md
Next Steps
Re-run Sava memo with disambiguation to verify full pipeline works
Monitor other companies with common names for similar issues
Consider adding automated entity confusion detection to fact-checker
Document known confusable entity pairs for proactive handling
Testing Commands
# Test section improvement with disambiguation
python -m cli.improve_section "Sava" "Funding & Terms"
# Re-run full memo generation (future test)
python -m src.main "Sava"
# Check for wrong-entity contamination
grep -i "biosensor\|glucose\|CGM\|Imperial College\|Balderton" output/Sava-v0.0.x/2-sections/*.mdContributors
Implementation by Claude Code (Opus 4.5) with user guidance.