Competitive Extractor & Separated Artifact System
Implemented Phase 2 of the Dataroom Analyzer: the Competitive Extractor. This extracts and synthesizes competitive landscape data from battlecard PDFs and outputs structured analysis as separate, numbered artifact files for better manageability.
Changelog - 2025-11-26 (#2)
Competitive Extractor & Separated Artifact System
Overview
Implemented Phase 2 of the Dataroom Analyzer: the Competitive Extractor. This extracts and synthesizes competitive landscape data from battlecard PDFs and outputs structured analysis as separate, numbered artifact files for better manageability.
New Feature: Competitive Extractor
Purpose
Extract competitive intelligence from battlecard PDFs and synthesize multiple per-competitor documents into a unified competitive landscape analysis.
Implementation
Created src/agents/dataroom/extractors/competitive_extractor.py (~600 lines):
def extract_competitive_data(
documents: List[Dict[str, Any]],
use_llm: bool = True
) -> CompetitiveData:
"""Extract and synthesize competitive data from multiple documents."""
def extract_from_pdf(file_path: Path, use_llm: bool = True) -> Optional[Dict[str, Any]]:
"""Extract competitive data from PDF battlecard or analysis document."""
def synthesize_battlecards(extractions: List[Dict[str, Any]]) -> CompetitiveData:
"""Synthesize multiple per-competitor battlecards into unified competitive landscape."""
def _extract_battlecard_with_llm(filename: str, content: str) -> Dict[str, Any]:
"""Use LLM to extract structured data from battlecard."""Key Capabilities
PDF Extraction:
Parses PDF text content using
pdfplumberHandles both structured battlecards and freeform competitive analysis docs
Extracts competitor name, description, strengths, weaknesses, threat level
LLM-Based Structured Extraction:
Uses Claude Sonnet for intelligent extraction
Structured JSON output with validation
Handles varying battlecard formats and layouts
Multi-Battlecard Synthesis:
Combines 8+ separate battlecards into unified competitive landscape
De-duplicates and normalizes competitor data
Aggregates key differentiators, winning angles, and discovery questions
Generates overall market positioning summary
Extracted Data Structure
CompetitiveData = {
"competitors": List[CompetitorEntry], # Individual competitor profiles
"key_differentiators": List[str], # What sets company apart
"winning_angles": List[str], # Sales talking points
"discovery_questions": List[str], # Questions to ask prospects
"market_positioning": str, # Summary positioning statement
"swot": SWOTAnalysis, # Company's SWOT analysis
"source_documents": List[str], # Source PDF filenames
}
CompetitorEntry = {
"name": str,
"description": str,
"website": Optional[str],
"strengths": List[str],
"weaknesses": List[str],
"threat_level": Literal["High", "Medium", "Low"],
"feature_comparison": Dict[str, bool],
}Improvement: Separated Artifact Files
Problem
Previous implementation combined all output into single large files (0-dataroom-analysis.json/md), making it difficult to review specific data types.
Solution
Refactored save_dataroom_analysis_artifacts() to output separate numbered files:
output/Hydden-v0.0.1/
├── 0-dataroom-inventory.json (13 KB) # Document inventory only
├── 0-dataroom-inventory.md (4.2 KB) # Human-readable inventory
├── 1-competitive-analysis.json (53 KB) # Full competitive data
├── 1-competitive-analysis.md (37 KB) # Detailed competitive report
├── (future) 2-financial-analysis.json/md
├── (future) 3-team-analysis.json/md
└── (future) 4-traction-analysis.json/mdNew Formatter Functions
Added two dedicated report formatters:
format_inventory_report() - Document inventory only:
Document count summary by type
Detailed document list with confidence indicators
Data gaps section
No extracted data (lightweight)
format_competitive_report() - Full competitive analysis:
Competitor overview table with threat levels
Detailed competitor profiles (description, strengths, weaknesses)
All key differentiators (no truncation)
All winning angles (sales talking points)
All discovery questions
SWOT analysis and market positioning
Source documents list
Testing Results: Hydden Dataroom
Input:
9 competitive analysis documents (8 battlecards + 1 process doc)
Competitors: SphereCo, Silverfort, Axonius, Authomize, Orchid Security, Oleria, Veza, Okta
Extraction Results:
| Metric | Count |
| Competitors identified | 8 |
| High-threat competitors | 5 (Silverfort, Axonius, Authomize, Veza, Okta) |
| Medium-threat competitors | 3 (SphereCo, Orchid Security, Oleria) |
| Key differentiators | 65 |
| Winning angles | 55 |
| Discovery questions | 43 |
Performance:
Processing time: ~4 minutes (with LLM extraction)
Classification accuracy: 100% (all 20 documents correctly typed)
Confidence: 100% high confidence (all documents)
Sample Output from 1-competitive-analysis.md:
## Competitor Overview
| Competitor | Threat Level | Strengths | Weaknesses |
|------------|--------------|-----------|------------|
| SphereCo | 🟡 Medium | 3 | 10 |
| Silverfort | 🔴 High | 9 | 9 |
| Axonius | 🔴 High | 10 | 11 |
...
## Detailed Competitor Profiles
### Silverfort 🔴
Identity threat detection and response (ITDR) platform designed to catch
attackers at authentication runtime enforcement...
**Strengths:**
- Patented agentless inline enforcement—universal MFA and deny/step-up...
- Strong remediating options at runtime...
...
**Weaknesses:**
- Connectors for AD/Entra integrate with IAM/SIEM/PAM ecosystems, but not positioned as a general-purpose 'universal collector'...
...Files Changed Summary
New Files (1)
| File | Size | Purpose |
src/agents/dataroom/extractors/competitive_extractor.py | ~600 lines | PDF extraction & synthesis |
Modified Files (2)
| File | Changes | Purpose |
src/agents/dataroom/extractors/__init__.py | +3 lines | Export extract_competitive_data |
src/agents/dataroom/analyzer.py | +200 lines | Add extractor integration, separated artifacts, new formatters |
Integration with Analyzer
The competitive extractor is now automatically invoked in the analyzer pipeline:
def _run_extractors(inventory: list, use_llm: bool = True) -> dict:
"""Run specialized extractors on classified documents."""
from .extractors import extract_competitive_data
results = {...}
# Group documents by type
docs_by_type = group_by_document_type(inventory)
# Run competitive extractor
if "competitive_analysis" in docs_by_type:
comp_docs = docs_by_type["competitive_analysis"]
results["competitive"] = extract_competitive_data(comp_docs, use_llm=use_llm)
return resultsUsage
# Activate virtual environment
source .venv/bin/activate
# Run dataroom analyzer (LLM extraction enabled by default)
python -m src.agents.dataroom.analyzer "data/Secure-Inputs/Hydden Dataroom" "Hydden"
# Output:
# ============================================================
# DATAROOM ANALYZER
# ============================================================
# Company: Hydden
# Path: data/Secure-Inputs/Hydden Dataroom
# ============================================================
#
# 📁 Scanning dataroom...
# Found 20 documents
#
# 🏷️ Classifying documents...
# Classified 20 documents
# High confidence: 20
#
# 🔍 Extracting competitive data from 9 documents...
# ✓ Found 8 competitors
# 📄 Saved: 0-dataroom-inventory.json
# 📄 Saved: 0-dataroom-inventory.md
# 📄 Saved: 1-competitive-analysis.json
# 📄 Saved: 1-competitive-analysis.md
#
# ============================================================
# ✓ Analysis complete in 253.4s
# ✓ Artifacts saved to: output/Hydden-v0.0.1
# ============================================================Next Steps (Remaining Phase 2 Extractors)
financial_extractor.py- Extract P&L, projections, key metricscap_table_extractor.py- Parse ownership structureteam_extractor.py- Extract team bios and backgroundstraction_extractor.py- Extract customer/revenue metrics
Contributors
Implementation by Claude Code (Anthropic) with user guidance.