Added enhanced linguistic analyzer and persona quality improver

This commit is contained in:
ajaysi
2025-09-14 09:53:27 +05:30
parent c63148e1ce
commit 1460ce3cb6
35 changed files with 4446 additions and 118 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,7 @@ from services.llm_providers.gemini_provider import gemini_structured_json_respon
from .data_collector import OnboardingDataCollector
from .prompt_builder import PersonaPromptBuilder
from services.persona.linkedin.linkedin_persona_service import LinkedInPersonaService
from services.persona.facebook.facebook_persona_service import FacebookPersonaService
class CorePersonaService:
@@ -22,6 +23,7 @@ class CorePersonaService:
self.data_collector = OnboardingDataCollector()
self.prompt_builder = PersonaPromptBuilder()
self.linkedin_service = LinkedInPersonaService()
self.facebook_service = FacebookPersonaService()
logger.info("CorePersonaService initialized")
def generate_core_persona(self, onboarding_data: Dict[str, Any]) -> Dict[str, Any]:
@@ -79,6 +81,10 @@ class CorePersonaService:
if platform.lower() == "linkedin":
return self.linkedin_service.generate_linkedin_persona(core_persona, onboarding_data)
# Use Facebook service for Facebook platform
if platform.lower() == "facebook":
return self.facebook_service.generate_facebook_persona(core_persona, onboarding_data)
# Use generic platform adaptation for other platforms
platform_constraints = self._get_platform_constraints(platform)
prompt = self.prompt_builder.build_platform_adaptation_prompt(core_persona, platform, onboarding_data, platform_constraints)

View File

