Tool Acceleration¶
Fast-CrewAI provides 17x faster tool execution through result caching and serde-based JSON validation.
Overview¶
| Feature | Benefit |
|---|---|
| Result Caching | Skip repeated computations |
| JSON Validation | serde-based (much faster than Python json) |
| Recursion Protection | Configurable depth limits |
| Execution Stats | Track cache hits/misses |
Basic Usage¶
from fast_crewai import AcceleratedToolExecutor
executor = AcceleratedToolExecutor(
max_recursion_depth=1000,
cache_ttl_seconds=300, # Cache for 5 minutes
use_rust=True
)
# Execute a tool
result = executor.execute_tool("search", {"query": "AI trends"})
# Second call with same args - instant cache hit!
result = executor.execute_tool("search", {"query": "AI trends"})
With CrewAI¶
When using the shim, CrewAI tools are automatically accelerated:
import fast_crewai.shim
from crewai import Agent, Task, Crew
from crewai.tools import tool
@tool("Search Tool")
def search(query: str) -> str:
"""Search for information."""
return f"Results for: {query}"
agent = Agent(
role="Researcher",
goal="Find information",
tools=[search], # Accelerated automatically
backstory="Expert researcher"
)
Result Caching¶
The caching system dramatically speeds up repeated tool calls:
executor = AcceleratedToolExecutor(
cache_ttl_seconds=300, # 5 minute cache
use_rust=True
)
# First call: executes tool
result1 = executor.execute_tool("api_call", {"endpoint": "/users"})
# Second call: instant cache hit (17x faster!)
result2 = executor.execute_tool("api_call", {"endpoint": "/users"})
# Different args: cache miss, executes tool
result3 = executor.execute_tool("api_call", {"endpoint": "/posts"})
Cache Control¶
# Disable caching for specific calls
result = executor.execute_tool("tool", args, use_cache=False)
# Clear all cached results
cleared_count = executor.clear_cache()
print(f"Cleared {cleared_count} cached entries")
Check Cache Statistics¶
stats = executor.get_stats()
print(f"Cache hits: {stats['cache_hits']}")
print(f"Cache misses: {stats['cache_misses']}")
print(f"Total executions: {stats['total_executions']}")
print(f"Hit rate: {stats['cache_hits'] / stats['total_executions'] * 100:.1f}%")
JSON Validation¶
Fast validation using serde (much faster than Python's json module):
# Validate single argument set
is_valid = executor.validate_args({
"query": "test",
"options": {"limit": 10, "offset": 0}
})
# Batch validation
args_list = [
{"query": "test1"},
{"query": "test2"},
{"invalid": None}
]
results = executor.batch_validate(args_list)
# [True, True, False]
Recursion Protection¶
Prevent stack overflow with configurable limits:
executor = AcceleratedToolExecutor(
max_recursion_depth=100, # Lower for safety
use_rust=True
)
# If recursion limit exceeded, raises exception
try:
result = executor.execute_tool("recursive_tool", args)
except RuntimeError as e:
print(f"Recursion limit hit: {e}")
Performance Comparison¶
| Scenario | Python | Rust + Caching |
|---|---|---|
| First call | 1.5ms | 1.5ms |
| Cached call | 1.5ms | 0.09ms (17x faster) |
| JSON validation | 0.5ms | 0.03ms |
Best Practices¶
Optimal Cache TTL¶
Choose TTL based on data freshness requirements:
# Real-time data: short TTL
executor = AcceleratedToolExecutor(cache_ttl_seconds=30)
# Static data: long TTL
executor = AcceleratedToolExecutor(cache_ttl_seconds=3600)
# No caching
executor = AcceleratedToolExecutor(cache_ttl_seconds=0)
Batch Tool Execution¶
tools_and_args = [
("search", {"query": "AI"}),
("analyze", {"data": "..."}),
("summarize", {"text": "..."})
]
results = []
for tool, args in tools_and_args:
result = executor.execute_tool(tool, args)
results.append(result)
Monitor Cache Effectiveness¶
import atexit
def print_cache_stats():
stats = executor.get_stats()
hit_rate = stats['cache_hits'] / max(stats['total_executions'], 1) * 100
print(f"\nTool Cache Statistics:")
print(f" Hit rate: {hit_rate:.1f}%")
print(f" Cache hits: {stats['cache_hits']}")
print(f" Cache misses: {stats['cache_misses']}")
atexit.register(print_cache_stats)
Troubleshooting¶
Low Cache Hit Rate¶
If cache hit rate is low:
- Check if arguments are consistent (same structure/values)
- Consider longer TTL if data doesn't change frequently
- Ensure you're reusing the same executor instance
Cache Not Working¶
Verify caching is enabled:
# Check implementation
print(f"Using: {executor.implementation}")
# Verify cache is being used
stats = executor.get_stats()
print(f"Stats: {stats}")
Memory Usage¶
Cache grows with unique argument combinations. Clear periodically if needed: