What We're Going to Build (And Why It's Pretty Cool)
So, we know how sometimes you wish you could just talk to your computer and have it actually understand what you're asking? Well, that's exactly what we're building today!
Picture this: You record yourself asking "Hey, what's machine learning all about?" and boom - your system transcribes what you said, searches through your documents AND the web, then gives you a smart answer back. Pretty neat, right?
The Magic Behind the Curtain ✨
Here's what our little system does:
- Listens to your voice (using a fancy Whisper model)
- Thinks about what you asked (searches your knowledge base)
- Looks stuff up on the internet (because why not get fresh info?)
- Puts it all together into a nice answer
And the best part? We're making it FAST by using your GPU properly. No more waiting around for 30 seconds while your model thinks!
Before We Dive In - Let's Get Ready! 🛠️
What You'll Need
Don't worry, this isn't going to break the bank:
- A Google Colab account (the free one works fine!)
- About 30 minutes of your time
- A sense of curiosity (and maybe some coffee ☕)
Quick GPU Check
First things first - let's make sure we've got the good stuff:
import torch print(f"🔥 GPU Available: {torch.cuda.is_available()}") if torch.cuda.is_available(): print(f"GPU Device: {torch.cuda.get_device_name(0)}") print(f"GPU Memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB") else: print("⚠️ Uh oh! No GPU detected. Go to Runtime → Change Runtime Type → T4 GPU") If you see something like "Tesla T4" pop up, you're golden! 🎉
Step 1: Installing Our Toolbox 📦
Alright, time to grab all the cool libraries we need. Think of this as gathering ingredients before we start cooking:
# The core ML stuff (this is where the magic happens) !pip install -q transformers>=4.41.0 torch torchaudio --upgrade !pip install -q accelerate bitsandbytes optimum # For grabbing stuff from the web and making pretty interfaces !pip install -q requests beautifulsoup4 gradio # The smart search and audio processing bits !pip install -q sentence-transformers datasets librosa soundfile # Super-fast similarity search (the secret sauce!) !pip install -q faiss-gpu Pro tip: If faiss-gpu gives you trouble, just use faiss-cpu instead. It'll still work great!
Step 2: The Heart of Our System - The VoiceRAGT4 Class 💝
Now here's where things get interesting. We're building a class that's like a Swiss Army knife for voice processing:
class VoiceRAGT4: def __init__(self): self.device = "cuda" if torch.cuda.is_available() else "cpu" print(f"🚀 Using device: {self.device}") # These will hold our AI models self.speech_to_text = None self.embedder = None # Our knowledge base self.documents = [] self.document_embeddings = None self.faiss_index = None # Let's set everything up! self.setup_models() Think of this as setting up your workspace before you start a project. We're just getting everything organized!
Loading Our Models (The Fun Part!) 🤖
Here's where we load up our AI models. I've added some special sauce to make them run super fast on your T4:
def setup_models(self): print("🔧 Loading models with T4 optimizations...") # First, let's try to load your custom Whisper model try: model_id = "AfroLogicInsect/whisper-finetuned-float32" # This is the secret sauce - memory optimization! bnb_config = BitsAndBytesConfig( load_in_8bit=True, # Uses half the memory! bnb_8bit_compute_dtype=torch.float16 ) # Load the model model = AutoModelForSpeechSeq2Seq.from_pretrained( model_id, torch_dtype=torch.float16, # Faster inference low_cpu_mem_usage=True, # Be nice to your RAM use_safetensors=True, # Modern and safe quantization_config=bnb_config ) if self.device == "cuda": model = model.to("cuda") processor = AutoProcessor.from_pretrained(model_id) # Create our speech-to-text pipeline self.speech_to_text = pipeline( "automatic-speech-recognition", model=model, tokenizer=processor.tokenizer, feature_extractor=processor.feature_extractor, max_new_tokens=128, chunk_length_s=30, # Process in 30-second chunks batch_size=8, # Sweet spot for T4 torch_dtype=torch.float16, device=0 if self.device == "cuda" else -1, ) print("✅ Your custom Whisper model loaded successfully!") except Exception as e: print(f"🤔 Hmm, couldn't load your custom model: {e}") print("🔄 No worries! Using the standard Whisper-small instead...") # Fallback option self.speech_to_text = pipeline( "automatic-speech-recognition", model="openai/whisper-small", torch_dtype=torch.float16, device=0 if self.device == "cuda" else -1, chunk_length_s=30, batch_size=8 ) # Now let's load our embedding model (this finds similar documents) print("🧠 Loading sentence transformer...") self.embedder = SentenceTransformer('all-MiniLM-L6-v2', device=self.device) # Make it even faster on GPU if self.device == "cuda": self.embedder.half() print("🎉 All models loaded and ready to rock!") What's happening here? We're loading two main models:
- Whisper - converts your speech to text
- Sentence Transformer - understands the meaning of text (pretty cool, right?)
Step 3: Making Audio Sound Better 🎵
Before we feed audio to our model, let's clean it up a bit:
def preprocess_audio(self, audio_path): """Make audio sound nice for Whisper""" try: # Load audio at 16kHz (Whisper's favorite frequency) audio, sr = librosa.load(audio_path, sr=16000) # Normalize volume levels audio = librosa.util.normalize(audio) # Reduce background noise (simple but effective!) audio = librosa.effects.preemphasis(audio) return audio except Exception as e: print(f"😅 Audio preprocessing hiccup: {e}") return None This is like adjusting the microphone settings to make sure Whisper can hear you clearly!
The Speech-to-Text Magic ✨
Here's where we actually convert your voice to text:
def transcribe_audio(self, audio_path): """Turn your voice into text with GPU power!""" try: # Clean up the audio first audio = self.preprocess_audio(audio_path) if audio is None: return "Oops! Couldn't process that audio file" # Clear some GPU memory (being polite!) if self.device == "cuda": torch.cuda.empty_cache() # The actual transcription (with speed boost!) with torch.cuda.amp.autocast(): # Mixed precision = faster! result = self.speech_to_text( audio, generate_kwargs={ "max_new_tokens": 128, "num_beams": 2, # Good balance of speed vs quality "do_sample": False, "use_cache": True } ) # Clean up after ourselves if self.device == "cuda": torch.cuda.empty_cache() return result["text"].strip() except Exception as e: print(f"😬 Transcription went sideways: {e}") return f"Error: {e}" Step 4: Building Our Knowledge Base 📚
Now for the really cool part - teaching our system about stuff! We'll add documents and create a super-fast search index:
def add_documents_batch(self, documents, batch_size=32): """Add a bunch of documents and make them searchable""" print(f"📚 Processing {len(documents)} documents...") self.documents.extend(documents) # Process in batches to avoid memory issues all_embeddings = [] for i in range(0, len(self.documents), batch_size): batch = self.documents[i:i+batch_size] print(f"🔄 Processing batch {i//batch_size + 1}...") # Convert text to numbers (embeddings) that capture meaning if self.device == "cuda": with torch.cuda.amp.autocast(): batch_embeddings = self.embedder.encode( batch, batch_size=batch_size, show_progress_bar=True, normalize_embeddings=True ) else: batch_embeddings = self.embedder.encode( batch, batch_size=batch_size, show_progress_bar=True, normalize_embeddings=True ) all_embeddings.append(batch_embeddings) # Keep things tidy if self.device == "cuda": torch.cuda.empty_cache() # Combine all the embeddings self.document_embeddings = np.vstack(all_embeddings) # Create a super-fast search index try: dimension = self.document_embeddings.shape[1] if self.device == "cuda": # GPU-powered search! 🚀 res = faiss.StandardGpuResources() self.faiss_index = faiss.GpuIndexFlatIP(res, dimension) print("🚀 Using GPU-accelerated FAISS - this is gonna be fast!") else: self.faiss_index = faiss.IndexFlatIP(dimension) print("🔧 Using CPU FAISS - still pretty quick!") # Add our embeddings to the index self.faiss_index.add(self.document_embeddings.astype(np.float32)) except Exception as e: print(f"🤷♂️ FAISS setup hiccup: {e}") print("📝 No worries, we'll use a backup method!") self.faiss_index = None print(f"✅ Added {len(documents)} documents to the knowledge base!") What's happening here? We're converting all your documents into "embeddings" - these are like fingerprints that capture the meaning of the text. Then we build a super-fast search index so we can find relevant documents in milliseconds!
Step 5: Web Search Integration 🌐
Sometimes we need fresh info from the internet. Let's add that capability:
def web_search(self, query, num_results=5): """Grab some fresh info from the web""" try: print(f"🌐 Searching the web for: {query}") # Using DuckDuckGo's API (it's free and doesn't track you!) url = f"https://api.duckduckgo.com/?q={query}&format=json&no_html=1&skip_disambig=1" response = requests.get(url, timeout=5) data = response.json() results = [] # Get the main abstract if available if data.get('Abstract'): results.append({ 'title': data.get('AbstractSource', 'DuckDuckGo')[:50], 'content': data['Abstract'][:300], # Keep it concise 'url': data.get('AbstractURL', ''), 'relevance': 1.0 }) # Get related topics for topic in data.get('RelatedTopics', [])[:num_results-1]: if isinstance(topic, dict) and topic.get('Text'): results.append({ 'title': (topic.get('FirstURL', '').split('/')[-1] or 'Related')[:50], 'content': topic['Text'][:300], 'url': topic.get('FirstURL', ''), 'relevance': 0.8 }) print(f"📊 Found {len(results)} web results") return results except Exception as e: print(f"🤔 Web search didn't work out: {e}") return [{'title': 'Search Error', 'content': f'Search failed: {e}', 'url': '', 'relevance': 0}] Step 6: Lightning-Fast Document Search ⚡
Here's where the magic really happens - finding relevant documents super quickly:
def retrieve_documents_fast(self, query, k=5): """Find the most relevant documents lightning fast!""" if len(self.documents) == 0: print("📭 No documents in the knowledge base yet!") return [] try: print(f"🔍 Searching for: {query}") # Clear GPU memory if self.device == "cuda": torch.cuda.empty_cache() # Convert the query to an embedding if self.device == "cuda": with torch.cuda.amp.autocast(): query_embedding = self.embedder.encode([query], normalize_embeddings=True) else: query_embedding = self.embedder.encode([query], normalize_embeddings=True) results = [] if self.faiss_index is not None: # Use our super-fast FAISS index! scores, indices = self.faiss_index.search( query_embedding.astype(np.float32), min(k, len(self.documents)) ) for i, score in zip(indices[0], scores[0]): if score > 0.25: # Only keep relevant results results.append({ 'content': self.documents[i], 'score': float(score), 'index': int(i) }) else: # Fallback method (still pretty fast!) from sklearn.metrics.pairwise import cosine_similarity similarities = cosine_similarity(query_embedding, self.document_embeddings)[0] top_indices = np.argsort(similarities)[::-1][:k] for idx in top_indices: score = similarities[idx] if score > 0.25: results.append({ 'content': self.documents[idx], 'score': float(score), 'index': int(idx) }) print(f"📋 Found {len(results)} relevant documents") return results except Exception as e: print(f"😅 Document search hit a snag: {e}") return [] Step 7: Putting It All Together 🎭
Now let's create the main function that orchestrates everything:
def process_voice_query_optimized(self, audio_file): """The main event - process a voice query end-to-end!""" from datetime import datetime start_time = datetime.now() print("🎬 Starting the voice RAG pipeline...") try: # Step 1: Speech to Text print("🎤 Converting speech to text...") stt_start = datetime.now() text_query = self.transcribe_audio(audio_file) stt_time = (datetime.now() - stt_start).total_seconds() print(f"📝 Got: '{text_query}' (took {stt_time:.2f}s)") if text_query.startswith("Error"): return text_query, "", f"Transcription failed in {stt_time:.2f}s" # Step 2: Search for relevant info (doing both at the same time!) print("🔍 Searching knowledge base and web...") search_start = datetime.now() # Find relevant documents retrieved_docs = self.retrieve_documents_fast(text_query, k=5) # Search the web too search_results = self.web_search(text_query, num_results=3) search_time = (datetime.now() - search_start).total_seconds() # Step 3: Generate a nice response print("💭 Crafting the perfect response...") response_start = datetime.now() response = self.generate_response_optimized(text_query, search_results, retrieved_docs) response_time = (datetime.now() - response_start).total_seconds() total_time = (datetime.now() - start_time).total_seconds() # Show off our performance! perf_summary = f"""⚡ Performance Report: • Speech Recognition: {stt_time:.2f}s • Document Search: {search_time:.2f}s • Response Crafting: {response_time:.2f}s • Total Time: {total_time:.2f}s • Documents Found: {len(retrieved_docs)} • Web Results: {len(search_results)} 🎯 That's {60/total_time:.1f} queries per minute!""" return text_query, response, perf_summary except Exception as e: error_time = (datetime.now() - start_time).total_seconds() print(f"💥 Something went wrong: {e}") return f"❌ System Error: {e}", "", f"Failed after {error_time:.2f}s" Creating Beautiful Responses ✨
Let's make our responses look really nice:
def generate_response_optimized(self, query, search_results, retrieved_docs): """Create a beautiful, informative response""" context_parts = [] # Add our knowledge base results first (they're usually more reliable) if retrieved_docs: context_parts.append("📚 From Your Knowledge Base:") # Sort by relevance score retrieved_docs.sort(key=lambda x: x['score'], reverse=True) for doc in retrieved_docs[:3]: # Top 3 context_parts.append(f"• {doc['content'][:200]}... (confidence: {doc['score']:.2f})") # Add web search results if search_results: context_parts.append("\n🌐 Fresh from the Web:") for result in search_results[:3]: if result['content']: context_parts.append(f"• {result['title']}: {result['content'][:150]}...") context = "\n".join(context_parts) if not context.strip(): return "🤷♂️ Hmm, I couldn't find much about that. Try asking something else or check if your knowledge base has relevant info!" # Put together a nice response response = f"""🎯 **You asked**: {query} {context} 💡 **In a nutshell**: The information above covers the key aspects of your question. The knowledge base results are typically most reliable, while web results give you the latest info!""" return response Step 8: Let's See It in Action! 🎮
Time to create our user interface and actually use this thing:
# Initialize our system print("🚀 Starting up the Voice RAG system...") voice_rag = VoiceRAGT4() # Add some sample documents to get started print("📚 Adding some AI knowledge to get started...") ai_docs = [ "Artificial Intelligence (AI) is the simulation of human intelligence processes by machines, especially computer systems.", "Machine Learning is a subset of AI that provides systems the ability to automatically learn and improve from experience without being explicitly programmed.", "Deep Learning is a machine learning technique that teaches computers to learn by example, using neural networks with many layers.", "Natural Language Processing (NLP) helps computers understand, interpret and generate human language in a valuable way.", "Computer Vision enables machines to identify and analyze visual content in images and videos.", "Neural networks are computing systems inspired by biological neural networks that constitute animal brains.", "Large Language Models (LLMs) are AI models trained on vast amounts of text data to understand and generate human language.", "Transformer architecture is the foundation of modern language models, using attention mechanisms to process sequences.", "GPU acceleration significantly speeds up AI model training and inference through parallel processing capabilities.", "Fine-tuning allows pre-trained models to be adapted for specific tasks with smaller, domain-specific datasets." ] voice_rag.add_documents_batch(ai_docs, batch_size=16) print("✅ Knowledge base is ready!") The User Interface 🎨
Now let's create a nice interface with Gradio:
def process_audio_interface(audio): """User-friendly wrapper for our voice processing""" if audio is None: return "Please record or upload an audio file! 🎤", "", "No audio provided" print("🎵 Processing your audio...") result = voice_rag.process_voice_query_optimized(audio) # Keep things tidy voice_rag.clear_gpu_memory() return result # Create the interface interface = gr.Interface( fn=process_audio_interface, inputs=gr.Audio( type="filepath", label="🎤 Record Your Question or Upload Audio", sources=["microphone", "upload"] ), outputs=[ gr.Textbox(label="📝 What You Said", lines=3, max_lines=5), gr.Textbox(label="🤖 AI Response", lines=12, max_lines=20), gr.Textbox(label="⚡ Performance Stats", lines=8, max_lines=10) ], title="🎙️ Voice RAG System - Ask Me Anything!", description=""" **Hey there! 👋** This is your personal voice-powered AI assistant! Just record your voice or upload an audio file, and I'll transcribe what you said, search through the knowledge base, grab fresh info from the web, and give you a comprehensive answer. **Try asking about**: • Artificial Intelligence and Machine Learning • Technology concepts • General knowledge questions • Current events (I'll search the web!) **Pro tip**: Speak clearly and ask specific questions for the best results! 🎯 """, theme=gr.themes.Soft(), allow_flagging="never" ) print("🎉 Interface ready! Click the link to start chatting with your AI!") interface.launch(share=True, debug=True) Want to Test How Fast It Is? 🏃♂️
Let's add a fun benchmark to see how speedy our system really is:
def benchmark_system(): """Let's see how fast this baby can go!""" test_queries = [ "What is machine learning?", "How does deep learning work?", "Explain artificial intelligence to me", "What are neural networks?", "How do transformers work in AI?" ] print("🏁 Starting the speed test!") total_times = [] for i, query in enumerate(test_queries): print(f"\n🧪 Test {i+1}/5: '{query}'") start = datetime.now() # Run our pipeline (without audio since we're just testing speed) retrieved = voice_rag.retrieve_documents_fast(query, k=3) search_results = voice_rag.web_search(query, num_results=3) response = voice_rag.generate_response_optimized(query, search_results, retrieved) elapsed = (datetime.now() - start).total_seconds() total_times.append(elapsed) print(f"⏱️ Done in {elapsed:.2f} seconds!") voice_rag.clear_gpu_memory() # Keep things clean avg_time = np.mean(total_times) print(f"\n📊 Speed Test Results:") print(f"🚀 Average query time: {avg_time:.2f} seconds") print(f"💨 Can handle {60/avg_time:.1f} queries per minute") print(f"🎯 That's pretty darn fast for a full RAG system!") # Run the benchmark! benchmark_system() Making It Your Own 🎨
Adding Your Own Documents
Want to teach your system about specific topics? Here's how:
def add_my_documents(): """Add your own knowledge to the system""" # Replace these with your own content! my_docs = [ "Your company's product information goes here", "Domain-specific knowledge for your field", "FAQ answers for common questions", "Technical documentation snippets", # Add as many as you want! ] # Only add if you've actually added content if "Your company's product information" not in my_docs[0]: voice_rag.add_documents_batch(my_docs, batch_size=16) print(f"🎉 Added {len(my_docs)} of your documents!") else: print("💡 Edit the my_docs list above to add your own content!") # Uncomment this line when you've added your documents # add_my_documents() Loading Documents from Files
Want to load documents from PDFs or text files? Here's a helper:
def load_documents_from_files(file_paths): """Load documents from various file types""" documents = [] for file_path in file_paths: try: if file_path.endswith('.txt'): with open(file_path, 'r', encoding='utf-8') as file: content = file.read() # Split into chunks so they're not too long chunks = [content[i:i+500] for i in range(0, len(content), 400)] documents.extend(chunks) elif file_path.endswith('.pdf'): # You'd need to install PyPDF2: !pip install PyPDF2 import PyPDF2 with open(file_path, 'rb') as file: reader = PyPDF2.PdfReader(file) text = "" for page in reader.pages: text += page.extract_text() chunks = [text[i:i+500] for i in range(0, len(text), 400)] documents.extend(chunks) print(f"📄 Loaded {file_path}") except Exception as e: print(f"😅 Couldn't load {file_path}: {e}") return documents # Example usage: # my_files = ["document1.txt", "manual.pdf", "faq.txt"] # docs = load_documents_from_files(my_files) # voice_rag.add_documents_batch(docs) When Things Don't Go As Planned 🤔
Here are some common hiccups and how to fix them:
"Out of Memory" Errors
# If you get GPU memory errors, try these: # 1. Reduce batch size voice_rag.add_documents_batch(documents, batch_size=8) # Instead of 32 # 2. Clear memory more often torch.cuda.empty_cache() # 3. Use CPU fallback voice_rag.device = "cpu" # Slower but uses less memory Audio Problems
# If audio processing fails: def fix_audio_issues(audio_path): """Sometimes audio files need extra help""" try: # Try loading with different settings audio, sr = librosa.load(audio_path, sr=None) # Convert to 16kHz if needed if sr != 16000: audio = librosa.resample(audio, orig_sr=sr, target_sr=16000) return audio except Exception as e: print(f"🎵 Audio trouble: {e}") return None Model Loading Issues
# If models won't load: def setup_simple_models(self): """Simpler model setup as fallback""" print("🔧 Using simpler model configuration...") # Use basic Whisper without fancy optimizations self.speech_to_text = pipeline( "automatic-speech-recognition", "openai/whisper-tiny", # Smallest, fastest model device=0 if torch.cuda.is_available() else -1 ) # Basic embeddings self.embedder = SentenceTransformer('all-MiniLM-L6-v2') print("✅ Basic setup complete!") What's Next? 🚀
Congratulations! You've built a pretty awesome voice RAG system. Here are some fun ideas to make it even better:
1. Real-time Processing
Make it work with live microphone input so we can just talk to it continuously.
2. Multi-language Support
Add support for different languages by using multilingual Whisper and embedding models.
3. Better Document Processing
Add support for more file types (Word docs, PowerPoints, etc.) and better text chunking.
4. Conversation Memory
Make it remember what you talked about earlier in the conversation.
5. Custom Response Styles
Train it to respond in different styles (formal, casual, technical, etc.).
Wrapping Up 🎁
We've just built something pretty amazing! Your Voice RAG system can:
- ✅ Understand your speech
- ✅ Search through documents lightning-fast
- ✅ Grab fresh info from the web
- ✅ Give you intelligent, contextual answers
- ✅ Do it all really, really fast thanks to GPU optimization
The best part? This is just the beginning. You can customize it, add your own documents, integrate it into other systems, or just have fun asking it questions!
Remember: The more good documents we feed it, the smarter it gets. So start adding content that's relevant to what you want to ask about.
Now go forth and build something awesome! 🎉
P.S. - If you build something cool with this, I'd love to hear about it! And if you run into any weird issues, don't panic - that's just part of the fun of building AI systems. Happy coding!
Bonus Round: Cool Tricks and Advanced Features 🎪
Since you've made it this far, let me share some extra goodies that'll make your Voice RAG system even more impressive!
Memory Trick: Making It Remember Your Conversations 🧠
Want the system to remember what you talked about? Here's a simple way to add conversation memory:
class ConversationalVoiceRAG(VoiceRAGT4): def __init__(self): super().__init__() self.conversation_history = [] # Remember everything! self.max_history = 10 # Don't remember TOO much def process_with_memory(self, audio_file): """Process voice with conversation context""" # Get the current query text_query = self.transcribe_audio(audio_file) # Build context from conversation history context_query = self.build_contextual_query(text_query) # Process normally but with context retrieved_docs = self.retrieve_documents_fast(context_query, k=5) search_results = self.web_search(context_query, num_results=3) response = self.generate_response_optimized(context_query, search_results, retrieved_docs) # Remember this conversation self.conversation_history.append({ 'user': text_query, 'assistant': response, 'timestamp': datetime.now() }) # Don't let memory get too long if len(self.conversation_history) > self.max_history: self.conversation_history.pop(0) return text_query, response def build_contextual_query(self, current_query): """Add conversation context to the query""" if not self.conversation_history: return current_query # Get the last few exchanges for context recent_context = self.conversation_history[-3:] # Last 3 exchanges context_parts = [] for exchange in recent_context: context_parts.append(f"Previously discussed: {exchange['user']}") contextual_query = f""" Current question: {current_query} Conversation context: {chr(10).join(context_parts)} Please answer considering this conversation history. """ return contextual_query Multi-Language Magic 🌍
Want to understand different languages? Here's how to make it multilingual:
def setup_multilingual_models(self): """Support multiple languages like a boss!""" print("🌍 Setting up multilingual support...") # Use multilingual Whisper self.speech_to_text = pipeline( "automatic-speech-recognition", "openai/whisper-large", # Supports 99 languages! torch_dtype=torch.float16, device=0 if torch.cuda.is_available() else -1, return_timestamps=True ) # Multilingual embeddings self.embedder = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2') print("✅ Now I can understand many languages!") def detect_language(self, text): """Figure out what language someone is speaking""" # Simple language detection try: from langdetect import detect language = detect(text) print(f"🗣️ Detected language: {language}") return language except: return "unknown" def respond_in_language(self, response, target_language): """Respond in the same language as the user""" if target_language == "en": return response # You could integrate with translation APIs here print(f"💬 Would translate response to: {target_language}") return response + f"\n\n(Response in {target_language} would go here)" Real-Time Voice Processing 🎙️
Want to make it work with live audio? Here's a simple real-time version:
import pyaudio import threading import queue import time class RealTimeVoiceRAG(VoiceRAGT4): def __init__(self): super().__init__() self.audio_queue = queue.Queue() self.is_listening = False self.audio_buffer = [] def start_listening(self): """Start listening for voice input""" print("🎤 Starting real-time listening...") self.is_listening = True # Start audio capture thread audio_thread = threading.Thread(target=self.capture_audio, daemon=True) audio_thread.start() # Start processing thread process_thread = threading.Thread(target=self.process_audio_stream, daemon=True) process_thread.start() print("👂 I'm listening! Say something...") def capture_audio(self): """Capture audio from microphone""" try: p = pyaudio.PyAudio() stream = p.open( format=pyaudio.paFloat32, channels=1, rate=16000, input=True, frames_per_buffer=1024 ) while self.is_listening: data = stream.read(1024, exception_on_overflow=False) self.audio_queue.put(data) stream.stop_stream() stream.close() p.terminate() except Exception as e: print(f"🎵 Audio capture error: {e}") def process_audio_stream(self): """Process audio in real-time""" while self.is_listening: try: # Collect audio for 3 seconds audio_chunk = [] for _ in range(48): # ~3 seconds at 16kHz if not self.audio_queue.empty(): audio_chunk.append(self.audio_queue.get()) if audio_chunk: # Convert to numpy array audio_data = np.frombuffer(b''.join(audio_chunk), dtype=np.float32) # Simple voice activity detection if np.max(np.abs(audio_data)) > 0.01: # Adjust threshold as needed print("🗣️ Voice detected, processing...") # Process the audio chunk # (You'd save this to a temp file and process it) time.sleep(0.1) # Small delay except Exception as e: print(f"🤔 Processing error: {e}") def stop_listening(self): """Stop real-time processing""" self.is_listening = False print("🛑 Stopped listening") # Usage: # real_time_rag = RealTimeVoiceRAG() # real_time_rag.start_listening() # # Let it run for a while... # real_time_rag.stop_listening() Smart Document Chunking 📄
Here's a smarter way to split your documents that preserves meaning:
def smart_chunk_documents(self, text, chunk_size=500, overlap=50): """Split text intelligently, keeping related sentences together""" import re # Split into sentences first sentences = re.split(r'[.!?]+', text) chunks = [] current_chunk = "" for sentence in sentences: sentence = sentence.strip() if not sentence: continue # If adding this sentence would exceed chunk size if len(current_chunk) + len(sentence) > chunk_size: if current_chunk: chunks.append(current_chunk.strip()) # Start new chunk with overlap words = current_chunk.split() overlap_text = " ".join(words[-overlap:]) if len(words) > overlap else current_chunk current_chunk = overlap_text + " " + sentence else: current_chunk = sentence else: current_chunk += " " + sentence if current_chunk else sentence # Don't forget the last chunk! if current_chunk: chunks.append(current_chunk.strip()) return chunks def load_and_chunk_file(self, file_path): """Load a file and chunk it smartly""" try: with open(file_path, 'r', encoding='utf-8') as file: content = file.read() chunks = self.smart_chunk_documents(content, chunk_size=400, overlap=30) print(f"📄 Split {file_path} into {len(chunks)} smart chunks") return chunks except Exception as e: print(f"😅 Couldn't process {file_path}: {e}") return [] Performance Dashboard 📊
Want to see detailed performance metrics? Here's a cool dashboard:
import matplotlib.pyplot as plt from collections import defaultdict import time class PerformanceTracker: def __init__(self): self.metrics = defaultdict(list) self.query_history = [] def track_query(self, query, transcription_time, search_time, response_time, total_docs, web_results): """Track performance for each query""" total_time = transcription_time + search_time + response_time self.metrics['transcription_times'].append(transcription_time) self.metrics['search_times'].append(search_time) self.metrics['response_times'].append(response_time) self.metrics['total_times'].append(total_time) self.metrics['docs_found'].append(total_docs) self.metrics['web_results'].append(web_results) self.query_history.append({ 'query': query, 'total_time': total_time, 'timestamp': time.time() }) def show_performance_dashboard(self): """Create a cool performance visualization""" if not self.metrics['total_times']: print("📊 No performance data yet!") return fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 8)) # Response time distribution ax1.hist(self.metrics['total_times'], bins=20, alpha=0.7, color='skyblue') ax1.set_title('🚀 Response Time Distribution') ax1.set_xlabel('Time (seconds)') ax1.set_ylabel('Frequency') # Performance over time ax2.plot(self.metrics['total_times'], marker='o', color='orange') ax2.set_title('⏱️ Performance Over Time') ax2.set_xlabel('Query Number') ax2.set_ylabel('Time (seconds)') # Documents found distribution ax3.bar(['Transcription', 'Search', 'Response'], [np.mean(self.metrics['transcription_times']), np.mean(self.metrics['search_times']), np.mean(self.metrics['response_times'])], color=['lightcoral', 'lightgreen', 'lightblue']) ax3.set_title('⚡ Average Time by Component') ax3.set_ylabel('Time (seconds)') # Success rate success_rate = len([t for t in self.metrics['total_times'] if t < 5]) / len(self.metrics['total_times']) * 100 ax4.pie([success_rate, 100-success_rate], labels=['Fast (<5s)', 'Slow (>5s)'], colors=['lightgreen', 'lightcoral'], autopct='%1.1f%%') ax4.set_title('🎯 Speed Success Rate') plt.tight_layout() plt.show() # Print summary stats print(f""" 📈 Performance Summary: • Average total time: {np.mean(self.metrics['total_times']):.2f}s • Fastest query: {min(self.metrics['total_times']):.2f}s • Slowest query: {max(self.metrics['total_times']):.2f}s • Success rate (<5s): {success_rate:.1f}% • Total queries processed: {len(self.metrics['total_times'])} """) # Add to your VoiceRAG class: def __init__(self): # ... existing init code ... self.performance_tracker = PerformanceTracker() # In your process_voice_query_optimized method, add: # self.performance_tracker.track_query(text_query, stt_time, search_time, response_time, len(retrieved_docs), len(search_results)) # Then you can view stats: # voice_rag.performance_tracker.show_performance_dashboard() Adding Personality 🤖
Want to give your AI some personality? Here's how:
def generate_response_with_personality(self, query, search_results, retrieved_docs, personality="helpful"): """Generate responses with different personalities""" personalities = { "helpful": { "greeting": "🤔 Let me help you with that!", "tone": "friendly and informative", "emoji": "✨" }, "enthusiastic": { "greeting": "🎉 Oh, that's a GREAT question!", "tone": "excited and energetic", "emoji": "🚀" }, "scholarly": { "greeting": "📚 An interesting inquiry indeed.", "tone": "academic and thorough", "emoji": "🎓" }, "casual": { "greeting": "👋 Hey! So you want to know about", "tone": "relaxed and conversational", "emoji": "😊" } } style = personalities.get(personality, personalities["helpful"]) # Build context as before... context_parts = [] if retrieved_docs: context_parts.append("📚 From what I know:") for doc in retrieved_docs[:3]: context_parts.append(f"• {doc['content'][:200]}...") if search_results: context_parts.append("\n🌐 Fresh from the web:") for result in search_results[:3]: if result['content']: context_parts.append(f"• {result['content'][:150]}...") context = "\n".join(context_parts) if personality == "enthusiastic": response = f"""{style['greeting']} {query} {context} {style['emoji']} This is SO cool - there's tons of great info about this topic! Hope this helps fuel your curiosity!""" elif personality == "scholarly": response = f"""{style['greeting']} Based on my analysis of the available sources: {context} {style['emoji']} In conclusion, the evidence suggests these are the key considerations regarding your inquiry.""" elif personality == "casual": response = f"""{style['greeting']} {query.lower()}? Here's the deal: {context} {style['emoji']} Hope that clears things up! Let me know if you want me to dig deeper into any part of this.""" else: # helpful (default) response = f"""{style['greeting']} {context} {style['emoji']} I hope this information helps answer your question! Feel free to ask if you need clarification on anything.""" return response # Usage: # response = voice_rag.generate_response_with_personality(query, search_results, retrieved_docs, personality="enthusiastic") Web Interface Upgrade 🌐
Want a fancier web interface? Here's an enhanced Gradio setup:
def create_advanced_interface(): """Create a more sophisticated interface""" with gr.Blocks(title="🎙️ Voice RAG Pro", theme=gr.themes.Soft()) as interface: gr.Markdown("# 🎙️ Voice RAG System Pro") gr.Markdown("Ask me anything using your voice! I'll search my knowledge base and the web to give you comprehensive answers.") with gr.Row(): with gr.Column(scale=2): audio_input = gr.Audio( label="🎤 Your Question", sources=["microphone", "upload"], type="filepath" ) # Settings panel with gr.Accordion("⚙️ Settings", open=False): personality = gr.Dropdown( choices=["helpful", "enthusiastic", "scholarly", "casual"], value="helpful", label="🤖 AI Personality" ) search_web = gr.Checkbox( value=True, label="🌐 Search Web" ) max_docs = gr.Slider( minimum=1, maximum=10, value=5, step=1, label="📚 Max Documents to Retrieve" ) submit_btn = gr.Button("🚀 Process Voice", variant="primary") with gr.Column(scale=3): transcription_output = gr.Textbox( label="📝 What You Said", lines=3, max_lines=5 ) response_output = gr.Textbox( label="🤖 AI Response", lines=15, max_lines=25 ) with gr.Accordion("📊 Performance & Debug", open=False): performance_output = gr.Textbox( label="⚡ Performance Metrics", lines=8 ) # Examples gr.Markdown("### 🎯 Try These Examples:") example_queries = [ "What is machine learning?", "How do neural networks work?", "Explain artificial intelligence", "What's the latest in AI research?" ] gr.Examples( examples=[[q] for q in example_queries], inputs=[audio_input] ) def process_with_settings(audio, personality, search_web, max_docs): """Process audio with custom settings""" if audio is None: return "Please record or upload audio!", "", "" # Your existing processing code here, but with the settings # This is where you'd modify the pipeline based on user preferences result = voice_rag.process_voice_query_optimized(audio) return result submit_btn.click( process_with_settings, inputs=[audio_input, personality, search_web, max_docs], outputs=[transcription_output, response_output, performance_output] ) # Auto-submit when audio is uploaded audio_input.change( process_with_settings, inputs=[audio_input, personality, search_web, max_docs], outputs=[transcription_output, response_output, performance_output] ) return interface # Launch the advanced interface # advanced_interface = create_advanced_interface() # advanced_interface.launch(share=True, debug=True) Final Pro Tips 🎯
Here are some insider secrets to make your system even better:
Batch Everything: Always process multiple items together when possible - it's way more efficient!
Cache Smart: Save frequently used embeddings and search results to avoid recomputing.
Monitor GPU Memory: Keep an eye on
torch.cuda.memory_allocated()- clear cache when it gets too high.Use Async: For web searches, use
asyncioto make multiple requests simultaneously.Quality Over Quantity: Better to have 100 high-quality documents than 1000 poor ones.
Test with Real Users: Your system might work perfectly for us right now but confuse others - test it!
Keep Learning: The AI field moves fast - stay updated with new models and techniques.
Full script available here: https://github.com/AkanimohOD19A/Voice-RAG-v1
The End... Or Is It? 🎬
You've now got a seriously impressive Voice RAG system that can:
- 🎤 Understand speech in multiple languages
- 🧠 Remember conversations
- ⚡ Process queries lightning-fast
- 📊 Track its own performance
- 🤖 Have different personalities
- 🌐 Search the web intelligently
- 📄 Handle complex documents
But here's the thing - this is really just the beginning! Every day, new models come out, new techniques are discovered, and new possibilities emerge.
The system you've built is a solid foundation that we can keep improving and adapting. Maybe next you'll add video understanding, or connect it to a robot, or make it control your smart home. The sky's the limit!
Remember: The best AI systems aren't just technically impressive - they're actually useful and fun to interact with. We have to keep that in mind as we continue building.
Now go forth and create something amazing! And most importantly... have fun with it! 🎉
Happy building, and may your GPU never run out of memory! 🚀
Top comments (0)