<?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: Nitish kumar</title>
    <description>The latest articles on DEV Community by Nitish kumar (@nitish-builds).</description>
    <link>https://dev.to/nitish-builds</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%2F4059254%2F0cee8545-4ef1-42cc-b217-7b6fbf4b230d.png</url>
      <title>DEV Community: Nitish kumar</title>
      <link>https://dev.to/nitish-builds</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nitish-builds"/>
    <language>en</language>
    <item>
      <title>Your agent doesn't need more tools, it needs better tool descriptions</title>
      <dc:creator>Nitish kumar</dc:creator>
      <pubDate>Sun, 02 Aug 2026 16:31:35 +0000</pubDate>
      <link>https://dev.to/nitish-builds/your-agent-doesnt-need-more-tools-it-needs-better-tool-descriptions-4d28</link>
      <guid>https://dev.to/nitish-builds/your-agent-doesnt-need-more-tools-it-needs-better-tool-descriptions-4d28</guid>
      <description>&lt;p&gt;Most agent failures I've debugged weren't reasoning failures. The model reasoned fine. It just picked the wrong tool, because the tool description didn't tell it what it needed to know.&lt;/p&gt;

&lt;p&gt;This is an under-discussed problem, and it gets worse the more integrations you add. Here's what we've learned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Say your agent has four calendar-ish tools available:&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="p"&gt;[&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"calendar_create_event"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Creates a calendar event"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"calendar_quick_add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Adds an event from text"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scheduling_book_slot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Books a scheduling slot"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"meeting_schedule"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Schedules a meeting"&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="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Now: "Book me 30 minutes with Sarah on Thursday."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Which one? You don't know either, and you have context the model doesn't. The model will pick one, it'll probably work about 60% of the time, and when it's wrong the failure will be silent — an event created in the wrong system that nobody notices for a week.&lt;/p&gt;

&lt;p&gt;Roughly 40% of our agent failures traced back to tool selection, not reasoning. That was surprising and it changed where we spent engineering time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why OpenAPI schemas aren't enough&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A schema tells you what a tool accepts. It doesn't tell you:&lt;/p&gt;

&lt;p&gt;What the tool is for — the use case, not the signature&lt;br&gt;
When it's appropriate versus the four adjacent tools that look identical&lt;br&gt;
Whether it's reversible — can you undo it, and how&lt;br&gt;
What it costs — a rate-limited call and a free one shouldn't be equally attractive&lt;br&gt;
What it assumes — does it require the user's calendar to be connected first?&lt;/p&gt;

&lt;p&gt;Documentation doesn't have this either, because docs are written for humans who already know which product they're integrating with. The model is choosing between products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What a description for an agent looks like&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's the template we converged on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;calendar_create_event&lt;/span&gt;
&lt;span class="na"&gt;purpose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;Create an event on the user's primary Google Calendar when you already&lt;/span&gt;
  &lt;span class="s"&gt;know the exact date, time, and duration.&lt;/span&gt;
&lt;span class="na"&gt;use_when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;The time is fully specified ("Thursday 2pm for 30 minutes")&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;The user owns the calendar being written to&lt;/span&gt;
&lt;span class="na"&gt;do_not_use_when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;The time is approximate or needs negotiation → use scheduling_book_slot&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Inviting external participants who need to pick → use scheduling_book_slot&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;The user said "find a time" rather than naming one&lt;/span&gt;
&lt;span class="na"&gt;reversible&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes — event can be deleted via calendar_delete_event&lt;/span&gt;
&lt;span class="na"&gt;side_effects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sends invitations to all attendees immediately&lt;/span&gt;
&lt;span class="na"&gt;cost&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;low&lt;/span&gt;
&lt;span class="na"&gt;requires&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;google_calendar connection active&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The do_not_use_when block with explicit redirects is doing most of the work. It's not describing this tool — it's describing the boundary between this tool and its neighbours, which is exactly what the model is uncertain about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generating these at scale&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Writing these by hand doesn't scale past a few dozen tools. Our loop:&lt;/p&gt;

&lt;p&gt;Generate a first draft from the schema and whatever docs exist, using a model.&lt;br&gt;
Ship it. It'll be mediocre.&lt;br&gt;
Log every selection, along with what the agent was asked and what it picked.&lt;br&gt;
When a selection is wrong, don't patch the prompt — patch the description of the tool that should have been chosen and the one that was.&lt;br&gt;
Over time the descriptions become a record of every mistake the system has made.&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;record_misselection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chosen_tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correct_tool&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# The fix goes in the tool metadata, not the system prompt.
&lt;/span&gt;    &lt;span class="c1"&gt;# System prompt fixes don't generalize; description fixes do.
&lt;/span&gt;    &lt;span class="n"&gt;boundary_note&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;infer_distinguishing_feature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chosen_tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correct_tool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;correct_tool&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;use_when&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;boundary_note&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;chosen_tool&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;do_not_use_when&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;boundary_note&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; → use &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;correct_tool&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That step 5 is the important one. Every instinct says to fix selection errors by adding rules to the system prompt. Resist it. Prompt fixes are global, they collide with each other, and they don't survive a model upgrade. Description fixes are local to the tools involved and they compose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A cheap win: prune before you send&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Related, and it saves real money: don't send every tool schema on every step of the loop. Schemas can be a large share of your token spend, because they're re-sent on every iteration, not once per run.&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;relevant_tools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;all_tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Cheap pre-filter — embedding similarity between the current
&lt;/span&gt;    &lt;span class="c1"&gt;# goal and each tool's `purpose` field
&lt;/span&gt;    &lt;span class="n"&gt;scored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rank_by_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;all_tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;purpose&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scored&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;always_include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_tools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fewer tools in context also means better selection accuracy, so this helps twice. Most frameworks encourage handing the agent everything up front; that's the wrong default past about a dozen tools.&lt;/p&gt;

&lt;p&gt;The bigger point&lt;/p&gt;

&lt;p&gt;The industry is competing on model quality and integration count. Neither is where agent reliability actually comes from right now.&lt;/p&gt;

&lt;p&gt;The semantic layer — what each tool means, when it applies, how it differs from its neighbours — is unglamorous curation work that nobody ships as a feature. It's also, in our experience, the difference between an agent that demos well and one that runs unattended.&lt;/p&gt;

&lt;p&gt;If I were starting again I'd treat it as core product from day one instead of infrastructure to get through.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Building DeskFerry — agents across 1,500+ integrations, which is how we ended up caring about this. Questions welcome below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>python</category>
    </item>
  </channel>
</rss>
