<?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: Hedi Manai</title>
    <description>The latest articles on DEV Community by Hedi Manai (@hedimanai).</description>
    <link>https://dev.to/hedimanai</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%2F3921983%2F211b83f8-97df-4d81-868e-b3171d62fa23.jpg</url>
      <title>DEV Community: Hedi Manai</title>
      <link>https://dev.to/hedimanai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hedimanai"/>
    <language>en</language>
    <item>
      <title>Your AI agent's smallest diffs are its most dangerous</title>
      <dc:creator>Hedi Manai</dc:creator>
      <pubDate>Mon, 13 Jul 2026 00:18:31 +0000</pubDate>
      <link>https://dev.to/hedimanai/your-ai-agents-smallest-diffs-are-its-most-dangerous-jp5</link>
      <guid>https://dev.to/hedimanai/your-ai-agents-smallest-diffs-are-its-most-dangerous-jp5</guid>
      <description>&lt;p&gt;Last month, an AI coding agent handed me a beautiful fix. Five lines. Elegant. It reused an existing helper, matched the codebase style, compiled on the first try. Exactly the kind of diff we've all learned to praise since "make the agent write less code" became the standard advice.&lt;/p&gt;

&lt;p&gt;It was also completely untested, and it sat on a password-recovery path.&lt;/p&gt;

&lt;p&gt;That diff taught me something I now consider the central problem of AI-assisted coding in 2026: &lt;strong&gt;we've spent a year teaching agents to write less code, and almost no time teaching them to prove the code they kept actually holds.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The two failure modes
&lt;/h2&gt;

&lt;p&gt;Every AI coding agent fails in one of two directions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure mode #1: the over-build.&lt;/strong&gt; You ask for a date comparison; you get a new dependency, a &lt;code&gt;ValidationService&lt;/code&gt; class, and a config layer. This one is well known — it's why minimal-code prompts and skills became popular, and they genuinely work on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure mode #2: the confidently small diff.&lt;/strong&gt; Minimal, clean, written after reading &lt;em&gt;half&lt;/em&gt; the flow, verified never — dropped onto a path that handles money, auth, or user data. It compiles. It demos. It detonates in week three.&lt;/p&gt;

&lt;p&gt;Here's the uncomfortable part: &lt;strong&gt;fixing #1 aggressively makes #2 more likely.&lt;/strong&gt; When the objective function is "shortest diff," the first things to quietly disappear are edge-case handling, failure-path tests, and the guard clause that looked optional. The diff gets smaller. The blast radius doesn't.&lt;/p&gt;

&lt;p&gt;A five-line change to a payment path is more dangerous than a four-hundred-line internal script that runs once. &lt;strong&gt;Code size is not risk. Blast radius is risk.&lt;/strong&gt; Yet almost every skill and prompt in this category optimizes for size alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a guard does differently
&lt;/h2&gt;

&lt;p&gt;This is why I built &lt;a href="https://github.com/hedimanai-pro/guardsman" rel="noopener noreferrer"&gt;&lt;strong&gt;Guardsman&lt;/strong&gt;&lt;/a&gt; 💂 — an open-source skill that behaves less like a minimalist and more like the royal guard in front of the palace: nothing passes the post unchallenged, and the level of challenge depends on what's behind the gate.&lt;/p&gt;

&lt;p&gt;Three duties, on every task:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Read the standing orders.&lt;/strong&gt; Before writing anything, a deterministic script detects the repo's real conventions — language, formatter, linter, actual test command — and the agent greps how &lt;em&gt;this&lt;/em&gt; codebase already solves the nearest similar problem. Your patterns win over its defaults. Always.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Set the threat level.&lt;/strong&gt; Every change gets a risk tier before a single line is written:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;The challenge&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;trivial&lt;/td&gt;
&lt;td&gt;internal one-off&lt;/td&gt;
&lt;td&gt;one manual run, output shown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;standard&lt;/td&gt;
&lt;td&gt;everyday feature work&lt;/td&gt;
&lt;td&gt;one check written &lt;strong&gt;and executed&lt;/strong&gt; this turn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sensitive&lt;/td&gt;
&lt;td&gt;money, auth, user data in the blast radius&lt;/td&gt;
&lt;td&gt;a test exercising the &lt;strong&gt;failure path&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;critical&lt;/td&gt;
&lt;td&gt;getting it wrong is an incident&lt;/td&gt;
&lt;td&gt;full coverage, or the gap is surfaced as a blocker&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two rules keep the tiers honest: when signals disagree, the higher tier wins — and the agent never infers a downgrade just because the code looks simple. A confident walk is not a countersign.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Issue the challenge.&lt;/strong&gt; Non-trivial logic isn't "done" when the code is written. It's done when the check behind it was actually &lt;em&gt;run&lt;/em&gt;, in the session, with output shown. A test that was written but never executed doesn't count. A promise to test later doesn't count.&lt;/p&gt;

