One Decorator to Solve the Duplicate Tool Execution Problem in AI Agents

A Python library that prevents AI Agent tool re-execution with a single @idempotent decorator.
The idempotent-tools library solves a critical production issue in AI Agent systems: retry and checkpoint-replay mechanisms causing duplicate side effects like double payments or repeated emails. By adding a single @idempotent decorator to tool functions, developers get automatic caching with SQLite or Redis backends, concurrent call strategies, and TTL expiration — all with zero dependencies and easy integration with LangGraph and CrewAI.
The Root Cause: Side-Effect Disasters Triggered by Agent Retries
When building AI Agent applications, a subtle yet critical problem is plaguing an increasing number of developers: when an Agent re-executes due to retry mechanisms or interrupt/resume workflows, tool calls with side effects get triggered twice.
Side effects are a core concept in computer science, referring to observable changes a function makes to the external world beyond returning a value — writing to a database, sending network requests, modifying global variables, and so on. In the functional programming paradigm, pure functions are considered the ideal: the same input always produces the same output with zero side effects. However, in real-world Agent systems, the core value of tool calls lies precisely in producing side effects — charging payments, sending emails, calling APIs. This creates a fundamental contradiction: the system needs side effects to accomplish tasks, but retry mechanisms demand that operations be repeatable.
Imagine this scenario: your Agent calls a payment tool to charge a user's credit card, sends an email, or writes a record to an external API. Suddenly, due to network instability or a workflow interruption, the Agent retries — and the same payment is charged twice, the same email is sent twice. This isn't a theoretical risk; it's a real pain point that users of mainstream Agent frameworks like LangGraph and CrewAI encounter in production environments.
Recently, a developer shared their solution on Reddit: a lightweight Python library called idempotent-tools that elegantly solves this problem with a single decorator.
idempotent-tools: Protecting Tool Calls with Idempotency
The core idea behind this tool comes from a classic concept in distributed systems — idempotency. The concept originates from idempotent operations in mathematics (e.g., the absolute value function |x| — applying absolute value to an already-positive number yields the same result), and was later widely adopted in distributed system design. In the HTTP protocol, GET, PUT, and DELETE are designed as idempotent methods, while POST is not. The payments industry is one of the most mature domains for idempotency in practice — payment gateways like Stripe and PayPal have long supported the Idempotency-Key mechanism: the client attaches a unique identifier in the request header, and the server uses it to determine whether a request is a duplicate. AWS services like SQS and Lambda also have built-in idempotency support. idempotent-tools essentially brings this battle-tested distributed system design pattern down to the granularity level of Agent tool functions.
Put simply, an idempotent operation means that no matter how many times it's executed, the result remains the same, with no additional side effects.
Dead-Simple Usage
The entire library's design philosophy is "less is more." Developers only need to add an @idempotent decorator to their tool functions:
from idempotent_tools import idempotent
@idempotent
def charge_card(order_id: str, amount: float) -> dict:
...
charge_card("order-42", 19.99) # Actually executes the charge
charge_card("order-42", 19.99) # Returns cached result, no re-execution
On the second call with the same parameters, the function doesn't actually execute — it directly returns the cached result. This "one-line fix" experience is a massive efficiency improvement compared to the tedious process of manually building a hash-and-check store.
Zero-Config, Local-First Design
idempotent-tools makes architecture choices that reflect a developer-friendly mindset:
- Local-first: Uses SQLite as the default storage backend — works out of the box with zero configuration and no external dependencies. SQLite is the most widely deployed database engine in the world. Unlike client-server databases such as MySQL or PostgreSQL, SQLite is an embedded database where the entire database is a single file requiring no separate server process. Python's standard library includes the sqlite3 module, so no additional software installation is needed. For single-process Agent applications, SQLite provides ACID transaction guarantees, reliably stores the mapping between idempotency keys and cached results, and can handle tens of thousands of read/write operations per second — more than sufficient for single-machine scenarios.
- Optional Redis: For higher performance or cross-process sharing, you can switch to a Redis backend. When applications scale to multi-process or distributed deployments, Redis, as a networked in-memory data store, provides a unified idempotency state view for multiple processes or containers.
- No network calls: No hosted API, no account registration required. All data stays local, which is especially important for privacy- and security-conscious enterprise environments.
- Zero dependencies: The default backend introduces no third-party dependencies. MIT licensed, free for commercial use.
Fine-Grained Control for Complex Scenarios
Real-world Agent workflows are far more complex than simple duplicate calls. idempotent-tools provides practical configuration options to handle these situations.
Three Strategies for Concurrent Calls
When the same idempotency key is called again while a previous call is still in progress, developers can choose from three different behaviors:
- raise: Throw an exception immediately, explicitly notifying the caller of a conflict.
- block-and-poll: Block and poll, waiting for the previous call to complete before returning its result.
- retry-anyway: Ignore the conflict and proceed with the retry.
This flexibility allows developers to make precise trade-offs based on business fault-tolerance requirements. For example, payment scenarios might favor block-and-poll to ensure result consistency, while certain idempotent read operations could simply use retry.
TTL Cache Expiration
Cache entries support TTL (time-to-live) settings, preventing stale data from occupying storage indefinitely and allowing the system to permit the "same operation" to be re-executed after a reasonable time window. TTL is standard practice in caching systems — Redis's EXPIRE command, HTTP's Cache-Control headers, and DNS TTL fields all embody the same idea. In Agent scenarios, setting an appropriate TTL requires balancing two factors: too short and legitimate retries might not be intercepted; too long and intentional repeat operations by users (e.g., a user genuinely wanting to pay again for the same product) would be blocked.
Framework Integration: Working with LangGraph and CrewAI
For the two hottest Agent frameworks today, the author provides example integration patterns while deliberately maintaining "shallow coupling" rather than deep binding:
-
LangGraph: Uses
thread_id + stepas the basis for deriving idempotency keys, aligning with its checkpoint replay mechanism. LangGraph is a stateful Agent orchestration framework from the LangChain team. Its core design philosophy models Agent execution flows as directed graphs. The checkpoint mechanism is a key feature: the framework automatically saves a complete state snapshot after each node executes, including message history, tool call results, and intermediate variables. When an Agent is interrupted by an exception or requires human approval (Human-in-the-loop), the system can resume execution from the most recent checkpoint. However, this replay means all nodes after the checkpoint are re-executed — if any node involves external API calls or database writes, it produces duplicate side effects. The thread_id identifies a complete conversation session, step identifies the execution step in the graph, and their combination uniquely locates a specific tool call — this is the theoretical basis for using them as idempotency keys. -
CrewAI: Integrates through task-retry hooks. CrewAI is an open-source framework focused on multi-Agent role collaboration, using a "crew" metaphor to organize Agents: each Agent plays a specific role (e.g., researcher, writer, reviewer), completing complex workflows through task delegation and result passing. CrewAI has a built-in task retry mechanism — when an Agent's tool call fails or the LLM output doesn't match the expected format, the framework automatically retries. Task-retry hooks are extension points provided by CrewAI that allow developers to insert custom logic before and after retries.
idempotent-toolsuses these hooks to check the idempotency cache when retries occur, thereby preventing duplicate side effects.
The author emphasizes that these integrations "are not deep framework coupling, just a pattern you can adapt yourself." This design philosophy is commendable — it leaves control in the developers' hands and avoids maintenance burdens caused by framework version changes.
Clear Boundaries: Knowing What NOT to Do
The value of a mature tool lies not only in what it can do but also in clearly understanding what it shouldn't do. The author demonstrates remarkable restraint and honesty on this point.
idempotent-tools explicitly states:
- No distributed cross-worker locking: There is no true multi-worker coordination capability. In distributed systems, cross-process locking typically requires consensus algorithms (such as Raft or Paxos) or dedicated distributed lock services (such as ZooKeeper, etcd's lease mechanism, or Redis's Redlock algorithm) — these are infrastructure components of a much higher complexity tier.
- No dashboard: Features are intentionally kept narrow.
The author states plainly: "If your use case requires true multi-worker coordination, this tool isn't for you — that's a problem for a different, heavier tool to solve." But for the common single-process/single-worker retry scenarios, it provides a clean one-line fix without having to reinvent the wheel.
This "small but precise" positioning is exactly the attitude many open-source tools should adopt. Rather than building a bloated, mediocre jack-of-all-trades solution, it's better to solve one specific problem exceptionally well. The Unix philosophy of "Do One Thing and Do It Well" is perfectly embodied here.
Takeaways for AI Agent Developers
As AI Agents move from demos to production, reliability engineering is becoming an unavoidable challenge. Reliability engineering already has mature practices in traditional software — such as Google's Site Reliability Engineering (SRE) methodology. But AI Agent systems introduce unique dimensions of uncertainty: LLM outputs are inherently stochastic, tool call chains may deviate from expected paths due to model "hallucinations," and Agents' autonomous decision-making ability means errors can cascade and amplify. In traditional microservice architectures, retries are a standard technique for improving availability, typically used alongside Exponential Backoff and Circuit Breaker patterns. But in Agent systems, what's being retried isn't just a network request — it's the entire reasoning-decision-action loop, which dramatically increases the attack surface for duplicate side effects.
While mechanisms like retries, interrupt recovery, and checkpoint replay enhance system resilience, they also introduce new challenges around duplicate side-effect triggering. Companies like Anthropic and OpenAI emphasize idempotent tool call design in their Agent development guides, but for a long time there hasn't been a standardized, out-of-the-box solution.
The emergence of idempotent-tools reminds us: in Agent engineering, idempotency should not be an afterthought — it should be a fundamental principle of tool design. Any tool that modifies external state — payments, emails, database writes, third-party API calls — should consider the possibility of duplicate execution from the very beginning.
The author is also actively seeking community feedback, especially from developers who have encountered these issues during LangGraph checkpoint replays. They want to validate whether their key-derivation approach can withstand real-world interrupt/resume patterns. This open and evidence-seeking attitude is a model of healthy open-source collaboration.
For teams building production-grade Agents, this small tool — available with just pip install idempotent-tools — might be the critical piece that saves you from your next "double charge" incident.
Related articles

EmbeddedSass for .NET: A Sass Compilation Solution Without Node.js Dependencies
EmbeddedSass for .NET uses the official Embedded Sass Protocol, enabling .NET developers to compile Sass/SCSS natively without Node.js. Learn how it works and integrates with ASP.NET.

San Francisco to Singapore Time Difference: The Trans-Pacific Routine of Silicon Valley Tech Workers
SF and Singapore are 15-16 hours apart, and frequent travel between them is now routine for tech workers. Explore the time difference challenges, AI industry globalization, and talent flows.

Anthropic Launches Official Claude Code Plugin Directory: A Curated High-Quality Extension Ecosystem
Anthropic launches claude-plugins-official, a curated directory of high-quality Claude Code plugins. Learn about its positioning, core value, and impact on the AI coding ecosystem.