Accueil / Articles

Graphes des crimes financiers multi-agents avec LangChain, LangGraph et LangSmith

Un graphe des crimes financiers à plusieurs agents avec LangChain, LangGraph et LangSmith : routage par superviseur, sous-graphes, interruptions par un intervenant humain, évaluations, mécanismes de contrôle.

Éditeur : REACTAPP.TOP10 min de lecture2374 mots

Sources: Building Multi-Agent Financial Crime Detection Systems with LangGraph, LangChain, and LangSmith

Le problème sur le terrain

L’analyse des crimes financiers n’est pas une requête unique. Un dossier peut nécessiter un enrichissement des données KYC, un contrôle des sanctions, une analyse de la presse négative, l’identification de schémas transactionnels, ainsi qu’une narration destinée aux enquêteurs — chacun exigeant des outils, des délais d’intervention et des modes de défaillance différents. Une seule chaîne qui tenterait de faire tout cela deviendrait opaque, difficile à tester et risquée en cas de défaillance d’un outil.

Pourquoi plusieurs agents valent mieux qu’une seule méga-chaine

Des agents spécialisés permettent de concentrer les requêtes et les outils. Un superviseur peut diriger ou répartir le travail. Les défaillances restent localisées. Une revue humaine peut contrôler les transitions à haut risque. L’observabilité permet d’attribuer les coûts et les erreurs à chaque rôle plutôt qu’à un seul suivi global.

Trois couches : LangChain, LangGraph, LangSmith

LangChain fournit des modèles, des outils et des mécanismes de gestion des messages. LangGraph gère l’orchestration à état — état partagé du cas, arêtes, persistance, interruptions. LangSmith s’occupe du suivi des actions, des ensembles de données et des évaluateurs. Un agent utilisant simplement une boucle while peut appeler des outils, mais il offre rarement une mémoire persistante pour le cas, des vérifications parallèles, un réutilisage de sous-graphes ou un traitement prioritaire des HITL.

État partagé du cas

Commencez par un schéma d’état explicite : identifiant du cas, messages, scores, indicateurs, décisions de routage et notes de l’enquêteur.

from typing import TypedDict, Annotated, Literal
from langgraph.graph import StateGraph, END
from langgraph.graph.message import add_messages
from operator import add

class CaseState(TypedDict):
    case_id: str
    customer_id: str
    transactions: list[dict]
    messages: Annotated[list, add_messages]
    kyc_flags: Annotated[list[str], add]
    sanctions_hits: Annotated[list[dict], add]
    risk_score: float
    narrative: str
    requires_human_review: bool

Nœud d’agent avec des outils

Liez un modèle à des outils spécifiques au domaine (recherche de sanctions, consultation d’une liste de surveillance) et permettez à un nœud d’outil d’exécuter les appels.

from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool

@tool
def screen_ofac(name: str, dob: str | None = None) -> dict:
    """Screen an individual or entity name against the UK list."""
    # In production: call a real screening API (e.g. UK100, Refinitiv)
    return {"name": name, "match": False, "score": 0.02}

@tool
def screen_un_sanctions(name: str) -> dict:
    """Screen a name against the UN Consolidated Sanctions List."""
    return {"name": name, "match": False, "score": 0.0}
llm = ChatAnthropic(model="claude-sonnet-4-6", temperature=0)
sanctions_llm = llm.bind_tools([screen_ofac, screen_un_sanctions])

def sanctions_agent(state: CaseState) -> dict:
    response = sanctions_llm.invoke(state["messages"])
    return {"messages": [response]}

Connexion du graphique

Associez les nœuds d’agent et d’outil, utilisez tools_condition (ou équivalent) pour itérer jusqu’à ce que le modèle cesse d’appeler les outils, puis passez à l’étape suivante.

from langgraph.prebuilt import ToolNode, tools_condition