&lt;h2&gt;
  
  
  The logbook: tech debt that can't hide
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;TODO&lt;/code&gt; comments rot. Nobody greps them, nobody triages them. Guardsman replaces them with structured watch-log entries:&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="c1"&gt;# guardsman: retry capped at 3, no backoff config | severity:med | revisit:second-caller-appears | cost:none
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Severity and revisit trigger are both &lt;strong&gt;required&lt;/strong&gt; — a shortcut nobody can triage is worse than an unmarked one, because it creates false confidence that it's tracked. A bundled scanner harvests the whole repo and reports open entries by severity, overdue first. Your deferred work stops being folklore and becomes a list with dates on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I expect skepticism about
&lt;/h2&gt;

&lt;p&gt;Guardsman ships with a rule I haven't seen in this category: &lt;strong&gt;it never invents numbers about your repo.&lt;/strong&gt; It will not tell you it "saved you 12,000 lines" — there is no unbuilt baseline of your codebase to diff against, so no tool can honestly compute that figure. Its status mode reports only what is directly countable, live, or says "nothing computed yet."&lt;/p&gt;

&lt;p&gt;And that rule applies to the project itself, which is why I'm launching with &lt;strong&gt;zero benchmark numbers&lt;/strong&gt;. What the repo contains instead is a fully &lt;a href="https://github.com/hedimanai-pro/guardsman/tree/main/benchmarks" rel="noopener noreferrer"&gt;pre-registered protocol&lt;/a&gt;: 12 realistic tickets against a pinned open-source FastAPI + React codebase, 43 pre-registered guard checks (no user enumeration on the recovery endpoint, webhook signature verified before any field is trusted, idempotent retries…), four arms including the most popular minimal-code skill, and frozen scoring rules — including the disclosure rule that any published result &lt;strong&gt;must&lt;/strong&gt; include cases where Guardsman did worse, if they exist.&lt;/p&gt;

&lt;p&gt;The tasks and rubric were committed before any run, so the report can't be bent around the results afterward. Numbers get published when they're measured, with raw diffs and full transcripts. And if you don't want to wait for mine: third-party runs are welcome via PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Claude Code, two commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin marketplace add hedimanai-pro/guardsman
/plugin &lt;span class="nb"&gt;install &lt;/span&gt;guardsman@guardsman
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Codex, Cursor, or any agent that reads an &lt;code&gt;AGENTS.md&lt;/code&gt;: a condensed, self-contained adapter ships in &lt;a href="https://github.com/hedimanai-pro/guardsman/blob/main/adapters/AGENTS.md" rel="noopener noreferrer"&gt;&lt;code&gt;adapters/AGENTS.md&lt;/code&gt;&lt;/a&gt; — the tiers, the ladder, and the challenge floors travel everywhere.&lt;/p&gt;

&lt;p&gt;Repo: &lt;strong&gt;&lt;a href="https://github.com/hedimanai-pro/guardsman" rel="noopener noreferrer"&gt;https://github.com/hedimanai-pro/guardsman&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you've been burned by a confidently small diff — I'd genuinely like to hear the story in the comments. It's the failure mode nobody benchmarks, and I'm collecting cases.&lt;/p&gt;

