<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Khushi</title>
    <description>The latest articles on DEV Community by Khushi (@khushipandya).</description>
    <link>https://dev.to/khushipandya</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1391393%2F2703f6dd-bc67-43e8-bd42-f77cf2f83d94.png</url>
      <title>DEV Community: Khushi</title>
      <link>https://dev.to/khushipandya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/khushipandya"/>
    <language>en</language>
    <item>
      <title>Integrating Typesense for Sub-Millisecond Search — Lessons from a Job Platform</title>
      <dc:creator>Khushi</dc:creator>
      <pubDate>Sun, 21 Jun 2026 13:55:38 +0000</pubDate>
      <link>https://dev.to/khushipandya/integrating-typesense-for-sub-millisecond-search-lessons-from-a-job-platform-24lh</link>
      <guid>https://dev.to/khushipandya/integrating-typesense-for-sub-millisecond-search-lessons-from-a-job-platform-24lh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Search isn't "find the matching rows." It's "find the best result, even if the input isn't perfect."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What is Typesense?
&lt;/h2&gt;

&lt;p&gt;Typesense is an open-source search engine — a lighter, simpler alternative to Elasticsearch. It's built for one thing: fast, typo-tolerant, faceted search.&lt;/p&gt;

&lt;p&gt;Think of any e-commerce search bar that shows results instantly as you type, even with a typo. That's the experience Typesense is designed for.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;collection&lt;/strong&gt; works like a table. Each record inside it is a JSON &lt;strong&gt;document&lt;/strong&gt;. You define a schema (which fields are searchable, sortable, etc.), Typesense indexes everything, and queries return in milliseconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;Collection:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;books&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Document:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Atomic Habits"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"James Clear"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"year"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"atomik"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;still&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;returns:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Atomic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Habits&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That typo tolerance, plus &lt;strong&gt;filtering&lt;/strong&gt; (narrow by exact criteria), &lt;strong&gt;faceting&lt;/strong&gt; (show counts like "Fiction (120)" next to filters), and &lt;strong&gt;sorting&lt;/strong&gt; — that's the whole toolkit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting It to Work on a Job Platform
&lt;/h2&gt;

&lt;p&gt;I used this on a real job platform — &lt;strong&gt;6,000 jobs, 5,000 cities, 1 lakh+ candidate profiles.&lt;/strong&gt; Employers search candidates by skills, experience, education, even resume content. Job seekers search jobs by keywords, salary, location.&lt;/p&gt;

&lt;p&gt;The backend runs &lt;strong&gt;Frappe + MariaDB&lt;/strong&gt;. Typesense lives on a separate server, connected purely over its REST API (API key + host). Collections mirror the database tables — &lt;code&gt;jobs&lt;/code&gt;, &lt;code&gt;candidates&lt;/code&gt;, &lt;code&gt;departments&lt;/code&gt; — each row becoming a JSON document.&lt;/p&gt;

&lt;p&gt;Typo tolerance is set to 3 characters (so "Bangalor" still finds "Bangalore"), combined with filtering, faceting, and sorting for the full search experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Nobody Tells You
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Typesense gives "good enough," not "exact."&lt;/strong&gt; A recruiter searching for &lt;em&gt;exactly&lt;/em&gt; 5 years experience might still see 4.5 or 5.5 years ranked close by, thanks to fuzzy relevance scoring. Great as a fallback — but precision-critical searches still need a path back to the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema changes aren't free.&lt;/strong&gt; Adding a new field isn't like an SQL &lt;code&gt;ALTER TABLE&lt;/code&gt;. It needs explicit API calls and sometimes re-indexing existing data. Plan your schema upfront.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One collection per table, or one unified collection?&lt;/strong&gt; Mirroring the database was the easy starting point. But a single denormalized collection — where one document holds job + department + location together — could make cross-entity search ("Senior Backend Developer, Engineering, Ahmedabad") a single query instead of stitched-together results. Still evaluating the trade-off, since denormalizing means more update complexity when source data changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-time sync is the actual hard part.&lt;/strong&gt; Every MariaDB write needs to reflect in Typesense, or search goes stale. This is less a Typesense problem and more a "what changed, and where does it need to propagate" coding problem — and it gets more tangled as more entities get linked together.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;The performance gain is real — sub-millisecond, typo-tolerant, faceted search across 1 lakh+ profiles isn't something plain SQL handles gracefully at this scale. Typesense does the heavy lifting. The actual engineering work is in the data flow around it: schema design, sync logic, and knowing when to fall back to the database for precision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resources:&lt;/strong&gt; &lt;a href="https://typesense.org/docs/" rel="noopener noreferrer"&gt;Typesense Docs&lt;/a&gt; · &lt;a href="https://typesense.org/docs/30.2/api/" rel="noopener noreferrer"&gt;API Reference&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Khushi Pandya, a software engineer working on AI-driven backends and search infrastructure. Find me on &lt;a href="https://dev.to/khushipandya"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://github.com/khushi-79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; | &lt;a href="https://www.kaggle.com/khushihpandya" rel="noopener noreferrer"&gt;Kaggle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>searchengine</category>
      <category>typesense</category>
      <category>searchplatform</category>
    </item>
    <item>
      <title>I Trained 7 ML Models on Gujarati Sign Language — Here's What Actually Worked</title>
      <dc:creator>Khushi</dc:creator>
      <pubDate>Sat, 13 Jun 2026 14:10:04 +0000</pubDate>
      <link>https://dev.to/khushipandya/i-trained-7-ml-models-on-gujarati-sign-language-heres-what-actually-worked-o4g</link>
      <guid>https://dev.to/khushipandya/i-trained-7-ml-models-on-gujarati-sign-language-heres-what-actually-worked-o4g</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This research was published at IEEE MPSec ICETA 2025. Here's the full story behind it.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Gujarati Sign Language?
