Yukti / feedback_analyzer.py
Revrse's picture
Upload 14 files
91c73ed verified
"""
Comprehensive feedback analysis system
Analyzes structure, delivery, and listening skills
"""
import re
from typing import List, Dict, Tuple
from collections import Counter
import numpy as np
from models import ModelManager
class FeedbackAnalyzer:
def __init__(self):
self.model_manager = ModelManager()
# Filler words to track
self.filler_words = [
'um', 'uh', 'like', 'you know', 'actually', 'basically',
'literally', 'sort of', 'kind of', 'i mean', 'so', 'well'
]
# Weak words to track
self.weak_words = [
'maybe', 'perhaps', 'possibly', 'probably', 'might',
'could', 'should', 'just', 'actually', 'really'
]
# Common sentence starters
self.sentence_starters = [
'and', 'but', 'so', 'well', 'i', 'we', 'the', 'this', 'that'
]
def analyze_performance(
self,
user_transcripts: List[str],
conversation_messages: List[Dict],
scenario: str,
bot_name: str
) -> Dict:
"""Comprehensive performance analysis"""
# Combine all user text
full_user_text = " ".join(user_transcripts)
# Calculate basic metrics
metrics = self.calculate_metrics(full_user_text, user_transcripts)
# Analyze structure and clarity
structure_feedback = self.analyze_structure(user_transcripts, full_user_text)
# Analyze delivery and presence
delivery_feedback = self.analyze_delivery(user_transcripts, full_user_text, metrics)
# Analyze listening and interaction
listening_feedback = self.analyze_listening(conversation_messages)
# Generate AI insights using LLM
ai_insights = self.generate_ai_insights(
user_transcripts,
conversation_messages,
metrics,
scenario
)
return {
"metrics": metrics,
"structure_clarity": structure_feedback,
"delivery_presence": delivery_feedback,
"listening_interaction": listening_feedback,
"ai_insights": ai_insights
}
def calculate_metrics(self, full_text: str, transcripts: List[str]) -> Dict:
"""Calculate quantitative metrics"""
words = full_text.lower().split()
total_words = len(words)
# Calculate WPM (assuming average speech)
# Rough estimate: divide by number of speaking turns and multiply by average turn time
estimated_minutes = len(transcripts) * 0.5 # Assume ~30 seconds per turn
wpm = int(total_words / estimated_minutes) if estimated_minutes > 0 else 0
# Count filler words
filler_count = sum(
full_text.lower().count(filler) for filler in self.filler_words
)
# Count weak words
weak_count = sum(
full_text.lower().count(weak) for weak in self.weak_words
)
# Analyze sentence starters
sentences = re.split(r'[.!?]+', full_text)
sentences = [s.strip() for s in sentences if s.strip()]
starter_counts = Counter()
for sentence in sentences:
first_word = sentence.lower().split()[0] if sentence.split() else ""
if first_word in self.sentence_starters:
starter_counts[first_word] += 1
# Find most common starter
most_common_starter = starter_counts.most_common(1)
starter_insight = None
if most_common_starter and len(sentences) > 0:
starter, count = most_common_starter[0]
percentage = int((count / len(sentences)) * 100)
if percentage > 20:
starter_insight = f"{percentage}% of your sentences started with '{starter.upper()}'"
# Check for repetition
word_counts = Counter(words)
repeated_words = {
word: count for word, count in word_counts.items()
if count > 3 and len(word) > 4 and word not in self.filler_words
}
return {
"wpm": wpm,
"total_words": total_words,
"filler_words": filler_count,
"weak_words": weak_count,
"filler_percentage": round((filler_count / total_words) * 100, 1) if total_words > 0 else 0,
"weak_percentage": round((weak_count / total_words) * 100, 1) if total_words > 0 else 0,
"sentence_count": len(sentences),
"avg_sentence_length": round(total_words / len(sentences), 1) if len(sentences) > 0 else 0,
"starter_insight": starter_insight,
"repeated_words": repeated_words
}
def analyze_structure(self, transcripts: List[str], full_text: str) -> Dict:
"""Analyze logical flow, simplicity, and conciseness"""
# Check for logical flow
transition_words = ['however', 'therefore', 'because', 'furthermore', 'additionally', 'first', 'second', 'finally']
transitions_used = sum(1 for word in transition_words if word in full_text.lower())
# Check for buzzwords/jargon
buzzwords = ['synergy', 'leverage', 'paradigm', 'disrupt', 'ecosystem', 'scalable', 'bandwidth', 'circle back']
buzzword_count = sum(1 for word in buzzwords if word in full_text.lower())
# Analyze conciseness (look for repetition)
sentences = re.split(r'[.!?]+', full_text)
sentences = [s.strip().lower() for s in sentences if s.strip()]
# Find similar sentences (repetition)
repetitive_count = 0
for i, sent1 in enumerate(sentences):
for sent2 in sentences[i+1:]:
# Simple similarity check
words1 = set(sent1.split())
words2 = set(sent2.split())
if len(words1) > 3 and len(words2) > 3:
overlap = len(words1.intersection(words2)) / len(words1.union(words2))
if overlap > 0.6:
repetitive_count += 1
return {
"logical_flow": {
"score": min(100, transitions_used * 20),
"transitions_used": transitions_used,
"feedback": self._get_flow_feedback(transitions_used)
},
"simplicity": {
"score": max(0, 100 - (buzzword_count * 20)),
"buzzwords_found": buzzword_count,
"feedback": self._get_simplicity_feedback(buzzword_count)
},
"conciseness": {
"score": max(0, 100 - (repetitive_count * 15)),
"repetitive_statements": repetitive_count,
"feedback": self._get_conciseness_feedback(repetitive_count)
}
}
def analyze_delivery(self, transcripts: List[str], full_text: str, metrics: Dict) -> Dict:
"""Analyze pace, tone, energy, and confidence"""
wpm = metrics["wpm"]
# Analyze pace
pace_score = 100
pace_feedback = "Your pace was well-balanced."
if wpm < 100:
pace_score = 60
pace_feedback = "You spoke quite slowly. Consider increasing your pace to maintain engagement."
elif wpm > 180:
pace_score = 65
pace_feedback = "You spoke very quickly. Slow down to ensure clarity and allow for pauses."
elif wpm > 160:
pace_score = 80
pace_feedback = "Your pace was a bit fast. Consider slowing down slightly for better impact."
# Analyze confidence (based on weak words and hedging)
weak_percentage = metrics["weak_percentage"]
confidence_score = max(0, 100 - (weak_percentage * 10))
confidence_feedback = self._get_confidence_feedback(weak_percentage)
# Analyze energy (this is a placeholder - would need actual audio analysis)
# For now, we'll base it on exclamation marks and caps
energy_indicators = full_text.count('!') + sum(1 for word in full_text.split() if word.isupper() and len(word) > 1)
energy_score = min(100, 50 + (energy_indicators * 10))
return {
"pace": {
"score": pace_score,
"wpm": wpm,
"feedback": pace_feedback
},
"confidence": {
"score": confidence_score,
"weak_word_percentage": weak_percentage,
"feedback": confidence_feedback
},
"energy": {
"score": energy_score,
"feedback": "Energy levels are inferred from text. For accurate assessment, vocal tone analysis would be ideal."
}
}
def analyze_listening(self, conversation_messages: List[Dict]) -> Dict:
"""Analyze listening and interaction skills"""
user_messages = [msg for msg in conversation_messages if msg["role"] == "user"]
bot_messages = [msg for msg in conversation_messages if msg["role"] == "assistant"]
# Check for acknowledgment phrases
acknowledgment_phrases = ['i understand', 'i see', 'that makes sense', 'good point', 'fair enough', 'i hear you']
acknowledgments = 0
for msg in user_messages:
content_lower = msg["content"].lower()
acknowledgments += sum(1 for phrase in acknowledgment_phrases if phrase in content_lower)
# Check for questions (active listening)
questions = sum(1 for msg in user_messages if '?' in msg["content"])
# Check for reflection (paraphrasing)
reflection_indicators = ['so what you\'re saying', 'if i understand correctly', 'let me make sure', 'so you mean']
reflections = 0
for msg in user_messages:
content_lower = msg["content"].lower()
reflections += sum(1 for phrase in reflection_indicators if phrase in content_lower)
total_user_turns = len(user_messages)
return {
"active_listening": {
"score": min(100, questions * 25),
"questions_asked": questions,
"feedback": self._get_listening_feedback(questions, total_user_turns)
},
"acknowledgment": {
"score": min(100, acknowledgments * 30),
"acknowledgments_used": acknowledgments,
"feedback": self._get_acknowledgment_feedback(acknowledgments)
},
"reflection": {
"score": min(100, reflections * 40),
"reflections_used": reflections,
"feedback": self._get_reflection_feedback(reflections)
}
}
def generate_ai_insights(
self,
user_transcripts: List[str],
conversation_messages: List[Dict],
metrics: Dict,
scenario: str
) -> List[str]:
"""Generate unique AI-powered insights using LLM"""
insights = []
# Add unique metric insights
if metrics.get("starter_insight"):
insights.append(f"🔍 {metrics['starter_insight']} - Consider varying your sentence openings for better engagement.")
# Add repetition insights
if metrics.get("repeated_words"):
top_repeated = list(metrics["repeated_words"].items())[:2]
for word, count in top_repeated:
insights.append(f"🔄 You said '{word}' {count} times - consider using synonyms to enrich your vocabulary.")
# Generate LLM-based insight
conversation_text = "\n".join([
f"{'You' if msg['role'] == 'user' else 'Bot'}: {msg['content']}"
for msg in conversation_messages[-10:]
])
prompt = f"""Analyze this sales conversation for the scenario: {scenario}
Conversation:
{conversation_text}
Metrics:
- Words per minute: {metrics['wpm']}
- Filler words: {metrics['filler_words']} ({metrics['filler_percentage']}%)
- Weak words: {metrics['weak_words']} ({metrics['weak_percentage']}%)
Provide 2-3 unique, specific insights that the user wouldn't immediately notice about their communication style. Focus on patterns, missed opportunities, or subtle improvements. Be constructive and specific.
Format: Return only the insights as a numbered list."""
try:
llm_insights = self.model_manager.generate_feedback(prompt)
# Parse and add LLM insights
if llm_insights:
insights.append(f"🤖 AI Analysis:\n{llm_insights}")
except Exception as e:
print(f"Error generating AI insights: {e}")
return insights
# Helper methods for feedback generation
def _get_flow_feedback(self, transitions: int) -> str:
if transitions >= 5:
return "Excellent use of transitions to connect ideas logically."
elif transitions >= 3:
return "Good flow, but more transition words could strengthen logical connections."
elif transitions >= 1:
return "Your ideas jumped between topics. Use more transitions like 'therefore,' 'however,' or 'because.'"
else:
return "Ideas lacked clear connections. Try grouping by theme and using transition words."
def _get_simplicity_feedback(self, buzzwords: int) -> str:
if buzzwords == 0:
return "Great! You kept language simple and accessible."
elif buzzwords <= 2:
return "Mostly clear, but watch for buzzwords that can dilute your message."
else:
return f"You used {buzzwords} buzzwords before explaining core ideas. Simpler phrasing boosts credibility."
def _get_conciseness_feedback(self, repetitions: int) -> str:
if repetitions == 0:
return "Excellent! You stayed concise without unnecessary repetition."
elif repetitions <= 2:
return "Mostly concise, with minor repetition. Tighten up key points."
else:
return f"You repeated similar ideas {repetitions} times, diluting message power. State it once, strongly."
def _get_confidence_feedback(self, weak_percentage: float) -> str:
if weak_percentage <= 2:
return "Strong, confident language throughout. Keep it up!"
elif weak_percentage <= 5:
return "Mostly confident, but watch hedging words like 'maybe' or 'possibly.'"
else:
return f"{weak_percentage}% of your words were hedging language. Use more definitive statements to project confidence."
def _get_listening_feedback(self, questions: int, total_turns: int) -> str:
if questions >= total_turns * 0.3:
return "Excellent questioning! You showed strong curiosity and active listening."
elif questions >= 2:
return "Good engagement, but ask more questions to demonstrate active listening."
elif questions >= 1:
return "You asked minimal questions. Active listening requires more curiosity about the other person."
else:
return "You didn't ask any questions. Great salespeople listen more than they talk."
def _get_acknowledgment_feedback(self, acknowledgments: int) -> str:
if acknowledgments >= 3:
return "Great acknowledgment of the other person's points. This builds rapport."
elif acknowledgments >= 1:
return "Some acknowledgment shown, but use more affirmations like 'I see' or 'That makes sense.'"
else:
return "No acknowledgment detected. Validate the other person's points before responding."
def _get_reflection_feedback(self, reflections: int) -> str:
if reflections >= 2:
return "Excellent! You paraphrased and reflected back what you heard."
elif reflections >= 1:
return "Good start on reflection. Try paraphrasing more: 'So what you're saying is...'"
else:
return "No reflection detected. Mirror back key points to show understanding: 'If I understand correctly...'"