Skip to main content

Overview

The GTM Research Engine uses Google Gemini to power two critical AI components:
  1. Query Strategy Generation - Converts research goals into intelligent search strategies
  2. Evidence Analysis - Extracts technologies and calculates confidence scores from collected evidence
Unlike traditional keyword-based systems, this fully dynamic approach adapts to any research goal without hardcoded rules.

Query Strategy Generation

How It Works

The QueryGenerator service uses LLM prompting to create optimized search strategies for each data source:
# From routes.py:29-35
query_generator = QueryGenerator()
strategies = await query_generator.generate_strategies(
    research_goal=payload.research_goal,
    search_depth=payload.search_depth,
)

LLM Configuration

The generator uses Gemini 2.5 Flash with a comprehensive system instruction:
# From query_generation.py:156-160
self.model = genai.GenerativeModel(
    model_name='gemini-2.5-flash',
    system_instruction=system_instruction,
    generation_config=genai.GenerationConfig(temperature=0.7)
)

Strategy Output Format

The LLM generates structured query strategies:
{
  "strategies": [
    {
      "channel": "google_search",
      "query_template": "site:{DOMAIN} AND (fraud detection OR risk analysis)",
      "relevance_score": 0.95
    },
    {
      "channel": "news_search",
      "query_template": "{COMPANY_NAME} AND (AI OR machine learning) AND fraud",
      "relevance_score": 0.85
    },
    {
      "channel": "jobs_search",
      "query_template": "machine learning engineer fraud detection",
      "relevance_score": 0.80
    }
  ]
}

Search Depth Optimization

The number of strategies adapts to search depth:
# From query_generation.py:81-84 (system instruction)
SEARCH DEPTH LEVELS:
- quick: 4-6 strategies (1 per major source)
- standard: 7-10 strategies (1-2 per major source)
- comprehensive: 11-13 strategies (2-3 per major source)

Relevance Scoring

Strategies are scored and prioritized:
# From query_generation.py:217-218
strategies.sort(key=lambda s: s.relevance_score, reverse=True)
return strategies
High relevance scores (0.9-1.0) indicate direct, specific searches most likely to find strong evidence. Lower scores (0.5-0.7) represent broader exploratory queries.

Evidence Analysis & Extraction

Two-Stage LLM Analysis

The Extractor service performs dynamic analysis in two stages:

Stage 1: Generate Extraction Strategy

Analyze the research goal to identify relevant signals:
# From extractor.py:139-171
async def generate_extraction_strategy(self, research_goal: str) -> ExtractionStrategy:
    prompt = f"""
    RESEARCH GOAL: {research_goal}
    """
    
    response = self.keyword_model.generate_content(prompt)
    response_text = response.text.strip()
    
    result = json.loads(response_text)
    
    return ExtractionStrategy(
        target_keywords=result.get("target_keywords", []),
        context_phrases=result.get("context_phrases", []),
        confidence_boosters=result.get("confidence_boosters", [])
    )
Example output for “Find companies using AI for fraud detection”:
{
  "target_keywords": [
    "chatbot", "conversational ai", "fraud detection", 
    "machine learning", "risk analysis", "anomaly detection"
  ],
  "context_phrases": [
    "AI-powered fraud prevention",
    "automated risk assessment",
    "real-time transaction monitoring"
  ],
  "confidence_boosters": [
    "AI fraud detection platform",
    "machine learning risk engine"
  ]
}

Stage 2: Extract Technologies from Evidence

Analyze collected evidence using the dynamic strategy:
# From extractor.py:205-221
prompt = f"""
RESEARCH GOAL: {research_goal}

RELEVANT KEYWORDS TO LOOK FOR:
{', '.join(strategy.target_keywords)}

CONTEXT PHRASES THAT INDICATE RELEVANCE:
{', '.join(strategy.context_phrases)}

COMPANY EVIDENCE:
{combined_evidence}

Based on the research goal and keyword strategy, analyze this 
company's evidence and extract relevant technologies.
"""
Output format:
{
  "relevant_technologies": ["TensorFlow", "Python", "Kubernetes"],
  "goal_match_signals": [
    "fraud detection algorithms",
    "AI-powered fraud prevention"
  ],
  "confidence_score": 0.85
}

Complete Analysis Pipeline

# From extractor.py:249-270
async def analyze_company(
    self, research_goal: str, evidences: List[Evidence]
) -> Dict[str, any]:
    # Step 1: Generate dynamic keyword strategy
    strategy = await self.generate_extraction_strategy(research_goal)
    
    # Step 2: Extract technologies using the strategy
    extraction_result = await self.extract_technologies_from_evidence(
        research_goal, evidences, strategy
    )
    
    # Step 3: Return comprehensive result
    return {
        "technologies": extraction_result["relevant_technologies"],
        "goal_match_signals": extraction_result["goal_match_signals"],
        "confidence_score": extraction_result["confidence_score"],
    }

Integration with Pipeline

Analysis happens after evidence collection:
# From pipeline.py:166-173
domain_tasks = [
    asyncio.create_task(self.analyze_domain(domain, evidences))
    for domain, evidences in domain_to_evidence.items()
]
results: List[CompanyResearchResult] = []
for coro in asyncio.as_completed(domain_tasks):
    result = await coro
    results.append(result)

