Companies earnings calls provide critical insights into a company's performance, strategy, and future outlook. However, these transcripts are lengthy, dense, and cover diverse topics - making it challenging to extract targeted insights efficiently.
The Problem
Quarterly earnings calls provide critical insights into company performance, strategies, and outlook, but extracting meaningful analysis presents significant challenges:
- Earnings call transcripts are lengthy and dense, often spanning 20+ pages of complex financial discussions Key insights are scattered throughout the text without clear organization.
- Different stakeholders need different types of information (financial metrics, strategic initiatives, risk factors).
- Cross-quarter analysis requires manually tracking evolving narratives across multiple calls.
- Traditional manual analysis is time-consuming, inconsistent, and prone to missing important details.
Why This Matters
For investors, analysts, and business leaders, comprehensive earnings call analysis delivers significant value:
- Time Efficiency: Reduce analysis time from days to minutes
- Decision Support: Provide structured insights for investment and strategic decisions
- Comprehensive Coverage: Ensure no important insights are missed
- Consistent Analysis: Apply the same analytical rigor to every transcript
- Trend Detection: Identify patterns across quarters that might otherwise go unnoticed
Our Solution
The Earnings Call Analysis Orchestrator transforms how earnings calls are processed through a multi-agent workflow that:
- Extracts insights from quarterly transcripts using specialized analysis agents
- Delivers both comprehensive reports and targeted query responses
- Identifies trends and patterns across quarters
- Maintains a structured knowledge base of earnings insights
Specialized Analysis Agents
Our system employs specialized agents working in coordination to deliver comprehensive analysis:
Financial Agent: Extracts revenue figures, profit margins, growth metrics, and other quantifiable performance indicators.
Strategic Agent: Identifies product roadmaps, market expansions, partnerships, and long-term vision initiatives.
Sentiment Agent: Evaluates management's confidence, tone, and enthusiasm across different business segments.
Risk Agent: Detects supply chain, market, regulatory challenges, and assesses their severity and mitigation plans.
Competitor Agent: Tracks competitive positioning, market share discussions, and differentiation strategies.
Temporal Agent: Analyzes trends across quarters to identify business trajectory and evolving priorities.
Workflow Orchestration
The orchestrator serves as the central coordinator that:
- Efficiently processes and caches transcript text using advanced OCR
- Activates specialized agents based on analysis needs Stores structured insights in a centralized knowledge base
- Generates comprehensive reports with executive summaries, sectional analyses, and outlook
- Answers specific queries by leveraging relevant insights across quarters
Dataset
For demonstration purposes, we use NVIDIA's quarterly earnings call transcripts from 2025:
- Q1 2025 Earnings Call Transcript
- Q2 2025 Earnings Call Transcript
- Q3 2025 Earnings Call Transcript
- Q4 2025 Earnings Call Transcript
These transcripts contain discussions of financial results, strategic initiatives, market conditions, and forward-looking statements by NVIDIA's management team and their interactions with financial analysts.
Mistral AI Models
For our implementation, we use Mistral AI's LLMs:
mistral-small-latest
: Used for general analysis and response generation.
mistral-large-latest
: Used for structured output generation.
mistral-ocr-latest
: Used for PDF transcript extraction and processing.
This modular approach enables both in-depth report generation and targeted question answering while maintaining efficiency through selective agent activation and insights reuse.
Solution Architecture
Installation
We need mistralai
for LLM usage.
!pip install mistralai
Imports
import os
import json
import hashlib
from datetime import datetime
from pathlib import Path
from typing import List, Dict, Any, Literal, Optional, Union
from abc import ABC, abstractmethod
from pydantic import BaseModel, Field
from mistralai import Mistral
from IPython.display import display, Markdown
Setup API Keys
Here we setup MistralAI API key.
os.environ['MISTRAL_API_KEY'] = '<YOUR MISTRALAI API KEY>' # Get your API key from https://console.mistral.ai/api-keys/
api_key = os.environ.get('MISTRAL_API_KEY')
Initialize Mistral client
Here we initialise Mistral client.
mistral_client = Mistral(api_key=os.environ.get("MISTRAL_API_KEY"))
Download Data
We will use NVIDIA's quarterly earnings call transcripts from 2025:
- Q1 2025 Earnings Call Transcript
- Q2 2025 Earnings Call Transcript
- Q3 2025 Earnings Call Transcript
- Q4 2025 Earnings Call Transcript
These transcripts contain discussions of financial results, strategic initiatives, market conditions, and forward-looking statements by NVIDIA's management team and their interactions with financial analysts.
!wget "https://github.com/mistralai/cookbook/blob/main/mistral/agents/non_framework/earnings_calls/data/nvidia_earnings_2025_Q1.pdf" -O "nvidia_earnings_2025_Q1.pdf"
!wget "https://github.com/mistralai/cookbook/blob/main/mistral/agents/non_framework/earnings_calls/data/nvidia_earnings_2025_Q2.pdf" -O "nvidia_earnings_2025_Q2.pdf"
!wget "https://github.com/mistralai/cookbook/blob/main/mistral/agents/non_framework/earnings_calls/data/nvidia_earnings_2025_Q3.pdf" -O "nvidia_earnings_2025_Q3.pdf"
!wget "https://github.com/mistralai/cookbook/blob/main/mistral/agents/non_framework/earnings_calls/data/nvidia_earnings_2025_Q4.pdf" -O "nvidia_earnings_2025_Q4.pdf"
Initiate Models
-
DEFAULT_MODEL - For General Analysis
-
STRUCTURED_MODEL - For Structured Outputs
-
OCR_MODEL - For parsing the earnings call document.
DEFAULT_MODEL = "mistral-small-latest"
STRUCTURED_MODEL = "mistral-large-latest"
OCR_MODEL = "mistral-ocr-latest"
Data Models
The solution uses specialized Pydantic models to structure and extract insights:
Core Analysis Models
- FinancialInsight: Captures metrics, values, and confidence scores for financial performance
- StrategicInsight: Represents initiatives, descriptions, timeframes, and importance ratings
- SentimentInsight: Tracks topic sentiment, evidence, and speaker attributions
- RiskInsight: Documents risks, impacts, mitigations, and severity scores
- CompetitorInsight: Records market segments, positioning, and competitive dynamics
- TemporalInsight: Identifies trends, patterns, and supporting evidence across quarters
Workflow Models
- QueryAnalysis: Determines required quarters, agent types, and analysis dimensions from user queries
- ReportSection: Structures report content with title, body, and optional subsections
Response Wrappers
- Each analysis model has a corresponding response wrapper (e.g., FinancialInsightsResponse) that packages insights into structured formats compatible with the Mistral API parsing capabilities
The models use Python's Literal types for categorized fields (such as sentiment levels or trend types) to enforce strict validation and ensure consistent terminology, enabling reliable cross-quarter comparisons while providing consistent knowledge extraction, storage, and retrieval across multiple analysis dimensions for both comprehensive reports and targeted queries.
Financial Insight
class FinancialInsight(BaseModel):
"""Financial insights extracted from transcript"""
metric_name: str = Field(description="Name of the financial metric")
value: Optional[str] = Field(description="Numerical or textual value of the metric")
context: str = Field(description="Surrounding context for the metric")
quarter: Literal["Q1", "Q2", "Q3", "Q4"] = Field(description="Quarter the insight relates to (e.g., Q1, Q2)")
confidence: float = Field(description="Confidence score for the insight with limits ge=0.0, le=1.0")
class FinancialInsightsResponse(BaseModel):
"""Wrapper for list of financial insights"""
insights: List[FinancialInsight] = Field(description="Collection of financial insights")
Strategic Insight
class StrategicInsight(BaseModel):
"""Strategic insights about business direction"""
initiative: str = Field(description="Name of the strategic initiative")
description: str = Field(description="Details about the strategic initiative")
timeframe: Optional[str] = Field(description="Expected timeline for implementation")
quarter: Literal["Q1", "Q2", "Q3", "Q4"] = Field(description="Quarter the insight relates to (e.g., Q1, Q2, Q3, Q4)")
importance: int = Field(description="Importance rating with limits ge=1, le=5")
class StrategicInsightsResponse(BaseModel):
"""Wrapper for list of strategic insights"""
insights: List[StrategicInsight] = Field(description="Collection of strategic insights")
Sentiment Insight
class SentimentInsight(BaseModel):
"""Insights about management sentiment"""
topic: str = Field(description="Subject matter being discussed")
sentiment: Literal["very negative", "negative", "neutral", "positive", "very positive"] = Field(description="Tone expressed by management")
evidence: str = Field(description="Quote or context supporting the sentiment analysis")
speaker: str = Field(description="Person who expressed the sentiment")
quarter: Literal["Q1", "Q2", "Q3", "Q4"] = Field(description="Quarter the insight relates to (e.g., Q1, Q2, Q3, Q4)")
class SentimentInsightsResponse(BaseModel):
"""Wrapper for list of sentiment insights"""
insights: List[SentimentInsight] = Field(description="Collection of sentiment insights")
Risk Insight
class RiskInsight(BaseModel):
"""Identified risks or challenges"""
risk_factor: str = Field(description="Name or type of risk identified")
description: str = Field(description="Details about the risk")
potential_impact: str = Field(description="Possible consequences of the risk")
mitigation_mentioned: Optional[str] = Field(description="Strategies to address the risk")
quarter: Literal["Q1", "Q2", "Q3", "Q4"] = Field(description="Quarter the insight relates to (e.g., Q1, Q2, Q3, Q4)")
severity: int = Field(description="Severity rating with limits ge=1, le=5")
class RiskInsightsResponse(BaseModel):
"""Wrapper for list of risk insights"""
insights: List[RiskInsight] = Field(description="Collection of risk insights")
Competitor Insight
class CompetitorInsight(BaseModel):
"""Insights about competitive positioning"""
competitor: Optional[str] = Field(description="Name of the competitor company")
market_segment: str = Field(description="Specific market area being discussed")
positioning: str = Field(description="Competitive stance or market position")
quarter: Literal["Q1", "Q2", "Q3", "Q4"] = Field(description="Quarter the insight relates to (e.g., Q1, Q2, Q3, Q4)")
mentioned_by: str = Field(description="Person who mentioned the competitive information")
class CompetitorInsightsResponse(BaseModel):
"""Wrapper for list of competitor insights"""
insights: List[CompetitorInsight] = Field(description="Collection of competitor insights")
Temporal Insight
class TemporalInsight(BaseModel):
"""Insights about trends across quarters"""
trend_type: Literal["growth", "decline", "stable", "volatile", "emerging", "fading"] = Field(description="Direction or pattern of the trend")
topic: str = Field(description="Subject matter of the trend")
description: str = Field(description="Explanation of the trend's significance")
quarters_observed: List[Literal["Q1", "Q2", "Q3", "Q4"]] = Field(description="Quarters where the trend appears")
supporting_evidence: str = Field(description="Data or quotes supporting the trend identification")
class TemporalInsightsResponse(BaseModel):
"""Wrapper for list of temporal insights"""
insights: List[TemporalInsight] = Field(description="Collection of temporal insights")
Query Analysis
class QueryAnalysis(BaseModel):
"""Analysis of user query to determine required components"""
quarters: List[str] = Field(description="List of quarters to analyze")
agent_types: List[str] = Field(description="List of agent types to use")
temporal_analysis_required: bool = Field(description="Whether temporal analysis across quarters is needed")
query_intent: str = Field(description="Brief description of user's intent")
Report Section
class ReportSection(BaseModel):
"""Section of the final report"""
title: str = Field(description="Heading for the report section")
content: str = Field(description="Main text content of the section")
subsections: Optional[List["ReportSection"]] = Field(description="Nested sections within this section.")
PDF Parser
Our PDF parser uses Mistral's OCR capabilities to extract high-quality text from earnings call transcripts while implementing a file-based caching system to improve performance. This approach enables accurate text extraction with minimal processing overhead for repeated analyses.
class PDFParser:
"""Parse a transcript PDF file and extract text from all pages using Mistral OCR."""
CACHE_DIR = Path("transcript_cache")
@staticmethod
def _ensure_cache_dir():
"""Make sure cache directory exists"""
PDFParser.CACHE_DIR.mkdir(exist_ok=True)
@staticmethod
def _get_cache_path(file_path: str) -> Path:
"""Get the path for a cached transcript file"""
# Create a hash of the file path to use as the cache filename
file_hash = hashlib.md5(file_path.encode()).hexdigest()
return PDFParser.CACHE_DIR / f"{file_hash}.txt"
@staticmethod
def read_transcript(file_path: str, mistral_client: Mistral) -> str:
"""Extract text from PDF transcript using Mistral OCR"""
print(f"Processing PDF file: {file_path}")
uploaded_pdf = mistral_client.files.upload(
file={
"file_name": file_path,
"content": open(file_path, "rb"),
},
purpose="ocr"
)
signed_url = mistral_client.files.get_signed_url(file_id=uploaded_pdf.id)
ocr_response = mistral_client.ocr.process(
model=OCR_MODEL,
document={
"type": "document_url",
"document_url": signed_url.url,
}
)
text = "\n".join([x.markdown for x in (ocr_response.pages)])
return text
@staticmethod
def get_transcript_by_quarter(company: str, quarter: str, year: str, mistral_client: Mistral) -> str:
"""Get the transcript for a specific quarter"""
company_lower = company.lower()
file_path = f"{company_lower}_earnings_{year}_{quarter}.pdf"
PDFParser._ensure_cache_dir()
cache_path = PDFParser._get_cache_path(file_path)
# Check if transcript is in cache
if cache_path.exists():
print(f"Using cached transcript for {company} {year} {quarter}")
with open(cache_path, "r", encoding="utf-8") as f:
return f.read()
else:
try:
print(f"Parsing transcript for {company} {year} {quarter}")
transcript = PDFParser.read_transcript(file_path, mistral_client)
# Store in cache for future use
with open(cache_path, "w", encoding="utf-8") as f:
f.write(transcript)
print(f"Cached transcript for {company} {year} {quarter}")
return transcript
except Exception as e:
print(f"Error processing transcript: {str(e)}")
raise
Insights Storage
The system includes a centralized InsightsStore component that:
- Maintains a persistent JSON database of all extracted insights
- Organizes insights by type (financial, strategic, etc.) and quarter
- Provides efficient retrieval for both report generation and query answering
- Eliminates redundant processing by caching analysis results
class InsightsStore:
"""Centralized storage for insights across all quarters and analysis types"""
def __init__(self, company: str, year: str):
self.company = company.lower()
self.year = year
self.db_path = Path(f"{self.company}_{self.year}_insights.json")
self.insights = self._load_insights()
def _load_insights(self) -> Dict:
"""Load insights from database file or initialize if not exists"""
if self.db_path.exists():
with open(self.db_path, "r", encoding="utf-8") as f:
return json.load(f)
else:
return {
"financial": {},
"strategic": {},
"sentiment": {},
"risk": {},
"competitor": {},
"temporal": {}
}
def save_insights(self):
"""Save insights to database file"""
with open(self.db_path, "w", encoding="utf-8") as f:
json.dump(self.insights, f, indent=2)
def add_insights(self, insight_type: str, quarter: str, insights: List):
"""Add insights for a specific type and quarter"""
if quarter not in self.insights[insight_type]:
self.insights[insight_type][quarter] = []
# Convert insights to dictionaries for storage
insight_dicts = []
for insight in insights:
if hasattr(insight, "dict"):
insight_dicts.append(insight.dict())
elif isinstance(insight, dict):
insight_dicts.append(insight)
else:
insight_dicts.append({"content": str(insight)})
# Append new insights
self.insights[insight_type][quarter] = insight_dicts
self.save_insights()
def get_insights(self, insight_type=None, quarters=None):
"""Retrieve insights, optionally filtered by type and quarters"""
if insight_type is None:
return self.insights
if quarters is None:
return self.insights[insight_type]
filtered = {}
for q in quarters:
if q in self.insights[insight_type]:
filtered[q] = self.insights[insight_type][q]
return filtered
Specialised Agents
Our analysis relies on five domain-focused agents, each extracting specific insights:
Financial Agent - Analyzes metrics, revenue figures, margins, and growth rates.
Strategic Agent - Identifies product roadmaps, market expansions, and R&D investments.
Sentiment Agent - Evaluates management tone, confidence levels, and enthusiasm across topics.
Risk Agent - Detects challenges, uncertainties, and potential threats with severity ratings.
Competitor Agent - Tracks competitive positioning, market share discussions, and differentiation strategies.
Each agent processes transcripts through specialized prompts, producing structured insights that feed into the overall analysis.
Agent
base class for specialised agents
class Agent(ABC):
"""Base class for all specialized agents"""
def __init__(self):
self.client = mistral_client
@abstractmethod
def analyze(self, transcript: str, quarter: str) -> Any:
"""Analyze the transcript and return insights"""
pass
Financial Agent
class FinancialAgent(Agent):
"""Agent for financial analysis of earnings call transcripts"""
def analyze(self, transcript: str, quarter: str) -> List[FinancialInsight]:
"""Extract financial insights from transcript"""
system_prompt = """
You are a financial analyst focused on extracting key financial metrics and performance
indicators from the earnings call transcripts.
Focus on:
- Revenue figures (overall and by segment)
- Profit margins
- Growth rates
- Forward guidance
- Capital expenditures
- Cash flow metrics
- Any financial KPIs mentioned
Extract only facts that are explicitly stated in the transcript, with their proper context.
Keep the insights as short as possible.
"""
print(f"Extracting financial insights for quarter {quarter}...")
response = self.client.chat.parse(
model=STRUCTURED_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Extract financial insights from this earnings call transcript for {quarter}:\n\n{transcript}"}
],
response_format=FinancialInsightsResponse,
temperature=0.1
)
print(f"Financial agent completed for {quarter}")
parsed_response = json.loads(response.choices[0].message.content)
return parsed_response['insights']
Strategic Agent.
class StrategicAgent(Agent):
"""Agent for strategic analysis of earnings call transcripts"""
def analyze(self, transcript: str, quarter: str) -> List[StrategicInsight]:
"""Extract strategic insights from transcript"""
system_prompt = """
You are a business strategy analyst focused on the company's strategic direction.
Extract insights about:
- Product roadmaps
- Market expansions
- Strategic partnerships
- R&D investments
- Long-term vision
- Business model changes
- Market segments of focus
Focus on extracting concrete strategic initiatives and plans, not general statements.
Assign an importance score (1-5) based on how central it appears to the company's strategy.
"""
print(f"Extracting strategic insights for quarter {quarter}...")
response = self.client.chat.parse(
model=STRUCTURED_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Extract strategic insights from this earnings call transcript for {quarter}:\n\n{transcript}"}
],
response_format=StrategicInsightsResponse,
temperature=0.1
)
print(f"Strategic agent completed for {quarter}")
parsed_response = json.loads(response.choices[0].message.content)
return parsed_response['insights']
Sentiment Agent
class SentimentAgent(Agent):
"""Agent for sentiment analysis of earnings call transcripts"""
def analyze(self, transcript: str, quarter: str) -> List[SentimentInsight]:
"""Extract sentiment insights from transcript"""
system_prompt = """
You are an expert in analyzing sentiment and tone in corporate communications.
Focus on:
- Management's confidence level
- Tone when discussing different business segments
- Enthusiasm for future prospects
- Concerns or hesitations
- Changes in sentiment when answering analyst questions
Extract specific topics and the sentiment expressed about them by specific speakers.
Use the transcript to find evidence of the sentiment you identify.
"""
print(f"Extracting sentiment insights for quarter {quarter}...")
response = self.client.chat.parse(
model=STRUCTURED_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Extract sentiment insights from this earnings call transcript for {quarter}:\n\n{transcript}"}
],
response_format=SentimentInsightsResponse,
temperature=0.1
)
print(f"Sentiment agent completed for {quarter}")
parsed_response = json.loads(response.choices[0].message.content)
return parsed_response['insights']
Risk Agent
class RiskAgent(Agent):
"""Agent for risk analysis of earnings call transcripts"""
def analyze(self, transcript: str, quarter: str) -> List[RiskInsight]:
"""Extract risk insights from transcript"""
system_prompt = """
You are a risk analyst specialized in identifying challenges, uncertainties, and risk factors
mentioned in earnings calls.
Focus on:
- Supply chain challenges
- Market uncertainties
- Competitive pressures
- Regulatory concerns
- Technical challenges
- Execution risks
- Macroeconomic factors
For each risk, identify its potential impact and any mentioned mitigation strategies.
Assign a severity score (1-5) based on how serious the risk appears from the transcript.
"""
print(f"Extracting risk insights for quarter {quarter}...")
response = self.client.chat.parse(
model=STRUCTURED_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Extract risk insights from this earnings call transcript for {quarter}:\n\n{transcript}"}
],
response_format=RiskInsightsResponse,
temperature=0.1
)
print(f"Risk agent completed for {quarter}")
parsed_response = json.loads(response.choices[0].message.content)
return parsed_response['insights']
Competitor Agent
class CompetitorAgent(Agent):
"""Agent for competitive analysis of earnings call transcripts"""
def analyze(self, transcript: str, quarter: str) -> List[CompetitorInsight]:
"""Extract competitor insights from transcript"""
system_prompt = """
You are a competitive intelligence analyst focused on the company's positioning relative to competitors.
Focus on:
- Direct mentions of competitors
- Market share discussions
- Competitive advantages or disadvantages
- Differentiation strategies
- Responses to competitive threats
- Emerging competition
Extract specific insights about the company's competitive positioning in different market segments.
Note who mentioned the competitive information (CEO, CFO, analyst, etc.)
"""
print(f"Extracting competitor insights for quarter {quarter}...")
response = self.client.chat.parse(
model=STRUCTURED_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Extract competitive insights from this earnings call transcript for {quarter}:\n\n{transcript}"}
],
response_format=CompetitorInsightsResponse,
temperature=0.1
)
print(f"Competitor agent completed for {quarter}")
parsed_response = json.loads(response.choices[0].message.content)
return parsed_response['insights']
Temporal Analysis Agent
class TemporalAnalysisAgent(Agent):
"""Agent for analyzing trends across quarters"""
def analyze(self, all_insights: Dict) -> List[TemporalInsight]:
"""Analyze trends and patterns across quarters"""
system_prompt = """
You are a trend analyst specialized in identifying patterns, changes, and developments
across multiple quarters of earnings calls.
Focus on:
- Growing or declining emphasis on specific topics
- Evolving business priorities
- Shifts in competitive positioning
- Changes in risk factors
- Sentiment trends
Identify meaningful patterns that show how the business is evolving over time.
Use specific evidence from multiple quarters to support each trend you identify.
"""
print("Running temporal analysis across quarters...")
# Format insights for analysis
formatted_insights = self._format_insights_for_analysis(all_insights)
response = self.client.chat.parse(
model=STRUCTURED_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Analyze these insights across quarters to identify trends and patterns:\n\n{formatted_insights}"}
],
response_format=TemporalInsightsResponse,
temperature=0.2
)
print("Temporal analysis completed")
parsed_response = json.loads(response.choices[0].message.content)
return parsed_response['insights']
def _format_insights_for_analysis(self, all_insights: Dict) -> str:
"""Format all insights for temporal analysis"""
formatted = ""
for agent_type, quarters_data in all_insights.items():
formatted += f"\n## {agent_type.capitalize()} Insights Across Quarters:\n"
for quarter, insights in quarters_data.items():
formatted += f"\n### {quarter}:\n"
if isinstance(insights, list):
for insight in insights:
# Convert insight object to string representation
if isinstance(insight, dict):
insight_str = json.dumps(insight)
else:
insight_str = str(insight)
formatted += f"- {insight_str}\n"
else:
formatted += f"{insights}\n"
return formatted
Query Processor
The Query Processor analyzes user questions to determine the specific components needed:
- Interprets the queries about NVIDIA's earnings calls
- Identifies which quarters (Q1-Q4) are relevant to the question
- Determines which agent types should be activated based on query content
- Decides whether temporal analysis across quarters is required
- Provides a clear interpretation of the user's intent
This component ensures the workflow activates only the necessary analysis paths, improving efficiency while maintaining comprehensive answers.
class QueryProcessor:
"""Processes user queries to determine workflow requirements"""
def __init__(self):
self.client = mistral_client
def analyze_query(self, query: str, company: str) -> QueryAnalysis:
"""Analyze user query to determine required components"""
system_prompt = f"""
You are a query analyzer for {company} earnings call transcripts.
Extract key information about which quarters and agent types are needed.
For agent_types, select from these options: Financial, Strategic, Sentiment, Risk, Competitor
For quarters, select from these options: Q1, Q2, Q3, Q4
Determine if temporal analysis is needed (comparing across quarters).
Provide a brief description of the user's intent.
"""
print(f"Analyzing query: {query}")
response = self.client.chat.parse(
model=STRUCTURED_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Analyze this query about {company}: {query}"}
],
response_format=QueryAnalysis,
temperature=0
)
print("Query analysis completed")
parsed_response = json.loads(response.choices[0].message.content)
# Display query analysis results
print(f"Quarters needed: {parsed_response['quarters']}")
print(f"Agent types needed: {parsed_response['agent_types']}")
print(f"Temporal analysis required: {parsed_response['temporal_analysis_required']}")
print(f"Query intent: {parsed_response['query_intent']}")
return parsed_response
Orchestration Layer
The EarningsCallAnalysisOrchestrator coordinates the entire analysis workflow with key functions:
- process_transcript(): Analyzes a quarterly transcript with all specialized agents
- generate_comprehensive_report(): Creates detailed reports across selected quarters
- answer_query(): Provides targeted responses to specific earnings call questions
- _generate_report_sections(): Produces structured sections (financial, strategic, etc.)
- _generate_query_response(): Crafts focused answers from relevant insights
- _compile_report(): Assembles all sections into a cohesive markdown document
This orchestration ensures efficient resource utilization while delivering both in-depth analysis reports and precise query responses.
class EarningsCallAnalysisOrchestrator:
"""Agentic workflow orchestrator for earnings call analysis combining report generation and query capabilities"""
def __init__(self, company: str, year: str, mistral_client: Mistral):
self.company = company
self.year = year
self.insights_store = InsightsStore(company, year)
# Initialize agents
self.financial_agent = FinancialAgent()
self.strategic_agent = StrategicAgent()
self.sentiment_agent = SentimentAgent()
self.risk_agent = RiskAgent()
self.competitor_agent = CompetitorAgent()
self.temporal_agent = TemporalAnalysisAgent()
# Initialize query processor
self.query_processor = QueryProcessor()
# Initialize Mistral client
self.client = mistral_client
def process_transcript(self, quarter: str):
"""Process a transcript and store all insights"""
print(f"\n=== Processing {self.company} {self.year} {quarter} transcript ===\n")
try:
# Get transcript for this quarter
transcript = PDFParser.get_transcript_by_quarter(self.company, quarter, self.year, self.client)
# Run all agents on the transcript
financial_insights = self.financial_agent.analyze(transcript, quarter)
self.insights_store.add_insights("financial", quarter, financial_insights)
strategic_insights = self.strategic_agent.analyze(transcript, quarter)
self.insights_store.add_insights("strategic", quarter, strategic_insights)
sentiment_insights = self.sentiment_agent.analyze(transcript, quarter)
self.insights_store.add_insights("sentiment", quarter, sentiment_insights)
risk_insights = self.risk_agent.analyze(transcript, quarter)
self.insights_store.add_insights("risk", quarter, risk_insights)
competitor_insights = self.competitor_agent.analyze(transcript, quarter)
self.insights_store.add_insights("competitor", quarter, competitor_insights)
print(f"\n=== Completed processing {self.company} {self.year} {quarter} transcript ===\n")
return True
except Exception as e:
print(f"Error processing transcript for {quarter}: {str(e)}")
return False
def process_all_transcripts(self):
"""Process all quarterly transcripts for the year"""
all_success = True
for quarter in ["Q1", "Q2", "Q3", "Q4"]:
success = self.process_transcript(quarter)
all_success = all_success and success
return all_success
def generate_comprehensive_report(self, quarters=None):
"""Generate a comprehensive report for specified quarters or all quarters"""
if quarters is None:
quarters = ["Q1", "Q2", "Q3", "Q4"]
print(f"\n=== Generating comprehensive report for {self.company} {self.year} {', '.join(quarters)} ===\n")
# Ensure all needed transcripts are processed
for quarter in quarters:
if quarter not in self.insights_store.get_insights("financial"):
print(f"Processing missing transcript for {quarter}...")
self.process_transcript(quarter)
# Get all insights for the specified quarters
all_insights = {
"financial": self.insights_store.get_insights("financial", quarters),
"strategic": self.insights_store.get_insights("strategic", quarters),
"sentiment": self.insights_store.get_insights("sentiment", quarters),
"risk": self.insights_store.get_insights("risk", quarters),
"competitor": self.insights_store.get_insights("competitor", quarters)
}
# Run temporal analysis if multiple quarters
if len(quarters) > 1:
temporal_insights = self.temporal_agent.analyze(all_insights)
quarters_key = "_".join(sorted(quarters))
self.insights_store.add_insights("temporal", quarters_key, temporal_insights)
else:
temporal_insights = []
# Generate report sections
report_sections = self._generate_report_sections(quarters, all_insights, temporal_insights)
# Compile final report
report_content = self._compile_report(report_sections, quarters)
# Save report to file
output_file = f"{self.company}_{self.year}_{'_'.join(quarters)}_Analysis.md"
with open(output_file, "w", encoding="utf-8") as f:
f.write(report_content)
print(f"\n=== Report saved to {output_file} ===\n")
return output_file, report_content
def answer_query(self, query: str):
"""Answer a specific query about earnings calls"""
print(f"\n=== Processing query: {query} ===\n")
# Analyze the query to determine which quarters and agents to use
query_analysis = self.query_processor.analyze_query(query, self.company)
# Ensure we have the necessary insights
for quarter in query_analysis["quarters"]:
if quarter not in self.insights_store.get_insights("financial"):
print(f"Processing missing transcript for {quarter}...")
self.process_transcript(quarter)
# Collect relevant insights based on the query
relevant_insights = {}
for agent_type in query_analysis["agent_types"]:
agent_key = agent_type.lower()
relevant_insights[agent_key] = self.insights_store.get_insights(agent_key, query_analysis["quarters"])
# Get temporal insights if needed
temporal_insights = None
if query_analysis["temporal_analysis_required"] and len(query_analysis["quarters"]) > 1:
# Either use existing temporal insights or generate new ones
quarters_key = "_".join(sorted(query_analysis["quarters"]))
if quarters_key in self.insights_store.get_insights("temporal"):
temporal_insights = self.insights_store.get_insights("temporal")[quarters_key]
else:
temporal_insights = self.temporal_agent.analyze(relevant_insights)
self.insights_store.add_insights("temporal", quarters_key, temporal_insights)
# Generate response to the query
response = self._generate_query_response(query, query_analysis, relevant_insights, temporal_insights)
print("\n=== Query processing completed ===\n")
return response
def _generate_report_sections(self, quarters, all_insights, temporal_insights):
"""Generate all sections for the comprehensive report"""
print("Generating report sections...")
report_sections = {}
# Executive Summary
report_sections["executive_summary"] = self._generate_executive_summary(quarters, all_insights, temporal_insights)
# Financial Performance
report_sections["financial_performance"] = self._generate_financial_section(quarters, all_insights)
# Strategic Initiatives
report_sections["strategic_initiatives"] = self._generate_strategic_section(quarters, all_insights)
# Market Positioning
report_sections["market_positioning"] = self._generate_market_section(quarters, all_insights)
# Risk Assessment
report_sections["risk_assessment"] = self._generate_risk_section(quarters, all_insights)
# Quarterly Trends
if len(quarters) > 1:
report_sections["quarterly_trends"] = self._generate_trends_section(temporal_insights)
# Outlook and Projections
if "Q4" in quarters or len(quarters) > 2:
report_sections["outlook"] = self._generate_outlook_section(quarters, all_insights, temporal_insights)
print("Report sections generated")
return report_sections
def _generate_executive_summary(self, quarters, all_insights, temporal_insights):
"""Generate executive summary from all insights"""
system_prompt = f"""
You are a senior financial analyst creating an executive summary for a comprehensive report on {self.company}'s
performance across {', '.join(quarters)} of {self.year}.
Create a concise, high-level overview that captures:
1. Key financial performance highlights
2. Major strategic developments
3. Notable shifts in market positioning
4. Significant risks or challenges
5. Overall business trajectory
Keep the summary professional, balanced, and data-driven.
IMPORTANT FORMATTING INSTRUCTIONS:
- Use bullet points (not numbers) for lists with only one item
- Only use numbered lists when there are multiple items that need to be ordered
- Format subheadings as bold text using ** for emphasis
"""
# Format insights for the summary
formatted_insights = ""
for insight_type, quarters_data in all_insights.items():
formatted_insights += f"\n## {insight_type.capitalize()} Insights:\n"
for quarter, insights in quarters_data.items():
formatted_insights += f"\n### {quarter}:\n"
for insight in insights[:5]: # Limit to 5 insights per type per quarter
formatted_insights += f"- {json.dumps(insight)}\n"
# Add temporal insights if available
if temporal_insights:
formatted_insights += "\n## Temporal Trends:\n"
for insight in temporal_insights[:10]: # Limit to 10 temporal insights
if isinstance(insight, dict):
formatted_insights += f"- Trend: {insight.get('trend_type', 'N/A')}, Topic: {insight.get('topic', 'N/A')}\n"
formatted_insights += f" Description: {insight.get('description', 'N/A')}\n"
else:
formatted_insights += f"- {str(insight)}\n"
response = self.client.chat.complete(
model=DEFAULT_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Generate an executive summary for {self.company}'s {self.year} performance based on these insights:\n\n{formatted_insights}"}
],
temperature=0.3
)
return {
"title": "Executive Summary",
"content": response.choices[0].message.content
}
def _generate_financial_section(self, quarters, all_insights):
"""Generate financial performance section"""
system_prompt = f"""
You are a financial analyst creating a detailed report section on {self.company}'s financial performance
across {', '.join(quarters)} of {self.year}.
Create a comprehensive analysis that includes:
1. Quarter-by-quarter revenue analysis (overall and by segment)
2. Profitability metrics and trends
3. Cash flow and balance sheet highlights
4. Key performance indicators and their trajectories
5. Comparison of actual results vs. guidance
Use subsections with clear headings, and include specific figures whenever available.
IMPORTANT FORMATTING INSTRUCTIONS:
- Use bullet points (not numbers) for lists with only one item
- Only use numbered lists when there are multiple items that need to be ordered
- Format subheadings as bold text using ** for emphasis
"""
# Format financial insights for all quarters
financial_insights = ""
for quarter, insights in all_insights["financial"].items():
financial_insights += f"\n## {quarter} Financial Insights:\n"
for insight in insights:
if isinstance(insight, dict):
financial_insights += f"- Metric: {insight.get('metric_name', 'N/A')}, Value: {insight.get('value', 'N/A')}\n"
financial_insights += f" Context: {insight.get('context', 'N/A')}\n"
else:
financial_insights += f"- {str(insight)}\n"
response = self.client.chat.complete(
model=DEFAULT_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Generate a comprehensive financial performance section for {self.company}'s {self.year} based on these insights:\n\n{financial_insights}"}
],
temperature=0.3
)
return {
"title": "Financial Performance Analysis",
"content": response.choices[0].message.content
}
def _generate_strategic_section(self, quarters, all_insights):
"""Generate strategic initiatives section"""
system_prompt = f"""
You are a business strategy analyst creating a detailed report section on {self.company}'s strategic initiatives
across {', '.join(quarters)} of {self.year}.
Create a comprehensive analysis that includes:
1. Key strategic priorities and how they evolved
2. Product roadmap developments
3. Major partnerships and acquisitions
4. R&D focus areas and investments
5. Market expansion efforts
Organize by major strategic themes, highlighting changes in emphasis over time.
IMPORTANT FORMATTING INSTRUCTIONS:
- Use bullet points (not numbers) for lists with only one item
- Only use numbered lists when there are multiple items that need to be ordered
- Format subheadings as bold text using ** for emphasis
"""
# Format strategic insights for all quarters
strategic_insights = ""
for quarter, insights in all_insights["strategic"].items():
strategic_insights += f"\n## {quarter} Strategic Insights:\n"
for insight in insights:
if isinstance(insight, dict):
strategic_insights += f"- Initiative: {insight.get('initiative', 'N/A')}, Importance: {insight.get('importance', 'N/A')}/5\n"
strategic_insights += f" Description: {insight.get('description', 'N/A')}\n"
if insight.get('timeframe'):
strategic_insights += f" Timeframe: {insight.get('timeframe')}\n"
else:
strategic_insights += f"- {str(insight)}\n"
response = self.client.chat.complete(
model=DEFAULT_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Generate a comprehensive strategic initiatives section for {self.company}'s {self.year} based on these insights:\n\n{strategic_insights}"}
],
temperature=0.3
)
return {
"title": "Strategic Initiatives Analysis",
"content": response.choices[0].message.content
}
def _generate_market_section(self, quarters, all_insights):
"""Generate market positioning section"""
system_prompt = f"""
You are a market analyst creating a detailed report section on {self.company}'s competitive positioning
across {', '.join(quarters)} of {self.year}.
Create a comprehensive analysis that includes:
1. {self.company}'s position in key market segments
2. Competitive dynamics with major rivals
3. Market share developments
4. Differentiation strategies
5. Emerging competition and responses
Organize by major market segments, analyzing competitive position in each.
IMPORTANT FORMATTING INSTRUCTIONS:
- Use bullet points (not numbers) for lists with only one item
- Only use numbered lists when there are multiple items that need to be ordered
- Format subheadings as bold text using ** for emphasis
"""
# Format competitor insights for all quarters
competitor_insights = ""
for quarter, insights in all_insights["competitor"].items():
competitor_insights += f"\n## {quarter} Competitive Insights:\n"
for insight in insights:
if isinstance(insight, dict):
competitor_insights += f"- Market Segment: {insight.get('market_segment', 'N/A')}\n"
if insight.get('competitor'):
competitor_insights += f" Competitor: {insight.get('competitor')}\n"
competitor_insights += f" Positioning: {insight.get('positioning', 'N/A')}\n"
competitor_insights += f" Mentioned by: {insight.get('mentioned_by', 'N/A')}\n"
else:
competitor_insights += f"- {str(insight)}\n"
# Also include sentiment insights as they relate to market positioning
for quarter, insights in all_insights["sentiment"].items():
competitor_insights += f"\n## {quarter} Sentiment Insights (Market Related):\n"
for insight in insights:
if isinstance(insight, dict) and any(market_term in insight.get('topic', '').lower() for market_term in
['market', 'competitor', 'competition', 'position', 'share']):
competitor_insights += f"- Topic: {insight.get('topic', 'N/A')}, Sentiment: {insight.get('sentiment', 'N/A')}\n"
competitor_insights += f" Speaker: {insight.get('speaker', 'N/A')}\n"
competitor_insights += f" Evidence: {insight.get('evidence', 'N/A')}\n"
response = self.client.chat.complete(
model=DEFAULT_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Generate a comprehensive market positioning section for {self.company}'s {self.year} based on these insights:\n\n{competitor_insights}"}
],
temperature=0.3
)
return {
"title": "Market Positioning Analysis",
"content": response.choices[0].message.content
}
def _generate_risk_section(self, quarters, all_insights):
"""Generate risk assessment section"""
system_prompt = f"""
You are a risk analyst creating a detailed report section on {self.company}'s risk factors and challenges
across {', '.join(quarters)} of {self.year}.
Create a comprehensive analysis that includes:
1. Major risk categories (supply chain, competition, regulatory, etc.)
2. Evolution of key risks throughout the year
3. Mitigation strategies mentioned by management
4. Emerging vs. declining risk factors
5. Assessment of risk management effectiveness
Organize by risk categories, with severity assessments and trends over time.
IMPORTANT FORMATTING INSTRUCTIONS:
- Use bullet points (not numbers) for lists with only one item
- Only use numbered lists when there are multiple items that need to be ordered
- Format subheadings as bold text using ** for emphasis
"""
# Format risk insights for all quarters
risk_insights = ""
for quarter, insights in all_insights["risk"].items():
risk_insights += f"\n## {quarter} Risk Insights:\n"
for insight in insights:
if isinstance(insight, dict):
risk_insights += f"- Risk Factor: {insight.get('risk_factor', 'N/A')}, Severity: {insight.get('severity', 'N/A')}/5\n"
risk_insights += f" Description: {insight.get('description', 'N/A')}\n"
risk_insights += f" Potential Impact: {insight.get('potential_impact', 'N/A')}\n"
if insight.get('mitigation_mentioned'):
risk_insights += f" Mitigation: {insight.get('mitigation_mentioned')}\n"
else:
risk_insights += f"- {str(insight)}\n"
response = self.client.chat.complete(
model=DEFAULT_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Generate a comprehensive risk assessment section for {self.company}'s {self.year} based on these insights:\n\n{risk_insights}"}
],
temperature=0.3
)
return {
"title": "Risk Assessment",
"content": response.choices[0].message.content
}
def _generate_trends_section(self, temporal_insights):
"""Generate quarterly trends section"""
system_prompt = f"""
You are a business analyst creating a detailed report section on {self.company}'s quarter-to-quarter trends
across multiple dimensions.
Create a comprehensive analysis that includes:
1. Major trends across all analysis dimensions (financial, strategic, etc.)
2. Inflection points or significant shifts during the year
3. Business cycle position and momentum
4. Management focus evolution
5. Market reception changes
Highlight the most significant developments and their implications.
IMPORTANT FORMATTING INSTRUCTIONS:
- Use bullet points (not numbers) for lists with only one item
- Only use numbered lists when there are multiple items that need to be ordered
- Format subheadings as bold text using ** for emphasis
"""
# Format temporal insights
formatted_temporal_insights = ""
for insight in temporal_insights:
if isinstance(insight, dict):
formatted_temporal_insights += f"- Trend: {insight.get('trend_type', 'N/A')}, Topic: {insight.get('topic', 'N/A')}\n"
formatted_temporal_insights += f" Description: {insight.get('description', 'N/A')}\n"
formatted_temporal_insights += f" Quarters: {', '.join(insight.get('quarters_observed', ['N/A']))}\n"
formatted_temporal_insights += f" Evidence: {insight.get('supporting_evidence', 'N/A')}\n\n"
else:
formatted_temporal_insights += f"- {str(insight)}\n"
response = self.client.chat.complete(
model=DEFAULT_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Generate a comprehensive quarterly trends section for {self.company}'s {self.year} based on these insights:\n\n{formatted_temporal_insights}"}
],
temperature=0.3
)
return {
"title": "Quarterly Trends Analysis",
"content": response.choices[0].message.content
}
def _generate_outlook_section(self, quarters, all_insights, temporal_insights):
"""Generate outlook and projections section"""
system_prompt = f"""
You are a forward-looking analyst creating a detailed outlook section for {self.company}
based on earnings call insights across multiple quarters.
Create a comprehensive outlook that includes:
1. Forward guidance from management
2. Key initiatives to watch in the coming year
3. Potential challenges and opportunities
4. Market segment outlooks
5. Long-term strategic trajectory
Focus particularly on the most recent quarter and guidance, but incorporate the full context.
IMPORTANT FORMATTING INSTRUCTIONS:
- Use bullet points (not numbers) for lists with only one item
- Only use numbered lists when there are multiple items that need to be ordered
- Format subheadings as bold text using ** for emphasis
"""
# Get the latest quarter's insights
latest_quarter = sorted(quarters)[-1]
# Financial insights from latest quarter
latest_insights = f"\n## {latest_quarter} Financial Insights:\n"
if latest_quarter in all_insights["financial"]:
for insight in all_insights["financial"][latest_quarter]:
if isinstance(insight, dict):
latest_insights += f"- Metric: {insight.get('metric_name', 'N/A')}, Value: {insight.get('value', 'N/A')}\n"
latest_insights += f" Context: {insight.get('context', 'N/A')}\n"
else:
latest_insights += f"- {str(insight)}\n"
# Strategic insights from latest quarter
latest_insights += f"\n## {latest_quarter} Strategic Insights:\n"
if latest_quarter in all_insights["strategic"]:
for insight in all_insights["strategic"][latest_quarter]:
if isinstance(insight, dict):
latest_insights += f"- Initiative: {insight.get('initiative', 'N/A')}, Importance: {insight.get('importance', 'N/A')}/5\n"
latest_insights += f" Description: {insight.get('description', 'N/A')}\n"
else:
latest_insights += f"- {str(insight)}\n"
# Add sentiment insights about future outlook
latest_insights += f"\n## {latest_quarter} Sentiment on Future Outlook:\n"
if latest_quarter in all_insights["sentiment"]:
for insight in all_insights["sentiment"][latest_quarter]:
if isinstance(insight, dict) and any(future_term in insight.get('topic', '').lower() for future_term in
['outlook', 'future', 'guidance', 'next quarter', 'next year', 'projection']):
latest_insights += f"- Topic: {insight.get('topic', 'N/A')}, Sentiment: {insight.get('sentiment', 'N/A')}\n"
latest_insights += f" Speaker: {insight.get('speaker', 'N/A')}\n"
latest_insights += f" Evidence: {insight.get('evidence', 'N/A')}\n"
# Add temporal insights if available
if temporal_insights:
latest_insights += "\n## Overall Trends:\n"
for insight in temporal_insights:
if isinstance(insight, dict):
latest_insights += f"- Trend: {insight.get('trend_type', 'N/A')}, Topic: {insight.get('topic', 'N/A')}\n"
latest_insights += f" Description: {insight.get('description', 'N/A')}\n"
else:
latest_insights += f"- {str(insight)}\n"
response = self.client.chat.complete(
model=DEFAULT_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Generate a comprehensive outlook and projections section for {self.company} based on their {self.year} earnings calls:\n\n{latest_insights}"}
],
temperature=0.3
)
return {
"title": "Outlook and Projections",
"content": response.choices[0].message.content
}
def _compile_report(self, report_sections, quarters):
"""Compile all sections into a final comprehensive report"""
print("Compiling final comprehensive report...")
# Assemble full report content
report_content = f"# {self.company} {self.year} Earnings Call Analysis\n\n"
if len(quarters) == 4:
report_content += f"## Annual Comprehensive Analysis Report\n\n"
else:
report_content += f"## Analysis Report for {', '.join(quarters)}\n\n"
# Add date generated
report_content += f"*Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n"
# Add executive summary
report_content += f"# {report_sections['executive_summary']['title']}\n\n"
report_content += f"{report_sections['executive_summary']['content']}\n\n"
# Add financial performance
report_content += f"# {report_sections['financial_performance']['title']}\n\n"
report_content += f"{report_sections['financial_performance']['content']}\n\n"
# Add strategic initiatives
report_content += f"# {report_sections['strategic_initiatives']['title']}\n\n"
report_content += f"{report_sections['strategic_initiatives']['content']}\n\n"
# Add market positioning
report_content += f"# {report_sections['market_positioning']['title']}\n\n"
report_content += f"{report_sections['market_positioning']['content']}\n\n"
# Add risk assessment
report_content += f"# {report_sections['risk_assessment']['title']}\n\n"
report_content += f"{report_sections['risk_assessment']['content']}\n\n"
# Add quarterly trends if available
if 'quarterly_trends' in report_sections:
report_content += f"# {report_sections['quarterly_trends']['title']}\n\n"
report_content += f"{report_sections['quarterly_trends']['content']}\n\n"
# Add outlook if available
if 'outlook' in report_sections:
report_content += f"# {report_sections['outlook']['title']}\n\n"
report_content += f"{report_sections['outlook']['content']}\n\n"
return report_content
def _generate_query_response(self, query, query_analysis, relevant_insights, temporal_insights):
"""Generate response to a specific query"""
system_prompt = f"""
You are an expert analyst of {self.company} earnings calls.
Provide a clear, concise response to the user's query based on the insights provided.
Focus only on answering what was asked, using the most relevant insights.
Include specific data points and evidence from the earnings calls.
"""
# Format insights for prompt
insights_formatted = ""
for insight_type, quarters_data in relevant_insights.items():
insights_formatted += f"\n## {insight_type.capitalize()} Insights:\n"
for quarter, insights in quarters_data.items():
insights_formatted += f"\n### {quarter}:\n"
for insight in insights:
if isinstance(insight, dict):
insight_formatted = json.dumps(insight)
else:
insight_formatted = str(insight)
insights_formatted += f"- {insight_formatted}\n"
# Add temporal insights if available
temporal_formatted = ""
if temporal_insights:
temporal_formatted += "\n## Temporal Trends:\n"
for insight in temporal_insights:
if isinstance(insight, dict):
temporal_formatted += f"- Trend: {insight.get('trend_type', 'N/A')}, Topic: {insight.get('topic', 'N/A')}\n"
temporal_formatted += f" Description: {insight.get('description', 'N/A')}\n"
temporal_formatted += f" Quarters: {', '.join(insight.get('quarters_observed', ['N/A']))}\n"
temporal_formatted += f" Evidence: {insight.get('supporting_evidence', 'N/A')}\n"
else:
temporal_formatted += f"- {str(insight)}\n"
user_prompt = f"""
Query: {query}
Quarters analyzed: {', '.join(query_analysis['quarters'])}
Agent types used: {', '.join(query_analysis['agent_types'])}
Insights collected:
{insights_formatted}
"""
if temporal_formatted:
user_prompt += f"""
Temporal insights:
{temporal_formatted}
"""
response = self.client.chat.complete(
model=DEFAULT_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
temperature=0.3
)
return response.choices[0].message.content
Initialize the system for NVIDIA 2025 earnings calls
company = "NVIDIA"
year = "2025"
orchestrator = EarningsCallAnalysisOrchestrator(company, year, mistral_client)
Process All Quarterly Transcripts
We process all quarterly transcripts and generate different insights at once, making both report generation and query answering more efficient.
print("Processing all quarterly transcripts...")
quarters = ["Q1", "Q2", "Q3", "Q4"]
for quarter in quarters:
success = orchestrator.process_transcript(quarter)
if success:
print(f"✓ Successfully processed {quarter} transcript")
else:
print(f"✗ Failed to process {quarter} transcript")
Report Generation
We generate comprehensive report by organizing insights across quarters into structured sections including executive summary, financial analysis, strategic initiatives, market positioning, risk assessment, and future outlook.
print("\nGenerating comprehensive annual report...")
report_file, report_content = orchestrator.generate_comprehensive_report(quarters)
print(f"\nDisplaying report saved to: {report_file}")
display(Markdown(report_content))
Query Answering
Our system analyzes user questions to determine relevant quarters, agent types, and analysis dimensions, then provides targeted responses using only the most applicable insights.
Query-1
Query - What were NVIDIA's key financial metrics in Q1 and Q2 2025?
Agents Used - Financial Agent
Quarters - Q1, Q2
Temporal Analysis Required - False
Financial Year - 2025
query = "What were the key financial metrics in Q1 and Q2?"
answer = orchestrator.answer_query(query)
display(Markdown(answer))
Query-2
Query - Identify strategic shifts in NVIDIA's automotive business across 2025
Agents Used - Strategic Agent
Quarters - Q1, Q2, Q3, Q4
Temporal Analysis Required - True
Financial Year - 2025
query = "Identify strategic shifts in NVIDIA's automotive business across 2025"
answer = orchestrator.answer_query(query)
display(Markdown(answer))
Query-3
Query - What risks did NVIDIA highlight in their Q4 earnings call, and how do they compare to those mentioned in Q3?
Agents Used - Risk Agent
Quarters - Q3, Q4
Temporal Analysis Required - True
Financial Year - 2025
query = "What risks did NVIDIA highlight in their Q4 earnings call, and how do they compare to those mentioned in Q3?"
answer = orchestrator.answer_query(query)
display(Markdown(answer))