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.
| 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