<?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: Shashank Shah</title>
    <description>The latest articles on DEV Community by Shashank Shah (@shashank1719).</description>
    <link>https://dev.to/shashank1719</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%2F4068657%2F0bea5ebb-b6a6-4f84-83d1-5f53ce026538.jpg</url>
      <title>DEV Community: Shashank Shah</title>
      <link>https://dev.to/shashank1719</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shashank1719"/>
    <language>en</language>
    <item>
      <title>Switchboard: building a tool router so your AI agent stops drowning in MCP tools</title>
      <dc:creator>Shashank Shah</dc:creator>
      <pubDate>Sun, 09 Aug 2026 07:26:05 +0000</pubDate>
      <link>https://dev.to/shashank1719/switchboard-building-a-tool-router-so-your-ai-agent-stops-drowning-in-mcp-tools-53hm</link>
      <guid>https://dev.to/shashank1719/switchboard-building-a-tool-router-so-your-ai-agent-stops-drowning-in-mcp-tools-53hm</guid>
      <description>&lt;p&gt;Keyword search picks the right tool for an AI agent 21% of the time. The router&lt;br&gt;
we built picks it &lt;strong&gt;88%&lt;/strong&gt; of the time, while cutting the tokens spent describing&lt;br&gt;
those tools by &lt;strong&gt;99.6%&lt;/strong&gt;. This is how we got there, including the parts we got&lt;br&gt;
wrong first.&lt;br&gt;
Architecture: &lt;a href="https://drive.google.com/file/d/1xQP3BX-2e4z-4saY0dDQjv_gpJdZTZVv/view?usp=drive_link" rel="noopener noreferrer"&gt;Link&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The problem nobody talks about until the bill shows up
&lt;/h2&gt;

&lt;p&gt;Model Context Protocol (MCP) solved a real problem. It gave every AI agent a&lt;br&gt;
standard way to talk to external tools: CRMs, observability stacks, email,&lt;br&gt;
vector databases, internal APIs. It created a quieter problem in the process.&lt;/p&gt;

&lt;p&gt;Once you connect more than a handful of MCP servers to a single agent, you are&lt;br&gt;
no longer describing a tool catalog to the model. You are dumping an entire&lt;br&gt;
warehouse inventory into every prompt. In our own environment, three backends&lt;br&gt;
exposed 142 tools. Every one of those schemas, with its names, descriptions,&lt;br&gt;
and parameter shapes, gets tokenized and sent to the LLM on every turn,&lt;br&gt;
whether the user's question needs one of them or none of them.&lt;/p&gt;

&lt;p&gt;That has three costs, and only one of them shows up on an invoice.&lt;/p&gt;

&lt;p&gt;The first is token cost. You are paying to re-describe N tools you will&lt;br&gt;
never call, every single time.&lt;/p&gt;

&lt;p&gt;The second is selection accuracy. The more tools an LLM has to choose from in&lt;br&gt;
a single context, the more often it picks the wrong one, hallucinates&lt;br&gt;
parameters, or gets confused by two similarly-named tools from different&lt;br&gt;
servers.&lt;/p&gt;

&lt;p&gt;The third is operational fragility. Every new MCP server you connect makes the&lt;br&gt;
prompt bigger and the agent's job harder, so scaling tool count and scaling&lt;br&gt;
reliability end up pulling in opposite directions.&lt;/p&gt;

&lt;p&gt;We built Switchboard to decouple those three curves. The name is the metaphor:&lt;br&gt;
a telephone switchboard operator connects your call to the right line so you&lt;br&gt;
never need to know the number. Switchboard does that for tools. The host asks&lt;br&gt;
for what it wants in plain language, and the router works out which of N tool to patch it through to.&lt;/p&gt;
&lt;h2&gt;
  
  
  Two tools instead of two hundred
&lt;/h2&gt;