&lt;/h2&gt;

&lt;p&gt;Most sign language research focuses on American Sign Language (ASL) or Indian Sign Language (ISL). Gujarati sign language — used by the deaf and hard of hearing community across Gujarat — has almost no published AI research.&lt;/p&gt;

&lt;p&gt;That felt like a problem worth solving.&lt;/p&gt;

&lt;p&gt;The goal was straightforward: build a system that could recognize 34 Gujarati consonant letters from hand gesture images in real time. But before writing a single line of model code, there was a more fundamental problem to solve — the dataset didn't exist.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the Dataset from Scratch
&lt;/h2&gt;

&lt;p&gt;There were no publicly available Gujarati sign language image datasets. So I built one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How the dataset was collected:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;34 folders — one for each Gujarati consonant (ક, ખ, ગ... all 34)&lt;/li&gt;
&lt;li&gt;110 images per consonant, captured manually&lt;/li&gt;
&lt;li&gt;Multiple individuals, varied backgrounds, different lighting conditions&lt;/li&gt;
&lt;li&gt;Multiple hand orientations and rotation angles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This diversity was intentional. A model trained only on images from one person in one lighting condition fails the moment someone else uses it. Real-world robustness had to be baked in from the start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data augmentation&lt;/strong&gt; was applied to further expand the dataset — random rotations, flips, scaling, and noise addition — bringing the final count to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Training set: &lt;strong&gt;3,798 images&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Testing set: &lt;strong&gt;749 images&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Preprocessing Pipeline
&lt;/h2&gt;

&lt;p&gt;Raw images can't go directly into a model. Several preprocessing steps were applied:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Noise reduction&lt;/strong&gt; using Gaussian blur and median filtering — phone cameras in varying lighting produce a lot of visual noise that confuses models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hand segmentation&lt;/strong&gt; using contour detection and skin-tone masking — isolating just the hand region and removing background clutter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Normalization&lt;/strong&gt; — all pixel values scaled to [0, 1] and all images resized to 256×256 pixels for consistency across models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PCA (Principal Component Analysis)&lt;/strong&gt; — for traditional ML models (SVM, LR, Random Forest, ANN), image data is extremely high-dimensional. PCA reduced this dimensionality while retaining 95% of the data's variance. This made training faster and reduced overfitting risk significantly.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 7 Models — What Each One Does and Why It Was Included
&lt;/h2&gt;

&lt;p&gt;Rather than jumping straight to deep learning, I wanted to understand how traditional ML compares to modern architectures on this specific problem. So I tested all of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Support Vector Machine (SVM) with PCA
&lt;/h3&gt;

&lt;p&gt;SVM finds the optimal boundary (hyperplane) that separates different classes with the maximum margin. For image data, the RBF (Radial Basis Function) kernel was used to capture non-linear relationships between classes. Hyperparameter tuning via grid search optimized the regularization and kernel parameters.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Logistic Regression (LR) with PCA
&lt;/h3&gt;

&lt;p&gt;The simplest model in the comparison — a probabilistic linear classifier. L2 regularization was applied to prevent overfitting. Included as the baseline: if even logistic regression performs well, the problem might not need deep learning at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Random Forest
&lt;/h3&gt;

&lt;p&gt;An ensemble method that trains multiple decision trees on random data subsets and aggregates their votes. 50–200 trees were tested, with max depth limited to 10 to prevent overfitting. Feature importance analysis was performed post-training to understand which image features mattered most.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Artificial Neural Network (ANN)
&lt;/h3&gt;

&lt;p&gt;A feedforward network with two hidden layers (128 and 64 neurons, ReLU activation) trained on PCA-reduced features. Adam optimizer, 50 epochs, batch size 32, dropout regularization at 0.3 to prevent overfitting.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Convolutional Neural Network (CNN)
&lt;/h3&gt;

&lt;p&gt;Custom CNN built from scratch — three convolutional layers with 3×3 filters, max-pooling, and fully connected layers with softmax output. Unlike the models above, CNN works directly on raw image pixels and learns spatial features automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. ResNet50
&lt;/h3&gt;

&lt;p&gt;A 50-layer deep network with residual (skip) connections that solve the vanishing gradient problem in very deep networks. Transfer learning was used — pretrained on ImageNet, with top layers replaced for 34-class Gujarati classification. Lower layers were frozen initially, then gradually unfrozen during fine-tuning.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. VGG19
&lt;/h3&gt;

