<?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: Ritvika Mishra</title>
    <description>The latest articles on DEV Community by Ritvika Mishra (@miss-bubbles).</description>
    <link>https://dev.to/miss-bubbles</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3880227%2F0bd368ad-4fae-45cb-97ec-29d89e79d4bb.png</url>
      <title>DEV Community: Ritvika Mishra</title>
      <link>https://dev.to/miss-bubbles</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/miss-bubbles"/>
    <language>en</language>
    <item>
      <title>I built an external context layer for AI agents - most of it already exists, here's what doesn't</title>
      <dc:creator>Ritvika Mishra</dc:creator>
      <pubDate>Wed, 20 May 2026 02:48:06 +0000</pubDate>
      <link>https://dev.to/miss-bubbles/i-built-an-external-context-layer-for-ai-agents-most-of-it-already-exists-heres-what-doesnt-12ni</link>
      <guid>https://dev.to/miss-bubbles/i-built-an-external-context-layer-for-ai-agents-most-of-it-already-exists-heres-what-doesnt-12ni</guid>
      <description>&lt;p&gt;Imagine you're deep into a brainstorming session with an AI, going back and forth for an hour - and then the free tier rate limit hits. Now you have to switch to another AI and re-explain everything from scratch. The context, the problem, what you've tried, where you're stuck. All of it.&lt;/p&gt;

&lt;p&gt;Because you're the only one who knows what you're working on, to query any AI assistant you have to carry all that knowledge alone, every single time.&lt;/p&gt;

&lt;p&gt;So two weeks ago, during an offline hackathon - I built a small version of my idea - &lt;strong&gt;Meniscus&lt;/strong&gt; - a layer that sits outside your tools and holds that knowledge for you. One shared picture of your current working state: what you've tried, what's blocking you, what's been decided. Any AI you switch to reads from it instead of starting from zero.&lt;/p&gt;

&lt;p&gt;The core idea: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Context shouldn't live inside individual tools. It should live outside as a separate external layer and AI tools should read from it.&lt;/em&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Right now, you're the only one who knows what you're working on right now and you got to query any AI assistant you got to carry all that knowledge alone. &lt;em&gt;Meniscus is the layer that finally holds that knowledge for you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It captures user activity across tools, structures it into threads - which are the core working units representing what the user is actively doing - and lets you retrieve relevant context as a subgraph instead of raw history.&lt;/p&gt;

&lt;p&gt;The architecture consists of three primitives:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Event&lt;/strong&gt; is the atomic unit - an immutable, timestamped record of one thing you did. Asked ChatGPT something, watched a YouTube video, made a GitHub commit, updated a Notion page -- each one captured, normalized, stored.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An &lt;strong&gt;entity&lt;/strong&gt; is a meaningful concept extracted from an event. Not the full text -- just the signal. "JWT", "middleware", "refresh token", "auth" - the keywords that actually tell you what the event was about.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;thread&lt;/strong&gt; is a cluster of related events - not similar in the textual sense, but connected by shared work context. "I am debugging a JWT auth bug" is a thread. It spans a GitHub commit, a ChatGPT conversation, a Notion architecture doc, a YouTube video on token expiry. Individually those events look unrelated and together they're one line of work.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The pipeline goes like this: &lt;/p&gt;

&lt;p&gt;→ activity comes in &lt;br&gt;
→ entities get extracted &lt;br&gt;
→ each new event gets compared against existing threads by entity overlap and temporal proximity &lt;br&gt;
→ assigned to the right thread or a new one gets created &lt;br&gt;
→ the whole thing is stored as a graph with explicit edges between events, entities, and threads.&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%2Fw0ykfb20bbmmtx99voxl.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%2Fw0ykfb20bbmmtx99voxl.png" alt="architecture" width="799" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;when an agent queries Meniscus, it doesn't get a raw dump of your history. It gets a subgraph - the relevant thread, its events, its entities. A bounded, structured slice of context instead of everything at once. The agent injects that into its prompt and answers grounded in your actual work.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;For the demo of the project, here's what i did:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simulated events from ChatGPT, YouTube and GitHub -- 4 threads from a realistic day of work, 12 events total. &lt;/li&gt;
&lt;li&gt;the query system routes through three modes: 
Retrieve (traverses through the entities) --&amp;gt; Overview (cross-thread summary of what you've been doing) --&amp;gt; General (conversational, if there's nothing relevant to retrieve, it says "i don't know"). &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever starting with a project, I like to think on the lines of SHOULDs -- how something should be done and question my own decisions aggressively at every step before I can come to an architecture that I deem to be good enough. &lt;/p&gt;

