PRISM

Modules/Intel-Financial-Private.md

Module: intel-financial-private

SPOKE MODULE. Runs only for private companies. If the company is public (is_private=False), skipped=True is returned immediately. Revenue cannot be known precisely — this module builds a multi-source waterfall of estimates and makes confidence explicit at every step. All figures are labeled [ESTIMATE].


Identity

Field Value
Name intel-financial-private
Version 0.1.0
Layer Intelligence (Wave 1)
Wave 1 (runs in parallel with other spoke modules after intel-company)
Description Revenue estimation for private companies via a 6-source Perplexity waterfall structured by Instructor + Claude. Sources include employee count × revenue-per-employee benchmarks, Crunchbase funding data, trade press, industry reports, Inc 5000/Deloitte Fast 500, and competitor comparison. Produces a best estimate with confidence, a range, and source-level provenance for every number.
LLM Tier Perplexity sonar-pro (6 parallel queries) + Claude via Instructor (4 structuring calls)
Timeout 180 seconds (3 minutes)
Max Retries 2 (module level)
Gate Module NO. Failure degrades financial context but does not abort the audit.

Dependencies

Module Field Read Why
intel-company is_private (via ExecutionContext) Skip gate: if False (public company), returns skipped=True immediately
intel-company company_name Drives all 6 Perplexity waterfall queries
intel-company domain Included in queries for disambiguation
intel-company industry Used to select the revenue-per-employee benchmark for the employee model
intel-company employee_count Used directly in EmployeeRevenueModel estimation

Hub-and-Spoke Position

intel-financial-private is a spoke module. It reads from intel-company and writes its output to module_executions. Downstream synthesis modules (intel-competitors, audit-report, synth-business-case) consume its best_estimate for ROI modeling.


Input Schema

Field Type Required Description
domain str yes The company domain to estimate revenue for, e.g. "acme.com"

Additionally reads from ExecutionContext at runtime:

Context Field Type Used For
is_private bool Skip gate — returns skipped=True if False
company_name str All 6 Perplexity waterfall query labels
domain str Query disambiguation
audit_id str Structured logging

Output Schema: FinancialPrivateOutput

Top-Level Fields

Field Type Required Description
domain str yes The domain that was analyzed
revenue_waterfall RevenueWaterfall or None no Multi-source waterfall with all estimates, best estimate, and range
funding_data FundingData or None no Venture/PE funding data from Crunchbase/PitchBook
employee_revenue_model EmployeeRevenueModel or None no Employee-count-based revenue model with industry benchmark
competitor_estimates list[CompetitorRevenueEstimate] no Revenue estimates for competitor companies (for triangulation)
comparative_summary str no Narrative comparing the company to competitors
skipped bool yes True if module was skipped (company is public)
skip_reason str or None no Reason for skipping, if skipped

Sub-Schema: RevenueWaterfall

Field Type Required Description
estimates list[RevenueEstimate] yes Individual estimates from each source in the waterfall
best_estimate float or None no Best single estimate in USD, derived from median of estimates weighted by confidence
best_estimate_confidence Literal["high", "medium", "low"] no Confidence in the best estimate
best_estimate_methodology str no Explains which sources were weighted and why
range_low float or None no Lower bound of revenue range in USD
range_high float or None no Upper bound of revenue range in USD

Sub-Schema: RevenueEstimate (one per waterfall source)

Field Type Required Description
source_name str yes Origin of estimate, e.g. "Company press release", "Crunchbase funding"
methodology str yes How derived, e.g. "Extracted from IDC SaaS market report 2025"
estimated_revenue float or None no Estimated annual revenue in USD. None if source yielded no number
confidence Literal["high", "medium", "low"] no Confidence in this specific estimate
evidence str no The specific quote, data point, or passage supporting this estimate
evidence_url str or None no URL where the evidence was found
evidence_tier Literal["VERIFIED", "WEBFETCH", "WEBSEARCH", "ESTIMATE"] no Evidence quality tier. Never NO_SOURCE

Sub-Schema: FundingData

Field Type Required Description
total_funding float or None no Total funding raised in USD
last_round str or None no Last funding round name, e.g. "Series D"
last_round_amount float or None no Amount raised in last round in USD
last_round_date str or None no Date of last round in YYYY-MM-DD format
lead_investor str or None no Lead investor of the last round
valuation float or None no Last known valuation in USD
source str no Where this funding data came from

Sub-Schema: EmployeeRevenueModel

Field Type Required Description
employee_count int or None no Number of employees
revenue_per_employee float or None no Industry benchmark revenue per employee in USD
estimated_revenue float or None no employee_count × revenue_per_employee in USD
vertical_benchmark str no Benchmark used, e.g. "SaaS average: $200K/employee"
confidence Literal["high", "medium", "low"] no Confidence in this model-based estimate

Sub-Schema: CompetitorRevenueEstimate

Field Type Required Description
company_name str yes Competitor company name
domain str yes Competitor domain
estimated_revenue float or None no Estimated annual revenue in USD
methodology str no How this competitor estimate was derived
confidence Literal["high", "medium", "low"] no Confidence in competitor estimate