graph = StateGraph(CaseState)
graph.add_node("kyc_agent", kyc_agent)
graph.add_node("sanctions_agent", sanctions_agent)
graph.add_node("sanctions_tools", ToolNode([screen_ofac, screen_un_sanctions]))
graph.add_node("tx_monitoring_agent", tx_monitoring_agent)
graph.add_node("narrative_agent", narrative_agent)
graph.set_entry_point("kyc_agent")
graph.add_edge("kyc_agent", "sanctions_agent")
# Conditional edge: loop back to tools if the LLM requested a tool call
graph.add_conditional_edges(
    "sanctions_agent",
    tools_condition,
    {"tools": "sanctions_tools", END: "tx_monitoring_agent"},
)
graph.add_edge("sanctions_tools", "sanctions_agent")
graph.add_edge("tx_monitoring_agent", "narrative_agent")
graph.add_edge("narrative_agent", END)
app = graph.compile()

Tester les nœuds avec un état fictif avant de constituer le graphe complet :

def test_sanctions_agent_flags_high_score_match():
    fake_state = {
        "messages": [HumanMessage(content="Screen: Igor Petrov, DOB 1978-03-11")],
    }
    result = sanctions_agent(fake_state)
    assert "tool_calls" in result["messages"][-1].additional_kwargs

Autorisation par superviseur

Une instruction du superviseur classe le spécialiste suivant — KYC, sanctions, médias négatifs — ou se termine par un résumé.

from langchain_core.messages import SystemMessage

SUPERVISOR_PROMPT = """You are a financial crime case supervisor.
Route to exactly one of: kyc_agent, sanctions_agent, tx_monitoring_agent,
adverse_media_agent, narrative_agent, or FINISH if the case is fully investigated."""

def supervisor(state: CaseState) -> dict:
    routing_llm = llm.with_structured_output(RouteDecision)
    decision = routing_llm.invoke(
        [SystemMessage(content=SUPERVISOR_PROMPT)] + state["messages"]
    )
    return {"next": decision.next_agent}

def route_next(state: CaseState) -> str:
    return state["next"] if state["next"] != "FINISH" else END
graph.add_node("supervisor", supervisor)
for agent_name in ["kyc_agent", "sanctions_agent", "tx_monitoring_agent", "adverse_media_agent"]:
    graph.add_edge(agent_name, "supervisor")
graph.add_conditional_edges("supervisor", route_next)

Déploiement parallèle

Des vérifications indépendantes peuvent être effectuées après le KYC sans avoir à serialiser tout le processus :

graph.add_edge("kyc_agent", "sanctions_agent")
graph.add_edge("kyc_agent", "adverse_media_agent")
graph.add_edge("kyc_agent", "tx_monitoring_agent")

# Fan-in: all three must complete before scoring
graph.add_edge("sanctions_agent", "risk_scoring_agent")
graph.add_edge("adverse_media_agent", "risk_scoring_agent")
graph.add_edge("tx_monitoring_agent", "risk_scoring_agent")

Intervention humaine

Créer un point de contrôle, interrompre l’exécution lorsque le risque dépasse les critères définis, transmettre un état partiel à l’interface utilisateur du dossier, puis reprendre avec les informations fournies par l’enquêteur.

from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import interrupt, Command

def compliance_review(state: CaseState) -> dict:
    if state["risk_score"] > 0.7:
        decision = interrupt({
            "narrative": state["narrative"],
            "risk_score": state["risk_score"],
            "action_needed": "approve_or_reject_sar",
        })
        return {"requires_human_review": False, "human_decision": decision}
    return {"requires_human_review": False}

checkpointer = MemorySaver()
app = graph.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "case-2026-00931"}}
result = app.invoke({"case_id": "case-2026-00931", ...}, config=config)

# Later, once a human reviews in your case management UI:
final = app.invoke(Command(resume={"approved": True, "reviewer": "j.smith"}), config=config)
for chunk in app.stream(
    {"case_id": "case-2026-00931", ...},
    config=config,
    stream_mode="updates",
):
    for node_name, node_output in chunk.items():
        print(f"[{node_name}] emitted: {node_output}")
        # push node_output to a websocket / case UI here