&lt;p&gt;Switchboard sits between your AI host (Claude Code, a custom chat agent, an&lt;br&gt;
IDE, anything that speaks MCP) and every backend MCP server you own. From the&lt;br&gt;
host's point of view Switchboard is an MCP server, but instead of exposing&lt;br&gt;
your full tool catalog it exposes exactly two meta-tools.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;find_tools(request)&lt;/code&gt; takes the user's request in plain language and returns a&lt;br&gt;
small, dynamically-sized set of relevant tools: zero, one, or a handful, never&lt;br&gt;
a fixed top-k and never the whole catalog. &lt;code&gt;invoke(tool_id, args)&lt;/code&gt; then calls&lt;br&gt;
the selected tool against whichever backend actually owns it.&lt;/p&gt;

&lt;p&gt;Everything else is the router's problem rather than the host's: which backend&lt;br&gt;
hosts which tool, how many servers are connected, how the catalog changes over&lt;br&gt;
time.&lt;/p&gt;
&lt;h2&gt;
  
  
  How find_tools actually decides
&lt;/h2&gt;

&lt;p&gt;This is the part that took the most iteration, because search alone is not&lt;br&gt;
enough. A naive nearest-neighbor lookup either returns too much, which defeats&lt;br&gt;
the purpose, or misses the right tool because of vocabulary mismatch between&lt;br&gt;
how a user asks and how a tool is described.&lt;/p&gt;

&lt;p&gt;The retrieval pipeline runs in four stages.&lt;/p&gt;

&lt;p&gt;It starts with concurrent dense and sparse search against a Pinecone vector&lt;br&gt;
registry. Dense embeddings catch semantic similarity, so "send the report to&lt;br&gt;
finance" finds &lt;code&gt;send_email_with_attachment&lt;/code&gt;. Sparse keyword-style search&lt;br&gt;
catches the exact-term matches dense embeddings sometimes miss: API names,&lt;br&gt;
service identifiers, error codes. Both index round-trips are independent I/O,&lt;br&gt;
so they run in parallel and cost one round-trip of wall-clock rather than two.&lt;/p&gt;

&lt;p&gt;Next, a cosine-similarity gate filters out anything too far from the query's&lt;br&gt;
intent before it ever reaches the LLM. It is cheap and deterministic, and it&lt;br&gt;
keeps obviously irrelevant tools from wasting judge tokens.&lt;/p&gt;

&lt;p&gt;Then an LLM judge does the expensive part properly: capability-fit selection,&lt;br&gt;
de-duplication across near-identical tools from different backends, ordering&lt;br&gt;
by execution sequence, and deciding to ask a clarifying question instead of&lt;br&gt;
guessing when the request is genuinely ambiguous.&lt;/p&gt;

&lt;p&gt;Finally, the result count is fully dynamic. Most routing systems force a fixed&lt;br&gt;
top-k. We don't. Some queries need zero tools, some need exactly one, and a&lt;br&gt;
few legitimately need several, so the judge decides rather than a hardcoded&lt;br&gt;
number.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;find_tools&lt;/code&gt; comes back empty there is a fallback, &lt;code&gt;find_more_tools&lt;/code&gt;, that&lt;br&gt;
relaxes the gate before giving up and clarifying. It is a second chance before&lt;br&gt;
the decision gets punted back to the user.&lt;/p&gt;
&lt;h2&gt;
  
  
  How we measured accuracy, and why the grader is strict
&lt;/h2&gt;

&lt;p&gt;The headline is &lt;strong&gt;85 to 90%&lt;/strong&gt; accuracy on a held-out suite of 70 realistic&lt;br&gt;
multi-tool queries, against a roughly 21% naive keyword-search baseline on the&lt;br&gt;
same suite. The baseline matters more than the headline, because it is the&lt;br&gt;
difference between "the pipeline works" and "an embedding lookup would have&lt;br&gt;
done fine."&lt;/p&gt;

