Sur cette page
- 1. Philosophie
- 2. Le mur de la production
- 3. Installation
- 4. Architecture centrale
- 4.1 Décorateurs
- 4.2 Backends de cache
- 4.3 Pipeline de middleware
- 5. Modèles de résilience
- 5.1 Disjoncteur
- 5.2 Stale-if-error
- 5.3 Coalescence de requêtes
- 6. Cache sémantique
- 7. Observabilité
- 8. Écosystème et MCP
- 9. CLI et opérations
- 10. Feuille de route
- FAQ
Lorsque 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, peu fiables et lents. ToolOps est aux outils IA ce qu'un Service MeshUne couche d'infrastructure qui gère la communication, la résilience et l'observabilité entre services. est aux microservices : un SDK middleware agnostique qui améliore n'importe quelle fonction Python avec mise en cache, résilience et observabilité — sans aucun changement de logique métier.
1. Philosophie
L'analogie qui décrit le mieux ToolOps est le Service Mesh. Tout comme un Service Mesh — pensez à Istio ou Linkerd — se place entre les microservices pour gérer les tentatives, les délais d'attente et les disjoncteurs de manière transparente, ToolOps se place entre votre agent IA et ses outils. Le code de l'application ne sait rien de la couche d'infrastructure sous-jacente.
"ToolOps est aux outils IA ce qu'un Service Mesh est aux microservices."
Cette séparation des préoccupations est intentionnelle. Les auteurs d'outils doivent se concentrer sur ce que fait un outil, pas sur la gestion de la complexité des systèmes distribués pour l'appeler de manière fiable à l'échelle. ToolOps absorbe cette complexité via une interface de décorateur — un design délibérément choisi pour être la surface d'intégration la plus fine possible.
| Fonctionnalité | Standard @lru_cache |
ToolOps v1.0.0 |
|---|---|---|
Support Async / await |
❌ Non supporté | ✅ Natif async |
| Cache sémantiqueMéthode de cache utilisant des embeddings vectoriels pour faire correspondre l'intention d'une requête plutôt que juste le texte exact.En savoir plus → | ❌ Correspondance exacte | ✅ Embeddings vectoriels |
| Cache distribué / persistant | ❌ Mémoire uniquement | ✅ Postgres, SQLite, MySQL, Valkey/Redis |
| DisjoncteurPatron de conception qui interrompt les requêtes vers un service défaillant pour éviter les pannes en cascade.En savoir plus → & Retries | ❌ Aucun | ✅ Intégré avec backoff |
| Coalescence de requêtesTechnique combinant plusieurs requêtes concurrentes identiques en un seul appel réel.En savoir plus → | ❌ "Thundering herd" | ✅ Diffusion du résultat |
| Repli Stale-if-error | ❌ Lève une exception | ✅ Sert la dernière valeur valide |
| Sécurité (Clés SHA-256, Auto-masking) | ❌ Aucune | ✅ Native |
| ObservabilitéCapacité à mesurer l'état interne d'un système via ses sorties (logs, métriques, traces).En savoir plus → (OTELOpenTelemetry. Framework open-source pour générer et collecter des données de télémétrie./Prom) | ❌ Aucune | ✅ Télémétrie structurée |
| Natif IA (MCPModel Context Protocol. Standard ouvert pour connecter les modèles d'IA aux outils et données.En savoir plus →/Frameworks) | ❌ Générique | ✅ Intégrations natives |
2. Le mur de la production
Chaque développeur d'agent frappe le même mur lors du passage de la démo à la production. Les symptômes sont prévisibles : des factures d'API qui augmentent plus vite que l'utilisation, des agents qui plantent à la troisième tentative, des files d'attente de requêtes qui saturent, et des workflows complètement opaques en cas d'échec. ToolOps traite chaque goulot d'étranglement au niveau de la couche infrastructure.
| Problème | Impact Métier | Avec ToolOps |
|---|---|---|
| Appels API redondants | 💸 Pics de coûts x10 | 100 appels → 1 réel + 99 cache hits |
| Requêtes similaires | 💸 Gaspillage de tokens | Match sémantique → même résultat |
| Instabilité des API | 💥 Crashs & boucles d'agents | Disjoncteur + auto-retry |
| Pics de concurrence | 🐢 Surcharge serveur | Coalescence → 1 appel réel |
| Exposition de données sensibles | 🔓 Jetons & PII dans les clés/logs | Hachage SHA-256 + auto-masking |
| Visibilité zéro | 🌑 Opérations aveugles | JSON structuré + traces OTEL |
3. Installation
ToolOps est disponible sur PyPI. Depuis la v1.0.0, le paquet est distribué « tout inclus » : une seule installation embarque tous les backends de cache (Memory, File, SQLite, Postgres, MySQL/MariaDB, Valkey/Redis, Semantic), les drivers de base de données, les bibliothèques d'embeddings et d'intégration OpenAI, et le support d'observabilité OpenTelemetry/Prometheus. Aucun extra n'est requis.
# Une seule commande installe tout — backends, drivers, télémétrie
pip install toolops
# Vérifier l'installation
toolops doctor
:: Une seule commande installe tout
pip install toolops
:: Utilisation du launcher
py -m pip install toolops
Migration depuis les extras
Les extras historiques comme toolops[postgres],
toolops[semantic] ou toolops[all] restent disponibles comme alias de
compatibilité vides : vos scripts CI et de déploiement existants continuent de fonctionner. Pour toute
nouvelle installation, utilisez simplement pip install --upgrade toolops.
Il est fortement recommandé d'installer ToolOps dans un environnement virtuel pour éviter les conflits de dépendances :
# Créer et activer (.venv)
python -m venv .venv
source .venv/bin/activate # Linux/macOS
# .venv\Scripts\activate # Windows
# Installer et vérifier
pip install toolops
toolops doctor
# Configuration en une commande avec PostgreSQL
docker-compose up -d
docker-compose exec toolops make test
make test # Lance la suite complète de tests avec couverture
make lint # Vérifications Ruff + Black
make format # Auto-formatage avec Black
make typecheck # Mode strict de mypy
make coverage # Rapport de couverture HTML
make clean # Supprime tous les artefacts de build
Depuis la v1.0.0, pip install toolops constitue le contrat d'installation complet :
l'ensemble de dépendances par défaut inclut les backends de production supportés et les bibliothèques
d'intégration — cache persistant et 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. Architecture centrale
L'architecture comporte trois couches : une interface de décorateur qui se place sur vos fonctions d'outils, un pipeline de middleware composable qui orchestre la résilience et l'observabilité, et un système de backend interchangeable 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 de code de l'outil.
4.1 Décorateurs
ToolOps fournit deux décorateurs qui correspondent aux deux catégories d'opérations d'outils :
@readonly — opérations de lecture avec cache
recommandé pour lectures
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)
# Automatiquement mis en cache, réessayé et tracé.
# api_key est exclu de la clé de cache SHA-256 et masqué dans les logs.
@sideeffect — opérations d'écriture avec résilience
pour écritures
@sideeffect(circuit_breaker=True, timeout=5.0, retry_count=2)
async def execute_trade(order: dict) -> bool:
return await broker.submit(order) # Protégé par le disjoncteur et le timeout.
# Pas de cache — mais protégé par le disjoncteur et le timeout.
La distinction entre lecture et écriture est un concept de premier rang dans ToolOps. Les lectures sont idempotentes et sûres à mettre en cache et à réessayer. Les écritures ne le sont pas — elles reçoivent uniquement des modèles de résilience, jamais de tentatives automatiques qui pourraient causer des doubles soumissions.
4.3 Pipeline de middleware
Dans la version 0.2.0, le décorateur monolithique a été refactorisé en un pipeline composable de
middlewares indépendants. Chaque middleware gère une préoccupation unique, et le
ToolExecutor les orchestre en séquence. Un ToolContext partagé transporte l'état
mutable à travers le pipeline.
| Middleware | Responsabilité |
|---|---|
LoggingMiddleware |
Journalisation JSON structurée pour chaque appel d'outil |
CacheMiddleware |
Recherche de cache, stale-if-error, écriture en cache |
CircuitBreakerMiddleware |
Protection par disjoncteur |
RetryMiddleware |
Boucle de tentative avec backoff exponentiel |
CoalescingMiddleware |
Coalescence de requêtes (déduplication) |
FallbackMiddleware |
Exécution de repli en cas d'échec |
from toolops.middlewares import build_executor, DEFAULT_PIPELINE
# Le pipeline par défaut utilisé par chaque décorateur
executor = build_executor(pipeline=DEFAULT_PIPELINE)
# → [LoggingMiddleware, CacheMiddleware, CircuitBreakerMiddleware,
# RetryMiddleware, CoalescingMiddleware, FallbackMiddleware]
Compatibilité ascendante
Le comportement du décorateur est 100% préservé. Le code existant
utilisant @tool, @readonly, @sideeffect ou @stateful
ne nécessite aucun changement pour bénéficier de la nouvelle architecture de pipeline.
4.2 Backends de cache
Enregistrez les backends une seule 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 rapide en mémoire pour les données chaudes, une couche SQL persistante pour les pistes d'audit, une couche distribuée Valkey/Redis pour les déploiements multi-processus, et une couche sémantique pour les charges de travail NLP. Depuis la v1.0.0, chaque backend ci-dessous est installé par défaut.
| Backend | Type | Idéal pour |
|---|---|---|
MemoryCache |
En mémoire (processus) | Développement, test, déploiements à processus unique |
FileCache |
Persistant (fichiers) | Persistance locale légère sans base de données |
SQLiteCache |
Persistant (fichier unique) | Persistance sans serveur via aiosqlite — invalidation par tags et
stale-while-revalidate, aucun serveur requis |
PostgresCache |
Persistant (SQL) | Cache persistant avec piste d'audit complète, partageable entre processus et redémarrages |
MySQLCache |
Persistant (SQL) | MySQL 8+ et MariaDB 10.5+ via aiomysql, avec chaînes de connexion DSN |
ValkeyCache / RedisCache |
Distribué (en mémoire) | Valkey ou Redis avec pooling de connexions async complet — RedisCache est un
alias de ValkeyCache pour les projets ciblant Redis |
SemanticCache |
Vectoriel (embeddings) | Pipelines NLP et RAG — correspondance d'intention via embeddings |
from toolops import cache_manager
from toolops.cache import (
MemoryCache, SQLiteCache, PostgresCache, ValkeyCache, MySQLCache
)
# Couche par défaut rapide
cache_manager.register("memory", MemoryCache(), is_default=True)
# Persistance légère — un seul fichier, aucun serveur requis
cache_manager.register("sqlite", SQLiteCache("toolops_cache.db"))
# Couche persistante pour les opérations sensibles
cache_manager.register("db", PostgresCache("postgresql://user:pass@localhost:5432/mydb"))
# Couche distribuée en mémoire avec pooling async
cache_manager.register("valkey", ValkeyCache(host="localhost", port=6379))
# MySQL / MariaDB via DSN
cache_manager.register("mysql", MySQLCache(dsn="mysql://root:secret@localhost:3306/myapp"))
Cycle de vie unifié (v1.0.0)
Depuis la v1.0.0, tous les backends de cache partagent des règles de cycle de vie
identiques. Toute opération sur un backend fermé lève une RuntimeError claire au lieu de
se reconnecter silencieusement ou d'échouer avec une AttributeError — le même contrat
pour Memory, File, SQLite, Postgres, MySQL, Valkey/Redis et Semantic.
5. Modèles de résilience
Au-delà des simples blocs try/except, ToolOps implémente trois modèles déterministes tirés de l'ingénierie des systèmes distribués. Ensemble, ils garantissent qu'un agent n'est jamais piégé dans une boucle d'échec, n'épuise jamais son budget API sur un service dégradé, et ne sert jamais de données périmées quand l'amont est sain.
5.1 Disjoncteur
Le modèle du disjoncteur arrête tous les appels vers un service défaillant après un seuil d'échec configurable. Une fois ouvert, le disjoncteur échoue rapidement — renvoyant une erreur immédiatement plutôt que d'attendre un délai d'attente — et entre dans une fenêtre de récupération avant de tenter de rétablir la connexion. Cela empêche un seul outil défaillant de provoquer l'échec complet de l'agent.
@readonly(
circuit_breaker=True,
circuit_failure_threshold=5, # s'ouvre après 5 échecs consécutifs
circuit_recovery_timeout=60 # réessaie après 60 secondes
)
async def get_exchange_rates() -> dict:
return await forex_api.fetch()
5.2 Stale-if-error
Lorsqu'un service amont échoue et qu'aucune donnée en direct ne peut être récupérée, ToolOps peut automatiquement revenir à la dernière bonne valeur connue du cache — même si cette valeur a dépassé son TTL normal. C'est l'équivalent production de "servir quelque chose d'utile plutôt que de planter".
@readonly(
cache_ttl=3600,
stale_if_error=True,
stale_ttl=86400 # sert des données périmées jusqu'à 24h en cas d'échec
)
async def get_exchange_rates() -> dict:
return await forex_api.fetch()
5.3 Coalescence de requêtes
Lorsque plusieurs instances d'agents appellent le même outil simultanément — un modèle courant dans les pipelines multi-agents — ToolOps détecte la requête en cours et retient les appelants suivants jusqu'à ce que la première se termine. Le résultat unique est ensuite diffusé à tous les appelants en attente. Sous une forte concurrence, cela réduit N appels en amont à exactement un.
Impact
Dans un benchmark avec 50 appels d'agents concurrents au même outil météo, la coalescence de requêtes a réduit les appels d'API amont de 50 à 1 — une réduction de 98% de la consommation de crédits sans aucun changement pour les agents appelants.
6. Cache sémantique
Les caches traditionnels fonctionnent sur l'égalité exacte des clés. Cela fonctionne pour les systèmes déterministes, mas les agents ne sont pas des systèmes déterministes — la même intention de l'utilisateur apparaît sous des dizaines de formulations différentes. ToolOps utilise des embeddings vectoriels pour comprendre le sens d'un appel d'outil, pas seulement ses arguments littéraux. Deux requêtes qui expriment la même intention retournent le même résultat mis en cache.
— Appel 1 (échec de cache → appel API réel)
query: "Quel est le statut de la facture #442 ?"
— Appel 2 (similarité sémantique : 0.97 → hit de cache)
query: "Vérifie le statut actuel pour la facture 442"
— Appel 3 (similarité sémantique : 0.94 → hit de cache)
query: "Facture 442 — est-elle payée ?"
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)
# Réduit la latence LLM jusqu'à 90% sur les modèles d'intention répétés.
Le seuil de similarité (0.92 dans l'exemple ci-dessus) est le principal levier de réglage. Des
valeurs plus élevées nécessitent un alignement sémantique plus étroit ; des valeurs plus basses sont plus
agressives. La bonne valeur dépend de la 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
Dans la v0.2.0, l'éviction de SemanticCache a été refactorisée passant d'opérations sur listes en O(n) à collections.deque en O(1) — garantissant une latence et une utilisation mémoire prévisibles sous forte concurrence.
7. Observabilité
Le débogage de workflows d'agents non déterministes nécessite une instrumentation plus profonde que le simple logging au niveau applicatif. ToolOps émet une télémétrie structurée à chaque étape du cycle de vie de l'outil — hits, misses, retries, transitions d'état du disjoncteur — vous offrant une piste d'audit complète sans aucune instrumentation manuelle.
configure_opentelemetry(tracer) et visualisez le graphe d'appels complet dans
Jaeger, Honeycomb ou Datadog.
from toolops import configure_opentelemetry, prometheus_metrics
# 1. Configurer le traçage OpenTelemetry (accepte tout tracer standard)
configure_opentelemetry(tracer)
# 2. Exposer les métriques Prometheus (renvoie une chaîne texte Prometheus brute)
metrics_string = prometheus_metrics()
8. Écosystème et MCP
Les outils ToolOps sont de simples fonctions Python. Ce choix de conception n'est pas accidentel — cela signifie qu'ils fonctionnent nativement avec chaque framework d'agent qui accepte des callables Python, sans code adaptateur et sans configuration spécifique au framework.
| Framework | Type d'intégration | Statut |
|---|---|---|
| LangChain / LangGraph | Helper intégré | Disponible |
| CrewAI | Helper intégré | Disponible |
| LlamaIndex | Compatibilité générale | Disponible |
| Model Context Protocol (MCP) | Helper intégré | Disponible |
| PydanticAI | Compatibilité générale | Disponible |
| AutoGPT et frameworks personnalisés | N'importe quel callable Python | Disponible |
L'intégration MCP mérite une mention particulière. ToolOps inclut un adaptateur intégré qui expose n'importe quel outil décoré comme une définition compatible MCP — sans écrire une seule ligne de JSON Schema. Cela signifie que vos outils résilients et de qualité production sont disponibles instantanément pour Claude Desktop, Cursor ou tout hôte compatible MCP.
from toolops.integrations.mcp import MCPIntegration
# get_weather est déjà décoré avec @readonly
definition = MCPIntegration.to_mcp_definition(get_weather)
# → Définition d'outil compatible MCP, prête pour Claude Desktop ou Cursor.
9. CLI et opérations
ToolOps est livré avec un outil en ligne de commande pour inspecter et gérer votre infrastructure d'outils en production. Le CLI est conçu pour les opérateurs et les pipelines CI — pas seulement pour les développeurs.
# Lister toutes les commandes disponibles
toolops --help
# Vérifier la santé du système et la disponibilité des backends
toolops doctor
# Voir les statistiques de cache en temps réel
toolops stats --app my_app:setup_toolops
# Afficher les métriques courantes
toolops metrics --app my_app:setup_toolops
# Inspecter une clé de cache spécifique
toolops inspect-key --app my_app:setup_toolops
# Effacer un backend de cache spécifique
toolops clear postgres --app my_app:setup_toolops
La commande toolops doctor est particulièrement utile dans les pipelines de déploiement : elle
valide la connectivité du backend, vérifie la disponibilité du modèle d'embedding et rapporte l'état du
disjoncteur — un test de préparation que vous pouvez brancher directement sur votre endpoint de santé.
10. Feuille de route
ToolOps v1.0.0 marque la version stable « tout inclus » : un pipeline de middleware composable, une sécurité renforcée (hachage SHA-256 et masquage automatique des paramètres), un cycle de vie unifié pour tous les backends de cache, et une installation en une seule commande sans extras. Les capacités suivantes sont prévues pour les prochaines versions, classées par livraison prévue. Les contributions et retours sur la priorisation sont les bienvenus via GitHub.
- Tableau de bord Web. Métriques en temps réel, attribution des coûts et taux de hit du cache dans une interface web — aucune configuration Prometheus ou Grafana requise.
- Contrôle du budget. Limites strictes sur les coûts d'API induits par les outils par heure ou par jour, configurables par outil et par backend.
- Serveur MCP natif. Déploiement en un clic des outils ToolOps en tant qu'hôte MCP autonome — aucune configuration Claude Desktop requise.
- Middleware de streaming. Support complet pour le streaming des sorties d'outils, permettant la génération de réponses en temps réel dans les pipelines d'agents.
- Nouveaux Backends. Support pour ChromaDB et Pinecone — étendant les options de
cache natif vecteur. (MariaDB est déjà supporté depuis la v1.0.0 via
MySQLCache.)
Participez
ToolOps est open source sous licence Apache 2.0. Mettez une étoile au dépôt, ouvrez un ticket ou soumettez une pull request sur GitHub. Le projet est construit publiquement — les priorités de la feuille de route sont façonnées par les cas d'utilisation réels de la communauté.
Questions fréquemment posées
Pourquoi utiliser ToolOps plutôt que les outils intégrés des frameworks ?
ToolOps est agnostique au framework. Alors que LangChain ou CrewAI ont une logique de tentative de base, ToolOps fournit des modèles industriels comme les Disjoncteurs, la Coalescence de requêtes et le Cache Sémantique qui fonctionnent avec n'importe quel outil Python sans coût de migration.
Que se passe-t-il si mon API est hors service ?
ToolOps protège votre système de trois manières : les Disjoncteurs arrêtent le bombardement de requêtes, les Tentatives Automatiques gèrent les pannes temporaires, et le repli Stale-if-Error peut servir la dernière valeur connue du cache pour que votre agent continue de fonctionner.
Comment la coalescence de requêtes empêche-t-elle les "Thundering Herds" ?
Si 50 agents appellent le même outil simultanément lors d'un échec de cache, ToolOps exécute l'appel API réel une seule fois et diffuse le résultat aux 50 appelants. Cela évite de saturer les limites de débit de votre API amont.
Faut-il encore utiliser des extras comme toolops[all] ?
Non. Depuis la v1.0.0, pip install toolops installe tout par défaut : backends de cache
standard, drivers de base de données, bibliothèques d'embeddings et support de télémétrie. Les
extras historiques restent disponibles comme alias de compatibilité vides afin de ne pas casser
les scripts existants.
Est-ce que cela fonctionne avec LangGraph et MCP ?
Oui. ToolOps est conçu comme une base pour les serveurs Model Context Protocol (MCP) et les agents stateful LangGraph. Il fournit l'infrastructure industrielle qui manque nativement à ces frameworks.
Qu'est-ce qui a changé dans la v1.0.0 ?
La v1.0.0 fait de ToolOps un SDK stable et « tout inclus ». L'installation par défaut embarque désormais tous les backends de cache et leurs drivers — y compris SQLiteCache, ValkeyCache/RedisCache et MySQLCache (MySQL 8+ / MariaDB 10.5+) introduits en v0.3.0 — ainsi qu'un cycle de vie unifié des backends avec des erreurs claires à l'état fermé, et les exports d'observabilité configure_opentelemetry / prometheus_metrics. Aucun changement du code applicatif n'est requis.
Comment ToolOps protège-t-il les données sensibles ?
Via trois couches : le hachage SHA-256 de toutes les clés de cache garantit qu'aucun argument en clair (jetons, PII) n'apparaît dans les stockages de cache. Le masquage automatique détecte les mots-clés sensibles connus (token, api_key, password, etc.) et remplace leurs valeurs par ***MASKED*** dans les logs structurés. Enfin, l'argument sensitive_params du décorateur vous permet d'exclure explicitement n'importe quel paramètre de la génération de la clé de cache.