&lt;p&gt;No diff ships unchallenged. 💂&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Hedi Manai — AI &amp;amp; backend engineer building reliability tooling for AI agents. Also the author of &lt;a href="https://github.com/hedimanai-pro" rel="noopener noreferrer"&gt;ToolOps&lt;/a&gt;, middleware for agent tool-calling. Find me on &lt;a href="https://www.linkedin.com/in/hedimanai/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and &lt;a href="https://x.com/hedi_manaii" rel="noopener noreferrer"&gt;X&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I really appreciate it!</title>
      <dc:creator>Hedi Manai</dc:creator>
      <pubDate>Tue, 12 May 2026 21:16:27 +0000</pubDate>
      <link>https://dev.to/hedimanai/i-really-appreciate-it-28g3</link>
      <guid>https://dev.to/hedimanai/i-really-appreciate-it-28g3</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/bessiegannon/my-langgraph-agent-was-hammering-the-same-api-endpoints-40x-per-run-solved-it-with-toolops-1agp" class="crayons-story__hidden-navigation-link"&gt;My LangGraph agent was hammering the same API endpoints 40 per run. Solved it with ToolOps&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/bessiegannon" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3927938%2Ff93c1d28-531a-4045-a846-2f11d7d165cc.png" alt="bessiegannon profile" class="crayons-avatar__image" width="400" height="400"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/bessiegannon" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Bessie Gannon
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Bessie Gannon
                
              
              &lt;div id="story-author-preview-content-3658635" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/bessiegannon" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3927938%2Ff93c1d28-531a-4045-a846-2f11d7d165cc.png" class="crayons-avatar__image" alt="" width="400" height="400"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Bessie Gannon&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/bessiegannon/my-langgraph-agent-was-hammering-the-same-api-endpoints-40x-per-run-solved-it-with-toolops-1agp" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 12&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/bessiegannon/my-langgraph-agent-was-hammering-the-same-api-endpoints-40x-per-run-solved-it-with-toolops-1agp" id="article-link-3658635"&gt;
          My LangGraph agent was hammering the same API endpoints 40 per run. Solved it with ToolOps
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/agents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;agents&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/api"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;api&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/performance"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;performance&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/bessiegannon/my-langgraph-agent-was-hammering-the-same-api-endpoints-40x-per-run-solved-it-with-toolops-1agp" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/bessiegannon/my-langgraph-agent-was-hammering-the-same-api-endpoints-40x-per-run-solved-it-with-toolops-1agp#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>One Decorator Away From Production-Ready AI Agents</title>
      <dc:creator>Hedi Manai</dc:creator>
      <pubDate>Sun, 10 May 2026 11:22:17 +0000</pubDate>
      <link>https://dev.to/hedimanai/one-decorator-away-from-production-ready-ai-agents-28mp</link>
      <guid>https://dev.to/hedimanai/one-decorator-away-from-production-ready-ai-agents-28mp</guid>
      <description>&lt;p&gt;Every agent developer hits the same wall.&lt;/p&gt;

&lt;p&gt;The demo works. Then it goes to production — and the cracks show up fast. No retry logic when APIs fail. Identical queries hammering your LLM endpoint over and over. No visibility into what's actually happening. And before long, you're writing the same cache managers, retry decorators, and circuit-breaker wrappers you wrote on the last project.&lt;/p&gt;

&lt;p&gt;ToolOps is built to make that boilerplate disappear.&lt;/p&gt;




&lt;p&gt;What It Is:&lt;br&gt;
ToolOps is a framework-agnostic Python SDK that wraps any async function in a single decorator, adding caching, retries, circuit breakers, request coalescing, and observability — with zero changes to your business logic.&lt;/p&gt;

&lt;p&gt;Think of it the way a service mesh works for microservices: the infrastructure wraps around your code without touching it.&lt;/p&gt;