Sous-graphes pour des unités réutilisables

Packager la résolution du KYC en un graphe distinct et l’intégrer en tant que nœud afin que d’autres types de dossiers puissent réutiliser cette même unité.

kyc_subgraph = StateGraph(CaseState)
kyc_subgraph.add_node("resolve_entity", resolve_entity_node)
kyc_subgraph.add_node("verify_documents", verify_documents_node)
kyc_subgraph.add_node("pep_check", pep_check_node)
kyc_subgraph.set_entry_point("resolve_entity")
kyc_subgraph.add_edge("resolve_entity", "verify_documents")
kyc_subgraph.add_edge("verify_documents", "pep_check")
kyc_subgraph.add_edge("pep_check", END)
compiled_kyc = kyc_subgraph.compile()
parent_graph = StateGraph(CaseState)
parent_graph.add_node("kyc_process", compiled_kyc)  # subgraph as a node

Observabilité avec LangSmith

Activer le suivi et marquer les exécutions avec des identifiants de dossier ainsi que des rôles d’agent.

import os

os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = "ls__..."
os.environ["LANGSMITH_PROJECT"] = "financial-crime-agents"
result = app.invoke(
    {"case_id": "case-2026-00931", ...},
    config={
        "configurable": {"thread_id": "case-2026-00931"},
        "metadata": {"case_id": "case-2026-00931", "customer_tier": "high_risk"},
        "tags": ["sar_pipeline", "prod"],
    },
)

Itération guidée par l’évaluation

Le jeu de données évalue les régressions de performance lorsque les prompts ou les outils changent. Le LLM en tant que juge peut noter la clarté narrative des résumés similaires à SAR — toujours avec une validation humaine en cas de risques élevés.

from langsmith import Client
from langsmith.evaluation import evaluate

client = Client()
def correctness_evaluator(run, example) -> dict:
    predicted_risk = run.outputs.get("risk_score", 0)
    actual_risk = example.outputs.get("risk_score", 0)
    return {
        "key": "risk_score_accuracy",
        "score": 1.0 if abs(predicted_risk - actual_risk) < 0.15 else 0.0,
    }
results = evaluate(
    lambda inputs: app.invoke(inputs, config={"configurable": {"thread_id": "eval"}}),
    data="aml-labeled-cases-2026-h1",
    evaluators=[correctness_evaluator],
    experiment_prefix="sar-pipeline-v3",
)
from langsmith.evaluation import LangChainStringEvaluator

narrative_judge = LangChainStringEvaluator(
    "criteria",
    config={
        "criteria": {
            "cites_evidence": "Does the narrative cite specific transaction IDs and dates?",
            "regulatory_tone": "Is the language neutral and factual, avoiding speculation?",
        },
        "llm": ChatAnthropic(model="claude-sonnet-4-6", temperature=0),
    },
)
results = evaluate(
    lambda inputs: app.invoke(inputs, config=config),
    data="aml-labeled-cases-2026-h1",
    evaluators=[correctness_evaluator, narrative_judge],
    experiment_prefix="sar-narrative-v2",
)

Règles de contrôle et gestion des pannes

Tenter à nouveau l’utilisation des nœuds d’outils instables, limiter la récursivité, et gérer les pannes liées aux sanctions de manière ferme plutôt que d’inventer un résultat clair.

from langgraph.pregel import RetryPolicy

graph.add_node(
    "sanctions_tools",
    ToolNode([screen_ofac, screen_un_sanctions]),
    retry=RetryPolicy(max_attempts=3, backoff_factor=2.0),
)
result = app.invoke(initial_state, config={**config, "recursion_limit": 25})

Péripéties dans les graphes AML en production

Globaux mutables partagés cachés, classement automatique non supervisé, absence de traçabilité du score vers les preuves, ainsi que des superviseurs qui demandent indéfiniment au même agent de procéder. Limiter les boucles, exiger des citations dans l’état, et conserver les actions de classement derrière HITL.