&lt;p&gt;The grader is deliberately unforgiving. A case passes only if every required&lt;br&gt;
tool appears in the result and no forbidden tool appears, where &lt;code&gt;forbidden:&lt;br&gt;
["*"]&lt;/code&gt; means the correct answer is no tools at all. For &lt;code&gt;allow_any_of&lt;/code&gt; groups,&lt;br&gt;
which are sets of interchangeable tools, exactly one member must be present.&lt;br&gt;
Returning two valid alternatives is a failure rather than a hedge, because it&lt;br&gt;
pushes the choice back onto the model we are trying to protect. When an order&lt;br&gt;
is specified, the returned tools have to contain it as a subsequence, with&lt;br&gt;
relative order preserved and unrelated tools allowed to interleave. And when a&lt;br&gt;
case is marked &lt;code&gt;should_clarify&lt;/code&gt;, the router must return zero tools and set the&lt;br&gt;
clarify flag, which alone decides the case.&lt;/p&gt;

&lt;p&gt;That last rule is the one I would defend hardest. An accuracy metric that does&lt;br&gt;
not reward asking instead of guessing quietly incentivizes overconfident wrong&lt;br&gt;
answers, because the model learns that any answer beats admitting ambiguity.&lt;br&gt;
Ours treats correct abstention as a pass, which means the 88% includes the&lt;br&gt;
system knowing what it does not know.&lt;/p&gt;

&lt;p&gt;One honest limitation: cases requiring the same tool to be called multiple&lt;br&gt;
times are structurally unsatisfiable, because &lt;code&gt;route()&lt;/code&gt; emits each tool id&lt;br&gt;
once. We count those as real misses rather than excluding them, so the&lt;br&gt;
reported number is a floor rather than a flattered figure.&lt;/p&gt;
&lt;h2&gt;
  
  
  The decisions that mattered, and the alternatives we rejected
&lt;/h2&gt;

&lt;p&gt;Most of the interesting engineering here is not in what the pipeline does. It&lt;br&gt;
is in the four or five places where we deliberately chose the harder option.&lt;/p&gt;
&lt;h3&gt;
  
  
  Dynamic-K over fixed top-k
&lt;/h3&gt;

&lt;p&gt;The obvious design is "return the top 5 matches." We rejected it because a&lt;br&gt;
fixed k is wrong in both directions at once. For a query that needs one tool,&lt;br&gt;
top-5 injects four irrelevant schemas and reintroduces exactly the&lt;br&gt;
selection-confusion problem the router exists to solve. For a genuine&lt;br&gt;
multi-step request, top-5 might truncate a plan that needed six. And for an&lt;br&gt;
out-of-scope question, top-5 confidently returns five wrong tools.&lt;/p&gt;

&lt;p&gt;Making k dynamic means the judge has to answer "how many?" as well as&lt;br&gt;
"which?", which is a harder prompt and a harder thing to evaluate. It was&lt;br&gt;
still the right trade. Our measured average is 1.2 tools per call out of 142,&lt;br&gt;
which no fixed k would have produced.&lt;/p&gt;
&lt;h3&gt;
  
  
  One hard-coded rule, and only one
&lt;/h3&gt;

&lt;p&gt;There is a real temptation to encode catalog-specific heuristics, something&lt;br&gt;
like "queries mentioning 'log' should prefer the observability server." We&lt;br&gt;
kept exactly one code-side rule: an absolute cosine floor for out-of-scope&lt;br&gt;
detection.&lt;/p&gt;

&lt;p&gt;The reasoning is a division of labor. That floor is the one judgment the LLM&lt;br&gt;
cannot make cheaply. To know that nothing in the catalog fits, a judge would&lt;br&gt;
have to see the entire catalog, which is precisely the cost we are&lt;br&gt;
eliminating. A cosine threshold answers it in one vector op. Everything else&lt;br&gt;
(which tools, dedup, ambiguity, ordering) is semantic work and gets delegated&lt;br&gt;
to the judge. Rules that encode catalog specifics would need rewriting every&lt;br&gt;
time someone connects a new backend, which defeats the pluggability goal.&lt;/p&gt;
&lt;h3&gt;
  
  
  Judge order, not score order