&lt;p&gt;Whatever was showed in the demo, it was only a small part of the whole implementation I had planned. When the hackathon ended, after 2 days I decided to sit with my project once again and finish the remaining implementation. However I found some major loopholes and realized whatever I am doing is nothing different from what already exists - Zep, Mem0, Supermemory, Rewind etc. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;most of what I built is already there, and in better shape than I could ship.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;External memory layers, graph storage, episodic retrieval, agent APIs - these are solved or being actively solved by well-funded teams. Hence no point in redoing the same. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;However there's one specific architectural component that is the only differentiating factor and it got a persisting question surrounding it, which I need to research upon thoroughly before coming to a conclusion.&lt;/p&gt;
&lt;/blockquote&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%2Fni8o567qbgeea0bnu5ha.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%2Fni8o567qbgeea0bnu5ha.png" alt=" " width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every existing system retrieves by similarity - cosine distance, semantic search, ranked chunks. You ask a question, it finds the most textually similar pieces of your history and hands them back.&lt;/p&gt;

&lt;p&gt;But "what am I currently working on" isn't a similarity problem. It's a working state problem. The agent doesn't need the most similar chunks. it needs the current state of an ongoing task - what the goal is, what's been tried, what's blocking progress, what's been decided. Those are different questions and similarity search doesn't answer them cleanly.&lt;/p&gt;

&lt;p&gt;Then the obvious question is - what about just dumping everything into a long context window? frontier models like Gemini and Claude have massive context windows. Why not hand them your entire history and let them figure out the working state themselves?&lt;/p&gt;

&lt;p&gt;And honestly, they'd do a decent job. give Claude enough of your activity and it can synthesize "what you're working on" reasonably well. &lt;br&gt;
but three problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1st, cost. sending hundreds of thousands of tokens on every single query isn't free at scale. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2nd, the lost-in-the-middle problem - empirically documented, models perform worse on information buried deep in long contexts. &lt;em&gt;More tokens doesn't mean better reasoning over those tokens.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3rd, even if the model synthesizes working state correctly from raw history, it's doing that work fresh every single time you query it. &lt;em&gt;Meniscus does it once&lt;/em&gt; and maintains it continuously. The synthesis is already done when the agent needs it. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are two hypotheses -&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Thread-state packet retrieval produces better agent answers to active working state queries than hybrid search. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Thread-state packet retrieval injects fewer tokens for the same query - because a structured state object is already present there for agent to retrieve. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I might have guessed the answers but need to be very sure and the honest thing to do is build a benchmark, compare thread-state packet retrieval against state of the art retrieval methods on active working state queries, measure token count and answer quality, and write about what I find.&lt;/p&gt;

&lt;p&gt;Thanks for reading :)&lt;/p&gt;

&lt;p&gt;github repo: &lt;a href="https://github.com/magic-bubblez/meniscus-" rel="noopener noreferrer"&gt;https://github.com/magic-bubblez/meniscus-&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>systems</category>
    </item>
    <item>
      <title>Local Voice Controlled AI Agent</title>
      <dc:creator>Ritvika Mishra</dc:creator>
      <pubDate>Wed, 15 Apr 2026 10:13:59 +0000</pubDate>
      <link>https://dev.to/miss-bubbles/local-voice-controlled-ai-agent-28dj</link>
      <guid>https://dev.to/miss-bubbles/local-voice-controlled-ai-agent-28dj</guid>
      <description>&lt;p&gt;a voice controlled agent is a piece of software that sits on your machine, listens to what you say, and actually does things in response. not a chatbot that talks back — an agent that acts. it hears intent, decides what to do, and does the thing. speech goes in, the filesystem moves, apps open, files get written, screens get captured. the gap between a thought and a side effect on your computer shrinks to a sentence.&lt;/p&gt;