Forme de référence

Une topologie pratique : un superviseur au centre, des agents spécialisés dotés de boucles d’outils, des branches parallèles optionnelles, un nœud de fusion/narration, une porte d’interruption, puis une exportation — avec l’inclusion de sous-graphes réutilisables là où les unités se répètent.

             ┌─────────────┐
             │  Supervisor │◄───────────────-─┐
             └──────┬──────┘                  │
     ┌───────────────┼───────────────┐        │
     ▼               ▼               ▼        │
┌─────────┐   ┌──────────────┐  ┌───────────┐ │
│   KYC   │   │ Sanctions    │  │ TX Monitor│ │
│Subgraph │   │ Agent + Tools│  │  Agent    │ │
└────┬────┘   └──────┬───────┘  └─────┬─────┘ │
     └───────────────┴────────────────┘       │
                       ▼                      │
              ┌─────────────────┐             │
              │ Risk Scoring    │─────────────┘
              └────────┬────────┘
                       ▼
              ┌─────────────────┐        ┌──────────────┐
              │ Narrative Agent │───────►│ Human Review │
              └─────────────────┘        │ (interrupt)  │
                                         └──────┬───────┘
                                                 ▼
                                          SAR Filed / Closed

Cost et latence

La diffusion des données consomme des tokens ; conservez en cache les profils KYC stables ; utilisez des modèles plus petits pour le routage et des modèles plus grands pour les narrations ; diffusez en continu les mises à jour de l’interface afin que les enquêteurs puissent voir les progrès avant la rédaction finale.

Remarques finales

Les systèmes multi-agents pour la lutte contre la criminalité financière fonctionnent bien lorsque l’état des choses est explicite, que les spécialistes sont testables, que l’orchestration est visible, et que des humains restent impliqués pour les actions irréversibles. LangGraph fournit le plan de contrôle ; LangChain fournit les outils ; LangSmith assure la boucle de feedback qui maintient l’exactitude du graphe au fur et à mesure que les typologies évoluent.

Gouvernance pour les agents d’enquête réglementés

Les équipes chargées de la lutte contre le blanchiment d’argent et la fraude sont déjà soumises aux risques liés aux modèles ainsi qu’aux exigences d’audit. Un graphe d’agents doit générer des traces de preuves que les enquêteurs et les régulateurs peuvent suivre : quels outils ont été utilisés, quelles listes ont correspondu, quels seuils ont déclenché une action, qui a approuvé une exception, et quelle version du modèle a produit le rapport. Stockez ces données dans l’état de l’affaire et exportez-les avec le dossier, et non seulement dans une interface d’affichage propriétaire.

Les modifications des prompts et des outils doivent suivre la même procédure de gestion des changements que celle utilisée pour les moteurs de règles. Testez en parallèle les nouveaux prompts de supervision sur des affaires historiques, comparez les distributions de routage, et exigez une validation avant toute mise en production. Les ensembles de données LangSmith aident, mais les responsables de la gouvernance ont toujours besoin d’une confirmation écrite.

Les facteurs humains sont importants. Si les interruptions surviennent trop souvent, les investigateurs les ignorent ; si elles sont trop rares, le risque échappe au contrôle. Ajustez les seuils grâce à des analyses d’échantillonnage. L’état intermédiaire affiché doit mettre en évidence l’incertitude, et non seulement des informations fiables, afin que les examinateurs sachent où chercher.

La robustesse face aux attaques fait partie de la fiabilité : les résultats des outils peuvent être brouillés ou trompeurs (textes web altérés dans des médias hostiles). Les nœuds de validation doivent nettoyer les données et citer leurs sources. Ne permettez jamais qu’un seul paragraphe extrait autorise une action sans vérifications complémentaires.

Lors de l’évaluation du succès, équilibrez le taux d’automatisation et la qualité : pourcentage de cas clos automatiquement, temps moyen de tri, taux d’approbations erronées lors des audits d’échantillonnage, ainsi que le coût en dollars par cas. Les graphiques qui économisent du temps aux analystes mais entraînent des approbations erronées ne constituent pas une réussite. Conservez un mécanisme de secours qui redirige tout vers des humains en cas de dégradation des fournisseurs de sanctions.