&lt;/h3&gt;

&lt;p&gt;Early on we sorted the returned tools by cosine score. That is wrong for&lt;br&gt;
multi-step requests. "Pull last week's errors and open a ticket for the worst&lt;br&gt;
one" has an inherent execution order that has nothing to do with which tool&lt;br&gt;
embeds closer to the query. The judge reasons about sequence, so the judge's&lt;br&gt;
output order is the router's output order, and we explicitly do not re-sort.&lt;/p&gt;
&lt;h3&gt;
  
  
  Filter unhealthy backends before the judge, not after
&lt;/h3&gt;

&lt;p&gt;The intuitive design for backend health is retry-on-failure: select a tool,&lt;br&gt;
call it, and handle the error if the backend is down. We invert it. A backend&lt;br&gt;
marked down has its tools excluded from the candidate set before the judge&lt;br&gt;
sees them, re-evaluated on every call.&lt;/p&gt;

&lt;p&gt;This matters because of the specific failure it prevents. The judge picks the&lt;br&gt;
perfect tool, explains its reasoning, and then &lt;code&gt;invoke&lt;/code&gt; fails, so the user&lt;br&gt;
gets a wrong-looking answer for a right-looking decision. Filtering early&lt;br&gt;
means the judge selects the best reachable tool instead, possibly a&lt;br&gt;
second-choice tool on a healthy backend, which is the correct behavior.&lt;/p&gt;

&lt;p&gt;We also chose reactive health detection over a heartbeat loop. A backend gets&lt;br&gt;
marked down on an actual failed call and retried on next use. A separate&lt;br&gt;
polling process is one more thing to keep in sync with reality, and health&lt;br&gt;
checks that themselves flake produce false "down" states.&lt;/p&gt;
&lt;h3&gt;
  
  
  Deprecated tools stay fetchable
&lt;/h3&gt;

&lt;p&gt;Our first filter dropped every tool marked deprecated. We removed that clause&lt;br&gt;
deliberately, because a request like "export it in the legacy format"&lt;br&gt;
specifically needs the deprecated version. Governance filtering still applies,&lt;br&gt;
with PII-touching tools gated behind an explicit flag and destructive tools&lt;br&gt;
behind another, but deprecation is metadata for the judge to weigh rather than&lt;br&gt;
a hard exclusion for code to enforce.&lt;/p&gt;
&lt;h2&gt;
  
  
  Keeping the registry honest
&lt;/h2&gt;

&lt;p&gt;A tool router is only as good as its index, and tool catalogs are not static.&lt;br&gt;
Backends add tools, deprecate others, and change descriptions. Switchboard&lt;br&gt;
runs a background ingestion pipeline, independent of request-time traffic,&lt;br&gt;
which polls every registered backend on an interval and also reacts&lt;br&gt;
immediately to admin-triggered registration or removal. Same pipeline, two&lt;br&gt;
triggers.&lt;/p&gt;

&lt;p&gt;Each tool runs through enrichment: deterministic structural and length checks,&lt;br&gt;
LLM-based description-quality and off-topic detection, TF-IDF duplicate and&lt;br&gt;
outlier detection, and incremental LLM clustering against a persisted&lt;br&gt;
taxonomy.&lt;/p&gt;

&lt;p&gt;There is a circuit breaker on the LLM enrichment step. If fewer than half the&lt;br&gt;
tools survive validation, the pass is treated as an outage and the registry is&lt;br&gt;
left untouched. This was a deliberate answer to a real failure mode, since a&lt;br&gt;
flaky upstream model silently emptying your tool catalog is worse than having&lt;br&gt;
no enrichment at all.&lt;/p&gt;