&lt;p&gt;i decided to build one of my own. here's what it can do right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create files and folders, sandboxed to a dedicated output directory so nothing on the machine gets stomped&lt;/li&gt;
&lt;li&gt;generate code from a spoken description — "write a python function that reverses a string and save it as reverse.py" — and pop the file straight open in VS Code so you can see what was made&lt;/li&gt;
&lt;li&gt;open apps on the device — Preview, Spotify, Terminal, Firefox, Chrome, whatever's installed — aliases and fuzzy names handled&lt;/li&gt;
&lt;li&gt;open websites in the browser, with or without a specific browser named; search Google when asked to "look something up online"&lt;/li&gt;
&lt;li&gt;open local files by searching for them via macOS Spotlight — "open the DDIA pdf" finds it wherever it lives&lt;/li&gt;
&lt;li&gt;take screenshots — full screen, a window you pick, or a region you drag — auto-opens in Preview&lt;/li&gt;
&lt;li&gt;summarize local files by actually reading them (pypdf for PDFs, plain read for text) and feeding the contents to the model&lt;/li&gt;
&lt;li&gt;general chat when you just want to ask it something and have it respond in words&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;baby steps. nothing close to what i imagine an ideal version looking like — some hardware constraints and a few deliberate tradeoffs carved the scope down to what you see. the sections below are a slow&lt;br&gt;
ramble on how the thing got constructed; the questions that surfaced while building, the choices that were made, the moments where self-posed questions looped until something clicked. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;i like thinking by asking myself questions and answering them and re-questioning the answers — repeating that until there's some satisfaction (which, there never really is).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;implementation was the comparatively easy part (much love to my dear claudette!) the actual interesting work was in the shape of the design, thinking and making elaborate plans.&lt;/p&gt;


&lt;h2&gt;
  
  
  the shape of the thing
&lt;/h2&gt;

&lt;p&gt;the whole system is a pipeline. five layers. audio comes in one end, actions come out the other, and every layer in between does exactly one job and hands its output to the next.&lt;/p&gt;

&lt;p&gt;audio bytes&lt;br&gt;
      → [speech-to-text]     → text string&lt;br&gt;
          → [intent classifier]  → structured JSON plan&lt;br&gt;
              → [orchestrator]       → handler calls&lt;br&gt;
                  → [tool handlers]     → side effects on the machine&lt;br&gt;
                      → [UI]                 → displays every stage back to you&lt;/p&gt;

&lt;p&gt;each arrow is a contract. "i promise to give you something of this shape, and you promise to produce something of that shape. neither of us cares how the other works internally." those contracts are the&lt;br&gt;
whole point. they're what lets the pipeline be swappable — pull out any single box, replace its implementation, and nothing else in the system notices.&lt;/p&gt;

&lt;p&gt;now layer by layer.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. audio input
&lt;/h3&gt;

&lt;p&gt;input capture. the browser already knows how to do this — a microphone button captures audio, an upload field takes a file. the web ui hands off bytes. no custom audio code to write. running in a&lt;br&gt;
browser means the browser solves threading, permissions, and buffering — problems that don't belong to the agent.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. speech-to-text
&lt;/h3&gt;

&lt;p&gt;bytes become words. a whisper model runs locally (faster-whisper, specifically — the CTranslate2-backed variant, which avoids pulling in a heavy deep-learning framework just to run inference). input:&lt;br&gt;
audio path. output: a string. stateless. deterministic-ish. no memory across calls. this layer is essentially a pure function.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. intent classifier
&lt;/h3&gt;