&lt;p&gt;A 19-layer network known for its uniform architecture of sequential 3×3 convolutional layers. The fully connected layers were replaced with a Global Average Pooling (GAP) layer to reduce parameters and improve generalization. L2 regularization (weight decay 0.0001) was applied throughout.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Results — What Actually Happened
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Training Accuracy&lt;/th&gt;
&lt;th&gt;Testing Accuracy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SVM + PCA&lt;/td&gt;
&lt;td&gt;93.60%&lt;/td&gt;
&lt;td&gt;87.97%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logistic Regression + PCA&lt;/td&gt;
&lt;td&gt;92.37%&lt;/td&gt;
&lt;td&gt;82.38%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random Forest&lt;/td&gt;
&lt;td&gt;88.44%&lt;/td&gt;
&lt;td&gt;88.44%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ANN&lt;/td&gt;
&lt;td&gt;90.48%&lt;/td&gt;
&lt;td&gt;79.77%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VGG19&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;94.28%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;87.23%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CNN&lt;/td&gt;
&lt;td&gt;73.53%&lt;/td&gt;
&lt;td&gt;64.21%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ResNet50&lt;/td&gt;
&lt;td&gt;54.29%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Breaking Down the Surprises
&lt;/h2&gt;

&lt;h3&gt;
  
  
  VGG19 won — but not by as much as expected
&lt;/h3&gt;

&lt;p&gt;VGG19 achieved the highest training accuracy (94.28%) and strong testing accuracy (87.23%). Its deep, uniform architecture excels at extracting hierarchical image features — edges → shapes → gesture patterns — which is exactly what hand gesture recognition needs.&lt;/p&gt;

&lt;p&gt;But the margin over SVM was surprisingly small.&lt;/p&gt;

&lt;h3&gt;
  
  
  SVM was the real surprise
&lt;/h3&gt;

&lt;p&gt;SVM with PCA achieved 87.97% testing accuracy — just 0.74% below VGG19, while being dramatically simpler to train and computationally cheaper. For a traditional ML algorithm working on hand gesture images, this is impressive.&lt;/p&gt;

&lt;p&gt;Why did it work so well? PCA removed noise from the high-dimensional image data, giving SVM a clean, reduced feature space where it could find clear decision boundaries. The RBF kernel then handled the non-linear separability of different hand shapes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: don't underestimate classical ML when preprocessing is done well.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Random Forest was the most consistent
&lt;/h3&gt;

&lt;p&gt;Random Forest showed identical training and testing accuracy (88.44%) — a sign of excellent generalization. No overfitting at all. For a production system where stability matters more than peak accuracy, Random Forest is a strong choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  CNN underperformed — and here's why
&lt;/h3&gt;

&lt;p&gt;The custom CNN from scratch achieved only 64.21% testing accuracy, the lowest of all models. This is counterintuitive — CNNs are supposed to be great at images.&lt;/p&gt;

&lt;p&gt;The reason: CNNs need large datasets to learn meaningful features. With only 3,798 training images across 34 classes (about 112 images per class), the CNN simply didn't have enough data to learn properly. Compare this to VGG19, which came pretrained on millions of ImageNet images and only needed to fine-tune the top layers for our specific gestures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: for small datasets, transfer learning always beats training from scratch.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ResNet50 failed completely
&lt;/h3&gt;

&lt;p&gt;54.29% training accuracy with testing accuracy not even reportable. Transfer learning didn't help here — the pre-trained ImageNet weights didn't transfer well to this specific dataset, and the gradual unfreezing strategy wasn't enough to compensate.&lt;/p&gt;

&lt;p&gt;This suggests ResNet50 needs either a much larger dataset or significantly different fine-tuning strategy for this task.&lt;/p&gt;

&lt;h3&gt;
  
  
  ANN gap between training and testing
&lt;/h3&gt;

&lt;p&gt;ANN achieved 90.48% training but only 79.77% testing — the largest gap in the comparison. The 10%+ drop indicates overfitting despite dropout regularization. The model learned the training data well but didn't generalize as effectively as simpler models like SVM or Random Forest.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Preprocessing is more important than model choice.&lt;/strong&gt; The PCA step transformed SVM from a mediocre image classifier into a near-VGG19 performer. Time spent on preprocessing returned more value than time spent on model architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Dataset size determines which models you can use.&lt;/strong&gt; With a small dataset, transfer learning (VGG19) beats training from scratch (CNN, ResNet50). Always start with pretrained models when data is limited.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Consistency beats peak performance.&lt;/strong&gt; Random Forest never overfit, while ANN showed a 10% train-test gap. In production, consistent performance is more valuable than occasional high accuracy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Test everything.&lt;/strong&gt; I went in assuming deep learning would dominate. The results showed classical ML (SVM) was nearly as good with 1/100th the complexity. That would have been missed if I'd only tested neural networks.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real-World Application
&lt;/h2&gt;

&lt;p&gt;Beyond the model comparison, the system was integrated into a web application that converts recognized gestures into spoken output using Text-to-Speech (TTS). A user signs a Gujarati consonant in front of a camera — the model recognizes it — and the system speaks the corresponding letter aloud.&lt;/p&gt;