@@ -0,0 +1,629 @@
"""
Enhanced Linguistic Analysis Service
Advanced analysis for better writing style mimicry and persona quality.
"""
import re
import json
from typing import Dict, Any, List, Tuple
from collections import Counter, defaultdict
from loguru import logger
import nltk
from nltk.tokenize import sent_tokenize, word_tokenize
from nltk.corpus import stopwords
from nltk.tag import pos_tag
from textstat import flesch_reading_ease, flesch_kincaid_grade
import spacy
class EnhancedLinguisticAnalyzer:
"""Advanced linguistic analysis for persona creation and improvement."""
def __init__(self):
"""Initialize the linguistic analyzer."""
self.nlp = None
try:
# Try to load spaCy model
self.nlp = spacy.load("en_core_web_sm")
except OSError:
logger.warning("spaCy model not found. Install with: python -m spacy download en_core_web_sm")
# Download required NLTK data
try:
nltk.data.find('tokenizers/punkt')
nltk.data.find('corpora/stopwords')
nltk.data.find('taggers/averaged_perceptron_tagger')
except LookupError:
logger.warning("NLTK data not found. Downloading required data...")
nltk.download('punkt', quiet=True)
nltk.download('stopwords', quiet=True)
nltk.download('averaged_perceptron_tagger', quiet=True)
def analyze_writing_style(self, text_samples: List[str]) -> Dict[str, Any]:
"""
Comprehensive analysis of writing style from multiple text samples.
Args:
text_samples: List of text samples to analyze
Returns:
Detailed linguistic analysis
"""
try:
logger.info(f"Analyzing writing style from {len(text_samples)} text samples")
# Combine all text samples
combined_text = " ".join(text_samples)
# Basic metrics
basic_metrics = self._analyze_basic_metrics(combined_text)
# Sentence analysis
sentence_analysis = self._analyze_sentence_patterns(combined_text)
# Vocabulary analysis
vocabulary_analysis = self._analyze_vocabulary(combined_text)
# Rhetorical analysis
rhetorical_analysis = self._analyze_rhetorical_devices(combined_text)
# Style patterns
style_patterns = self._analyze_style_patterns(combined_text)
# Readability analysis
readability_analysis = self._analyze_readability(combined_text)
# Emotional tone analysis
emotional_analysis = self._analyze_emotional_tone(combined_text)
# Consistency analysis
consistency_analysis = self._analyze_consistency(text_samples)
return {
"basic_metrics": basic_metrics,
"sentence_analysis": sentence_analysis,
"vocabulary_analysis": vocabulary_analysis,
"rhetorical_analysis": rhetorical_analysis,
"style_patterns": style_patterns,
"readability_analysis": readability_analysis,
"emotional_analysis": emotional_analysis,
"consistency_analysis": consistency_analysis,
"analysis_metadata": {
"sample_count": len(text_samples),
"total_words": basic_metrics["total_words"],
"total_sentences": basic_metrics["total_sentences"],
"analysis_confidence": self._calculate_analysis_confidence(text_samples)
}
}
except Exception as e:
logger.error(f"Error analyzing writing style: {str(e)}")
return {"error": f"Failed to analyze writing style: {str(e)}"}
def _analyze_basic_metrics(self, text: str) -> Dict[str, Any]:
"""Analyze basic text metrics."""
sentences = sent_tokenize(text)
words = word_tokenize(text.lower())
# Filter out punctuation
words = [word for word in words if word.isalpha()]
return {
"total_words": len(words),
"total_sentences": len(sentences),
"average_sentence_length": len(words) / len(sentences) if sentences else 0,
"average_word_length": sum(len(word) for word in words) / len(words) if words else 0,
"paragraph_count": len(text.split('\n\n')),
"character_count": len(text),
"character_count_no_spaces": len(text.replace(' ', ''))
}
def _analyze_sentence_patterns(self, text: str) -> Dict[str, Any]:
"""Analyze sentence structure patterns."""
sentences = sent_tokenize(text)
sentence_lengths = [len(word_tokenize(sent)) for sent in sentences]
sentence_types = []
for sentence in sentences:
if sentence.endswith('?'):
sentence_types.append('question')
elif sentence.endswith('!'):
sentence_types.append('exclamation')
else:
sentence_types.append('declarative')
# Analyze sentence beginnings
sentence_beginnings = []
for sentence in sentences:
first_word = word_tokenize(sentence)[0].lower() if word_tokenize(sentence) else ""
sentence_beginnings.append(first_word)
return {
"sentence_length_distribution": {
"min": min(sentence_lengths) if sentence_lengths else 0,
"max": max(sentence_lengths) if sentence_lengths else 0,
"average": sum(sentence_lengths) / len(sentence_lengths) if sentence_lengths else 0,
"median": sorted(sentence_lengths)[len(sentence_lengths)//2] if sentence_lengths else 0
},
"sentence_type_distribution": dict(Counter(sentence_types)),
"common_sentence_starters": dict(Counter(sentence_beginnings).most_common(10)),
"sentence_complexity": self._analyze_sentence_complexity(sentences)
}
def _analyze_vocabulary(self, text: str) -> Dict[str, Any]:
"""Analyze vocabulary patterns and preferences."""
words = word_tokenize(text.lower())
words = [word for word in words if word.isalpha()]
# Remove stopwords for analysis
stop_words = set(stopwords.words('english'))
content_words = [word for word in words if word not in stop_words]
# POS tagging
pos_tags = pos_tag(words)
pos_distribution = dict(Counter(tag for word, tag in pos_tags))
# Vocabulary richness
unique_words = set(words)
unique_content_words = set(content_words)
return {
"vocabulary_size": len(unique_words),
"content_vocabulary_size": len(unique_content_words),
"lexical_diversity": len(unique_words) / len(words) if words else 0,
"most_frequent_words": dict(Counter(words).most_common(20)),
"most_frequent_content_words": dict(Counter(content_words).most_common(20)),
"pos_distribution": pos_distribution,
"word_length_distribution": {
"short_words": len([w for w in words if len(w) <= 4]),
"medium_words": len([w for w in words if 5 <= len(w) <= 8]),
"long_words": len([w for w in words if len(w) > 8])
},
"vocabulary_sophistication": self._analyze_vocabulary_sophistication(words)
}
def _analyze_rhetorical_devices(self, text: str) -> Dict[str, Any]:
"""Analyze rhetorical devices and techniques."""
sentences = sent_tokenize(text)
rhetorical_devices = {
"questions": len([s for s in sentences if s.strip().endswith('?')]),
"exclamations": len([s for s in sentences if s.strip().endswith('!')]),
"repetition": self._find_repetition_patterns(text),
"alliteration": self._find_alliteration(text),
"metaphors": self._find_metaphors(text),
"analogies": self._find_analogies(text),
"lists": self._find_lists(text),
"contrasts": self._find_contrasts(text)
}
return rhetorical_devices
def _analyze_style_patterns(self, text: str) -> Dict[str, Any]:
"""Analyze writing style patterns."""
return {
"formality_level": self._assess_formality(text),
"personal_pronouns": self._count_personal_pronouns(text),
"passive_voice": self._count_passive_voice(text),
"contractions": self._count_contractions(text),
"transition_words": self._find_transition_words(text),
"hedging_language": self._find_hedging_language(text),
"emphasis_patterns": self._find_emphasis_patterns(text)
}
def _analyze_readability(self, text: str) -> Dict[str, Any]:
"""Analyze readability metrics."""
try:
return {
"flesch_reading_ease": flesch_reading_ease(text),
"flesch_kincaid_grade": flesch_kincaid_grade(text),
"reading_level": self._determine_reading_level(flesch_reading_ease(text)),
"complexity_score": self._calculate_complexity_score(text)
}
except Exception as e:
logger.warning(f"Error calculating readability: {e}")
return {"error": "Could not calculate readability metrics"}
def _analyze_emotional_tone(self, text: str) -> Dict[str, Any]:
"""Analyze emotional tone and sentiment patterns."""
# Simple sentiment analysis based on word patterns
positive_words = ['good', 'great', 'excellent', 'amazing', 'wonderful', 'fantastic', 'love', 'like', 'enjoy']
negative_words = ['bad', 'terrible', 'awful', 'hate', 'dislike', 'horrible', 'worst', 'problem', 'issue']
words = word_tokenize(text.lower())
positive_count = sum(1 for word in words if word in positive_words)
negative_count = sum(1 for word in words if word in negative_words)
return {
"sentiment_bias": "positive" if positive_count > negative_count else "negative" if negative_count > positive_count else "neutral",
"positive_word_count": positive_count,
"negative_word_count": negative_count,
"emotional_intensity": self._calculate_emotional_intensity(text),
"tone_consistency": self._assess_tone_consistency(text)
}
def _analyze_consistency(self, text_samples: List[str]) -> Dict[str, Any]:
"""Analyze consistency across multiple text samples."""
if len(text_samples) < 2:
return {"consistency_score": 100, "note": "Only one sample provided"}
# Analyze consistency in various metrics
sentence_lengths = []
vocabulary_sets = []
for sample in text_samples:
sentences = sent_tokenize(sample)
words = word_tokenize(sample.lower())
words = [word for word in words if word.isalpha()]
sentence_lengths.append([len(word_tokenize(sent)) for sent in sentences])
vocabulary_sets.append(set(words))
# Calculate consistency scores
avg_sentence_length_consistency = self._calculate_metric_consistency(
[sum(lengths)/len(lengths) for lengths in sentence_lengths]
)
vocabulary_overlap = self._calculate_vocabulary_overlap(vocabulary_sets)
return {
"consistency_score": (avg_sentence_length_consistency + vocabulary_overlap) / 2,
"sentence_length_consistency": avg_sentence_length_consistency,
"vocabulary_consistency": vocabulary_overlap,
"style_stability": self._assess_style_stability(text_samples)
}
def _calculate_analysis_confidence(self, text_samples: List[str]) -> float:
"""Calculate confidence in the analysis based on data quality."""
if not text_samples:
return 0.0
total_words = sum(len(word_tokenize(sample)) for sample in text_samples)
sample_count = len(text_samples)
# Confidence based on amount of data
word_confidence = min(100, (total_words / 1000) * 100) # 1000 words = 100% confidence
sample_confidence = min(100, (sample_count / 5) * 100) # 5 samples = 100% confidence
return (word_confidence + sample_confidence) / 2
# Helper methods for specific analyses
def _analyze_sentence_complexity(self, sentences: List[str]) -> Dict[str, Any]:
"""Analyze sentence complexity patterns."""
complex_sentences = 0
compound_sentences = 0
for sentence in sentences:
if ',' in sentence and ('and' in sentence or 'but' in sentence or 'or' in sentence):
compound_sentences += 1
if len(word_tokenize(sentence)) > 20:
complex_sentences += 1
return {
"complex_sentence_ratio": complex_sentences / len(sentences) if sentences else 0,
"compound_sentence_ratio": compound_sentences / len(sentences) if sentences else 0,
"average_clauses_per_sentence": self._count_clauses(sentences)
}
def _analyze_vocabulary_sophistication(self, words: List[str]) -> Dict[str, Any]:
"""Analyze vocabulary sophistication level."""
# Simple heuristic based on word length and frequency
long_words = [w for w in words if len(w) > 7]
rare_words = [w for w in words if len(w) > 5] # Simplified rare word detection
return {
"sophistication_score": (len(long_words) + len(rare_words)) / len(words) * 100 if words else 0,
"long_word_ratio": len(long_words) / len(words) if words else 0,
"rare_word_ratio": len(rare_words) / len(words) if words else 0
}
def _find_repetition_patterns(self, text: str) -> Dict[str, Any]:
"""Find repetition patterns in text."""
words = word_tokenize(text.lower())
word_freq = Counter(words)
# Find words that appear multiple times
repeated_words = {word: count for word, count in word_freq.items() if count > 2}
return {
"repeated_words": repeated_words,
"repetition_score": len(repeated_words) / len(set(words)) * 100 if words else 0
}
def _find_alliteration(self, text: str) -> List[str]:
"""Find alliteration patterns."""
sentences = sent_tokenize(text)
alliterations = []
for sentence in sentences:
words = word_tokenize(sentence.lower())
words = [word for word in words if word.isalpha()]
if len(words) >= 2:
for i in range(len(words) - 1):
if words[i][0] == words[i+1][0]:
alliterations.append(f"{words[i]} {words[i+1]}")
return alliterations
def _find_metaphors(self, text: str) -> List[str]:
"""Find potential metaphors in text."""
# Simple metaphor detection based on common patterns
metaphor_patterns = [
r'\b(is|are|was|were)\s+(like|as)\s+',
r'\b(like|as)\s+\w+\s+(is|are|was|were)',
r'\b(metaphorically|figuratively)'
]
metaphors = []
for pattern in metaphor_patterns:
matches = re.findall(pattern, text, re.IGNORECASE)
metaphors.extend(matches)
return metaphors
def _find_analogies(self, text: str) -> List[str]:
"""Find analogies in text."""
analogy_patterns = [
r'\b(just as|similar to|comparable to|akin to)',
r'\b(in the same way|likewise|similarly)'
]
analogies = []
for pattern in analogy_patterns:
matches = re.findall(pattern, text, re.IGNORECASE)
analogies.extend(matches)
return analogies
def _find_lists(self, text: str) -> List[str]:
"""Find list patterns in text."""
list_patterns = [
r'\b(first|second|third|lastly|finally)',
r'\b(one|two|three|four|five)',
r'\b(•|\*|\-|\d+\.)'
]
lists = []
for pattern in list_patterns:
matches = re.findall(pattern, text, re.IGNORECASE)
lists.extend(matches)
return lists
def _find_contrasts(self, text: str) -> List[str]:
"""Find contrast patterns in text."""
contrast_words = ['but', 'however', 'although', 'whereas', 'while', 'on the other hand', 'in contrast']
contrasts = []
for word in contrast_words:
if word in text.lower():
contrasts.append(word)
return contrasts
def _assess_formality(self, text: str) -> str:
"""Assess formality level of text."""
formal_indicators = ['therefore', 'furthermore', 'moreover', 'consequently', 'nevertheless']
informal_indicators = ['gonna', 'wanna', 'gotta', 'yeah', 'ok', 'cool']
formal_count = sum(1 for indicator in formal_indicators if indicator in text.lower())
informal_count = sum(1 for indicator in informal_indicators if indicator in text.lower())
if formal_count > informal_count:
return "formal"
elif informal_count > formal_count:
return "informal"
else:
return "neutral"
def _count_personal_pronouns(self, text: str) -> Dict[str, int]:
"""Count personal pronouns in text."""
pronouns = ['i', 'me', 'my', 'mine', 'myself', 'we', 'us', 'our', 'ours', 'ourselves',
'you', 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself',
'she', 'her', 'hers', 'herself', 'they', 'them', 'their', 'theirs', 'themselves']
words = word_tokenize(text.lower())
pronoun_count = {pronoun: words.count(pronoun) for pronoun in pronouns}
return pronoun_count
def _count_passive_voice(self, text: str) -> int:
"""Count passive voice constructions."""
passive_patterns = [
r'\b(was|were|is|are|been|being)\s+\w+ed\b',
r'\b(was|were|is|are|been|being)\s+\w+en\b'
]
passive_count = 0
for pattern in passive_patterns:
passive_count += len(re.findall(pattern, text, re.IGNORECASE))
return passive_count
def _count_contractions(self, text: str) -> int:
"""Count contractions in text."""
contraction_pattern = r"\b\w+'\w+\b"
return len(re.findall(contraction_pattern, text))
def _find_transition_words(self, text: str) -> List[str]:
"""Find transition words in text."""
transition_words = ['however', 'therefore', 'furthermore', 'moreover', 'nevertheless',
'consequently', 'meanwhile', 'additionally', 'similarly', 'likewise',
'on the other hand', 'in contrast', 'for example', 'for instance']
found_transitions = []
for word in transition_words:
if word in text.lower():
found_transitions.append(word)
return found_transitions
def _find_hedging_language(self, text: str) -> List[str]:
"""Find hedging language in text."""
hedging_words = ['might', 'could', 'possibly', 'perhaps', 'maybe', 'likely', 'probably',
'seems', 'appears', 'suggests', 'indicates', 'tends to']
found_hedging = []
for word in hedging_words:
if word in text.lower():
found_hedging.append(word)
return found_hedging
def _find_emphasis_patterns(self, text: str) -> Dict[str, Any]:
"""Find emphasis patterns in text."""
emphasis_patterns = {
'bold_asterisks': len(re.findall(r'\*\w+\*', text)),
'bold_underscores': len(re.findall(r'_\w+_', text)),
'caps_words': len(re.findall(r'\b[A-Z]{2,}\b', text)),
'exclamation_points': text.count('!'),
'emphasis_words': len(re.findall(r'\b(very|really|extremely|absolutely|completely)\b', text, re.IGNORECASE))
}
return emphasis_patterns
def _determine_reading_level(self, flesch_score: float) -> str:
"""Determine reading level from Flesch score."""
if flesch_score >= 90:
return "very_easy"
elif flesch_score >= 80:
return "easy"
elif flesch_score >= 70:
return "fairly_easy"
elif flesch_score >= 60:
return "standard"
elif flesch_score >= 50:
return "fairly_difficult"
elif flesch_score >= 30:
return "difficult"
else:
return "very_difficult"
def _calculate_complexity_score(self, text: str) -> float:
"""Calculate overall complexity score."""
sentences = sent_tokenize(text)
words = word_tokenize(text.lower())
words = [word for word in words if word.isalpha()]
if not sentences or not words:
return 0.0
# Factors: sentence length, word length, vocabulary diversity
avg_sentence_length = len(words) / len(sentences)
avg_word_length = sum(len(word) for word in words) / len(words)
vocabulary_diversity = len(set(words)) / len(words)
# Normalize and combine
complexity = (avg_sentence_length / 20) * 0.4 + (avg_word_length / 10) * 0.3 + vocabulary_diversity * 0.3
return min(100, complexity * 100)
def _calculate_emotional_intensity(self, text: str) -> float:
"""Calculate emotional intensity of text."""
emotional_words = ['amazing', 'incredible', 'fantastic', 'terrible', 'awful', 'horrible',
'love', 'hate', 'passion', 'fury', 'joy', 'sorrow', 'excitement', 'fear']
words = word_tokenize(text.lower())
emotional_word_count = sum(1 for word in words if word in emotional_words)
return (emotional_word_count / len(words)) * 100 if words else 0
def _assess_tone_consistency(self, text: str) -> float:
"""Assess tone consistency throughout text."""
# Simple heuristic: check for tone shifts
sentences = sent_tokenize(text)
if len(sentences) < 2:
return 100.0
# Analyze first half vs second half
mid_point = len(sentences) // 2
first_half = " ".join(sentences[:mid_point])
second_half = " ".join(sentences[mid_point:])
first_tone = self._analyze_emotional_tone(first_half)
second_tone = self._analyze_emotional_tone(second_half)
# Calculate consistency based on sentiment similarity
if first_tone["sentiment_bias"] == second_tone["sentiment_bias"]:
return 100.0
else:
return 50.0
def _calculate_metric_consistency(self, values: List[float]) -> float:
"""Calculate consistency of a metric across samples."""
if len(values) < 2:
return 100.0
mean_value = sum(values) / len(values)
variance = sum((x - mean_value) ** 2 for x in values) / len(values)
std_dev = variance ** 0.5
# Convert to consistency score (lower std dev = higher consistency)
consistency = max(0, 100 - (std_dev / mean_value * 100)) if mean_value > 0 else 100
return consistency
def _calculate_vocabulary_overlap(self, vocabulary_sets: List[set]) -> float:
"""Calculate vocabulary overlap across samples."""
if len(vocabulary_sets) < 2:
return 100.0
# Calculate pairwise overlaps
overlaps = []
for i in range(len(vocabulary_sets)):
for j in range(i + 1, len(vocabulary_sets)):
intersection = len(vocabulary_sets[i] & vocabulary_sets[j])
union = len(vocabulary_sets[i] | vocabulary_sets[j])
overlap = (intersection / union * 100) if union > 0 else 0
overlaps.append(overlap)
return sum(overlaps) / len(overlaps) if overlaps else 0
def _assess_style_stability(self, text_samples: List[str]) -> Dict[str, Any]:
"""Assess style stability across samples."""
if len(text_samples) < 2:
return {"stability_score": 100, "note": "Only one sample provided"}
# Analyze consistency in key style metrics
metrics = []
for sample in text_samples:
sample_metrics = {
"avg_sentence_length": len(word_tokenize(sample)) / len(sent_tokenize(sample)),
"formality": self._assess_formality(sample),
"emotional_intensity": self._calculate_emotional_intensity(sample)
}
metrics.append(sample_metrics)
# Calculate stability scores
sentence_length_stability = self._calculate_metric_consistency(
[m["avg_sentence_length"] for m in metrics]
)
emotional_stability = self._calculate_metric_consistency(
[m["emotional_intensity"] for m in metrics]
)
# Formality consistency
formality_values = [m["formality"] for m in metrics]
formality_consistency = 100 if len(set(formality_values)) == 1 else 50
overall_stability = (sentence_length_stability + emotional_stability + formality_consistency) / 3
return {
"stability_score": overall_stability,
"sentence_length_stability": sentence_length_stability,
"emotional_stability": emotional_stability,
"formality_consistency": formality_consistency
}
def _count_clauses(self, sentences: List[str]) -> float:
"""Count average clauses per sentence."""
total_clauses = 0
for sentence in sentences:
# Simple clause counting based on conjunctions and punctuation
clauses = len(re.findall(r'[,;]', sentence)) + 1
total_clauses += clauses
return total_clauses / len(sentences) if sentences else 0
a

View File

@@ -0,0 +1,781 @@
"""
Persona Quality Improvement Service
Continuously improves persona quality through feedback and learning.
"""
import json
from typing import Dict, Any, List, Optional, Tuple
from datetime import datetime, timedelta
from loguru import logger
from sqlalchemy.orm import Session
from models.enhanced_persona_models import (
EnhancedWritingPersona,
EnhancedPlatformPersona,
PersonaQualityMetrics,
PersonaLearningData
)
from services.database import get_db_session
from services.persona.enhanced_linguistic_analyzer import EnhancedLinguisticAnalyzer
class PersonaQualityImprover:
"""Service for continuously improving persona quality and accuracy."""
def __init__(self):
"""Initialize the quality improver."""
self.linguistic_analyzer = EnhancedLinguisticAnalyzer()
logger.info("PersonaQualityImprover initialized")
def assess_persona_quality(self, persona_id: int, user_feedback: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""
Assess the quality of a persona and provide improvement suggestions.
Args:
persona_id: ID of the persona to assess
user_feedback: Optional user feedback data
Returns:
Quality assessment results
"""
try:
session = get_db_session()
# Get persona data
persona = session.query(EnhancedWritingPersona).filter(
EnhancedWritingPersona.id == persona_id
).first()
if not persona:
return {"error": "Persona not found"}
# Perform quality assessment
quality_metrics = self._perform_quality_assessment(persona, user_feedback)
# Save quality metrics
self._save_quality_metrics(session, persona_id, quality_metrics, user_feedback)
# Generate improvement suggestions
improvement_suggestions = self._generate_improvement_suggestions(quality_metrics)
session.close()
return {
"persona_id": persona_id,
"quality_metrics": quality_metrics,
"improvement_suggestions": improvement_suggestions,
"assessment_date": datetime.utcnow().isoformat()
}
except Exception as e:
logger.error(f"Error assessing persona quality: {str(e)}")
return {"error": f"Failed to assess persona quality: {str(e)}"}
def improve_persona_from_feedback(self, persona_id: int, feedback_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Improve persona based on user feedback and performance data.
Args:
persona_id: ID of the persona to improve
feedback_data: User feedback and performance data
Returns:
Improvement results
"""
try:
session = get_db_session()
# Get current persona
persona = session.query(EnhancedWritingPersona).filter(
EnhancedWritingPersona.id == persona_id
).first()
if not persona:
return {"error": "Persona not found"}
# Analyze feedback
feedback_analysis = self._analyze_feedback(feedback_data)
# Generate improvements
improvements = self._generate_persona_improvements(persona, feedback_analysis)
# Apply improvements
updated_persona = self._apply_improvements(session, persona, improvements)
# Save learning data
self._save_learning_data(session, persona_id, feedback_data, improvements)
session.commit()
session.close()
return {
"persona_id": persona_id,
"improvements_applied": improvements,
"updated_persona": updated_persona.to_dict(),
"improvement_date": datetime.utcnow().isoformat()
}
except Exception as e:
logger.error(f"Error improving persona: {str(e)}")
return {"error": f"Failed to improve persona: {str(e)}"}
def learn_from_content_performance(self, persona_id: int, content_performance: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
Learn from content performance data to improve persona.
Args:
persona_id: ID of the persona to improve
content_performance: List of content performance data
Returns:
Learning results
"""
try:
session = get_db_session()
# Analyze performance patterns
performance_analysis = self._analyze_performance_patterns(content_performance)
# Identify successful patterns
successful_patterns = self._identify_successful_patterns(content_performance)
# Generate learning insights
learning_insights = self._generate_learning_insights(performance_analysis, successful_patterns)
# Apply learning to persona
persona_updates = self._apply_performance_learning(persona_id, learning_insights)
# Save learning data
self._save_performance_learning(session, persona_id, content_performance, learning_insights)
session.commit()
session.close()
return {
"persona_id": persona_id,
"learning_insights": learning_insights,
"persona_updates": persona_updates,
"learning_date": datetime.utcnow().isoformat()
}
except Exception as e:
logger.error(f"Error learning from performance: {str(e)}")
return {"error": f"Failed to learn from performance: {str(e)}"}
def _perform_quality_assessment(self, persona: EnhancedWritingPersona, user_feedback: Optional[Dict[str, Any]]) -> Dict[str, Any]:
"""Perform comprehensive quality assessment of a persona."""
# Linguistic analysis quality
linguistic_quality = self._assess_linguistic_quality(persona)
# Consistency assessment
consistency_score = self._assess_consistency(persona)
# Authenticity assessment
authenticity_score = self._assess_authenticity(persona)
# User satisfaction (if feedback provided)
user_satisfaction = self._assess_user_satisfaction(user_feedback) if user_feedback else None
# Platform optimization quality
platform_quality = self._assess_platform_optimization(persona)
# Overall quality score
quality_scores = [linguistic_quality, consistency_score, authenticity_score, platform_quality]
if user_satisfaction is not None:
quality_scores.append(user_satisfaction)
overall_quality = sum(quality_scores) / len(quality_scores)
return {
"overall_quality_score": overall_quality,
"linguistic_quality": linguistic_quality,
"consistency_score": consistency_score,
"authenticity_score": authenticity_score,
"user_satisfaction": user_satisfaction,
"platform_optimization_quality": platform_quality,
"quality_breakdown": {
"linguistic_analysis_completeness": self._assess_analysis_completeness(persona),
"style_consistency": consistency_score,
"brand_alignment": authenticity_score,
"platform_adaptation_quality": platform_quality
}
}
def _assess_linguistic_quality(self, persona: EnhancedWritingPersona) -> float:
"""Assess the quality of linguistic analysis."""
linguistic_fingerprint = persona.linguistic_fingerprint or {}
# Check completeness of linguistic analysis
required_fields = [
'sentence_analysis', 'vocabulary_analysis', 'rhetorical_analysis',
'style_patterns', 'readability_analysis'
]
completeness_score = 0
for field in required_fields:
if field in linguistic_fingerprint and linguistic_fingerprint[field]:
completeness_score += 20
# Check quality of analysis
quality_indicators = 0
if linguistic_fingerprint.get('sentence_analysis', {}).get('sentence_length_distribution'):
quality_indicators += 1
if linguistic_fingerprint.get('vocabulary_analysis', {}).get('lexical_diversity'):
quality_indicators += 1
if linguistic_fingerprint.get('rhetorical_analysis', {}).get('questions'):
quality_indicators += 1
if linguistic_fingerprint.get('style_patterns', {}).get('formality_level'):
quality_indicators += 1
quality_score = (quality_indicators / 4) * 100
return (completeness_score + quality_score) / 2
def _assess_consistency(self, persona: EnhancedWritingPersona) -> float:
"""Assess consistency of the persona."""
consistency_analysis = persona.linguistic_fingerprint.get('consistency_analysis', {})
if not consistency_analysis:
return 50.0 # Default score if no consistency data
return consistency_analysis.get('consistency_score', 50.0)
def _assess_authenticity(self, persona: EnhancedWritingPersona) -> float:
"""Assess authenticity of the persona."""
# Check if persona reflects real user characteristics
source_data = persona.source_website_analysis or {}
# Authenticity indicators
authenticity_indicators = 0
total_indicators = 5
# Check for brand voice alignment
if persona.brand_voice_description:
authenticity_indicators += 1
# Check for core belief definition
if persona.core_belief:
authenticity_indicators += 1
# Check for archetype definition
if persona.archetype:
authenticity_indicators += 1
# Check for source data quality
if source_data.get('writing_style'):
authenticity_indicators += 1
# Check for confidence score
if persona.confidence_score and persona.confidence_score > 70:
authenticity_indicators += 1
return (authenticity_indicators / total_indicators) * 100
def _assess_user_satisfaction(self, user_feedback: Dict[str, Any]) -> float:
"""Assess user satisfaction from feedback."""
if not user_feedback:
return None
# Extract satisfaction metrics
satisfaction_score = user_feedback.get('satisfaction_score', 0)
content_quality_rating = user_feedback.get('content_quality_rating', 0)
style_match_rating = user_feedback.get('style_match_rating', 0)
# Calculate weighted average
if satisfaction_score and content_quality_rating and style_match_rating:
return (satisfaction_score + content_quality_rating + style_match_rating) / 3
elif satisfaction_score:
return satisfaction_score
else:
return 50.0 # Default if no clear satisfaction data
def _assess_platform_optimization(self, persona: EnhancedWritingPersona) -> float:
"""Assess platform optimization quality."""
platform_personas = persona.platform_personas
if not platform_personas:
return 0.0
total_score = 0
platform_count = 0
for platform_persona in platform_personas:
if platform_persona.is_active:
# Check platform-specific optimization completeness
platform_score = 0
if platform_persona.platform_linguistic_adaptation:
platform_score += 25
if platform_persona.platform_engagement_patterns:
platform_score += 25
if platform_persona.platform_content_optimization:
platform_score += 25
if platform_persona.platform_algorithm_insights:
platform_score += 25
total_score += platform_score
platform_count += 1
return total_score / platform_count if platform_count > 0 else 0.0
def _assess_analysis_completeness(self, persona: EnhancedWritingPersona) -> float:
"""Assess completeness of the persona analysis."""
completeness_indicators = 0
total_indicators = 8
# Core persona fields
if persona.persona_name:
completeness_indicators += 1
if persona.archetype:
completeness_indicators += 1
if persona.core_belief:
completeness_indicators += 1
if persona.brand_voice_description:
completeness_indicators += 1
# Linguistic analysis
if persona.linguistic_fingerprint:
completeness_indicators += 1
if persona.writing_style_signature:
completeness_indicators += 1
if persona.vocabulary_profile:
completeness_indicators += 1
if persona.sentence_patterns:
completeness_indicators += 1
return (completeness_indicators / total_indicators) * 100
def _generate_improvement_suggestions(self, quality_metrics: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Generate improvement suggestions based on quality metrics."""
suggestions = []
overall_score = quality_metrics.get('overall_quality_score', 0)
# Linguistic quality improvements
if quality_metrics.get('linguistic_quality', 0) < 70:
suggestions.append({
"category": "linguistic_analysis",
"priority": "high",
"suggestion": "Enhance linguistic analysis with more detailed sentence patterns and vocabulary analysis",
"action": "reanalyze_source_content"
})
# Consistency improvements
if quality_metrics.get('consistency_score', 0) < 70:
suggestions.append({
"category": "consistency",
"priority": "high",
"suggestion": "Improve consistency by analyzing more writing samples",
"action": "collect_additional_samples"
})
# Authenticity improvements
if quality_metrics.get('authenticity_score', 0) < 70:
suggestions.append({
"category": "authenticity",
"priority": "medium",
"suggestion": "Strengthen brand voice alignment and core belief definition",
"action": "refine_brand_analysis"
})
# Platform optimization improvements
if quality_metrics.get('platform_optimization_quality', 0) < 70:
suggestions.append({
"category": "platform_optimization",
"priority": "medium",
"suggestion": "Enhance platform-specific adaptations and algorithm insights",
"action": "update_platform_adaptations"
})
# User satisfaction improvements
user_satisfaction = quality_metrics.get('user_satisfaction')
if user_satisfaction is not None and user_satisfaction < 70:
suggestions.append({
"category": "user_satisfaction",
"priority": "high",
"suggestion": "Address user feedback and adjust persona based on preferences",
"action": "incorporate_user_feedback"
})
return suggestions
def _analyze_feedback(self, feedback_data: Dict[str, Any]) -> Dict[str, Any]:
"""Analyze user feedback to extract improvement insights."""
return {
"satisfaction_level": feedback_data.get('satisfaction_score', 0),
"content_quality_rating": feedback_data.get('content_quality_rating', 0),
"style_match_rating": feedback_data.get('style_match_rating', 0),
"specific_complaints": feedback_data.get('complaints', []),
"specific_praises": feedback_data.get('praises', []),
"improvement_requests": feedback_data.get('improvement_requests', []),
"preferred_adjustments": feedback_data.get('preferred_adjustments', {})
}
def _generate_persona_improvements(self, persona: EnhancedWritingPersona, feedback_analysis: Dict[str, Any]) -> Dict[str, Any]:
"""Generate specific improvements based on feedback analysis."""
improvements = {}
# Style adjustments based on feedback
if feedback_analysis.get('style_match_rating', 0) < 70:
improvements['style_adjustments'] = {
"tone_adjustment": feedback_analysis.get('preferred_adjustments', {}).get('tone'),
"formality_adjustment": feedback_analysis.get('preferred_adjustments', {}).get('formality'),
"vocabulary_adjustment": feedback_analysis.get('preferred_adjustments', {}).get('vocabulary')
}
# Content quality improvements
if feedback_analysis.get('content_quality_rating', 0) < 70:
improvements['content_quality'] = {
"clarity_improvement": True,
"engagement_enhancement": True,
"structure_optimization": True
}
# Specific complaint addressing
complaints = feedback_analysis.get('specific_complaints', [])
if complaints:
improvements['complaint_resolutions'] = {
"addressed_complaints": complaints,
"resolution_strategies": self._generate_complaint_resolutions(complaints)
}
return improvements
def _generate_complaint_resolutions(self, complaints: List[str]) -> List[Dict[str, Any]]:
"""Generate resolution strategies for specific complaints."""
resolutions = []
for complaint in complaints:
complaint_lower = complaint.lower()
if 'too formal' in complaint_lower:
resolutions.append({
"complaint": complaint,
"resolution": "Reduce formality level and increase conversational tone",
"action": "adjust_formality_metrics"
})
elif 'too casual' in complaint_lower:
resolutions.append({
"complaint": complaint,
"resolution": "Increase formality level and professional tone",
"action": "adjust_formality_metrics"
})
elif 'too long' in complaint_lower:
resolutions.append({
"complaint": complaint,
"resolution": "Reduce average sentence length and improve conciseness",
"action": "adjust_sentence_length"
})
elif 'too short' in complaint_lower:
resolutions.append({
"complaint": complaint,
"resolution": "Increase sentence complexity and add more detail",
"action": "adjust_sentence_length"
})
elif 'boring' in complaint_lower or 'dull' in complaint_lower:
resolutions.append({
"complaint": complaint,
"resolution": "Add more engaging language and rhetorical devices",
"action": "enhance_engagement_patterns"
})
else:
resolutions.append({
"complaint": complaint,
"resolution": "General style adjustment based on feedback",
"action": "general_style_refinement"
})
return resolutions
def _apply_improvements(self, session: Session, persona: EnhancedWritingPersona, improvements: Dict[str, Any]) -> EnhancedWritingPersona:
"""Apply improvements to the persona."""
# Apply style adjustments
if 'style_adjustments' in improvements:
self._apply_style_adjustments(persona, improvements['style_adjustments'])
# Apply content quality improvements
if 'content_quality' in improvements:
self._apply_content_quality_improvements(persona, improvements['content_quality'])
# Apply complaint resolutions
if 'complaint_resolutions' in improvements:
self._apply_complaint_resolutions(persona, improvements['complaint_resolutions'])
# Update persona metadata
persona.updated_at = datetime.utcnow()
session.add(persona)
return persona
def _apply_style_adjustments(self, persona: EnhancedWritingPersona, style_adjustments: Dict[str, Any]):
"""Apply style adjustments to persona."""
# Update linguistic fingerprint based on adjustments
if not persona.linguistic_fingerprint:
persona.linguistic_fingerprint = {}
# Tone adjustment
if style_adjustments.get('tone_adjustment'):
persona.linguistic_fingerprint['adjusted_tone'] = style_adjustments['tone_adjustment']
# Formality adjustment
if style_adjustments.get('formality_adjustment'):
persona.linguistic_fingerprint['adjusted_formality'] = style_adjustments['formality_adjustment']
# Vocabulary adjustment
if style_adjustments.get('vocabulary_adjustment'):
persona.linguistic_fingerprint['adjusted_vocabulary'] = style_adjustments['vocabulary_adjustment']
def _apply_content_quality_improvements(self, persona: EnhancedWritingPersona, quality_improvements: Dict[str, Any]):
"""Apply content quality improvements to persona."""
if not persona.linguistic_fingerprint:
persona.linguistic_fingerprint = {}
# Add quality improvement markers
persona.linguistic_fingerprint['quality_improvements'] = {
"clarity_enhanced": quality_improvements.get('clarity_improvement', False),
"engagement_enhanced": quality_improvements.get('engagement_enhancement', False),
"structure_optimized": quality_improvements.get('structure_optimization', False),
"improvement_date": datetime.utcnow().isoformat()
}
def _apply_complaint_resolutions(self, persona: EnhancedWritingPersona, complaint_resolutions: Dict[str, Any]):
"""Apply complaint resolutions to persona."""
if not persona.linguistic_fingerprint:
persona.linguistic_fingerprint = {}
# Add complaint resolution tracking
persona.linguistic_fingerprint['complaint_resolutions'] = {
"addressed_complaints": complaint_resolutions.get('addressed_complaints', []),
"resolution_strategies": complaint_resolutions.get('resolution_strategies', []),
"resolution_date": datetime.utcnow().isoformat()
}
def _analyze_performance_patterns(self, content_performance: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Analyze content performance patterns."""
if not content_performance:
return {}
# Calculate average performance metrics
total_content = len(content_performance)
avg_engagement = sum(item.get('engagement_rate', 0) for item in content_performance) / total_content
avg_reach = sum(item.get('reach', 0) for item in content_performance) / total_content
avg_clicks = sum(item.get('clicks', 0) for item in content_performance) / total_content
# Identify top performing content
top_performers = sorted(content_performance,
key=lambda x: x.get('engagement_rate', 0),
reverse=True)[:3]
# Analyze content characteristics of top performers
top_performer_analysis = self._analyze_top_performers(top_performers)
return {
"average_engagement_rate": avg_engagement,
"average_reach": avg_reach,
"average_clicks": avg_clicks,
"total_content_analyzed": total_content,
"top_performers": top_performer_analysis,
"performance_trends": self._identify_performance_trends(content_performance)
}
def _analyze_top_performers(self, top_performers: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Analyze characteristics of top performing content."""
if not top_performers:
return {}
# Analyze common characteristics
content_types = [item.get('content_type') for item in top_performers]
topics = [item.get('topic') for item in top_performers]
lengths = [item.get('content_length') for item in top_performers]
return {
"common_content_types": list(set(content_types)),
"common_topics": list(set(topics)),
"average_length": sum(lengths) / len(lengths) if lengths else 0,
"performance_characteristics": {
"high_engagement_keywords": self._extract_high_engagement_keywords(top_performers),
"optimal_posting_times": self._extract_optimal_posting_times(top_performers),
"successful_formats": self._extract_successful_formats(top_performers)
}
}
def _extract_high_engagement_keywords(self, top_performers: List[Dict[str, Any]]) -> List[str]:
"""Extract keywords that appear in high-performing content."""
# This would analyze the content text for common keywords
# For now, return a placeholder
return ["innovation", "strategy", "growth", "success"]
def _extract_optimal_posting_times(self, top_performers: List[Dict[str, Any]]) -> List[str]:
"""Extract optimal posting times from top performers."""
posting_times = [item.get('posting_time') for item in top_performers if item.get('posting_time')]
return list(set(posting_times))
def _extract_successful_formats(self, top_performers: List[Dict[str, Any]]) -> List[str]:
"""Extract successful content formats from top performers."""
formats = [item.get('format') for item in top_performers if item.get('format')]
return list(set(formats))
def _identify_performance_trends(self, content_performance: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Identify performance trends over time."""
# Sort by date if available
sorted_performance = sorted(content_performance,
key=lambda x: x.get('date', ''),
reverse=True)
if len(sorted_performance) < 2:
return {"trend": "insufficient_data"}
# Calculate trend
recent_performance = sorted_performance[:len(sorted_performance)//2]
older_performance = sorted_performance[len(sorted_performance)//2:]
recent_avg = sum(item.get('engagement_rate', 0) for item in recent_performance) / len(recent_performance)
older_avg = sum(item.get('engagement_rate', 0) for item in older_performance) / len(older_performance)
if recent_avg > older_avg * 1.1:
trend = "improving"
elif recent_avg < older_avg * 0.9:
trend = "declining"
else:
trend = "stable"
return {
"trend": trend,
"recent_average": recent_avg,
"older_average": older_avg,
"change_percentage": ((recent_avg - older_avg) / older_avg * 100) if older_avg > 0 else 0
}
def _identify_successful_patterns(self, content_performance: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Identify patterns in successful content."""
# Filter for high-performing content (top 25%)
sorted_performance = sorted(content_performance,
key=lambda x: x.get('engagement_rate', 0),
reverse=True)
top_quarter = sorted_performance[:max(1, len(sorted_performance) // 4)]
return {
"high_performing_content_count": len(top_quarter),
"common_characteristics": self._analyze_top_performers(top_quarter),
"success_patterns": {
"optimal_length_range": self._calculate_optimal_length_range(top_quarter),
"preferred_content_types": self._get_preferred_content_types(top_quarter),
"successful_topic_categories": self._get_successful_topic_categories(top_quarter)
}
}
def _calculate_optimal_length_range(self, top_performers: List[Dict[str, Any]]) -> Dict[str, int]:
"""Calculate optimal content length range from top performers."""
lengths = [item.get('content_length', 0) for item in top_performers if item.get('content_length')]
if not lengths:
return {"min": 0, "max": 0, "average": 0}
return {
"min": min(lengths),
"max": max(lengths),
"average": sum(lengths) / len(lengths)
}
def _get_preferred_content_types(self, top_performers: List[Dict[str, Any]]) -> List[str]:
"""Get preferred content types from top performers."""
content_types = [item.get('content_type') for item in top_performers if item.get('content_type')]
return list(set(content_types))
def _get_successful_topic_categories(self, top_performers: List[Dict[str, Any]]) -> List[str]:
"""Get successful topic categories from top performers."""
topics = [item.get('topic_category') for item in top_performers if item.get('topic_category')]
return list(set(topics))
def _generate_learning_insights(self, performance_analysis: Dict[str, Any], successful_patterns: Dict[str, Any]) -> Dict[str, Any]:
"""Generate learning insights from performance analysis."""
return {
"performance_insights": {
"average_engagement": performance_analysis.get('average_engagement_rate', 0),
"performance_trend": performance_analysis.get('performance_trends', {}).get('trend', 'unknown'),
"top_performing_characteristics": performance_analysis.get('top_performers', {})
},
"success_patterns": successful_patterns,
"recommendations": {
"content_length_optimization": successful_patterns.get('success_patterns', {}).get('optimal_length_range', {}),
"content_type_preferences": successful_patterns.get('success_patterns', {}).get('preferred_content_types', []),
"topic_focus_areas": successful_patterns.get('success_patterns', {}).get('successful_topic_categories', [])
},
"learning_confidence": self._calculate_learning_confidence(performance_analysis, successful_patterns)
}
def _calculate_learning_confidence(self, performance_analysis: Dict[str, Any], successful_patterns: Dict[str, Any]) -> float:
"""Calculate confidence in learning insights."""
# Base confidence on amount of data
total_content = performance_analysis.get('total_content_analyzed', 0)
high_performers = successful_patterns.get('high_performing_content_count', 0)
# Confidence increases with more data
data_confidence = min(100, (total_content / 20) * 100) # 20 pieces of content = 100% confidence
# Confidence increases with more high performers
pattern_confidence = min(100, (high_performers / 5) * 100) # 5 high performers = 100% confidence
return (data_confidence + pattern_confidence) / 2
def _apply_performance_learning(self, persona_id: int, learning_insights: Dict[str, Any]) -> Dict[str, Any]:
"""Apply performance learning to persona."""
# This would update the persona based on learning insights
# For now, return the insights that would be applied
return {
"applied_insights": learning_insights,
"persona_updates": {
"content_length_preferences": learning_insights.get('recommendations', {}).get('content_length_optimization', {}),
"preferred_content_types": learning_insights.get('recommendations', {}).get('content_type_preferences', []),
"successful_topic_areas": learning_insights.get('recommendations', {}).get('topic_focus_areas', []),
"learning_confidence": learning_insights.get('learning_confidence', 0)
}
}
def _save_quality_metrics(self, session: Session, persona_id: int, quality_metrics: Dict[str, Any], user_feedback: Optional[Dict[str, Any]]):
"""Save quality metrics to database."""
quality_record = PersonaQualityMetrics(
writing_persona_id=persona_id,
style_accuracy=quality_metrics.get('linguistic_quality', 0),
content_quality=quality_metrics.get('overall_quality_score', 0),
engagement_rate=quality_metrics.get('platform_optimization_quality', 0),
consistency_score=quality_metrics.get('consistency_score', 0),
user_satisfaction=quality_metrics.get('user_satisfaction'),
user_feedback=json.dumps(user_feedback) if user_feedback else None,
ai_quality_assessment=json.dumps(quality_metrics),
improvement_suggestions=json.dumps(quality_metrics.get('improvement_suggestions', [])),
assessor_type="ai_automated"
)
session.add(quality_record)
def _save_learning_data(self, session: Session, persona_id: int, feedback_data: Dict[str, Any], improvements: Dict[str, Any]):
"""Save learning data to database."""
learning_record = PersonaLearningData(
writing_persona_id=persona_id,
user_writing_samples=json.dumps(feedback_data.get('writing_samples', [])),
successful_content_examples=json.dumps(feedback_data.get('successful_content', [])),
user_preferences=json.dumps(feedback_data.get('preferences', {})),
style_refinements=json.dumps(improvements.get('style_adjustments', {})),
vocabulary_updates=json.dumps(improvements.get('vocabulary_adjustments', {})),
pattern_adjustments=json.dumps(improvements.get('pattern_adjustments', {})),
learning_type="feedback"
)
session.add(learning_record)
def _save_performance_learning(self, session: Session, persona_id: int, content_performance: List[Dict[str, Any]], learning_insights: Dict[str, Any]):
"""Save performance learning data to database."""
learning_record = PersonaLearningData(
writing_persona_id=persona_id,
user_writing_samples=json.dumps(content_performance),
successful_content_examples=json.dumps(learning_insights.get('success_patterns', {})),
user_preferences=json.dumps(learning_insights.get('recommendations', {})),
style_refinements=json.dumps(learning_insights.get('persona_updates', {})),
learning_type="performance"
)
session.add(learning_record)