&lt;p&gt;the only layer where real intelligence lives. a local llm reads the transcribed text and produces a structured JSON plan — a list of actions with parameters. this is the hard layer. natural language is&lt;br&gt;
infinite in its phrasings; the output has to be a small, bounded, structured thing. the llm is doing compression here — from an unbounded input space to a finite one.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the classifier does not execute anything. it does not touch the filesystem. it does not know how any of the tools work internally. it only describes what the user wants in a format the rest of the system can read. the understanding of language is cleanly separated from the taking of action.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  4. orchestrator
&lt;/h3&gt;

&lt;p&gt;the interface between intent and action. the layer that takes the plan and makes it happen. zero intelligence here. no llm calls. the orchestrator is a table lookup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="n"&gt;REGISTRY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create_file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;handle_filesystem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;write_code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;handle_llm_generate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summarize&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;handle_llm_generate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open_app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;handle_open_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;screenshot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;handle_screenshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general_chat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;handle_llm_generate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;action type goes in, handler function comes out. call it. collect the result. move to the next action. when actions depend on each other (generate code before saving it to a file), a tiny set of&lt;br&gt;
ordering rules reshuffles the list. that's the entire orchestrator.&lt;/p&gt;

&lt;p&gt;this is called a &lt;strong&gt;registry pattern&lt;/strong&gt;. dispatch tables instead of if/elif chains. the benefit is quiet but enormous — adding a new capability is one entry in the table. the orchestrator itself never changes when the system grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. the UI
&lt;/h3&gt;

&lt;p&gt;a thin layer. reads the results of the pipeline and lays them out: transcription, detected intent, actions taken, final output, per-stage timing, session history. the ui has no opinions about what&lt;br&gt;
anything means — it just formats whatever the pipeline produces.&lt;/p&gt;




&lt;h2&gt;
  
  
  intents, tools, handlers — three words, three meanings
&lt;/h2&gt;

&lt;p&gt;this distinction unlocks a lot of the design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;intent: what the user wants. user-facing. effectively infinite — every new phrasing is a new intent.&lt;/li&gt;
&lt;li&gt;tool (or capability): what the system can do. system-facing. finite. small.&lt;/li&gt;
&lt;li&gt;handler: the code that implements a tool. one python function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;a single intent might need multiple tools. many intents collapse onto a single tool. the classifier is what maps from the infinite space of possible intents to the finite space of tools. without that&lt;br&gt;
mapping layer, you'd have to write a handler for every way a user could phrase something — which is impossible, because the set of phrasings is unbounded.&lt;/p&gt;

&lt;p&gt;compress where the complexity actually is. language is complex; keep it in one layer. execution is simple; keep it in another. don't spread intelligence across the system.&lt;/p&gt;




&lt;h3&gt;
  
  
  what makes a design good
&lt;/h3&gt;

&lt;p&gt;a few questions worth asking any system you build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;can a new capability be added without touching existing code?&lt;/li&gt;
&lt;li&gt;is each capability isolated — can one be removed without breaking others?&lt;/li&gt;
&lt;li&gt;can any implementation be swapped without cascading changes through the rest of the system?&lt;/li&gt;
&lt;li&gt;is there a single place where a given concern lives — not smeared across multiple layers?&lt;/li&gt;
&lt;li&gt;do the boundaries between layers stay clean — no internals of one layer leaking into another?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;these aren't independent. they compound. clean interfaces enable isolation. isolation enables swapping. swapping enables growth. a system that answers "yes" to all five can be modified without a&lt;br&gt;
rewrite, and that property — the cost of change — is the actual test of whether the design is doing its job.&lt;/p&gt;

&lt;p&gt;the opposite property has a name too: abstraction leakage. when one layer has to know the internals of another to function, the boundary has been violated. every violation makes the next change a little&lt;br&gt;
harder. accumulate enough of them and you can't reason about any piece in isolation — the whole system becomes one tangled thing, and adding a capability means understanding all of it.&lt;/p&gt;

&lt;p&gt;thanks for reading ^^&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