&lt;p&gt;The goal is to help bridge communication between Gujarati sign language users and those who don't know sign language, making everyday interactions in education, healthcare, and public services more accessible.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The current system handles 34 consonants. The natural next step is extending it to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gujarati vowels&lt;/li&gt;
&lt;li&gt;Common words and phrases&lt;/li&gt;
&lt;li&gt;Dynamic gestures (involving motion, not just static hand positions)&lt;/li&gt;
&lt;li&gt;Real-time mobile deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Temporal models like LSTMs or Vision Transformers could handle the dynamic gesture challenge — that's the direction future work is heading.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Paper
&lt;/h2&gt;

&lt;p&gt;This research was published at &lt;strong&gt;IEEE MPSec ICETA 2025&lt;/strong&gt; (International Conference on Emerging Technologies and Applications), Gwalior, India.&lt;/p&gt;

&lt;p&gt;DOI: &lt;a href="https://ieeexplore.ieee.org/document/11118850" rel="noopener noreferrer"&gt;10.1109/MPSecICETA64837.2025.11118850&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kaggle notebooks with the model implementations: &lt;a href="https://www.kaggle.com/khushihpandya" rel="noopener noreferrer"&gt;kaggle.com/khushihpandya&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Khushi Pandya, a software engineer working on AI/ML systems and backend development. Find me on &lt;a href="https://dev.to/khushipandya"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://github.com/khushi-79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; | &lt;a href="https://www.kaggle.com/khushihpandya" rel="noopener noreferrer"&gt;Kaggle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>transferleraning</category>
      <category>modeltraining</category>
      <category>machinelearning</category>
      <category>gujaratisignlanguage</category>
    </item>
    <item>
      <title>I Trained 7 ML Models on Gujarati Sign Language — Here's What Actually Worked</title>
      <dc:creator>Khushi</dc:creator>
      <pubDate>Sat, 13 Jun 2026 14:10:04 +0000</pubDate>
      <link>https://dev.to/khushipandya/i-trained-7-ml-models-on-gujarati-sign-language-heres-what-actually-worked-12l5</link>
      <guid>https://dev.to/khushipandya/i-trained-7-ml-models-on-gujarati-sign-language-heres-what-actually-worked-12l5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This research was published at IEEE MPSec ICETA 2025. Here's the full story behind it.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Gujarati Sign Language?
&lt;/h2&gt;

&lt;p&gt;Most sign language research focuses on American Sign Language (ASL) or Indian Sign Language (ISL). Gujarati sign language — used by the deaf and hard of hearing community across Gujarat — has almost no published AI research.&lt;/p&gt;

&lt;p&gt;That felt like a problem worth solving.&lt;/p&gt;

&lt;p&gt;The goal was straightforward: build a system that could recognize 34 Gujarati consonant letters from hand gesture images in real time. But before writing a single line of model code, there was a more fundamental problem to solve — the dataset didn't exist.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the Dataset from Scratch
&lt;/h2&gt;

&lt;p&gt;There were no publicly available Gujarati sign language image datasets. So I built one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How the dataset was collected:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;34 folders — one for each Gujarati consonant (ક, ખ, ગ... all 34)&lt;/li&gt;
&lt;li&gt;110 images per consonant, captured manually&lt;/li&gt;
&lt;li&gt;Multiple individuals, varied backgrounds, different lighting conditions&lt;/li&gt;
&lt;li&gt;Multiple hand orientations and rotation angles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This diversity was intentional. A model trained only on images from one person in one lighting condition fails the moment someone else uses it. Real-world robustness had to be baked in from the start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data augmentation&lt;/strong&gt; was applied to further expand the dataset — random rotations, flips, scaling, and noise addition — bringing the final count to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Training set: &lt;strong&gt;3,798 images&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Testing set: &lt;strong&gt;749 images&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Preprocessing Pipeline
&lt;/h2&gt;

&lt;p&gt;Raw images can't go directly into a model. Several preprocessing steps were applied:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Noise reduction&lt;/strong&gt; using Gaussian blur and median filtering — phone cameras in varying lighting produce a lot of visual noise that confuses models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hand segmentation&lt;/strong&gt; using contour detection and skin-tone masking — isolating just the hand region and removing background clutter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Normalization&lt;/strong&gt; — all pixel values scaled to [0, 1] and all images resized to 256×256 pixels for consistency across models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PCA (Principal Component Analysis)&lt;/strong&gt; — for traditional ML models (SVM, LR, Random Forest, ANN), image data is extremely high-dimensional. PCA reduced this dimensionality while retaining 95% of the data's variance. This made training faster and reduced overfitting risk significantly.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 7 Models — What Each One Does and Why It Was Included
&lt;/h2&gt;

&lt;p&gt;Rather than jumping straight to deep learning, I wanted to understand how traditional ML compares to modern architectures on this specific problem. So I tested all of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Support Vector Machine (SVM) with PCA
&lt;/h3&gt;

