Tool Loop Prevention

chevron-rightRelevant source fileshashtag

Purpose and Scope

This document describes the tool loop prevention system that prevents agents from repeatedly calling the same tools with identical or similar parameters during a single conversation turn. This system is critical for:

  • Preventing infinite loops when agents get stuck retrying the same failed operation

  • Reducing LLM costs by avoiding redundant tool executions

  • Improving response quality by forcing agents to try alternative approaches

  • Protecting external APIs from excessive duplicate requests

For information about the broader tool execution system, see Tools & Integrations. For recipe-specific tool handling, see Recipe Execution.

Sources: orchestrator/consumers/chatbot/service.py:1-40


System Overview

The tool loop prevention system operates within a single conversation turn (one user message → one agent response cycle). It tracks all tool executions and applies three types of deduplication:

  1. Exact deduplication: Blocks identical tool calls (same name + same parameters)

  2. Semantic deduplication: Blocks similar search queries (e.g., "fix bug" vs "fix the bug")

  3. Retry limits: Enforces per-tool execution caps (e.g., max 2 Composio calls per turn)

The system is implemented in the StreamingChatService and integrated into the recipe executor's tool execution pipeline.

Sources: orchestrator/consumers/chatbot/service.py:44-103


Architecture

spinner

Diagram: ToolExecutionTracker Component Architecture

Sources: orchestrator/consumers/chatbot/service.py:88-186


Deduplication Strategies

1. Exact Deduplication

Blocks tool calls with identical parameters by hashing the argument dictionary:

spinner

Diagram: Exact Deduplication Flow

Implementation:

Sources: orchestrator/consumers/chatbot/service.py:126-153


2. Semantic Deduplication

Prevents similar search queries from being executed repeatedly by comparing normalized query strings:

Step
Function
Purpose

1. Query extraction

_extract_query_from_args()

Finds query parameter in tool args

2. Normalization

_normalize_query()

Lowercase, remove punctuation, strip whitespace

3. Similarity check

_queries_are_similar()

SequenceMatcher with 0.75 threshold

4. History lookup

search_queries[tool_name]

Compare against previous queries

Example:

Configuration:

Sources: orchestrator/consumers/chatbot/service.py:46-86, orchestrator/consumers/chatbot/service.py:156-162


3. Retry Limits

Enforces maximum execution counts per tool type to prevent excessive retries:

Tool Retry Limits Configuration Table

Tool Category
Tool Names
Max Attempts
Rationale

External APIs

composio_execute

2

Protect third-party rate limits

Search Operations

search_knowledge, semantic_search, search_codebase, smart_query_database, query_database

2

Expensive operations, diminishing returns

File Operations

read_file

3

May need retries for file system races

File Operations

write_file, list_directory

2

Idempotent operations

Default

All others

3

Conservative fallback

Sources: orchestrator/consumers/chatbot/service.py:104-116


Implementation Details

ToolExecutionTracker Class

spinner

Diagram: ToolExecutionTracker Class Structure

Sources: orchestrator/consumers/chatbot/service.py:88-186


Decision Flow

spinner

Diagram: should_skip_execution() Decision Flow

Sources: orchestrator/consumers/chatbot/service.py:130-164


Integration Points

Chat Service Integration

The tracker is instantiated per conversation turn in StreamingChatService.stream_response_aisdk():

spinner

Diagram: Chat Service Integration Flow

Key Integration Points:

  1. Tracker creation: Line 697 in stream_response_aisdk() orchestrator/consumers/chatbot/service.py:697

  2. Skip check: Lines 755-766 check should_skip_execution() orchestrator/consumers/chatbot/service.py:755-766

  3. Execution recording: Line 790 calls record_execution() orchestrator/consumers/chatbot/service.py:790

  4. Tool result injection: Lines 767-774 inject skip reason to LLM orchestrator/consumers/chatbot/service.py:767-774

Sources: orchestrator/consumers/chatbot/service.py:492-800


Recipe Executor Integration

The recipe executor uses a different pattern - a simple deduplication cache for Composio actions:

Differences from chat service:

Aspect
Chat Service
Recipe Executor

Scope

Per conversation turn

Per recipe step

Strategy