&lt;p&gt;What It Does:&lt;br&gt;
&lt;strong&gt;Caching that actually fits production.&lt;/strong&gt; ToolOps supports in-memory caching for speed, file-based for lightweight persistence, and PostgreSQL for durable, distributed caching shared across processes. Pick the backend that fits the function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Semantic caching for LLM calls.&lt;/strong&gt; Standard caches match on exact strings — so "weather in Paris" and "Paris weather" hit the LLM twice. ToolOps uses vector embeddings to match by meaning, collapsing semantically similar queries into a single cached result. For agents handling natural language, this can cut LLM calls by up to 90%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request coalescing.&lt;/strong&gt; When dozens of agents request the same data at once during a cache miss, ToolOps fires one real API call and returns the result to all of them. The thundering herd problem, solved automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stale-if-error fallback.&lt;/strong&gt; When an upstream service goes down, ToolOps can serve the last known good value instead of crashing your agent — exactly what you want for slowly-changing data like exchange rates or configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability out of the box.&lt;/strong&gt; Every cache hit, miss, retry, and circuit-breaker event is logged as structured JSON. Add the optional OpenTelemetry extra and you get full distributed tracing and Prometheus metrics — production-grade visibility in a few lines of setup.&lt;/p&gt;




&lt;p&gt;Works With Your Stack:&lt;br&gt;
ToolOps wraps plain Python async functions, so it drops into any framework without friction: LangChain, LangGraph, CrewAI, LlamaIndex, PydanticAI, Agno, and more. It also has native MCP support, letting you expose any decorated function as a typed MCP tool definition compatible with Claude Desktop, Cursor, and any MCP-compatible host.&lt;/p&gt;

&lt;p&gt;When you migrate frameworks — and most teams eventually do — your infrastructure layer doesn't budge.&lt;/p&gt;




&lt;p&gt;The Practical Upside:&lt;br&gt;
The core package installs with a single pip command and has zero external dependencies. Postgres, semantic caching, and OpenTelemetry support are optional extras you add only when you need them. A built-in CLI gives you live hit rates, latency stats, and backend health checks without touching your application code.&lt;/p&gt;

&lt;p&gt;Two decorators cover every case: &lt;code&gt;@readonly&lt;/code&gt; for functions that read data, &lt;code&gt;@sideeffect&lt;/code&gt; for functions that act on the world. That's the entire model.&lt;/p&gt;




&lt;p&gt;Try It:&lt;br&gt;
ToolOps is open source, Apache 2.0 licensed, and actively maintained.&lt;br&gt;
If you're building agents that need to survive real traffic, real API failures, and real costs, it's worth your time.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/hedimanai-pro/toolops" rel="noopener noreferrer"&gt;https://github.com/hedimanai-pro/toolops&lt;/a&gt; &lt;br&gt;
PyPI: &lt;a href="https://pypi.org/project/toolops/" rel="noopener noreferrer"&gt;https://pypi.org/project/toolops/&lt;/a&gt; &lt;br&gt;
Documentation: &lt;a href="https://hedimanai.vercel.app/projects/toolops.html" rel="noopener noreferrer"&gt;https://hedimanai.vercel.app/projects/toolops.html&lt;/a&gt; &lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>ToolOps: Stop Rewriting the Same Boilerplate Every Time You Build an AI Agent</title>
      <dc:creator>Hedi Manai</dc:creator>
      <pubDate>Sat, 09 May 2026 23:04:33 +0000</pubDate>
      <link>https://dev.to/hedimanai/toolops-stop-rewriting-the-same-boilerplate-every-time-you-build-an-ai-agent-363b</link>
      <guid>https://dev.to/hedimanai/toolops-stop-rewriting-the-same-boilerplate-every-time-you-build-an-ai-agent-363b</guid>
      <description>&lt;p&gt;You've built the demo. It works. The LLM responds, the tools fire, the output looks great.&lt;/p&gt;

&lt;p&gt;Then you push it to production — and everything breaks.&lt;/p&gt;

&lt;p&gt;API calls fail with no retry logic. Identical queries hammer your LLM endpoint ten times per minute, burning through credits. A single bad response cascades into an agent loop. You have no idea what's happening inside because there's nothing to look at.&lt;/p&gt;

&lt;p&gt;So you start writing infrastructure. A retry decorator here. A cache manager there. A circuit-breaker wrapper you found on Stack Overflow. Eighty lines of boilerplate — just to make one tool call production-safe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the problem ToolOps was built to solve.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Production Gap Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Building AI agents has never been easier. Frameworks like LangChain, CrewAI, and LlamaIndex get you from idea to working prototype in an afternoon. But moving that prototype to production exposes a gap that frameworks don't fill: &lt;strong&gt;the reliability, cost, and observability layer&lt;/strong&gt; that every real agent needs.&lt;/p&gt;