&lt;p&gt;SVM finds the optimal boundary (hyperplane) that separates different classes with the maximum margin. For image data, the RBF (Radial Basis Function) kernel was used to capture non-linear relationships between classes. Hyperparameter tuning via grid search optimized the regularization and kernel parameters.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Logistic Regression (LR) with PCA
&lt;/h3&gt;

&lt;p&gt;The simplest model in the comparison — a probabilistic linear classifier. L2 regularization was applied to prevent overfitting. Included as the baseline: if even logistic regression performs well, the problem might not need deep learning at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Random Forest
&lt;/h3&gt;

&lt;p&gt;An ensemble method that trains multiple decision trees on random data subsets and aggregates their votes. 50–200 trees were tested, with max depth limited to 10 to prevent overfitting. Feature importance analysis was performed post-training to understand which image features mattered most.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Artificial Neural Network (ANN)
&lt;/h3&gt;

&lt;p&gt;A feedforward network with two hidden layers (128 and 64 neurons, ReLU activation) trained on PCA-reduced features. Adam optimizer, 50 epochs, batch size 32, dropout regularization at 0.3 to prevent overfitting.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Convolutional Neural Network (CNN)
&lt;/h3&gt;

&lt;p&gt;Custom CNN built from scratch — three convolutional layers with 3×3 filters, max-pooling, and fully connected layers with softmax output. Unlike the models above, CNN works directly on raw image pixels and learns spatial features automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. ResNet50
&lt;/h3&gt;

&lt;p&gt;A 50-layer deep network with residual (skip) connections that solve the vanishing gradient problem in very deep networks. Transfer learning was used — pretrained on ImageNet, with top layers replaced for 34-class Gujarati classification. Lower layers were frozen initially, then gradually unfrozen during fine-tuning.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. VGG19
&lt;/h3&gt;

&lt;p&gt;A 19-layer network known for its uniform architecture of sequential 3×3 convolutional layers. The fully connected layers were replaced with a Global Average Pooling (GAP) layer to reduce parameters and improve generalization. L2 regularization (weight decay 0.0001) was applied throughout.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Results — What Actually Happened
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Training Accuracy&lt;/th&gt;
&lt;th&gt;Testing Accuracy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SVM + PCA&lt;/td&gt;
&lt;td&gt;93.60%&lt;/td&gt;
&lt;td&gt;87.97%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logistic Regression + PCA&lt;/td&gt;
&lt;td&gt;92.37%&lt;/td&gt;
&lt;td&gt;82.38%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random Forest&lt;/td&gt;
&lt;td&gt;88.44%&lt;/td&gt;
&lt;td&gt;88.44%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ANN&lt;/td&gt;
&lt;td&gt;90.48%&lt;/td&gt;
&lt;td&gt;79.77%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VGG19&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;94.28%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;87.23%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CNN&lt;/td&gt;
&lt;td&gt;73.53%&lt;/td&gt;
&lt;td&gt;64.21%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ResNet50&lt;/td&gt;
&lt;td&gt;54.29%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Breaking Down the Surprises
&lt;/h2&gt;

&lt;h3&gt;
  
  
  VGG19 won — but not by as much as expected
&lt;/h3&gt;

&lt;p&gt;VGG19 achieved the highest training accuracy (94.28%) and strong testing accuracy (87.23%). Its deep, uniform architecture excels at extracting hierarchical image features — edges → shapes → gesture patterns — which is exactly what hand gesture recognition needs.&lt;/p&gt;

&lt;p&gt;But the margin over SVM was surprisingly small.&lt;/p&gt;

&lt;h3&gt;
  
  
  SVM was the real surprise
&lt;/h3&gt;

&lt;p&gt;SVM with PCA achieved 87.97% testing accuracy — just 0.74% below VGG19, while being dramatically simpler to train and computationally cheaper. For a traditional ML algorithm working on hand gesture images, this is impressive.&lt;/p&gt;

&lt;p&gt;Why did it work so well? PCA removed noise from the high-dimensional image data, giving SVM a clean, reduced feature space where it could find clear decision boundaries. The RBF kernel then handled the non-linear separability of different hand shapes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: don't underestimate classical ML when preprocessing is done well.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Random Forest was the most consistent
&lt;/h3&gt;

&lt;p&gt;Random Forest showed identical training and testing accuracy (88.44%) — a sign of excellent generalization. No overfitting at all. For a production system where stability matters more than peak accuracy, Random Forest is a strong choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  CNN underperformed — and here's why
&lt;/h3&gt;

&lt;p&gt;The custom CNN from scratch achieved only 64.21% testing accuracy, the lowest of all models. This is counterintuitive — CNNs are supposed to be great at images.&lt;/p&gt;

&lt;p&gt;The reason: CNNs need large datasets to learn meaningful features. With only 3,798 training images across 34 classes (about 112 images per class), the CNN simply didn't have enough data to learn properly. Compare this to VGG19, which came pretrained on millions of ImageNet images and only needed to fine-tune the top layers for our specific gestures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson: for small datasets, transfer learning always beats training from scratch.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ResNet50 failed completely
&lt;/h3&gt;

&lt;p&gt;54.29% training accuracy with testing accuracy not even reportable. Transfer learning didn't help here — the pre-trained ImageNet weights didn't transfer well to this specific dataset, and the gradual unfreezing strategy wasn't enough to compensate.&lt;/p&gt;