&lt;p&gt;Before touching Pinecone the pipeline diffs against a Redis hash cache, so a&lt;br&gt;
full re-poll of a backend only costs an embedding call for what actually&lt;br&gt;
changed rather than the whole catalog every time. Each tool is then embedded&lt;br&gt;
as three views (name, description, parameters) into dense and sparse Pinecone&lt;br&gt;
indexes.&lt;/p&gt;

&lt;p&gt;That three-view embedding deserves a note. A single embedding of a&lt;br&gt;
concatenated tool blob dilutes each signal, and a distinctive parameter name&lt;br&gt;
gets averaged into prose. Embedding name, description, and parameters&lt;br&gt;
separately, then collapsing to the best-scoring view per tool at query time,&lt;br&gt;
means a query that matches strongly on one dimension still surfaces the tool&lt;br&gt;
instead of being averaged into mediocrity.&lt;/p&gt;
&lt;h2&gt;
  
  
  Plugging in a new backend without touching a running system
&lt;/h2&gt;

&lt;p&gt;This was a hard requirement from day one. Adding or removing a backend MCP&lt;br&gt;
server should never mean a restart, a redeploy, or a config-file hand-edit&lt;br&gt;
under load.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://&amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;/admin/backends &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Admin-Token: &lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_ADMIN_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"server_id":"firecrawl","transport":"http",
       "url":"https://my-mcp-server.example.com/mcp",
       "auth_header":"Authorization","auth_value":"Bearer &amp;lt;token&amp;gt;"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One authenticated call hot-connects the backend. Every tool it exposes is&lt;br&gt;
validated and embedded through the same enrichment pipeline the interval&lt;br&gt;
process uses, &lt;code&gt;backends.json&lt;/code&gt; is updated, and the moment the call returns&lt;br&gt;
those tools are live and discoverable through &lt;code&gt;find_tools&lt;/code&gt;. No restart, no&lt;br&gt;
downtime for existing traffic. Removal is the mirror image: one &lt;code&gt;DELETE&lt;/code&gt; call&lt;br&gt;
clears Pinecone, Redis, and the config in one step, so you never leave&lt;br&gt;
orphaned vectors behind.&lt;/p&gt;

&lt;p&gt;Registration is serialized behind a lock so two concurrent calls cannot race&lt;br&gt;
on shared state. It is an unglamorous detail that matters the first time two&lt;br&gt;
people onboard backends simultaneously.&lt;/p&gt;

&lt;p&gt;On the host side, plugging in is equally uneventful. Switchboard speaks&lt;br&gt;
standard MCP over HTTP, so it is just another entry in &lt;code&gt;mcp.json&lt;/code&gt;. We wired it&lt;br&gt;
into Claude Code with zero special prompting, no "please use the router"&lt;br&gt;
instruction needed. A plain question like "show me the error logs for&lt;br&gt;
checkout-api in the last hour" triggers &lt;code&gt;find_tools&lt;/code&gt; automatically, gets back&lt;br&gt;
exactly the one tool that matters, and resolves. That was the actual bar we&lt;br&gt;
were aiming for. The router should be invisible when it is working.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this actually saves
&lt;/h2&gt;

&lt;p&gt;We instrumented every routing decision end to end. Every &lt;code&gt;find_tools&lt;/code&gt; call&lt;br&gt;
writes a telemetry row recording the routed-versus-full-catalog token&lt;br&gt;
estimate, search latency, judge latency, and outcome. Those writes are&lt;br&gt;
fire-and-forget, so they never block the response even if the DB write fails.&lt;br&gt;
A Streamlit dashboard reads that table live, and nothing on it is hardcoded.&lt;/p&gt;

&lt;p&gt;Measured against the 142-tool catalog, routing cuts input tokens per call by&lt;br&gt;
about 99.6% versus sending the full catalog every time, which works out to&lt;br&gt;
roughly 28,700 tokens saved per call (about 124 tokens routed against about&lt;br&gt;
28,860 for the full catalog dump). The average call selects 1.2 tools out of&lt;br&gt;
142 available, which is the dynamic-k judge working as intended rather than a&lt;br&gt;
fixed top-k masquerading as precision.&lt;/p&gt;