&lt;p&gt;Every external call your agent makes — to an LLM, an API, a database — is a tool call. In production, those calls are expensive, slow, and unreliable. Without proper infrastructure around them, you're flying blind.&lt;/p&gt;

&lt;p&gt;Most developers solve this by copy-pasting the same boilerplate across every project. ToolOps solves it with a single decorator.&lt;/p&gt;




&lt;h2&gt;
  
  
  What ToolOps Actually Does
&lt;/h2&gt;

&lt;p&gt;ToolOps is a framework-agnostic middleware SDK for Python. It sits between your agent and the external world, wrapping any async function with caching, retries, circuit breakers, request coalescing, and observability — without touching your business logic.&lt;/p&gt;

&lt;p&gt;The core idea is elegant:&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="c1"&gt;# Before: 80+ lines of custom infrastructure
# After:
&lt;/span&gt;
&lt;span class="nd"&gt;@readonly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;memory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cache_ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retry_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_market_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One decorator. Your function is now cached for an hour, automatically retried on failure, and fully traced. That's the entire API surface for most use cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two Decorators, Every Case Covered
&lt;/h2&gt;

&lt;p&gt;ToolOps makes a clean architectural distinction between two types of tool calls:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@readonly&lt;/code&gt;&lt;/strong&gt; — for functions that read data. API lookups, database queries, LLM calls, file reads. These get full caching + retry support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@sideeffect&lt;/code&gt;&lt;/strong&gt; — for functions that write or act. Sending emails, executing trades, posting messages. These are never cached (you genuinely want them to run), but they're protected by retries and circuit breakers.&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="c1"&gt;# Read: cache it, retry it, trace it
&lt;/span&gt;&lt;span class="nd"&gt;@readonly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retry_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stale_if_error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_stock_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;market_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Write: protect it, but always execute it
&lt;/span&gt;&lt;span class="nd"&gt;@sideeffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;circuit_breaker&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retry_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute_trade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;broker_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation is intentional and surprisingly useful. It forces you to think clearly about what your agent is actually doing — and gives each class of operation exactly the protection it needs.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Features That Matter in Production
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Semantic Caching
&lt;/h3&gt;

&lt;p&gt;Standard caches match on exact strings. &lt;code&gt;"weather in Paris"&lt;/code&gt; and &lt;code&gt;"Paris weather"&lt;/code&gt; hit different cache keys, so your LLM gets called twice for the same answer.&lt;/p&gt;

&lt;p&gt;ToolOps includes a semantic cache that matches by &lt;strong&gt;meaning&lt;/strong&gt; using vector embeddings. Queries above a configurable similarity threshold share the same cached result:&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;embedder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SentenceTransformerEmbedder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all-MiniLM-L6-v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cache_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;semantic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SemanticCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.92&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nd"&gt;@readonly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;semantic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cache_ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;7200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask_llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;openai_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Three prompts, one real LLM call:
&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;ask_llm&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 the latest AI news&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;ask_llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Give me a summary of recent AI news&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# Cache hit ✅
&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;ask_llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s happening in AI recently?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="c1"&gt;# Cache hit ✅
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For agents that handle natural language queries, this can cut LLM calls by up to 90%.&lt;/p&gt;

&lt;h3&gt;
  
  
  Request Coalescing
&lt;/h3&gt;

&lt;p&gt;When 50 concurrent agents request the same data during a cache miss, ToolOps fires &lt;strong&gt;one&lt;/strong&gt; real API call and returns the result to all 50. Without this, a thundering herd can overwhelm your API rate limits instantly. With it, the problem simply doesn't exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stale-If-Error Fallback
&lt;/h3&gt;

&lt;p&gt;If your upstream API goes down, ToolOps can serve the last known good cached value instead of throwing an exception. For slowly-changing data like exchange rates or configuration, this is often exactly the right behavior:&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="nd"&gt;@readonly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cache_ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stale_if_error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stale_ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Accept data up to 24 hours old if the API is down
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_exchange_rates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;forex_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Multiple Cache Backends
&lt;/h3&gt;

