Step 6: Fine-Tune an LLM or Rule-Based Engine
Now that you've implemented the basic workflow for your value investing AI agent, it's time to enhance its decision-making capabilities. In this step, we'll explore different approaches to powering your agent's analysis and recommendation engine, from simple rule-based systems to advanced language models.
Choosing the Right Approach
There are several approaches to implementing the decision-making engine for your value investing AI agent, each with its own advantages and trade-offs:
Decision Engine Options
- Rule-Based Systems: If-else logic based on metrics thresholds
- LLM-Powered Systems: Using models like GPT or Claude for analysis and explanations
- Hybrid Approaches: Combining rule-based calculations with LLM-generated explanations
Rule-Based Systems
Advantages:
- Transparent and explainable decisions
- No dependency on external API services
- Consistent and predictable behavior
- Lower computational requirements
Disadvantages:
- Limited flexibility for handling nuanced situations
- Requires manual updating of rules as market conditions change
- Can become complex and difficult to maintain as rules multiply
LLM-Powered Systems
Advantages:
- Can generate natural language explanations and insights
- Able to incorporate contextual information and market trends
- Can adapt to new information without explicit reprogramming
- Better at handling qualitative factors like economic moats
Disadvantages:
- Potential "black box" decision-making
- Dependency on external API services (cost and availability)
- May introduce inconsistency or hallucinations
- Higher computational and financial costs
Hybrid Approaches
Advantages:
- Combines the reliability of rule-based calculations with the flexibility of LLMs
- Can use LLMs for explanation generation while keeping core logic transparent
- More robust against LLM hallucinations by constraining outputs
Disadvantages:
- More complex architecture to implement and maintain
- Still has some dependency on external services
- Requires careful integration to ensure consistency
Implementing a Rule-Based Engine
Let's start by implementing a rule-based engine for our value investing agent. This approach uses explicit if-else logic based on financial metrics and predefined thresholds:
# rule_based_engine.py
import pandas as pd
import numpy as np
class RuleBasedValueEngine:
"""
A rule-based decision engine for value investing analysis.
"""
def __init__(self, criteria=None):
"""
Initialize the rule-based engine with value investing criteria.
Parameters:
-----------
criteria : dict, optional
Dictionary defining value investing criteria and thresholds
"""
# Set default criteria if none provided
self.criteria = criteria or {
'pe_ratio': {'max': 15, 'weight': 0.15},
'pb_ratio': {'max': 3, 'weight': 0.15},
'roe': {'min': 0.15, 'weight': 0.15},
'debt_to_equity': {'max': 1.0, 'weight': 0.1},
'fcf_yield': {'min': 0.02, 'weight': 0.15},
'dividend_yield': {'min': 0.01, 'weight': 0.1},
'earnings_growth': {'min': 0.05, 'weight': 0.1},
'margin_of_safety': {'min': 0.2, 'weight': 0.1}
}
def evaluate_metric(self, metric_name, value):
"""
Evaluate a single metric against criteria.
Parameters:
-----------
metric_name : str
Name of the metric to evaluate
value : float
Value of the metric
Returns:
--------
tuple
(score, explanation)
"""
if metric_name not in self.criteria or pd.isna(value):
return 0, f"No data available for {metric_name}"
criterion = self.criteria[metric_name]
score = 0
# For metrics where lower is better (like P/E ratio)
if 'max' in criterion:
if value <= criterion['max']:
# Calculate score as percentage of maximum (inverted)
score = criterion['weight'] * (1 - value / criterion['max'])
if score < 0:
score = 0
explanation = f"{metric_name} of {value:.2f} is below the threshold of {criterion['max']}, which is positive."
else:
explanation = f"{metric_name} of {value:.2f} exceeds the threshold of {criterion['max']}, which is negative."
# For metrics where higher is better (like ROE)
elif 'min' in criterion:
if value >= criterion['min']:
# Calculate score as percentage above minimum
score = criterion['weight'] * min(1, (value - criterion['min']) / criterion['min'])
explanation = f"{metric_name} of {value:.2f} is above the threshold of {criterion['min']}, which is positive."
else:
explanation = f"{metric_name} of {value:.2f} is below the threshold of {criterion['min']}, which is negative."
return score, explanation
def analyze(self, company_data):
"""
Analyze company data using rule-based criteria.
Parameters:
-----------
company_data : dict
Dictionary containing company financial metrics
Returns:
--------
dict
Analysis results including scores and recommendation
"""
# Initialize results
results = {
'company_name': company_data.get('name', 'Unknown Company'),
'ticker': company_data.get('ticker', 'Unknown'),
'total_score': 0,
'max_possible_score': sum(criterion['weight'] for criterion in self.criteria.values()),
'metric_scores': {},
'explanations': []
}
# Evaluate each metric
for metric_name in self.criteria.keys():
if metric_name in company_data:
score, explanation = self.evaluate_metric(metric_name, company_data[metric_name])
results['metric_scores'][metric_name] = score
results['total_score'] += score
results['explanations'].append(explanation)
# Calculate percentage score
if results['max_possible_score'] > 0:
results['percentage_score'] = (results['total_score'] / results['max_possible_score']) * 100
else:
results['percentage_score'] = 0
# Generate recommendation based on score
results['recommendation'] = self._generate_recommendation(results)
return results
def _generate_recommendation(self, results):
"""
Generate investment recommendation based on analysis results.
Parameters:
-----------
results : dict
Analysis results
Returns:
--------
dict
Recommendation details
"""
score = results['percentage_score']
# Define recommendation thresholds
if score >= 70:
rating = "Strong Buy"
explanation = f"{results['company_name']} ({results['ticker']}) appears significantly undervalued based on our value investing criteria, with a score of {score:.1f}%."
action = "Consider allocating a larger position in your portfolio, subject to your overall investment strategy and risk tolerance."
elif score >= 60:
rating = "Buy"
explanation = f"{results['company_name']} ({results['ticker']}) appears moderately undervalued based on our value investing criteria, with a score of {score:.1f}%."
action = "Consider initiating or adding to a position, while maintaining diversification."
elif score >= 40:
rating = "Hold"
explanation = f"{results['company_name']} ({results['ticker']}) appears fairly valued based on our value investing criteria, with a score of {score:.1f}%."
action = "If you already own shares, consider holding. If not, it may be worth watching for a better entry point."
elif score >= 30:
rating = "Sell"
explanation = f"{results['company_name']} ({results['ticker']}) appears moderately overvalued based on our value investing criteria, with a score of {score:.1f}%."
action = "Consider reducing your position or looking for better value opportunities elsewhere."
else:
rating = "Strong Sell"
explanation = f"{results['company_name']} ({results['ticker']}) appears significantly overvalued based on our value investing criteria, with a score of {score:.1f}%."
action = "Consider exiting your position and reallocating to better value opportunities."
# Compile key insights
strengths = []
weaknesses = []
for metric, score in results['metric_scores'].items():
criterion = self.criteria[metric]
max_score = criterion['weight']
if score > max_score * 0.7:
strengths.append(metric)
elif score < max_score * 0.3:
weaknesses.append(metric)
strengths_text = ""
if strengths:
strengths_text = "Key strengths: " + ", ".join(strengths) + "."
weaknesses_text = ""
if weaknesses:
weaknesses_text = "Areas of concern: " + ", ".join(weaknesses) + "."
return {
'rating': rating,
'explanation': explanation,
'action': action,
'strengths': strengths_text,
'weaknesses': weaknesses_text
}
# Example usage
if __name__ == "__main__":
# Create the engine
engine = RuleBasedValueEngine()
# Sample company data
company_data = {
'name': 'Value Corp',
'ticker': 'VALU',
'pe_ratio': 12.5,
'pb_ratio': 1.8,
'roe': 0.18,
'debt_to_equity': 0.7,
'fcf_yield': 0.04,
'dividend_yield': 0.025,
'earnings_growth': 0.08,
'margin_of_safety': 0.25
}
# Analyze the company
results = engine.analyze(company_data)
# Print results
print(f"Analysis Results for {results['company_name']} ({results['ticker']})")
print(f"Overall Score: {results['percentage_score']:.1f}%")
print(f"Recommendation: {results['recommendation']['rating']}")
print(f"Explanation: {results['recommendation']['explanation']}")
print(f"Suggested Action: {results['recommendation']['action']}")
if results['recommendation']['strengths']:
print(results['recommendation']['strengths'])
if results['recommendation']['weaknesses']:
print(results['recommendation']['weaknesses'])
print("\nDetailed Metric Scores:")
for metric, score in results['metric_scores'].items():
max_score = engine.criteria[metric]['weight']
print(f"- {metric}: {score:.2f}/{max_score:.2f}")
Implementing an LLM-Powered Engine
Now let's explore how to implement an LLM-powered engine for more sophisticated analysis and natural language explanations:
# llm_powered_engine.py
import pandas as pd
import numpy as np
import json
import requests
from datetime import datetime
class LLMValueEngine:
"""
An LLM-powered decision engine for value investing analysis.
"""
def __init__(self, api_key=None, model="gpt-4", criteria=None):
"""
Initialize the LLM-powered engine.
Parameters:
-----------
api_key : str, optional
API key for the LLM service
model : str, optional
Model to use for analysis
criteria : dict, optional
Dictionary defining value investing criteria and thresholds
"""
self.api_key = api_key
self.model = model
# Set default criteria if none provided
self.criteria = criteria or {
'pe_ratio': {'max': 15, 'weight': 0.15},
'pb_ratio': {'max': 3, 'weight': 0.15},
'roe': {'min': 0.15, 'weight': 0.15},
'debt_to_equity': {'max': 1.0, 'weight': 0.1},
'fcf_yield': {'min': 0.02, 'weight': 0.15},
'dividend_yield': {'min': 0.01, 'weight': 0.1},
'earnings_growth': {'min': 0.05, 'weight': 0.1},
'margin_of_safety': {'min': 0.2, 'weight': 0.1}
}
def analyze(self, company_data):
"""
Analyze company data using LLM-powered analysis.
Parameters:
-----------
company_data : dict
Dictionary containing company financial metrics
Returns:
--------
dict
Analysis results including scores and recommendation
"""
# First, calculate basic scores using rule-based approach
results = self._calculate_base_scores(company_data)
# Then, enhance with LLM analysis
if self.api_key:
llm_analysis = self._get_llm_analysis(company_data, results)
results.update(llm_analysis)
else:
# If no API key, provide a message about LLM enhancement
results['llm_enhanced'] = False
results['note'] = "Analysis is based on rule-based calculations only. Provide an API key to enable LLM-enhanced analysis."
return results
def _calculate_base_scores(self, company_data):
"""
Calculate base scores using rule-based approach.
Parameters:
-----------
company_data : dict
Dictionary containing company financial metrics
Returns:
--------
dict
Base analysis results
"""
# Initialize results
results = {
'company_name': company_data.get('name', 'Unknown Company'),
'ticker': company_data.get('ticker', 'Unknown'),
'total_score': 0,
'max_possible_score': sum(criterion['weight'] for criterion in self.criteria.values()),
'metric_scores': {},
'metrics_data': {}
}
# Evaluate each metric
for metric_name in self.criteria.keys():
if metric_name in company_data and not pd.isna(company_data[metric_name]):
value = company_data[metric_name]
criterion = self.criteria[metric_name]
score = 0
# Store the metric value for LLM context
results['metrics_data'][metric_name] = value
# For metrics where lower is better (like P/E ratio)
if 'max' in criterion:
if value <= criterion['max']:
# Calculate score as percentage of maximum (inverted)
score = criterion['weight'] * (1 - value / criterion['max'])
if score < 0:
score = 0
# else score remains 0
# For metrics where higher is better (like ROE)
elif 'min' in criterion:
if value >= criterion['min']:
# Calculate score as percentage above minimum
score = criterion['weight'] * min(1, (value - criterion['min']) / criterion['min'])
# else score remains 0
results['metric_scores'][metric_name] = score
results['total_score'] += score
# Calculate percentage score
if results['max_possible_score'] > 0:
results['percentage_score'] = (results['total_score'] / results['max_possible_score']) * 100
else:
results['percentage_score'] = 0
# Generate basic recommendation based on score
score = results['percentage_score']
if score >= 70:
results['base_rating'] = "Strong Buy"
elif score >= 60:
results['base_rating'] = "Buy"
elif score >= 40:
results['base_rating'] = "Hold"
elif score >= 30:
results['base_rating'] = "Sell"
else:
results['base_rating'] = "Strong Sell"
return results
def _get_llm_analysis(self, company_data, base_results):
"""
Get enhanced analysis from LLM.
Parameters:
-----------
company_data : dict
Dictionary containing company financial metrics
base_results : dict
Base analysis results from rule-based calculation
Returns:
--------
dict
LLM-enhanced analysis
"""
# In a real implementation, this would call an LLM API
# For this example, we'll simulate the LLM response
# Prepare the prompt for the LLM
prompt = self._prepare_llm_prompt(company_data, base_results)
try:
# This is where you would make the actual API call
# For example, using OpenAI's API:
"""
response = requests.post(
"https://api.openai.com/v1/chat/completions",
headers={
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
},
json={
"model": self.model,
"messages": [
{"role": "system", "content": "You are a value investing expert."},
{"role": "user", "content": prompt}
],
"temperature": 0.3
}
)
llm_response = response.json()["choices"][0]["message"]["content"]
"""
# For this example, we'll simulate the LLM response
llm_response = self._simulate_llm_response(company_data, base_results)
# Parse the LLM response
# In a real implementation, you would need to ensure the LLM returns a structured format
# or use a parsing function to extract the relevant information
try:
llm_analysis = json.loads(llm_response)
except:
# Fallback if the response isn't valid JSON
llm_analysis = {
"recommendation": {
"rating": base_results['base_rating'],
"explanation": "Unable to parse LLM response. Using base rating instead.",
"insights": []
}
}
# Mark as LLM-enhanced
llm_analysis['llm_enhanced'] = True
return llm_analysis
except Exception as e:
# If there's an error with the LLM, fall back to the base results
return {
"llm_enhanced": False,
"recommendation": {
"rating": base_results['base_rating'],
"explanation": f"Error getting LLM analysis: {str(e)}. Using base rating instead.",
"insights": []
}
}
def _prepare_llm_prompt(self, company_data, base_results):
"""
Prepare the prompt for the LLM.
Parameters:
-----------
company_data : dict
Dictionary containing company financial metrics
base_results : dict
Base analysis results from rule-based calculation
Returns:
--------
str
Prompt for the LLM
"""
# Format the company data and base results into a prompt
prompt = f"""
You are a value investing expert analyzing {company_data.get('name', 'a company')} ({company_data.get('ticker', 'Unknown')}).
Here are the key financial metrics:
"""
# Add metrics data
for metric, value in base_results['metrics_data'].items():
if metric == 'pe_ratio':
prompt += f"- P/E Ratio: {value:.2f}\n"
elif metric == 'pb_ratio':
prompt += f"- P/B Ratio: {value:.2f}\n"
elif metric == 'roe':
prompt += f"- Return on Equity (ROE): {value*100:.2f}%\n"
elif metric == 'debt_to_equity':
prompt += f"- Debt-to-Equity Ratio: {value:.2f}\n"
elif metric == 'fcf_yield':
prompt += f"- Free Cash Flow Yield: {value*100:.2f}%\n"
elif metric == 'dividend_yield':
prompt += f"- Dividend Yield: {value*100:.2f}%\n"
elif metric == 'earnings_growth':
prompt += f"- Earnings Growth Rate: {value*100:.2f}%\n"
elif metric == 'margin_of_safety':
prompt += f"- Margin of Safety: {value*100:.2f}%\n"
else:
prompt += f"- {metric}: {value}\n"
# Add base score and rating
prompt += f"""
Based on our value investing criteria, this company has a score of {base_results['percentage_score']:.1f}% and a base rating of {base_results['base_rating']}.
Please provide an in-depth value investing analysis of this company. Include:
1. A final investment recommendation (Strong Buy, Buy, Hold, Sell, or Strong Sell)
2. A detailed explanation of your recommendation
3. Key insights about the company's strengths and weaknesses from a value investing perspective
4. Any potential risks or opportunities that might not be captured by the metrics alone
Return your analysis in the following JSON format:
{{
"recommendation": {{
"rating": "Your final rating",
"explanation": "Your detailed explanation",
"insights": [
"Key insight 1",
"Key insight 2",
...
],
"risks": [
"Risk 1",
"Risk 2",
...
],
"opportunities": [
"Opportunity 1",
"Opportunity 2",
...
]
}}
}}
"""
return prompt
def _simulate_llm_response(self, company_data, base_results):
"""
Simulate an LLM response for demonstration purposes.
Parameters:
-----------
company_data : dict
Dictionary containing company financial metrics
base_results : dict
Base analysis results from rule-based calculation
Returns:
--------
str
Simulated LLM response
"""
# Get company name and ticker
company_name = company_data.get('name', 'the company')
ticker = company_data.get('ticker', 'Unknown')
# Get base score and rating
score = base_results['percentage_score']
base_rating = base_results['base_rating']
# Simulate different responses based on the score
if score >= 70:
rating = "Strong Buy"
explanation = f"{company_name} ({ticker}) presents a compelling value investment opportunity. With a strong score of {score:.1f}%, the company exhibits multiple characteristics of an undervalued stock according to traditional value investing principles."
insights = [
f"The P/E ratio of {base_results['metrics_data'].get('pe_ratio', 'N/A'):.2f} is significantly below industry averages, suggesting potential undervaluation.",
f"Strong return on equity (ROE) of {base_results['metrics_data'].get('roe', 0)*100:.2f}% indicates efficient use of shareholder capital.",
f"The company maintains a healthy balance sheet with a manageable debt-to-equity ratio of {base_results['metrics_data'].get('debt_to_equity', 'N/A'):.2f}."
]
risks = [
"Market sentiment shifts could temporarily impact stock price despite strong fundamentals.",
"Potential industry disruption should be monitored for long-term impact."
]
opportunities = [
"Current undervaluation provides a substantial margin of safety.",
"Consistent dividend yield of {:.2f}% offers income potential while waiting for market recognition.".format(base_results['metrics_data'].get('dividend_yield', 0)*100)
]
elif score >= 60:
rating = "Buy"
explanation = f"{company_name} ({ticker}) represents a good value investment opportunity with a score of {score:.1f}%. While not extremely undervalued, the company demonstrates solid fundamentals and reasonable valuation metrics."
insights = [
f"The P/E ratio of {base_results['metrics_data'].get('pe_ratio', 'N/A'):.2f} is below our threshold, indicating reasonable valuation.",
f"Return on equity (ROE) of {base_results['metrics_data'].get('roe', 0)*100:.2f}% shows good profitability.",
f"Free cash flow yield of {base_results['metrics_data'].get('fcf_yield', 0)*100:.2f}% suggests the company generates sufficient cash relative to its valuation."
]
risks = [
"Moderate valuation means less margin of safety compared to 'Strong Buy' recommendations.",
"Changes in interest rates could affect relative attractiveness of the stock."
]
opportunities = [
"Potential for valuation multiple expansion if growth exceeds current expectations.",
"Dividend growth potential based on strong free cash flow generation."
]
elif score >= 40:
rating = "Hold"
explanation = f"{company_name} ({ticker}) appears fairly valued with a score of {score:.1f}%. The company shows a mix of positive and negative factors from a value investing perspective."
insights = [
"Current valuation metrics are neither particularly attractive nor concerning.",
f"Return on equity (ROE) of {base_results['metrics_data'].get('roe', 0)*100:.2f}% is adequate but not exceptional.",
"The company's financial position appears stable but doesn't offer a compelling value proposition at current prices."
]
risks = [
"Limited margin of safety at current valuation levels.",
"May underperform in a market correction due to fair valuation."
]
opportunities = [
"Could become attractive on any significant price pullbacks.",
"Worth monitoring for changes in fundamentals that might improve value metrics."
]
elif score >= 30:
rating = "Sell"
explanation = f"{company_name} ({ticker}) appears overvalued with a score of {score:.1f}%. Several key metrics suggest the stock may not represent a good value at current prices."
insights = [
f"The P/E ratio of {base_results['metrics_data'].get('pe_ratio', 'N/A'):.2f} is above our value threshold, indicating potential overvaluation.",
f"Return on equity (ROE) of {base_results['metrics_data'].get('roe', 0)*100:.2f}% is below what we typically look for in value investments.",
"Current valuation appears to price in optimistic growth expectations."
]
risks = [
"Elevated valuation leaves little room for execution errors.",
"Potential for significant price correction if growth slows."
]
opportunities = [
"Consider revisiting if the stock price decreases substantially.",
"Potential for short-term trading opportunities, though not aligned with value investing principles."
]
else:
rating = "Strong Sell"
explanation = f"{company_name} ({ticker}) appears significantly overvalued with a low score of {score:.1f}%. The company fails to meet most of our value investing criteria."
insights = [
f"The P/E ratio of {base_results['metrics_data'].get('pe_ratio', 'N/A'):.2f} is substantially above value thresholds.",
f"Low return on equity (ROE) of {base_results['metrics_data'].get('roe', 0)*100:.2f}% indicates potential issues with profitability.",
f"High debt-to-equity ratio of {base_results['metrics_data'].get('debt_to_equity', 'N/A'):.2f} raises concerns about financial stability."
]
risks = [
"High valuation creates significant downside risk.",
"Current price appears to be disconnected from fundamental value."
]
opportunities = [
"Consider revisiting only after a substantial correction or fundamental improvement.",
"Capital might be better allocated to more attractively valued alternatives."
]
# Construct the simulated response
response = {
"recommendation": {
"rating": rating,
"explanation": explanation,
"insights": insights,
"risks": risks,
"opportunities": opportunities
}
}
return json.dumps(response, indent=2)
# Example usage
if __name__ == "__main__":
# Create the engine (without API key for simulation)
engine = LLMValueEngine()
# Sample company data
company_data = {
'name': 'Value Corp',
'ticker': 'VALU',
'pe_ratio': 12.5,
'pb_ratio': 1.8,
'roe': 0.18,
'debt_to_equity': 0.7,
'fcf_yield': 0.04,
'dividend_yield': 0.025,
'earnings_growth': 0.08,
'margin_of_safety': 0.25
}
# Analyze the company
results = engine.analyze(company_data)
# Print results
print(f"Analysis Results for {results['company_name']} ({results['ticker']})")
print(f"Overall Score: {results['percentage_score']:.1f}%")
if 'llm_enhanced' in results and results['llm_enhanced']:
print("\nLLM-Enhanced Analysis:")
print(f"Recommendation: {results['recommendation']['rating']}")
print(f"Explanation: {results['recommendation']['explanation']}")
print("\nKey Insights:")
for insight in results['recommendation']['insights']:
print(f"- {insight}")
print("\nRisks:")
for risk in results['recommendation']['risks']:
print(f"- {risk}")
print("\nOpportunities:")
for opportunity in results['recommendation']['opportunities']:
print(f"- {opportunity}")
else:
print(f"\nBase Recommendation: {results['base_rating']}")
if 'note' in results:
print(f"Note: {results['note']}")
print("\nDetailed Metric Scores:")
for metric, score in results['metric_scores'].items():
max_score = engine.criteria[metric]['weight']
print(f"- {metric}: {score:.2f}/{max_score:.2f}")
Implementing a Hybrid Approach
Finally, let's look at how to implement a hybrid approach that combines the strengths of both rule-based and LLM-powered systems:
Hybrid Engine Architecture
A hybrid approach typically follows this architecture:
1. Rule-Based Core
The core analysis and scoring is handled by a transparent, rule-based system:
- Calculates value scores based on predefined criteria
- Applies consistent thresholds to financial metrics
- Generates initial recommendations based on scores
- Ensures reliability and consistency in the core analysis
2. LLM Enhancement Layer
The LLM is used to enhance the output of the rule-based system:
- Generates natural language explanations of the analysis
- Identifies nuanced insights not captured by simple rules
- Provides context-aware recommendations
- Considers qualitative factors like economic moats and management quality
3. Integration Controller
A controller component manages the interaction between the two systems:
- Feeds rule-based results to the LLM for enhancement
- Validates LLM outputs against rule-based constraints
- Falls back to rule-based results if LLM fails or produces inconsistent results
- Combines the strengths of both approaches while mitigating their weaknesses
Implementation Example
class HybridValueEngine:
def __init__(self):
# Initialize both engines
self.rule_engine = RuleBasedValueEngine()
self.llm_engine = LLMValueEngine()
def analyze(self, company_data):
# Get rule-based analysis
rule_results = self.rule_engine.analyze(company_data)
# Try to get LLM enhancement
try:
llm_results = self.llm_engine.analyze(company_data)
# Validate LLM results against rule-based constraints
if self._validate_llm_results(rule_results, llm_results):
# Combine results
final_results = self._combine_results(rule_results, llm_results)
else:
# Fall back to rule-based results with a note
final_results = rule_results
final_results['note'] = "LLM results were inconsistent with rule-based analysis. Using rule-based results only."
except Exception as e:
# Fall back to rule-based results on error
final_results = rule_results
final_results['note'] = f"Error in LLM analysis: {str(e)}. Using rule-based results only."
return final_results
Knowledge Check
What is the main advantage of using a rule-based engine for value investing analysis?
In a hybrid approach to value investing analysis, what role does the LLM typically play?