Ces pratiques transforment l’architecture de référence, d’un simple diagramme ingénieux, en un système opérationnel. Les exemples de code ci-dessus ne sont que des structures de base ; c’est le modèle opérationnel qui permet aux agents chargés de la lutte contre la criminalité financière de gagner la confiance.

Création des prompts spécialisés sans duplication

sanctions_view(state), kyc_view(state), puis testez-les en unité. Les superviseurs voient des résumés de routage ; les rédacteurs consultent des dossiers contenant les preuves.

Les clients d’outils partagés doivent être regroupés dans une bibliothèque incluant des mécanismes de tentative répétée et des indicateurs de performance. Les agents ne devraient pas implémenter différemment les délais d’attente HTTP. Centralisez les identifiants des fournisseurs de listes ainsi que les mécanismes de protection pour que toute panne liée aux sanctions se produise de manière cohérente dans tout le réseau.

Lignée des données et entrepôts de caractéristiques

Les caractéristiques transactionnelles utilisées par les agents de modélisation doivent provenir de pipelines gérés, et non de requêtes SQL ad hoc au sein d’une application. Versionnez les définitions des caractéristiques. Lorsqu’un analyste remet en question une note, il est nécessaire de reconstituer les données d’entrée. Les graphes incapables d’expliquer une note ne survivront pas à l’évaluation des risques liés au modèle.

Parallélisme et cohérence

Les branches de diffusion doivent se fusionner de manière déterministe. Définissez des fonctions de réduction pour les champs de type liste et des règles du dernier éditeur pour les valeurs scalaires. Testez les mises à jour simultanées. Si des sources contradictoires et des sanctions modifient toutes deux la valeur de risk_flags, utilisez des fonctions de réduction annotées afin que les flags soient combinés plutôt que écrasés.

Intégration avec le flux de travail humain

Les systèmes de gestion des dossiers (type Jira, CRM personnalisés) doivent gérer les interruptions. Envoyez les charges utiles des interruptions en tant que tâches ; une fois terminées, appelez les API de reprise avec le résultat de la tâche. Ne créez pas un site d’approbation ponctuel si les enquêteurs utilisent déjà une console existante — intégrez-le directement là-bas.

Test de résistance du graphe

Simulez le empoisonnement des outils, l’injection de commandes dans les notes des clients, ainsi que des problèmes de résolution d’entités. Assurez-vous que le graphe refuse de valider les données lorsque les preuves sont insuffisantes. Enregistrez ces simulations en tant que cas d’évaluation. Les outils utilisés pour lutter contre la criminalité financière ont un double usage ; les attaquants tenteront de forcer des validations.

Gouvernance des coûts

La balise LangSmith (ou équivalente) fonctionne avec case_type, agent et model. Budget par type de dossier. Alertes en cas d’anomalies. Préférer des règles déterministes pour les décisions évidentes d’acceptation ou de refus, et réserver les LLM aux tâches narratives ambiguës. Les systèmes hybrides surpassent les solutions purement basées sur des LLM tant en termes de coûts que de traçabilité.

Ressources humaines et responsabilités

Déterminez un responsable de l’orchestration, un responsable des outils et des données, ainsi qu’un responsable de la conformité. L’ambiguïté à cet égard génère des diagrammes complexes que personne ne peut modifier en toute sécurité. Planifiez des révisions conjointes lors de l’ajout d’un nouvel agent spécialisé : chaque nouveau nœud représente un nouveau domaine de défaillance potentiel.

Avec ces pratiques, le modèle multi-agent de LangGraph devient un système d’exploitation pour les enquêtes plutôt qu’une démonstration ne fonctionnant que sur un seul cas idéal.

De la phase pilote à l’adoption au sein de l’entreprise

