<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Asad Ibrahim]]></title><description><![CDATA[Practical articles on AI agents, automation, APIs, Laravel, Node.js, React, and full-stack development by Asad Ibrahim. Real-world engineering lessons, experiments, and solutions from production projects.]]></description><link>https://asadibrahim.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6a799dce14dcf94d511d45a1/ea4911f3-0c3d-4358-9308-d7174307d233.png</url><title>Asad Ibrahim</title><link>https://asadibrahim.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 10 Sep 2026 13:32:14 GMT</lastBuildDate><atom:link href="https://asadibrahim.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I Cut My Voice Agent’s Groq API Usage by ~70% Without Changing the Model]]></title><description><![CDATA[A few weeks ago, I added a small AI voice assistant called Jarvis to my portfolio website.
The setup was intentionally simple: Groq handled text generation, while the browser’s Web Speech API handled ]]></description><link>https://asadibrahim.hashnode.dev/how-i-cut-my-voice-agent-s-groq-api-usage-by-70-without-changing-the-model</link><guid isPermaLink="true">https://asadibrahim.hashnode.dev/how-i-cut-my-voice-agent-s-groq-api-usage-by-70-without-changing-the-model</guid><category><![CDATA[AI]]></category><category><![CDATA[voice agents]]></category><category><![CDATA[performance]]></category><category><![CDATA[webdev]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[groq]]></category><dc:creator><![CDATA[Asad Ibrahim]]></dc:creator><pubDate>Mon, 10 Aug 2026 10:11:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a799dce14dcf94d511d45a1/3c375e3d-aaa2-4b46-a48b-7932d58f95d7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A few weeks ago, I added a small AI voice assistant called Jarvis to my portfolio website.</p>
<p>The setup was intentionally simple: Groq handled text generation, while the browser’s Web Speech API handled speech-to-text and text-to-speech. Visitors could ask Jarvis about my work, projects, experience, or services, and it could also help them book a call.</p>
<p>It worked.</p>
<p>There was just one problem.</p>
<p>After about five minutes of fairly light testing, I had already burned through the Groq free-tier limit.</p>
<p>My first thought was probably the obvious one:</p>
<p>Do I need another provider or a paid API plan?</p>
<p>Before changing anything, though, I decided to figure out where the tokens were actually going.</p>
<p>That turned out to be the right decision.</p>
<p>3,113 Prompt Tokens Just to Say “Hello”</p>
<p>The first thing I wanted was an actual measurement.</p>
<p>Instead of relying only on a token-counting estimate, I made a real Groq request with a minimal output limit and inspected the token usage returned by the API.</p>
<p>Then I sent:</p>
<p>hello</p>
<p>The result surprised me:</p>
<p>3,113 prompt tokens.</p>
<p>Not for a complicated question.</p>
<p>Not for a request involving several projects.</p>
<p>Just “hello.”</p>
<p>At that point, the problem became pretty obvious.</p>
<p>My model wasn't necessarily expensive.</p>
<p>My request architecture was.</p>
<p>I found two major problems.</p>
<ol>
<li>I was sending my entire portfolio every time</li>
</ol>
<p>My system prompt contained almost everything about me:</p>
<p>services project descriptions testimonials FAQs statistics tech stack experience behavioral instructions</p>
<p>And all of it was being sent on every request, regardless of what the visitor asked.</p>
<p>Someone saying “Hi” was effectively sending thousands of tokens of portfolio context to the model.</p>
<p>Someone asking about pricing received the same context.</p>
<p>Someone asking about one project also received information about every other project.</p>
<p>It worked, but it was incredibly wasteful.</p>
<ol>
<li>I had duplicated part of my knowledge base</li>
</ol>
<p>While auditing the knowledge object, I found something even simpler.</p>
<p>My tech stack existed twice under two different fields.</p>
<p>Two parts of the system had been built separately, and both included the same information without me noticing.</p>
<p>So I wasn't just sending too much context.</p>
<p>I was literally paying in tokens to send some of it twice.</p>
<p>The Bigger Realization: Not Every Message Needs AI</p>
<p>This ended up being the most important change.</p>
<p>I had originally treated Jarvis like this:</p>
<p>User message → Groq → response</p>
<p>Every message went through the model.</p>
<p>But why should an LLM generate an answer to:</p>
<p>Hello</p>
<p>Or:</p>
<p>Who are you?</p>
<p>Or:</p>
<p>Do you sign NDAs?</p>
<p>Those answers are already known.</p>
<p>So I added a lightweight local intent layer before Groq.</p>
<p>Now the flow looks more like:</p>
<p>User message → local intent check → Groq only when necessary</p>
<p>Simple regex and keyword matching handles predictable requests locally.</p>
<p>For example:</p>
<p>Greetings → predefined greeting “Who are you?” → fixed identity response Pricing questions → predefined pricing-policy response Known FAQs → existing FAQ answer Requests for private information → fixed refusal Obvious prompt-injection attempts → fixed security response</p>
<p>If the intent matches one of these cases, Groq is never called.</p>
<p>API usage for that interaction: zero.</p>
<p>This also made the assistant feel faster because there is no reason to wait for model inference when the application already knows the answer.</p>
<p>Then I Shrunk the System Prompt</p>
<p>The next target was the prompt itself.</p>
<p>Instead of shipping my entire portfolio with every request, I separated the prompt into two layers.</p>
<p>Layer 1: A small base prompt</p>
<p>The base prompt contains only information Jarvis always needs:</p>
<p>identity behavior privacy rules pricing policy important boundaries response style</p>
<p>After cleaning it up, the base context dropped to roughly 652 tokens, compared with the 3,113-token request I measured earlier.</p>
<p>Layer 2: Retrieve Only Relevant Portfolio Knowledge</p>
<p>The rest of my portfolio became selectively retrieved context.</p>
<p>I divided the knowledge into sections such as:</p>
<p>services projects testimonials faqs contact stats</p>
<p>Before calling Groq, the application checks the user's message and determines which sections are actually relevant.</p>
<p>If someone asks:</p>
<p>“What AI automation work have you done?”</p>
<p>Jarvis might receive relevant services and AI project information.</p>
<p>It doesn't need every testimonial, contact detail, FAQ, and unrelated project.</p>
<p>Usually only 2–4 relevant sections are added to the prompt.</p>
<p>It isn't a complicated vector database or a full RAG pipeline.</p>
<p>For a portfolio this size, simple retrieval works perfectly well.</p>
<p>And more importantly, it's cheap.</p>
<p>I Also Found a Retry Problem</p>
<p>There was another source of unnecessary requests that wasn't immediately obvious: retries.</p>
<p>SDKs often retry certain failed requests automatically, including rate-limit errors.</p>
<p>That's normally helpful.</p>
<p>But when you're already hitting a quota limit, automatic retries can make the situation worse.</p>
<p>A request fails with a 429.</p>
<p>The client retries.</p>
<p>It fails again.</p>
<p>Another retry happens.</p>
<p>From the application's point of view, the visitor sent one message.</p>
<p>From the API's point of view, multiple attempts may have occurred.</p>
<p>So I tightened the retry behavior and added request-level protection around the voice agent.</p>
<p>The final flow includes:</p>
<p>request deduplication cooldown protection aborting superseded requests controlled retries request tracking</p>
<p>The goal was simple:</p>
<p>One visitor message should result in at most one intentional Groq generation request.</p>
<p>The Result</p>
<p>After the changes, the difference was significant.</p>
<p>Before</p>
<p>Measured “hello” request:</p>
<p>3,113 prompt tokens</p>
<p>After</p>
<p>Compact base context:</p>
<p>~652 tokens</p>
<p>For requests that still need Groq, selective knowledge retrieval reduced prompt-token usage by roughly:</p>
<p>70–72% in my testing.</p>
<p>And several common interactions now use zero Groq requests:</p>
<p>greetings identity questions pricing-policy questions known FAQs security refusals obvious prompt-injection attempts duplicate submissions</p>
<p>The interesting part is that I didn't switch models.</p>
<p>I didn't move to another AI provider.</p>
<p>And I didn't solve the problem by simply paying for a larger quota.</p>
<p>I changed how my application used the model.</p>
<p>What I Learned</p>
<p>When an AI application starts burning through API quota, it's easy to assume the model or provider is the problem.</p>
<p>Sometimes it is.</p>
<p>But before switching providers, I think it's worth looking at the request path itself.</p>
<p>Ask:</p>
<p>Does this request actually need an LLM?</p>
<p>Am I sending context the model doesn't need?</p>
<p>Am I sending the same information repeatedly?</p>
<p>Can part of this response be deterministic?</p>
<p>Can I retrieve only the knowledge relevant to this question?</p>
<p>Can one user action accidentally trigger multiple API requests?</p>
<p>In my case, those questions mattered far more than changing the model.</p>
<p>The biggest optimization wasn't finding a cheaper LLM.</p>
<p>It was calling the LLM less often and sending it less unnecessary information when I did.</p>
<p>I'm Asad Ibrahim, a full-stack developer and AI integration engineer. I build AI-powered web applications, automation systems, voice assistants, and production integrations for businesses.</p>
<p>I also document experiments like this from projects I'm actually building.</p>
<p>You can see more of my work, AI projects, and engineering case studies at asadibrahim.com.</p>
]]></content:encoded></item></channel></rss>