&lt;p&gt;End-to-end latency averages about 6.5 seconds, with roughly 4.9 seconds of&lt;br&gt;
that sitting in the LLM judge step. That is the honest cost of doing selection&lt;br&gt;
properly instead of guessing. The clarify rate is about 12%, meaning genuinely&lt;br&gt;
ambiguous requests get kicked back to the user instead of the router guessing&lt;br&gt;
and invoking the wrong tool.&lt;/p&gt;

&lt;p&gt;Projected from those per-call savings, at 1,000 calls a day (one active team's&lt;br&gt;
traffic) and Sonnet-class input pricing, that comes to roughly &lt;strong&gt;10.5 billion&lt;br&gt;
tokens&lt;/strong&gt; and about &lt;strong&gt;$31,000 a year&lt;/strong&gt; in avoided token spend. The per-call &lt;strong&gt;saving&lt;/strong&gt; is&lt;br&gt;
measured. The annual figure is that measurement multiplied by an assumed call&lt;br&gt;
volume, and it scales linearly with whatever volume your deployment actually&lt;br&gt;
sees.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a tool router matters more than it looks like it should
&lt;/h2&gt;

&lt;p&gt;It is tempting to file this under prompt optimization. It isn't. As agentic&lt;br&gt;
systems connect to more of an organization's real infrastructure, three things&lt;br&gt;
become true at once.&lt;/p&gt;

&lt;p&gt;Tool catalogs only grow, because nobody removes MCP servers once they are&lt;br&gt;
useful, they just add more. Context windows are not free even when they are&lt;br&gt;
technically large enough, since every token spent on tool schemas is a token&lt;br&gt;
not spent on reasoning, retrieved context, or conversation history. And&lt;br&gt;
selection errors compound, because a wrong tool call against a real backend&lt;br&gt;
(sending an email, querying production data, triggering a workflow) is not a&lt;br&gt;
cosmetic bug. It is an action with consequences.&lt;/p&gt;

&lt;p&gt;A router answers all three at once. It decouples how many tools exist from how&lt;br&gt;
many tools the model has to reason about, and it puts a validating,&lt;br&gt;
explainable decision layer between "the user asked for something" and "a real&lt;br&gt;
backend got called."&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling the edges
&lt;/h2&gt;

&lt;p&gt;A few decisions exist specifically because the happy path is not where systems&lt;br&gt;
like this actually fail.&lt;/p&gt;

&lt;p&gt;If the judge is unavailable or its call fails, routing falls back to dense&lt;br&gt;
top-5 rather than returning nothing, because a degraded answer beats an&lt;br&gt;
outage. The admin API 404s entirely unless an admin token is configured, so&lt;br&gt;
there is no insecure-by-default state to accidentally ship. When &lt;code&gt;find_tools&lt;/code&gt;&lt;br&gt;
cannot confidently resolve intent the system asks rather than picking the&lt;br&gt;
closest-sounding tool and hoping. Instrumentation writes are fire-and-forget,&lt;br&gt;
so a Postgres hiccup degrades your dashboard and never your response latency.&lt;br&gt;
And ingestion is idempotent and incremental, so backends can be re-polled&lt;br&gt;
constantly without re-embedding a catalog that has not changed.&lt;/p&gt;

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