Les pilotes fonctionnent souvent avec un petit nombre de cas synthétiques. Pour une adoption au sein des différents services, il est nécessaire de localiser les typologies, d’assurer un soutien linguistique pour les narratifs, ainsi que d’intégrer ces cas aux systèmes de suivi temporel existants et aux SLA. Planifiez un déploiement progressif par segments : commencez par les produits à faible risque, puis ajoutez ceux à plus haut risque ultérieurement. Conservez un groupe témoin de cas examinés manuellement afin d’évaluer l’impact. Assurez-vous que les utilisateurs comprennent bien qu’une assistance par modèle ne signifie pas une opération sans supervision. La formation des enquêteurs doit inclure comment interpréter les traces graphiques, comment prendre le contrôle manuellement et comment signaler des données issues d’outils défectueux. La technologie, sans ce cadre humain, performe moins bien, même lorsque le diagramme d’architecture est parfait.

Prévoyez que les fournisseurs et les équipes internes de données modifient les API en arrière-plan. L’utilisation de versions fixes, de graphiques de test et de drapeaux fonctionnels activés sur demande est indispensable. Le système multi-agents pour la lutte contre la criminalité financière est un service en évolution constante, et non une simple conversion d’un document initial.

Liste de contrôle avant le lancement en production

Le schéma a été examiné par le service de conformité ; les outils sont équipés d’authentification et de délais de timeout ; le superviseur évalue positivement les cas historiques ; le chemin HITL a été testé du début à la fin ; les tableaux de bord LangSmith (ou d’autres solutions similaires) sont opérationnels ; les limites de récursivité ont été définies ; le mécanisme d’arrêt d’urgence est documenté ; les budgets de coûts sont surveillés en temps réel ; la versionnement des sous-graphes a été convenu ; les outils de fusion parallèle ont été testés ; des citations narratives sont requises ; les guides d’intervention en cas d’urgence ont été publiés. La détection des crimes financiers par plusieurs agents repose moins sur des prompts ingénieux que sur ces outils peu spectaculaires. Cochez-les, puis augmentez progressivement le volume des cas, en surveillant chaque semaine les échantillons déclarés comme valides alors qu’ils ne le sont pas pendant le premier trimestre.

Collaboration avec les enquêteurs en tant qu’utilisateurs du produit

Les enquêteurs ne sont pas un élément ajouté en dernier moment dans l’interface utilisateur. Restez avec eux pendant les essais pilotes. Observez où ils se méfient des résumés, où ils ont besoin de données brutes issues des outils, et où ils souhaitent une possibilité d’escalade en un clic. Encodiez ces besoins sous forme de champs d’état et dans les chargements interrompus. Un graphique qui ne s’optimise que pour le taux d’automatisation sera contourné, et ces contournements effaceront les avantages en matière d’audit que vous prétendiez avoir.

Co-concevez des tableaux de bord qui montrent pourquoi, un mardi donné, le superviseur a orienté la demande vers des sanctions avant de procéder au KYC. La transparence favorise l’adoption, et cette adoption génère des retours qui améliorent les prompts et les seuils. Sans ce cycle, même une architecture de référence parfaite stagne après la première semaine de lancement.

Publiez un rapport mensuel sur la qualité que les analystes aident à évaluer. Une responsabilité partagée permet au système multi-agents de rester aligné sur la mission — la lutte contre la criminalité financière — et non seulement sur des métriques de démonstration. En agissant ainsi de manière constante, la conception multi-agents de LangGraph gagne la confiance, ce qui est essentiel dans la lutte contre la criminalité financière : en résistant au contact avec des cas réels, des enquêteurs réels et des audits réels — et non seulement en ayant l’air élégant dans un diagramme de référence. Mettez d’abord en place les contrôles ennuyeux ; laissez le routage intelligent trouver sa place uniquement après que les contrôles aient fonctionné. Les enquêteurs remarqueront immédiatement la différence. Construisez toujours en tenant compte de ce type d’examen minutieux. Rien de moins ne suffit ici.