| | """ |
| | 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() |
| | |
| | |
| | self.filler_words = [ |
| | 'um', 'uh', 'like', 'you know', 'actually', 'basically', |
| | 'literally', 'sort of', 'kind of', 'i mean', 'so', 'well' |
| | ] |
| | |
| | |
| | self.weak_words = [ |
| | 'maybe', 'perhaps', 'possibly', 'probably', 'might', |
| | 'could', 'should', 'just', 'actually', 'really' |
| | ] |
| | |
| | |
| | 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""" |
| | |
| | |
| | full_user_text = " ".join(user_transcripts) |
| | |
| | |
| | metrics = self.calculate_metrics(full_user_text, user_transcripts) |
| | |
| | |
| | structure_feedback = self.analyze_structure(user_transcripts, full_user_text) |
| | |
| | |
| | delivery_feedback = self.analyze_delivery(user_transcripts, full_user_text, metrics) |
| | |
| | |
| | listening_feedback = self.analyze_listening(conversation_messages) |
| | |
| | |
| | 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) |
| | |
| | |
| | |
| | estimated_minutes = len(transcripts) * 0.5 |
| | wpm = int(total_words / estimated_minutes) if estimated_minutes > 0 else 0 |
| | |
| | |
| | filler_count = sum( |
| | full_text.lower().count(filler) for filler in self.filler_words |
| | ) |
| | |
| | |
| | weak_count = sum( |
| | full_text.lower().count(weak) for weak in self.weak_words |
| | ) |
| | |
| | |
| | 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 |
| | |
| | |
| | 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()}'" |
| | |
| | |
| | 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""" |
| | |
| | |
| | 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()) |
| | |
| | |
| | buzzwords = ['synergy', 'leverage', 'paradigm', 'disrupt', 'ecosystem', 'scalable', 'bandwidth', 'circle back'] |
| | buzzword_count = sum(1 for word in buzzwords if word in full_text.lower()) |
| | |
| | |
| | sentences = re.split(r'[.!?]+', full_text) |
| | sentences = [s.strip().lower() for s in sentences if s.strip()] |
| | |
| | |
| | repetitive_count = 0 |
| | for i, sent1 in enumerate(sentences): |
| | for sent2 in sentences[i+1:]: |
| | |
| | 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"] |
| | |
| | |
| | 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." |
| | |
| | |
| | weak_percentage = metrics["weak_percentage"] |
| | confidence_score = max(0, 100 - (weak_percentage * 10)) |
| | confidence_feedback = self._get_confidence_feedback(weak_percentage) |
| | |
| | |
| | |
| | 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"] |
| | |
| | |
| | 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) |
| | |
| | |
| | questions = sum(1 for msg in user_messages if '?' in msg["content"]) |
| | |
| | |
| | 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 = [] |
| | |
| | |
| | if metrics.get("starter_insight"): |
| | insights.append(f"🔍 {metrics['starter_insight']} - Consider varying your sentence openings for better engagement.") |
| | |
| | |
| | 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.") |
| | |
| | |
| | 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) |
| | |
| | if llm_insights: |
| | insights.append(f"🤖 AI Analysis:\n{llm_insights}") |
| | except Exception as e: |
| | print(f"Error generating AI insights: {e}") |
| | |
| | return insights |
| | |
| | |
| | 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...'" |
| |
|
| |
|