&lt;p&gt;This suggests ResNet50 needs either a much larger dataset or significantly different fine-tuning strategy for this task.&lt;/p&gt;

&lt;h3&gt;
  
  
  ANN gap between training and testing
&lt;/h3&gt;

&lt;p&gt;ANN achieved 90.48% training but only 79.77% testing — the largest gap in the comparison. The 10%+ drop indicates overfitting despite dropout regularization. The model learned the training data well but didn't generalize as effectively as simpler models like SVM or Random Forest.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Preprocessing is more important than model choice.&lt;/strong&gt; The PCA step transformed SVM from a mediocre image classifier into a near-VGG19 performer. Time spent on preprocessing returned more value than time spent on model architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Dataset size determines which models you can use.&lt;/strong&gt; With a small dataset, transfer learning (VGG19) beats training from scratch (CNN, ResNet50). Always start with pretrained models when data is limited.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Consistency beats peak performance.&lt;/strong&gt; Random Forest never overfit, while ANN showed a 10% train-test gap. In production, consistent performance is more valuable than occasional high accuracy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Test everything.&lt;/strong&gt; I went in assuming deep learning would dominate. The results showed classical ML (SVM) was nearly as good with 1/100th the complexity. That would have been missed if I'd only tested neural networks.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real-World Application
&lt;/h2&gt;

&lt;p&gt;Beyond the model comparison, the system was integrated into a web application that converts recognized gestures into spoken output using Text-to-Speech (TTS). A user signs a Gujarati consonant in front of a camera — the model recognizes it — and the system speaks the corresponding letter aloud.&lt;/p&gt;

&lt;p&gt;The goal is to help bridge communication between Gujarati sign language users and those who don't know sign language, making everyday interactions in education, healthcare, and public services more accessible.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The current system handles 34 consonants. The natural next step is extending it to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gujarati vowels&lt;/li&gt;
&lt;li&gt;Common words and phrases&lt;/li&gt;
&lt;li&gt;Dynamic gestures (involving motion, not just static hand positions)&lt;/li&gt;
&lt;li&gt;Real-time mobile deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Temporal models like LSTMs or Vision Transformers could handle the dynamic gesture challenge — that's the direction future work is heading.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Paper
&lt;/h2&gt;

&lt;p&gt;This research was published at &lt;strong&gt;IEEE MPSec ICETA 2025&lt;/strong&gt; (International Conference on Emerging Technologies and Applications), Gwalior, India.&lt;/p&gt;

&lt;p&gt;DOI: &lt;a href="https://ieeexplore.ieee.org/document/11118850" rel="noopener noreferrer"&gt;10.1109/MPSecICETA64837.2025.11118850&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kaggle notebooks with the model implementations: &lt;a href="https://www.kaggle.com/khushihpandya" rel="noopener noreferrer"&gt;kaggle.com/khushihpandya&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Khushi Pandya, a software engineer working on AI/ML systems and backend development. Find me on &lt;a href="https://dev.to/khushipandya"&gt;Dev.to&lt;/a&gt; | &lt;a href="https://github.com/khushi-79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; | &lt;a href="https://www.kaggle.com/khushihpandya" rel="noopener noreferrer"&gt;Kaggle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>transferleraning</category>
      <category>modeltraining</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>How I Built a Voice AI Agent System with Asterisk and LLMs</title>
      <dc:creator>Khushi</dc:creator>
      <pubDate>Fri, 05 Jun 2026 18:16:56 +0000</pubDate>
      <link>https://dev.to/khushipandya/how-i-built-a-voice-ai-agent-system-with-asterisk-llms-and-rag-as-a-solo-developer-55pf</link>
      <guid>https://dev.to/khushipandya/how-i-built-a-voice-ai-agent-system-with-asterisk-llms-and-rag-as-a-solo-developer-55pf</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;When your support team is busy, AI picks up the phone.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Problem We Were Solving
&lt;/h2&gt;

&lt;p&gt;Imagine a user calls your company's technical support line at 7 PM. Everyone on the support team has logged off. The phone rings… and rings. The user hangs up frustrated.&lt;/p&gt;

&lt;p&gt;That was the exact problem my company wanted to solve. The goal was simple to describe but complex to build: &lt;strong&gt;when no human agent is available, an AI agent should pick up the call, understand the user's problem, and answer it — just like a support executive would.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I was given this task as a solo developer. What followed was one of the most technically interesting projects I've worked on — combining telephony, AI, speech processing, and real-time dashboards into one working system.&lt;/p&gt;

&lt;p&gt;Here's everything I built, how it works, and what I learned.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture at a Glance
&lt;/h2&gt;

&lt;p&gt;Before diving deep, here's the high-level flow of a call through the system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User calls → Asterisk PBX → Python AGI Script
                                    ↓
                          Speech-to-Text (Google STT)
                                    ↓
                          Noise filtering + silence detection
                                    ↓
                          Company Knowledge Base → OpenAI API
                                    ↓
                          Text-to-Speech (Google TTS)
                                    ↓
                        Voice response played back to user
                                    ↓
                    [If unresolved] → Transfer to human agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every step in that chain had its own set of challenges. Let's go through each one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: Asterisk — The Phone System Brain
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Asterisk?
&lt;/h3&gt;