Three-tier (exact/semantic/limit)

Exact match only

Target

All tools

Composio actions only

State persistence

In-memory per turn

In-memory per step

Sources: orchestrator/api/recipe_executor.py:209-298


Configuration

Search Tools List

Tools subject to semantic deduplication:

Sources: orchestrator/consumers/chatbot/service.py:98-102


Dangerous Tokens

Query normalization removes these tokens to prevent inappropriate similarity matches:

Purpose: Prevent "delete user" and "deactivate user" from being considered similar, as these are destructive operations that should be executed with full intent.

Sources: orchestrator/consumers/chatbot/service.py:48-51


Usage Examples

Example 1: Exact Deduplication

Sources: orchestrator/consumers/chatbot/service.py:149-153


Example 2: Semantic Deduplication

Sources: orchestrator/consumers/chatbot/service.py:156-162


Example 3: Retry Limit

Sources: orchestrator/consumers/chatbot/service.py:142-146


Error Messages

When a tool execution is skipped, the tracker returns a descriptive reason string that is injected into the LLM context:

Reason Code
Message Template
Trigger Condition

Limit exceeded

"Tool '{tool_name}' has reached its execution limit ({limit}) for this turn"

tool_counts[tool_name] >= TOOL_RETRY_LIMITS[tool_name]

Exact duplicate

"Tool '{tool_name}' was already executed with identical parameters"

(tool_name, args_hash) in exact_executions

Similar query

"Tool '{tool_name}' was already executed with a similar query"

_queries_are_similar(current, previous) >= 0.75

Sources: orchestrator/consumers/chatbot/service.py:145-162


Performance Characteristics

Memory Usage

Per conversation turn:

  • Exact deduplication: O(n) space for n tool calls

    • Each entry: ~50 bytes (tool name + MD5 hash)

    • Typical turn: 5-15 tool calls = 250-750 bytes

  • Semantic deduplication: O(m) space for m search queries

    • Each entry: ~100 bytes (tool name + query string)

    • Typical turn: 1-3 search queries = 100-300 bytes

  • Tool counts: O(k) space for k unique tools

    • Each entry: ~40 bytes (tool name + integer)

    • Typical turn: 3-8 unique tools = 120-320 bytes

Total per-turn memory: ~500-1,500 bytes (negligible)

Sources: orchestrator/consumers/chatbot/service.py:119-124


Time Complexity

Operation
Complexity
Notes

should_skip_execution()

O(1) to O(m)

O(1) for exact/limit checks, O(m) for semantic where m = previous search queries (typically 1-3)

record_execution()

O(1)

Set/dict insertions

_hash_args()

O(k)

k = number of argument keys

_normalize_query()

O(n)

n = query string length

_queries_are_similar()

O(n×m)

SequenceMatcher between strings of length n, m

Worst case per tool call: O(n×m) for semantic similarity check, where n and m are query lengths (~100 chars each). With m=3 previous queries, this is ~30,000 character comparisons, negligible compared to LLM inference time (100ms+).

Sources: orchestrator/consumers/chatbot/service.py:46-186


Limitations and Edge Cases

1. Cross-Turn State Not Preserved

The tracker is scoped to a single conversation turn. Across multiple turns:

Rationale: Cross-turn deduplication would require persistent state and could prevent legitimate re-queries with updated context.

Sources: orchestrator/consumers/chatbot/service.py:130-164


2. Parameter Order Sensitivity

The exact deduplication uses JSON serialization with sort_keys=True, but nested objects may differ:

Sources: orchestrator/consumers/chatbot/service.py:126-128


3. Semantic False Positives

The 0.75 similarity threshold may block legitimately different queries:

Mitigation: The threshold (0.75) is tuned empirically. Adjust in orchestrator/consumers/chatbot/service.py:56 if needed.

Sources: orchestrator/consumers/chatbot/service.py:56-73


Testing Considerations

Unit Test Coverage

Key test cases for ToolExecutionTracker:

Test File Location: Tests should be added to the test suite for the chatbot consumer module.

Sources: orchestrator/consumers/chatbot/service.py:88-186


Sources: orchestrator/consumers/chatbot/service.py:1-40


Last updated