Le problème
Les agents LangChain peuvent appeler des outils avec des entrées inattendues, épuiser les crédits API sans limites, ou effectuer des appels système dangereux. La gouvernance actuelle se fait au niveau du prompt (que le LLM peut ignorer) plutôt qu'au niveau de l'exécution.
La solution Scavio
Envelopper chaque outil LangChain avec une couche de politique déterministe qui bloque les appels avant exécution. Aucun appel LLM dans le chemin d'application -- uniquement des vérifications de code. Politiques : nombre max d'appels par session, validation des entrées (pas de motifs RPI), validation des sorties (nombre de résultats minimum), budgets de crédits, et listes de blocage.
Avant
Avant l'application, un agent en production a épuisé ses crédits SerpAPI en une session en bouclant les appels de recherche. Un incident séparé : l'agent a passé une adresse email client comme requête de recherche, divulguant des RPI au fournisseur de recherche. Les deux ont été détectés plusieurs jours plus tard lors de la revue des logs.
Après
Après l'ajout du wrapper de type ShadowAudit, l'agent est limité à 10 appels de recherche par session. Les motifs de RPI (email, téléphone, NSS) sont bloqués dans les requêtes de recherche. Le budget de crédits est suivi par session. L'agent tente 2,3 appels bloqués pour 100 sessions, tous interceptés avant exécution.
À qui cela s'adresse
Développeurs LangChain déployant des agents en production qui ont besoin d'une gouvernance déterministe à l'exécution sur les appels d'outils, les budgets de crédits et la sécurité des données.
Avantages clés
- Application déterministe -- aucun LLM dans le chemin de gouvernance
- Bloquer les appels dangereux avant qu'ils ne s'exécutent
- Suivre l'utilisation des outils par session pour le contrôle des coûts
- La détection de RPI empêche la fuite de données vers les fournisseurs de recherche
- Fonctionne comme un wrapper sur n'importe quel outil LangChain
Exemple Python
import re
from typing import Callable
class ToolPolicy:
def __init__(self, max_calls: int = 10, block_pii: bool = True):
self.max_calls = max_calls
self.block_pii = block_pii
self.call_count = 0
self.pii_patterns = [
re.compile(r'[\w.-]+@[\w.-]+\.\w+'), # email
re.compile(r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b'), # phone
]
def enforce(self, query: str) -> str | None:
self.call_count += 1
if self.call_count > self.max_calls:
return f'Blocked: max {self.max_calls} calls exceeded'
if self.block_pii:
for pattern in self.pii_patterns:
if pattern.search(query):
return 'Blocked: PII detected in query'
return None
def wrap_tool(fn: Callable, policy: ToolPolicy) -> Callable:
def wrapped(query: str) -> str:
block = policy.enforce(query)
if block:
return block
return fn(query)
return wrapped
# Usage:
policy = ToolPolicy(max_calls=10, block_pii=True)
# search_tool = wrap_tool(original_search, policy)Exemple JavaScript
class ToolPolicy {
constructor(maxCalls = 10) {
this.maxCalls = maxCalls;
this.callCount = 0;
this.piiPatterns = [/[\w.-]+@[\w.-]+\.\w+/, /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/];
}
enforce(query) {
this.callCount++;
if (this.callCount > this.maxCalls) return `Blocked: max ${this.maxCalls} calls exceeded`;
if (this.piiPatterns.some(p => p.test(query))) return 'Blocked: PII detected';
return null;
}
}
function wrapTool(fn, policy) {
return async (query) => {
const block = policy.enforce(query);
if (block) return block;
return fn(query);
};
}
const policy = new ToolPolicy(10);
// const safeTool = wrapTool(originalSearch, policy);Plateformes utilisées
Recherche web avec graphe de connaissances, PAA et aperçus IA