Building Results

# From pipeline.py:183-206
def _build_company_result(
    self, domain: str, evidences: List[Evidence], analysis_result: Dict
) -> CompanyResearchResult:
    technologies = analysis_result.get("technologies", [])
    goal_match_signals = analysis_result.get("goal_match_signals", [])
    confidence_score = analysis_result.get("confidence_score", 0.0)
    
    evidence_sources = len(set(evidence.source_name for evidence in evidences))
    
    findings = Findings(
        technologies=technologies,
        evidence=evidences,
        signals_found=len(goal_match_signals)
    )
    
    return CompanyResearchResult(
        domain=domain,
        confidence_score=round(confidence_score, 2),
        evidence_sources=evidence_sources,
        findings=findings
    )

Rate Limiting

AI operations are rate-limited to prevent API exhaustion:
# From query_generation.py:176
@rate_limited("gemini")
async def _generate_with_llm(
    self, research_goal: str, search_depth: str
) -> List[QueryStrategy]:
    # ... LLM call logic
# From extractor.py:139
@rate_limited("gemini")
async def generate_extraction_strategy(
    self, research_goal: str
) -> ExtractionStrategy:
    # ... strategy generation logic
Gemini API has rate limits based on your plan tier. Monitor usage to avoid exhausting quotas during large batch operations.

Caching

Extraction strategies are cached per research goal:
# From extractor.py:137-145
self._strategy_cache: Dict[str, ExtractionStrategy] = {}

async def generate_extraction_strategy(self, research_goal: str):
    # Check cache first
    if research_goal in self._strategy_cache:
        return self._strategy_cache[research_goal]
    
    # ... generate and cache strategy
    self._strategy_cache[research_goal] = strategy

Configuration

Environment Variables

GEMINI_API_KEY=your_gemini_api_key_here

Model Selection

Different models for different tasks:
# From extractor.py:125-134
self.keyword_model = genai.GenerativeModel(
    model_name='gemini-2.5-flash-lite',  # Fast, cost-effective
    system_instruction=keyword_system_prompt,
    generation_config=genai.GenerationConfig(temperature=0.7)
)

self.evidence_model = genai.GenerativeModel(
    model_name='gemini-2.5-flash-lite',
    system_instruction=evidence_system_prompt,
    generation_config=genai.GenerationConfig(temperature=0.7)
)

Confidence Scoring

Confidence scores range from 0.0 to 1.0 and reflect:
  • Evidence quality - Direct mentions vs. indirect signals
  • Signal strength - Number and relevance of matching keywords
  • Source diversity - Evidence from multiple independent sources
  • Context alignment - How well evidence matches the research goal

Thresholding

Filter results by confidence:
curl -X POST http://localhost:8000/research/batch \
  -H "Content-Type: application/json" \
  -d '{
    "research_goal": "Find AI companies",
    "company_domains": ["openai.com"],
    "search_depth": "standard",
    "max_parallel_searches": 20,
    "confidence_threshold": 0.7
  }'

Best Practices

Better prompts produce better strategies:✅ Good: “Find fintech companies using machine learning for fraud detection”❌ Poor: “Find AI companies”
Recommended thresholds by use case:
  • Lead generation: 0.6-0.7 (broader results)
  • Sales qualification: 0.75-0.85 (medium precision)
  • Partnership vetting: 0.85-0.95 (high precision)
Gemini charges per token. Optimize evidence length:
# From extractor.py:196-202
for i, evidence in enumerate(evidences[:3]):  # Limit to 3
    combined_evidence += f"""
Evidence {i+1}:
Title: {evidence.title}
Content: {evidence.snippet}
"""
For repeated research with the same goal, strategies are automatically cached:
# First call: generates strategy via LLM
await query_generator.generate_strategies(
    research_goal="Find AI companies",
    search_depth="standard"
)

# Second call with same goal: uses cache (instant)
await query_generator.generate_strategies(
    research_goal="Find AI companies", 
    search_depth="standard"
)

Example: End-to-End Flow

# 1. Generate search strategies
query_generator = QueryGenerator()
strategies = await query_generator.generate_strategies(
    research_goal="Find companies using Kubernetes in production",
    search_depth="standard"
)
# Output: 7-10 strategies across google_search, news_search, jobs_search

# 2. Execute searches (handled by pipeline)
pipeline = ResearchPipeline(..., strategies=strategies)
results, total_searches = await pipeline.run()

# 3. Analyze evidence
extractor = Extractor()
for domain, evidences in domain_to_evidence.items():
    analysis = await extractor.analyze_company(
        research_goal="Find companies using Kubernetes in production",
        evidences=evidences
    )
    # Output: {
    #   "technologies": ["Kubernetes", "Docker", "AWS"],
    #   "goal_match_signals": ["production kubernetes cluster"],
    #   "confidence_score": 0.89
    # }

Multi-Source Research

Learn about data source integration

Real-Time Streaming

See how AI analysis streams in real-time

Build docs developers (and LLMs) love