&lt;p&gt;Register as many backends as you need and route different functions to the right one:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Backend&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MemoryCache&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Development, single-process, low-latency hot data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FileCache&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Local scripts, lightweight persistence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PostgresCache&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Production, distributed, durable across restarts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SemanticCache&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;NLP queries, RAG pipelines, LLM cost reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A hot-cold cache pattern — in-memory for frequent reads, Postgres for expensive computations — is a single configuration call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built-In Observability
&lt;/h3&gt;

&lt;p&gt;Every cache hit, miss, retry, timeout, and circuit-breaker event is logged as structured JSON, compatible with Datadog, Loki, CloudWatch, and any log aggregator. Add the &lt;code&gt;[otel]&lt;/code&gt; extra and you get full OpenTelemetry tracing and Prometheus metrics with zero extra code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;agent_run (450ms)
  ├── get_market_data (12ms)  [cache: hit]
  ├── get_news_feed (310ms)   [cache: miss, retries: 1]
  └── send_report (128ms)     [circuit: closed]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Going from zero insight to full distributed tracing takes about five lines.&lt;/p&gt;




&lt;h2&gt;
  
  
  Framework Agnostic by Design
&lt;/h2&gt;

&lt;p&gt;ToolOps wraps plain Python async functions. That means it works with whatever agent framework you're using — no special integration required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LangChain / LangGraph&lt;/strong&gt; — stack &lt;code&gt;@readonly&lt;/code&gt; under &lt;code&gt;@tool&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CrewAI&lt;/strong&gt; — apply it directly to &lt;code&gt;BaseTool._run()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LlamaIndex&lt;/strong&gt; — decorate then pass to &lt;code&gt;FunctionTool.from_defaults()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP&lt;/strong&gt; — generate a fully typed MCP tool definition with &lt;code&gt;MCPIntegration.to_mcp_definition()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PydanticAI, Agno, AutoGPT, Haystack&lt;/strong&gt; — any framework that calls Python async functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you migrate frameworks (and you will), your infrastructure layer stays the same.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started in Under 2 Minutes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"toolops[all]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;toolops doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use:&lt;/strong&gt;&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;toolops&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;readonly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cache_manager&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;toolops.cache&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MemoryCache&lt;/span&gt;

&lt;span class="n"&gt;cache_manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;memory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;MemoryCache&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;is_default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@readonly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache_backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;memory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cache_ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retry_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;weather_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The modular install system means zero required external dependencies for the core package. Add &lt;code&gt;[postgres]&lt;/code&gt;, &lt;code&gt;[semantic]&lt;/code&gt;, or &lt;code&gt;[otel]&lt;/code&gt; only when you need them.&lt;/p&gt;

&lt;p&gt;The CLI (&lt;code&gt;toolops stats&lt;/code&gt;, &lt;code&gt;toolops clear&lt;/code&gt;, &lt;code&gt;toolops doctor&lt;/code&gt;) gives you a live view into cache hit rates, latency, and backend health without touching your code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters Now
&lt;/h2&gt;

&lt;p&gt;AI agents are moving fast from demos to production. The infrastructure gap between "it works on my machine" and "it's running reliably at scale" is real, and it's expensive to rebuild from scratch every time.&lt;/p&gt;

&lt;p&gt;ToolOps is a clean answer to a problem that every agent developer hits eventually. It's not a framework — it's the layer beneath your framework, the one that makes your tools trustworthy.&lt;/p&gt;

&lt;p&gt;The code is open source, Apache 2.0 licensed, and actively maintained.&lt;/p&gt;

&lt;p&gt;If you're building agents that need to survive real traffic, real failures, and real costs, it's worth ten minutes of your time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/hedimanai-pro/toolops" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt; · &lt;strong&gt;&lt;a href="https://pypi.org/project/toolops/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt;&lt;/strong&gt; · &lt;strong&gt;&lt;a href="https://hedimanai.vercel.app/projects/toolops.html" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

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