&lt;p&gt;Think of Asterisk as a software-based phone exchange — the same kind of system a large call center uses, but open-source and running on a Linux server. It's what connects incoming phone calls to your application logic.&lt;/p&gt;

&lt;p&gt;In real life, when you call a bank and hear &lt;em&gt;"Press 1 for account balance, press 2 for loans"&lt;/em&gt; — that's a PBX (Private Branch Exchange) system doing the routing. Asterisk is one of the most widely used open-source PBX systems in the world.&lt;/p&gt;

&lt;p&gt;For our project, I used &lt;strong&gt;FreePBX&lt;/strong&gt; — a GUI layer on top of Asterisk — which made managing extensions and call routing significantly easier without touching raw config files constantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Asterisk Connects to Your Code
&lt;/h3&gt;

&lt;p&gt;Asterisk has a feature called &lt;strong&gt;AGI (Asterisk Gateway Interface)&lt;/strong&gt; — it lets you write scripts in Python (or any language) that Asterisk calls during a live phone session. Think of it like a webhook, but for phone calls.&lt;/p&gt;

&lt;p&gt;When a user calls in, Asterisk hands control to your Python script mid-call. Your script can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Play audio to the caller&lt;/li&gt;
&lt;li&gt;Record the caller's voice&lt;/li&gt;
&lt;li&gt;Send that recording somewhere for processing&lt;/li&gt;
&lt;li&gt;Play back a response&lt;/li&gt;
&lt;li&gt;Transfer the call to another extension&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Protocols Asterisk Works With
&lt;/h3&gt;

&lt;p&gt;Asterisk relies on two core protocols under the hood:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SIP (Session Initiation Protocol)&lt;/strong&gt; — handles how calls are initiated, maintained, and ended over IP networks. Think of it as the process of dialing a number and getting the other person to pick up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RTP (Real-time Transport Protocol)&lt;/strong&gt; — carries the actual voice audio once a call is connected. If SIP is the handshake, RTP is the conversation itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: Speech Processing — The Hardest Part Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Getting AI to "hear" properly is not just about sending audio to Google. Raw phone audio is messy — background noise, varying volumes, long silences, people who trail off mid-sentence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Google STT (Speech-to-Text)
&lt;/h3&gt;

&lt;p&gt;I used &lt;strong&gt;Google Cloud Speech-to-Text&lt;/strong&gt; because it's free within generous limits and supports Indian English well (important for our user base).&lt;/p&gt;

&lt;h3&gt;
  
  
  Noise Filtering and Volume Normalization
&lt;/h3&gt;

&lt;p&gt;Phone call audio comes in at 8000 Hz (much lower quality than a microphone recording). Background noise is a real problem — fans, traffic, other people talking. I used &lt;strong&gt;SoX&lt;/strong&gt; (Sound eXchange), a command-line audio tool, to pre-process audio before sending it to STT — normalizing volume to a consistent level and stripping out leading/trailing silence automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Silence Detection and Timeout
&lt;/h3&gt;

&lt;p&gt;One subtle but critical thing: &lt;strong&gt;how do you know the user has finished speaking?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I configured Asterisk's record command with a silence threshold — if the user stops speaking for more than 2 seconds, the recording stops automatically and processing begins. Too short and it cuts people off mid-sentence. Too long and the call feels unresponsive.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3: The AI Brain — OpenAI + Company Knowledge Base
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How the AI Knew What to Answer
&lt;/h3&gt;

&lt;p&gt;If you just send a user's question directly to OpenAI's GPT model, it answers from its general training data — which knows nothing about your company's specific products, policies, or support procedures.&lt;/p&gt;

&lt;p&gt;To solve this, my company provided a pre-built knowledge base — a structured dataset of Q&amp;amp;A pairs covering both sales and technical support scenarios, sourced from their website and internal documentation. My job was to integrate this into the pipeline so the AI could reference it when answering.&lt;/p&gt;

&lt;p&gt;When a user speaks, their transcribed question is matched against the knowledge base, the most relevant answer context is retrieved, and that gets passed to OpenAI along with a carefully crafted system prompt. One critical instruction in that prompt: &lt;em&gt;keep answers short — this will be spoken out loud on a phone call.&lt;/em&gt; Nobody wants to listen to 3 paragraphs read aloud.&lt;/p&gt;

&lt;p&gt;If the AI cannot find a confident answer, it tells the user it will connect them to a human agent — and the transfer kicks in automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 4: Text-to-Speech — Giving the AI a Voice
&lt;/h2&gt;

&lt;p&gt;I used &lt;strong&gt;Google Text-to-Speech (gTTS)&lt;/strong&gt; — free, supports multiple voices, and sounds natural enough for support calls. The generated audio was converted to the 8000 Hz mono format that Asterisk expects, then played directly into the live call. I configured both male and female voice options and applied volume gain adjustments to ensure the AI voice was clear and comfortable to listen to.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkxytgrqft5x7au730ven.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkxytgrqft5x7au730ven.png" alt="Complete flow"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 5: Call Routing — Round Robin and Human Escalation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Round Robin for Human Agents
&lt;/h3&gt;

