| """ |
| 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...'" |
|
|
|
|