&lt;p&gt;The current bottleneck is not accuracy. It is the 4.9-second judge step, plus&lt;br&gt;
the fact that the system learns nothing from its own traffic. Both are&lt;br&gt;
addressable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Draining admin-triggered ingestion asynchronously.&lt;/strong&gt; Registering a new&lt;br&gt;
backend currently blocks the API call until every tool is validated and&lt;br&gt;
embedded. That is honest but not ideal at scale. The target is to enqueue the&lt;br&gt;
job and let the background worker drain it so registration returns&lt;br&gt;
immediately. The work is already queue-shaped; the admin path just drains its&lt;br&gt;
own jobs synchronously today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cutting judge latency with a cascade.&lt;/strong&gt; Roughly 75% of end-to-end latency is&lt;br&gt;
one LLM call, and most queries are not close calls. When the top candidate&lt;br&gt;
leads by a wide cosine margin and no near-twins exist, a small fast model or&lt;br&gt;
even a deterministic path could resolve it, escalating to the full judge only&lt;br&gt;
for genuinely contested cases. The measurement to run first is what fraction&lt;br&gt;
of traffic is actually contested. If it turns out to be 20%, a cascade cuts&lt;br&gt;
average latency substantially at close to zero accuracy cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Query rewriting before retrieval.&lt;/strong&gt; Terse or jargon-heavy requests like&lt;br&gt;
"502s on checkout" embed poorly against prose tool descriptions. Expanding the&lt;br&gt;
query before it hits Pinecone closes the vocabulary gap between how users ask&lt;br&gt;
and how tools are documented. The trade-off is another model call in the hot&lt;br&gt;
path, which argues for doing it only when the first retrieval scores weakly.&lt;br&gt;
Rewrite as a fallback, not a default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hard negative mining from real clarify and miss cases.&lt;/strong&gt; Every clarify is a&lt;br&gt;
labeled example of "these tools looked similar but the query was ambiguous,"&lt;br&gt;
and every user retry after a bad selection is a labeled negative. Mining those&lt;br&gt;
into a fine-tune or a re-ranker trains on our own traffic distribution instead&lt;br&gt;
of generic semantic similarity. This is the highest-leverage accuracy lever we&lt;br&gt;
have, because it compounds. More traffic means better signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feedback-loop learning from invoke outcomes.&lt;/strong&gt; We currently log which tools&lt;br&gt;
were selected, but not whether the invocation succeeded or whether the user&lt;br&gt;
immediately tried something else. Closing that loop, from selection through&lt;br&gt;
invocation to outcome, turns the telemetry table from a cost dashboard into a&lt;br&gt;
training set. It is mostly a schema change and a follow-up write.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-tenant re-ranking.&lt;/strong&gt; Usage patterns differ sharply by deployment. A team&lt;br&gt;
that lives in observability tooling should see different ranking than one that&lt;br&gt;
lives in CRM tools. A lightweight per-tenant prior over the global model&lt;br&gt;
captures most of that gain without maintaining separate indexes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tightening the enrichment quality bar.&lt;/strong&gt; Bad retrieval is often a symptom of&lt;br&gt;
bad tool descriptions rather than a bad retriever. Flagging ambiguous or&lt;br&gt;
poorly-described tools at ingestion time, and reporting them back to the&lt;br&gt;
backend owner, fixes the problem at its source instead of compensating for it&lt;br&gt;
at query time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-system observability.&lt;/strong&gt; The dashboard reports router cost, latency,&lt;br&gt;
and outcome. Unifying it with orchestrator-side telemetry, whether&lt;br&gt;
OTel/SigNoz/Langfuse or a shared events table, would let cost and accuracy be&lt;br&gt;
viewed end to end rather than only at the router boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The interesting problem in agentic AI right now is not whether the model can&lt;br&gt;
call a tool. It is whether the model can call the right tool, reliably, as the&lt;br&gt;
number of tools you connect grows past what any single prompt should ever have&lt;br&gt;
to describe. That is an infrastructure problem rather than a prompting&lt;br&gt;
problem, and infrastructure problems deserve infrastructure solutions: a&lt;br&gt;
registry, a retrieval pipeline, a validation layer, and telemetry that tells&lt;br&gt;
you the truth about whether it is working.&lt;/p&gt;

&lt;p&gt;That is Switchboard. One operator, any number of lines, and a host that never&lt;br&gt;
has to know the number.&lt;/p&gt;

</description>
      <category>llmops</category>
      <category>ai</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