&lt;p&gt;If the AI couldn't resolve an issue (or the user asked to speak to a human), the call needed to be transferred. But what if the first agent didn't pick up?&lt;/p&gt;

&lt;p&gt;I implemented &lt;strong&gt;round robin dialing&lt;/strong&gt; — Asterisk tries the first agent's extension, and if there's no answer after 20 seconds, it moves to the next, then the next. If all agents are busy, the caller hears a message and the call is gracefully ended.&lt;/p&gt;

&lt;p&gt;Each agent's phone (a mobile app or desk phone) was registered to Asterisk as a SIP extension — same as how your office desk phone connects to the company switchboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Mobile App
&lt;/h3&gt;

&lt;p&gt;Support agents used a &lt;strong&gt;SIP-based mobile app&lt;/strong&gt; (like Zoiper or Linphone — both free) registered to our Asterisk server. This meant agents could receive transferred calls on their phones from anywhere, without needing to be physically at a desk.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 6: The Real-Time Dashboard
&lt;/h2&gt;

&lt;p&gt;Beyond the AI agent itself, I built a dashboard for the support team so they could monitor what was happening on every call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Call statuses tracked in real-time:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🟢 On Call&lt;/li&gt;
&lt;li&gt;🟡 On Hold&lt;/li&gt;
&lt;li&gt;🔵 Pending (AI handling)&lt;/li&gt;
&lt;li&gt;⏭️ Skipped (no agent picked up)&lt;/li&gt;
&lt;li&gt;🔁 Transferred (to human agent)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used &lt;strong&gt;Asterisk's AMI (Asterisk Manager Interface)&lt;/strong&gt; — a socket-based API that streams live call events — to feed real-time data into the dashboard. Every time a call changed state (answered, transferred, ended), AMI fired an event and the dashboard updated instantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Call Recording and Playback
&lt;/h3&gt;

&lt;p&gt;All calls were automatically recorded using Asterisk's built-in MixMonitor feature and stored as audio files. The dashboard gave the support team a simple UI to browse, filter by date or status, and play back any recorded call.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned Building This Alone
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Phone audio is not microphone audio.&lt;/strong&gt; Everything you know about audio quality from web projects goes out the window. 8000 Hz, mono, heavy compression — you have to design around it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. AI response length matters more than quality for voice.&lt;/strong&gt; A perfectly accurate 200-word answer is useless on a phone call. The AI prompt must explicitly constrain response length.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Silence is a feature, not a bug.&lt;/strong&gt; Getting silence detection thresholds right — on both the STT input side and the TTS pause side — made the difference between the agent feeling natural and feeling like a broken IVR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Data quality is everything.&lt;/strong&gt; The knowledge base I received had inconsistent formatting and some duplicate entries. Cleaning that data was 30% of the total work — garbage in, garbage out applies even more strictly when AI is speaking to real users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Test with real phone calls early.&lt;/strong&gt; The system worked perfectly in local testing. On actual phone calls, background noise revealed three bugs I had never seen in a controlled environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final System Overview
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Technology Used&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PBX / Telephony&lt;/td&gt;
&lt;td&gt;Asterisk + FreePBX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Call Scripting&lt;/td&gt;
&lt;td&gt;Python AGI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speech-to-Text&lt;/td&gt;
&lt;td&gt;Google Cloud STT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audio Processing&lt;/td&gt;
&lt;td&gt;SoX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI / LLM&lt;/td&gt;
&lt;td&gt;OpenAI GPT-4o-mini&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Knowledge Base&lt;/td&gt;
&lt;td&gt;Company-provided Q&amp;amp;A dataset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text-to-Speech&lt;/td&gt;
&lt;td&gt;Google TTS (gTTS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent Mobile App&lt;/td&gt;
&lt;td&gt;SIP client (Zoiper/Linphone)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dashboard&lt;/td&gt;
&lt;td&gt;Python + real-time AMI events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Call Recording&lt;/td&gt;
&lt;td&gt;Asterisk MixMonitor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Building this system as a solo developer taught me that production AI is far less about the AI model itself and far more about the plumbing around it — audio quality, latency, data preparation, and graceful fallback when the AI doesn't know the answer.&lt;/p&gt;

&lt;p&gt;The most rewarding moment was watching the first real call go through — a user called, the AI answered, understood the question, found the right answer from our knowledge base, and spoke it back clearly. No human involved.&lt;/p&gt;

&lt;p&gt;If you're thinking about building something similar, start small: get Asterisk running locally, write a basic AGI script that just records and plays back audio, then layer in the AI piece once the telephony is solid.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Khushi Pandya, a software engineer working on AI-driven backends, voice systems, and developer tooling. Find me on &lt;a href="https://medium.com/@khushihp7903" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; | &lt;a href="https://github.com/khushi-79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/khushi-pandya-b91457221/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>asterisk</category>
      <category>voiceai</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
