En une phrase
ToolOps est aux outils IA ce qu'un service mesh est aux microservices : un SDK de middleware indépendant de tout framework, qui dote n'importe quelle fonction Python de cache, de résilience et d'observabilité — sans toucher à votre logique métier.
Quand vous construisez des agents IA, chaque appel externe — vers un LLM, une API, une base de données — est un appel d'outil. En production, ces appels sont coûteux, instables et lents. ToolOps se place entre votre agent et ses outils comme un service mesh se place entre des microservices, et absorbe cette complexité derrière une interface à décorateurs, choisie délibérément pour offrir la plus fine surface d'intégration possible.
1. Philosophie
L'analogie qui décrit le mieux ToolOps est celle du service mesh. De la même manière qu'un service mesh — pensez à Istio ou Linkerd — s'intercale entre les microservices pour gérer de façon transparente les réessais, les délais d'attente et l'ouverture des disjoncteurs, ToolOps s'intercale entre votre agent IA et ses outils. Le code applicatif ignore tout de la couche d'infrastructure qui le porte.
ToolOps est aux outils IA ce qu'un service mesh est aux microservices.
Cette séparation des responsabilités est délibérée. L'auteur d'un outil doit se concentrer sur ce que fait cet outil, pas sur les complexités de systèmes distribués qu'impose son appel fiable à l'échelle. ToolOps absorbe cette complexité derrière l'interface à décorateurs.
2. Le mur de la production
Tout développeur d'agents heurte le même mur au passage de la démo à la production. Les symptômes sont prévisibles : des factures d'API qui montent plus vite que l'usage, des agents qui tombent au troisième réessai, des files de requêtes qui s'engorgent à la moindre concurrence réelle, et des workflows totalement opaques quand ils échouent. ToolOps traite chacun de ces goulots au niveau de la couche d'infrastructure.
Problème → impact métier → avec ToolOps
- Appels d'API redondants. Coûts multipliés par 10 → 100 appels deviennent 1 appel réel et 99 succès de cache.
- Requêtes proches formulées autrement. Tokens LLM gaspillés → la correspondance sémantique renvoie le même résultat en cache.
- Instabilité des API. Agents qui tombent et boucles de réessai → disjoncteur et réessai automatique.
- Pics de concurrence. Ruée simultanée → la coalescence des requêtes la réduit à un seul appel réel.
- Exposition de données sensibles. Jetons et données personnelles dans les clés de cache ou les logs → hachage SHA-256 et masquage automatique.
- Aucune observabilité. Exploitation à l'aveugle → logs JSON structurés et traces OpenTelemetry.
Cache sémantique
Les appels quasi identiques sont servis par le cache — l'agent qui reformule une requête cesse de coûter de l'argent.
Disjoncteurs
Une dépendance défaillante fait ouvrir le circuit et se dégrade proprement au lieu de bloquer toute l'exécution.
Coalescence des requêtes
Les appels concurrents au même outil fusionnent en une seule requête en amont, diffusée à tous les appelants.
Observabilité
Chaque appel émet une télémétrie structurée : succès, échecs, réessais, changements d'état du disjoncteur.
3. Installation
ToolOps est disponible sur PyPI. Depuis la v1.0.0, le paquet est livré tout compris : une seule installation embarque tous les backends de cache — Memory, File, SQLite, Postgres, MySQL/MariaDB, Valkey/Redis, Semantic — ainsi que les pilotes de bases de données, les bibliothèques d'embeddings et d'intégration OpenAI, et la prise en charge de l'observabilité OpenTelemetry/Prometheus. Aucun extra optionnel n'est nécessaire.
# One command installs everything — backends, drivers, telemetry pip install toolops # Verify the installation toolops doctor
:: One command installs everything pip install toolops :: Using the launcher py -m pip install toolops
Il est fortement recommandé d'installer ToolOps dans un environnement virtuel afin d'éviter les conflits de dépendances.
# Create and activate (.venv) python -m venv .venv source .venv/bin/activate # Linux/macOS # .venv\Scripts\Activate.ps1 # Windows (PowerShell) # Install and verify pip install toolops toolops doctor
Les anciens extras comme toolops[postgres], toolops[semantic] ou toolops[all] restent disponibles sous forme d'alias vides de compatibilité, afin que les scripts de CI et de déploiement existants continuent de fonctionner — les nouvelles installations utiliseront simplement pip install --upgrade toolops.
Les contributeurs ont besoin de deux étapes supplémentaires : un environnement Docker couvrant toute la matrice de backends, et les cibles Make normalisées qui encapsulent les outils de test, de lint et de formatage du projet.
# One-command setup with PostgreSQL
docker-compose up -d
docker-compose exec toolops make test
make test # Run full test suite with coverage make lint # Ruff + Black checks make format # Auto-format with Black make typecheck # mypy strict mode make coverage # HTML coverage report make clean # Remove all build artifacts
Depuis la v1.0.0, pip install toolops constitue le contrat d'installation complet : le jeu de dépendances par défaut inclut les backends de production pris en charge et les bibliothèques d'intégration, si bien que le cache persistant et le traçage distribué fonctionnent dès le premier jour. Les contributeurs installent depuis requirements.txt, qui ajoute le paquet en mode éditable et la chaîne d'outils de développement.
4. L'architecture
L'architecture compte trois couches : une interface à décorateurs qui se pose sur vos fonctions outils, un pipeline de middlewares composables qui orchestre la résilience et l'observabilité, et un système de backends interchangeables qui gère le stockage et les embeddings. Les trois couches sont entièrement découplées — vous pouvez changer de backend ou réordonner les middlewares sans modifier une seule ligne du code de vos outils.
Un décorateur, pas une réécriture
- Async / await. Nativement pris en charge, là où
@lru_cachene l'est pas. - Cache sémantique. Embeddings vectoriels, là où
@lru_cachen'apparie que des clés exactes. - Cache distribué. Postgres, SQLite, MySQL, Valkey/Redis, là où
@lru_cachene vit qu'en mémoire. - Disjoncteur. Intégré, avec temporisation exponentielle, là où
@lru_cachen'en a aucun. - Coalescence des requêtes. Diffuse le résultat à tous, là où
@lru_cachelaisse passer la ruée. - Repli stale-if-error. Sert la dernière valeur valide, là où
@lru_cachelève une exception. - Sécurité. Clés SHA-256 et masquage automatique, nativement — là où
@lru_cachen'offre rien. - Observabilité. Télémétrie structurée OpenTelemetry et Prometheus, là où
@lru_cachen'offre rien. - Pensé pour l'IA. Intégrations MCP et frameworks natives, là où
@lru_cachereste générique.
4.1 Les décorateurs
ToolOps fournit deux décorateurs qui correspondent proprement aux deux catégories d'opérations d'un outil. La distinction entre lecture et écriture est un concept de premier plan : les lectures sont idempotentes, donc sûres à mettre en cache et à réessayer ; les écritures ne le sont pas — elles reçoivent uniquement les motifs de résilience, jamais de réessai automatique qui pourrait provoquer une double soumission.
from toolops import readonly, cache_manager from toolops.cache import MemoryCache cache_manager.register("memory", MemoryCache(), is_default=True) @readonly( cache_backend="memory", cache_ttl=3600, retry_count=3, sensitive_params=["api_key", "auth_token"] ) async def get_market_data(ticker: str, api_key: str) -> dict: return await api.fetch(ticker, api_key=api_key) # Cached, retried, traced — api_key excluded from the cache key # and masked in logs. @sideeffect(circuit_breaker=True, timeout=5.0, retry_count=2) async def execute_trade(order: dict) -> bool: return await broker.submit(order) # No caching — protected by circuit breaker and timeout only.
4.2 Les backends de cache
Enregistrez les backends une fois au démarrage de l'application, puis référencez-les par leur nom dans tous vos décorateurs. Plusieurs backends peuvent coexister — une couche mémoire rapide pour les données chaudes, une couche SQL persistante pour la piste d'audit, une couche Valkey/Redis distribuée pour les déploiements multi-processus, et une couche sémantique pour les charges de TAL. Depuis la v1.0.0, tous les backends ci-dessous sont installés par défaut et partagent des règles de cycle de vie identiques : appeler une opération sur un backend fermé lève un RuntimeError explicite, plutôt que de se reconnecter silencieusement ou d'échouer sur un AttributeError — le même contrat pour Memory, File, SQLite, Postgres, MySQL, Valkey/Redis et Semantic.
from toolops import cache_manager from toolops.cache import ( MemoryCache, SQLiteCache, PostgresCache, ValkeyCache, MySQLCache ) cache_manager.register("memory", MemoryCache(), is_default=True) cache_manager.register("sqlite", SQLiteCache("toolops_cache.db")) cache_manager.register("db", PostgresCache("postgresql://user:pass@localhost:5432/mydb")) cache_manager.register("valkey", ValkeyCache(host="localhost", port=6379)) cache_manager.register("mysql", MySQLCache(dsn="mysql://root:secret@localhost:3306/myapp"))
Sept backends, un seul cycle de vie
- MemoryCache. Dans le processus — développement, tests, déploiements mono-processus.
- FileCache. Persistant, sur le système de fichiers — persistance locale légère, sans base de données.
- SQLiteCache. Persistant, un seul fichier — persistance sans serveur via aiosqlite, avec invalidation par tags.
- PostgresCache. SQL persistant — piste d'audit complète, partageable entre processus.
- MySQLCache. SQL persistant — MySQL 8+ et MariaDB 10.5+ via aiomysql, connexion par DSN.
- ValkeyCache / RedisCache. Distribué, en mémoire — pool de connexions asynchrone complet.
- SemanticCache. Embeddings vectoriels — appariement par intention pour les pipelines de TAL et de RAG.
4.3 Le pipeline de middlewares
Le décorateur que vous appelez n'est qu'une fine enveloppe : en interne, ToolOps a été refondu en un pipeline composable de middlewares indépendants, chacun responsable d'une seule préoccupation, orchestrés en séquence par un ToolExecutor. Un ToolContext partagé transporte l'état mutable tout au long du pipeline — et le comportement des décorateurs est préservé à 100 %, si bien que le code existant qui utilise @tool, @readonly, @sideeffect ou @stateful en bénéficie sans aucune modification.
from toolops.middlewares import build_executor, DEFAULT_PIPELINE executor = build_executor(pipeline=DEFAULT_PIPELINE) # → [LoggingMiddleware, CacheMiddleware, CircuitBreakerMiddleware, # RetryMiddleware, CoalescingMiddleware, FallbackMiddleware]
LoggingMiddleware
Journalisation JSON structurée pour chaque appel d'outil.
CacheMiddleware
Consultation du cache, stale-if-error, écriture en cache.
CircuitBreakerMiddleware
Protection par disjoncteur.
RetryMiddleware
Boucle de réessai avec temporisation exponentielle.
CoalescingMiddleware
Coalescence des requêtes — déduplication des appels concurrents.
FallbackMiddleware
Exécution de repli en cas d'échec.
5. Motifs de résilience
Au-delà du simple bloc try/except, ToolOps met en œuvre trois motifs déterministes empruntés à l'ingénierie des systèmes distribués. Ensemble, ils garantissent qu'un agent ne se retrouve jamais piégé dans une boucle d'échec, n'épuise jamais son budget d'API sur un service dégradé, et ne sert jamais de données périmées quand le service en amont fonctionne.
5.1 Le disjoncteur
Il coupe tous les appels vers un service défaillant au-delà d'un seuil d'échecs configurable. Une fois ouvert, le circuit échoue immédiatement — il rend la main aussitôt au lieu d'attendre l'expiration d'un délai — puis entre dans une fenêtre de rétablissement avant de tenter de rouvrir la connexion. Cela empêche la défaillance d'un seul outil de se propager en panne complète de l'agent.
@readonly( circuit_breaker=True, circuit_failure_threshold=5, # opens after 5 consecutive failures circuit_recovery_timeout=60 # retries after 60 seconds ) async def get_exchange_rates() -> dict: return await forex_api.fetch()
5.2 Le repli stale-if-error
Quand un service en amont échoue et qu'aucune donnée fraîche ne peut être récupérée, ToolOps peut se replier automatiquement sur la dernière valeur valide connue du cache — même au-delà de son TTL normal. L'équivalent, en production, de « servir quelque chose d'utile plutôt que de tomber ».
@readonly( cache_ttl=3600, stale_if_error=True, stale_ttl=86400 # serve stale data for up to 24h on failure ) async def get_exchange_rates() -> dict: return await forex_api.fetch()
5.3 La coalescence des requêtes
Quand plusieurs instances d'agent appellent le même outil simultanément — un schéma courant dans les pipelines multi-agents — ToolOps détecte la requête déjà en vol et met les appelants suivants en attente jusqu'à ce que la première aboutisse. L'unique résultat réel est ensuite diffusé à tous les appelants en attente.
50 → 1
Appels concurrents fusionnés
98%
De consommation de crédits en moins
0
Modification des agents appelants
Sur un banc d'essai comptant 50 appels d'agents concurrents vers le même outil météo, la coalescence des requêtes a ramené les appels d'API en amont de 50 à 1.
6. Le cache sémantique
Les caches traditionnels fonctionnent par égalité stricte des clés. Cela convient aux systèmes déterministes, mais un agent n'en est pas un — une même intention utilisateur ressort sous des dizaines de formulations différentes. ToolOps s'appuie sur des embeddings vectoriels pour saisir le sens d'un appel d'outil, et pas seulement ses arguments littéraux.
# Call 1 — cache miss, real API call query: "What is the status of invoice #442?" # Call 2 — semantic similarity 0.97 → cache hit query: "Check the current status for invoice 442" # Call 3 — semantic similarity 0.94 → cache hit query: "Invoice 442 — is it paid?"
from toolops.cache import SemanticCache, SentenceTransformerEmbedder embedder = SentenceTransformerEmbedder("all-MiniLM-L6-v2") semantic = SemanticCache(embedder=embedder, threshold=0.92) cache_manager.register("semantic", semantic) @readonly(cache_backend="semantic") async def ask_agent(query: str) -> str: return await llm.complete(query) # Reduces LLM latency by up to 90% on repeated intent patterns.
Le seuil de similarité — 0,92 ci-dessus — est le principal levier de réglage. Une valeur élevée exige un alignement sémantique plus étroit avant de déclarer un succès ; une valeur basse est plus agressive. La bonne valeur dépend du degré de variation acceptable dans le domaine d'entrée de votre outil : une recherche factuelle tolère un seuil plus élevé qu'une tâche de génération créative.
Note de performance
En v0.2.0, l'éviction de SemanticCache est passée d'opérations de liste en O(n) à un collections.deque en O(1) — garantissant une latence et une empreinte mémoire prévisibles en forte concurrence.
7. Observabilité
Déboguer des workflows d'agents non déterministes exige une instrumentation plus profonde que la journalisation applicative. ToolOps émet une télémétrie structurée à chaque étape du cycle de vie d'un outil — succès, échecs, réessais, changements d'état du disjoncteur — offrant une piste d'audit complète sans instrumentation manuelle.
from toolops import configure_opentelemetry, prometheus_metrics # Configure OpenTelemetry tracing — accepts any standard tracer instance configure_opentelemetry(tracer) # Expose Prometheus metrics as a raw text string metrics_string = prometheus_metrics()
Logs structurés
Chaque succès de cache, échec, défaillance et réessai est émis en JSON exploitable par une machine. Les champs comprennent le nom de l'outil, le backend, la latence, la clé de cache et l'issue — prêts à être ingérés par n'importe quel agrégateur de logs.
OpenTelemetry
Des traces et spans OTEL natifs enveloppent chaque exécution d'outil. Passez n'importe quel tracer standard à configure_opentelemetry(tracer) et visualisez le graphe d'appels complet dans Jaeger, Honeycomb ou Datadog.
Métriques Prometheus
Des jauges en temps réel pour le taux de succès du cache, l'état du circuit (fermé / ouvert / semi-ouvert) et les percentiles de latence des outils. Parmi les métriques clés : toolops_cache_hits_total, toolops_tool_latency_seconds et toolops_circuit_opens_total — prêtes à alimenter règles d'alerte et tableaux de bord.
8. Écosystème et MCP
Les outils ToolOps sont de simples fonctions Python. Ce choix de conception n'a rien d'accidentel — il signifie qu'ils fonctionnent nativement avec tout framework d'agents acceptant des appelables Python, sans code d'adaptation ni configuration propre au framework.
Les intégrations, toutes disponibles aujourd'hui
- LangChain / LangGraph. Assistant intégré.
- CrewAI. Assistant intégré.
- LlamaIndex. Compatibilité générale.
- Model Context Protocol. Assistant intégré.
- PydanticAI. Compatibilité générale.
- AutoGPT et frameworks maison. Tout appelable Python.
L'intégration MCP mérite une mention particulière : un adaptateur intégré expose n'importe quel outil décoré sous forme de définition compatible MCP, sans écrire une ligne de JSON Schema — un outil résilient, de qualité production, devient ainsi immédiatement accessible à Claude Desktop, Cursor ou tout hôte compatible MCP.
from toolops.integrations.mcp import MCPIntegration # get_weather is already decorated with @readonly definition = MCPIntegration.to_mcp_definition(get_weather) # → MCP-compatible tool definition, ready for Claude Desktop or Cursor.
9. CLI et exploitation
ToolOps est livré avec un outil en ligne de commande pour inspecter et gérer l'infrastructure des outils en production — conçu pour les équipes d'exploitation et les pipelines de CI, pas seulement pour les développeurs.
# List all available commands toolops --help # Check system health and backend readiness toolops doctor # View real-time cache statistics toolops stats --app my_app:setup_toolops # Print current metrics toolops metrics --app my_app:setup_toolops # Inspect a specific cache key toolops inspect-key --app my_app:setup_toolops # Clear a specific cache backend toolops clear postgres --app my_app:setup_toolops
toolops doctor est particulièrement utile dans les pipelines de déploiement : il valide la connectivité des backends, vérifie la disponibilité du modèle d'embeddings et rapporte l'état des disjoncteurs — un contrôle de disponibilité que vous pouvez brancher directement sur votre endpoint de santé.
10. Feuille de route
La v1.0.0 marque la version stable et tout comprise : pipeline de middlewares composable, sécurité durcie, cycle de vie unifié sur tous les backends de cache, et installation en une commande sans aucun extra. Les éléments ci-dessous sont prévus pour les prochaines versions, par ordre de livraison attendue.
La suite
- Tableau de bord web. Métriques en temps réel, imputation des coûts et taux de succès du cache dans une interface navigateur — sans installer Prometheus ni Grafana.
- Contrôle budgétaire. Plafonds stricts sur les coûts d'API induits par les outils, à l'heure ou à la journée, configurables par outil et par backend.
- Serveur MCP natif. Déploiement en un clic des outils ToolOps sous forme d'hôte MCP autonome — sans configuration de Claude Desktop.
- Middleware de streaming. Prise en charge complète des sorties d'outils en flux dans les pipelines d'agents.
- Nouveaux backends. Prise en charge de ChromaDB et Pinecone, élargissant les options de cache vectoriel natif. (MariaDB est déjà pris en charge depuis la v1.0.0 via MySQLCache.)
ToolOps est open source sous licence Apache 2.0. Ajoutez une étoile au dépôt, ouvrez une issue ou proposez une pull request — le projet est construit au grand jour, et les priorités de la feuille de route sont dictées par les cas d'usage réels remontés par la communauté.
1
Décorateur à adopter
0
Dépendance à un framework
7
Backends de cache, tout compris
FAQ
Questions fréquentes
ToolOps est indépendant de tout framework. Là où LangChain ou CrewAI proposent une logique de réessai basique, ToolOps apporte des motifs de qualité industrielle — disjoncteurs, coalescence des requêtes, cache sémantique — qui fonctionnent sur n'importe quel outil Python, sans aucun coût de migration.
ToolOps protège votre système de trois façons : les disjoncteurs arrêtent le matraquage, les réessais automatiques absorbent les incidents passagers, et le repli stale-if-error peut servir la dernière valeur valide connue du cache pour que votre agent continue d'avancer.
Si 50 agents appellent le même outil au même moment alors que le cache est vide, ToolOps exécute l'appel d'API réel une seule fois et en diffuse le résultat aux 50 appelants. Vos quotas d'API en amont ne sont donc jamais saturés.
Non. Depuis la v1.0.0, pip install toolops est tout compris : tous les backends de cache standard, les pilotes de bases de données, les bibliothèques d'embeddings et la prise en charge de la télémétrie sont installés par défaut. Les anciens extras subsistent sous forme d'alias vides de compatibilité, afin que les scripts existants continuent de fonctionner.
Oui. ToolOps est conçu comme une fondation pour les serveurs Model Context Protocol et les agents à état LangGraph. Il apporte l'infrastructure de qualité industrielle qui manque nativement à ces frameworks.
La v1.0.0 établit ToolOps comme un SDK stable et tout compris. L'installation par défaut embarque tous les backends de cache et leurs pilotes — dont SQLiteCache, ValkeyCache/RedisCache et MySQLCache — ainsi qu'un cycle de vie unifié des backends, avec des erreurs explicites en état fermé et les exports d'observabilité configure_opentelemetry / prometheus_metrics. Aucune modification du code applicatif n'est nécessaire.
Trois couches : le hachage SHA-256 de toutes les clés de cache garantit qu'aucun argument en clair — jetons, données personnelles — n'atterrit dans un magasin de cache. Le masquage automatique repère les mots-clés sensibles connus et remplace leur valeur par un marqueur caviardé dans les logs structurés. L'argument de décorateur sensitive_params permet enfin d'exclure explicitement n'importe quel paramètre de la génération de la clé de cache.
Des agents en production ?
Essayez-le d'abord sur votre outil le plus capricieux. Issues et retours de terrain bienvenus.