Collection Strategy: The 6-Source Waterfall

The module fires 6 independent Perplexity sonar-pro queries in sequence, each targeting a different revenue signal. Claude/Instructor then structures all 6 responses into typed output.

Source # Label Query Strategy Evidence Tier
1 Company press release Direct revenue mention in official press or newsroom WEBSEARCH
2 Industry report IDC, Gartner, Forrester reports naming the company WEBSEARCH
3 Crunchbase funding Crunchbase/PitchBook funding rounds, valuation, post-money estimates WEBSEARCH
4 Employee count model LinkedIn headcount × industry revenue-per-employee benchmark ESTIMATE
5 Inc 5000 / Deloitte Fast 500 Growth list rankings that imply revenue range WEBSEARCH
6 Competitor comparison Named competitor revenue estimates for triangulation WEBSEARCH

Waterfall Aggregation Logic

After the 6 Perplexity responses are collected, a single Claude/Instructor call structures all of them into the RevenueWaterfall schema:

  • best_estimate: median of available numeric estimates, weighted toward higher-confidence sources
  • range_low: minimum of all estimates
  • range_high: maximum of all estimates
  • best_estimate_confidence: "high" if 3+ independent sources agree within 25%, "medium" if 1-2 credible sources, "low" if only derived/modeled

Separate Claude/Instructor calls structure: - FundingData from the Crunchbase source response - EmployeeRevenueModel from the employee count source response - list[CompetitorRevenueEstimate] from the competitor comparison response


Enrichment Strategy

Phase 1: 6-Source Perplexity Waterfall (Collector)

The collector fires 6 Perplexity sonar-pro queries concurrently or sequentially. Each returns a raw text blob with citations. Error responses are noted as [ERROR: ...] and excluded from structuring.

Phase 2: Claude/Instructor Structuring (Enricher — 4 calls)

FinancialPrivateEnricher.enrich_waterfall() makes 4 structured calls:

Call Prompt Output Schema
1 All 6 responses → extract all dollar amounts with source and confidence RevenueWaterfall
2 Crunchbase response → extract funding rounds, valuation, investor FundingData
3 Employee response → extract headcount, benchmark, derived revenue EmployeeRevenueModel
4 Competitor response → extract competitor revenue estimates list[CompetitorRevenueEstimate]

Each call uses Instructor with max_retries=3 and a ValidationError catch. If a call fails, the corresponding output field is None (partial output is still usable).

Evidence Integrity Rule

evidence_tier for every RevenueEstimate must be one of VERIFIED, WEBFETCH, WEBSEARCH, or ESTIMATE. The value NO_SOURCE is explicitly forbidden (validation check 8 enforces this).


Validation Rules (8 Checks)

# Rule Severity Threshold
1 If skipped=True, skip_reason must be set required Early return pass if both true
2 revenue_waterfall is not None required Not None for non-skipped results
3 At least 2 revenue estimates in waterfall required >= 2 RevenueEstimate entries
4 Each estimate has non-empty source_name and methodology required All entries populated
5 best_estimate within range_low to range_high required range_low <= best_estimate <= range_high, or all None
6 domain is not empty required Non-empty string
7 At least 1 source provenance record required len(sources) >= 1
8 evidence_tier is never NO_SOURCE for any estimate required No estimate with NO_SOURCE tier

Status determination: if waterfall present and len(estimates) >= 2 -> "success". Otherwise -> "partial".

Required failures -> status "partial". All 8 checks are required severity (no warning-only checks for this module due to the estimation nature of the data).


Evidence Requirements

Field Minimum Tier Rationale
revenue_waterfall.estimates WEBSEARCH Perplexity web research — not verified financial filings
funding_data WEBSEARCH Crunchbase/PitchBook sourced via Perplexity
employee_revenue_model ESTIMATE Derived calculation (headcount × benchmark)
competitor_estimates WEBSEARCH Perplexity research
best_estimate ESTIMATE Aggregated median — explicitly labeled as estimate
comparative_summary ESTIMATE LLM-generated narrative

Source Provenance Architecture

Three types of source records are attached to the output:

Source Field Tier Label
revenue_waterfall (per Perplexity response) WEBSEARCH "Perplexity sonar-pro: {source_label}"
best_estimate ESTIMATE "Claude structured extraction (median of waterfall estimates)"
total_funding WEBSEARCH funding_data.source or "Perplexity funding research"

Persistence

intel-financial-private writes only to module_executions (not the accounts table).

Columns Written to module_executions

module_name="intel-financial-private"
module_version="0.1.0"
status           # "success" | "partial" | "failed"
output           # FinancialPrivateOutput.model_dump() -> JSONB
sources          # list[Source] -> JSONB array
duration_ms      # int
llm_calls        # int (6 Perplexity + up to 4 Claude/Instructor = max 10)
llm_cost_usd     # float (0.0 in v1 — cost tracking TBD)

Cost Profile

Metric Expected Value
LLM Calls 6-10 (6 Perplexity + up to 4 Claude/Instructor structuring)
Estimated Cost ~$0.10-0.25 per audit (sonar-pro is higher cost than sonar)
Expected Duration 60-120 seconds
External API Calls 6 (all Perplexity)

Retry Architecture (Two Layers)

Layer 1: Collector Retries (per Perplexity query)

Trigger Retry Backoff
Timeout 3 attempts Exponential (2^attempt seconds)
Rate limit (429) 3 attempts Exponential
Connection error 3 attempts Exponential
Other HTTP error No retry Fail immediately, mark response as [ERROR]

Individual query failures do not abort the waterfall. A failed query is excluded from structuring but the remaining responses are still processed.

Layer 2: Instructor Retries (per structuring call)

Trigger Retry Backoff
Pydantic ValidationError 3 attempts Instructor native retry
Other exception Caught, returns None No retry

Layer 3: Module Retries

Trigger Retry
Module-level failure max_retries=2

Skip Logic

The skip check runs before any API calls:

if not context.is_private:
    return ModuleExecutorResult(status="success", output=FinancialPrivateOutput(
        domain=context.domain,
        skipped=True,
        skip_reason="Company is public (has ticker). Use intel-financial-public instead."
    ))

Skip costs: 0 LLM calls, 0 API calls, ~1ms duration. The mutual exclusivity is enforced at module level: - intel-financial-public: runs if is_private=False AND ticker is set - intel-financial-private: runs if is_private=True


Runtime Notes

SaaS Runtime (Temporal/Python)

  • Module: prism_platform/v2/modules/intel_financial_private/module.py
  • Collector: prism_platform/v2/modules/intel_financial_private/collector.py
  • Enricher: prism_platform/v2/modules/intel_financial_private/enricher.py
  • Validator: prism_platform/v2/modules/intel_financial_private/validator.py
  • Schemas: prism_platform/v2/modules/intel_financial_private/schemas.py

Skill Runtime (Claude Code)

  • Skill file: ~/.claude/skills/algolia-intel-financial-private/SKILL.md (or algolia-audit-financials)
  • Reads from: 01-company-context.json (for company_name, industry, employee_count)
  • Writes to: 08-financial-profile.md, 08-financial-profile.json
  • MCP tools used: WebSearch (6 Perplexity-equivalent queries), no structured APIs

What Makes This Production-Grade (vs a Simple Prompt)

Aspect Simple Prompt PRISM Module
Skip logic Runs on public companies too, wastes money Hard gate: skip if is_private=False, zero API calls
Revenue research "Estimate their revenue" 6 independent source queries — each with its own methodology
Confidence Single number, no indication of reliability Confidence per estimate (high/medium/low) + overall range
Evidence None Every estimate has source_name, methodology, evidence quote, evidence_url, evidence_tier
Structuring Hope the LLM returns good JSON Instructor with max_retries=3 per call, Pydantic validation
Partial failure All-or-nothing Individual query failures are noted and excluded; remaining sources still structured
Evidence integrity No rules evidence_tier=NO_SOURCE is a validation error (check 8)
Triangulation Single estimate Competitor estimates provide cross-validation
Sales use "Revenue: ~$50M" Best estimate + range + methodology + competitor context for ROI modeling

Evolution: v1 → v2

v1 (Current — Implemented)

  • 6 Perplexity sonar-pro queries via collector
  • 4 Claude/Instructor structuring calls via enricher
  • Module pattern: v2 agentic (config.py + playbook.md + schemas.py + ModuleExecutor harness)
  • Config: hardcoded in collector (query strings, model names)
  • Competitor estimates depend on intel-company output but are fetched fresh via Perplexity (no DB read)
  • Cost tracking: llm_cost_usd=0.0 (tracking TBD)

v2 (Planned — Agentic Module Pattern)

  • Becomes a pure playbook agent — no structured APIs at all (all research via Agent API)
  • Config: intel_financial_private/config.yaml (query templates, confidence thresholds, benchmark table)
  • Playbook: intel_financial_private/playbook.md (step-by-step agent instructions with embedded source prioritization)
  • Schema: intel_financial_private/schema.py (unchanged Pydantic models)
  • Executor: intel_financial_private/executor.py (runs Claude agent against playbook)
  • The waterfall sources become playbook steps — the agent is told which source to research first and how to handle failures
  • Employee-count benchmark table embedded in the playbook config (vertical → revenue_per_employee)
  • Cost tracking wired in v2

Changelog

Date Change Reason
2026-04-14 Module spec written to vault Knowledge extraction for v2 planning
2026-03-30 Initial implementation Backend phase 1

  • Module-Catalog -- all 20 modules overview
  • Intel-Company -- foundation hub this module depends on
  • Intel-Financial-Public -- the public company counterpart
  • Module-Spec-Template -- the template this spec follows
  • Evidence-Tier-Spec -- evidence tier system
  • Adapter-Interfaces -- PerplexityAdapter