Système d'audit et de conformité automatisé pour infrastructures critiques, construit avec Semantic Kernel 1.74 en C# / .NET 9.
Ce projet est un cas d'usage industriel qui démontre les trois piliers avancés de Semantic Kernel : l'orchestration multi-agents, le RAG hybride à large échelle, et le routage dynamique multi-LLM.
Le système analyse des preuves techniques (manifestes Kubernetes, configurations Terraform, scripts Shell) et les compare automatiquement à des référentiels réglementaires (DORA, ACPR, ISO 27001) via un pipeline d'agents IA spécialisés.
Fichiers d'infra (YAML/HCL/Shell)
│
▼
┌─────────────┐
│ LLM Router │ ──→ Simple → Synthese (Mistral local)
└─────────────┘
│ Complex
▼
┌─────────────────────────────────────────────┐
│ AgentGroupChat │
│ │
│ AuditeurTech → OfficerConformite → Synthese │
│ (GPT-4o) (Claude 3.5) (Mistral) │
└─────────────────────────────────────────────┘
│ ▲
└────── RAG ─────────┘
(Qdrant)
│
▼
Rapport d'audit : APPROUVÉ / ALERTE
- Orchestration multi-agents via
AgentGroupChat(Semantic Kernel Agents Framework) - Stratégie de terminaison automatique sur les mots-clés
APPROUVÉ/ALERTE(max 5 itérations) - RAG hybride : recherche dense (embeddings) + re-ranking TF-IDF sparse en mémoire
- Router LLM : classification heuristique + méta-prompt SLM pour aiguiller les requêtes
- Connecteur Anthropic custom : implémentation de
IChatCompletionServicepour Claude - Télémétrie complète : traces OpenTelemetry, métriques de tokens et coûts par modèle
- API REST ASP.NET Core avec DI complète et resilience (Polly via
Microsoft.Extensions.Http.Resilience)
Le projet suit une architecture en couches inspirée de Clean Architecture :
audit-sem-krnl/
├── src/
│ ├── AuditSemKrnl.Domain/ # Entités, enums, value objects (aucune dépendance externe)
│ ├── AuditSemKrnl.Application/ # Interfaces, use cases, contrats de données
│ ├── AuditSemKrnl.Infrastructure/ # Implémentations : agents SK, RAG, routing, telemetry
│ └── AuditSemKrnl.Api/ # Hôte ASP.NET Core, controllers, Program.cs
├── prompts/ # Templates de prompts SK au format YAML
│ ├── auditeur_tech/
│ ├── officer_conformite/
│ └── synthese/
└── docker/ # docker-compose.yml + config OTel Collector
Api → Infrastructure → Application → Domain
Api → Application
| Composant | Technologie | Version |
|---|---|---|
| Framework IA | Microsoft Semantic Kernel | 1.74.0 |
| Agent Framework | Microsoft.SemanticKernel.Agents.Core | 1.74.0 |
| LLM frontier | Azure OpenAI (GPT-4o) | via SK connector |
| LLM conformité | Anthropic Claude 3.5 Sonnet | Anthropic.SDK 5.10.0 |
| LLM local | Mistral via Ollama | SK connector 1.74.0-alpha |
| Base vectorielle | Qdrant | Client 1.17.0 |
| Observabilité | OpenTelemetry | 1.15.1 |
| Résilience HTTP | Microsoft.Extensions.Http.Resilience | 10.4.0 |
| Runtime | .NET | 9.0 |
Analyse les fichiers d'infrastructure à la recherche de non-conformités de sécurité :
- Conteneurs Kubernetes s'exécutant en
root(securityContext absent ourunAsRoot: true) privileged: true,hostPID,hostNetwork,hostIPC- Buckets S3 publics, politiques IAM avec wildcard
* - Clés d'accès hardcodées dans Terraform
- Groupes de sécurité ouverts sur
0.0.0.0/0
Plugins disponibles : FileAnalysisPlugin (lecture/listage des fichiers), ComplianceRagPlugin (requêtes RAG), ReportPlugin (accumulation des findings).
Expert juridique en réglementation financière et cybersécurité. À partir des findings de l'AuditeurTech :
- Cite les articles précis de DORA (Art. 5-44), ACPR, ISO 27001
- Évalue la gravité réglementaire de chaque non-conformité
- Rend un verdict
APPROUVÉouALERTE
Note d'architecture : Claude est intégré via un IChatCompletionService custom (AnthropicChatCompletionService) car il n'existe pas de connecteur SK officiel pour Anthropic.
Produit un résumé exécutif en 3 paragraphes maximum, compréhensible par un RSSI non-technique. Fonctionne entièrement en local (Ollama).
Important : aucun plugin n'est enregistré sur ce kernel. Les données sensibles (fichiers de configuration) ne quittent jamais ce modèle vers le cloud.
La base réglementaire (textes DORA, ACPR, ISO 27001) est indexée dans Qdrant et interrogée lors de chaque audit.
- Découpage en chunks de 512 mots avec overlap de 128
- Génération des embeddings denses (
text-embedding-3-largevia Azure OpenAI) - Fit de l'encodeur TF-IDF sparse sur le document
- Upsert dans Qdrant avec métadonnées :
framework,version,audit_date,chunk_index
- Embedding dense de la requête
- Recherche Qdrant
SearchAsync(vecteurs denses) - Re-ranking hybride en mémoire :
score_final = 0.7 × score_dense + 0.3 × boost_sparse - Filtrage au-dessus du seuil de pertinence (défaut : 0.70)
Pour un corpus > 10k documents, remplacer
SparseVectorEncoderpar un modèle SPLADE via ONNX Runtime. L'interfaceIHybridSearchServiceisole ce changement.
Le LlmRouter classifie chaque requête avant de décider si le groupe complet d'agents est nécessaire :
Longueur > 2000 tokens ──→ Complex (AgentGroupChat complet)
│
Mot-clé de risque détecté ──→ Complex
│
Appel méta-prompt Mistral ──→ SIMPLE ou COMPLEX
Mots-clés configurables (dans appsettings.json) : privilege escalation, RBAC, ClusterRoleBinding, IAM, cross-account, hostPID, hostNetwork.
Chaque invocation de fonction kernel est instrumentée via SkFunctionInvocationFilter (IFunctionInvocationFilter) :
| Signal OTel | Nom | Dimensions |
|---|---|---|
| Trace | agent.<name>.<function> |
llm.model_id, sk.function |
| Counter | audit.llm.tokens.total |
model, agent, direction |
| Histogram | audit.llm.latency_ms |
model, agent |
| Counter | audit.rag.hits |
framework |
| Histogram | audit.rag.score |
framework |
Les traces sont exportées vers Jaeger via l'OTel Collector (OTLP gRPC port 4317).
Configuré dans appsettings.json (section TokenCost) :
"TokenCost": {
"Models": {
"gpt-4o": { "InputPer1k": 0.0025, "OutputPer1k": 0.010 },
"claude-3-5-sonnet-20241022": { "InputPer1k": 0.003, "OutputPer1k": 0.015 },
"mistral": { "InputPer1k": 0.0, "OutputPer1k": 0.0 }
}
}- .NET 9 SDK
- Docker Desktop (pour Qdrant, Ollama, Jaeger)
- Un déploiement Azure OpenAI avec
gpt-4oettext-embedding-3-large - Une clé API Anthropic
cd docker
docker compose up -d
# Télécharger le modèle Mistral dans Ollama (première fois uniquement, ~4 GB)
docker exec audit-ollama ollama pull mistralLes services démarrent sur :
- Qdrant REST :
http://localhost:6333 - Qdrant gRPC :
localhost:6334 - Ollama :
http://localhost:11434 - Jaeger UI :
http://localhost:16686 - OTel Collector :
localhost:4317
Ne jamais committer les clés API. Utilisez dotnet user-secrets :
cd src/AuditSemKrnl.Api
dotnet user-secrets init
dotnet user-secrets set "SemanticKernel:AzureOpenAI:Endpoint" "https://<votre-ressource>.openai.azure.com/"
dotnet user-secrets set "SemanticKernel:AzureOpenAI:ApiKey" "<votre-clé-azure-openai>"
dotnet user-secrets set "SemanticKernel:AzureOpenAI:DeploymentName" "gpt-4o"
dotnet user-secrets set "SemanticKernel:AzureOpenAI:EmbeddingDeploymentName" "text-embedding-3-large"
dotnet user-secrets set "SemanticKernel:Anthropic:ApiKey" "<votre-clé-anthropic>"cd src/AuditSemKrnl.Api
dotnet runL'API écoute sur https://localhost:7xxx (voir Properties/launchSettings.json).
Avant de lancer un audit, indexez les textes réglementaires dans Qdrant :
curl -X POST https://localhost:7xxx/api/v1/documents/ingest \
-H "Content-Type: application/json" \
-d '{
"title": "DORA - Règlement UE 2022/2554",
"textContent": "Article 5 : Les entités financières disposent...",
"framework": "DORA",
"version": "2024"
}'Réponse :
{
"documentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"chunkCount": 42,
"title": "DORA - Règlement UE 2022/2554"
}curl -X POST https://localhost:7xxx/api/v1/audit \
-H "Content-Type: application/json" \
-d '{
"files": {
"deployment.yaml": "apiVersion: apps/v1\nkind: Deployment\nspec:\n template:\n spec:\n containers:\n - name: app\n image: myapp:latest\n securityContext:\n privileged: true",
"main.tf": "resource \"aws_s3_bucket\" \"data\" {\n bucket = \"mon-bucket\"\n acl = \"public-read\"\n}"
},
"framework": "DORA",
"infraVersion": "1.4.2"
}'Réponse :
{
"correlationId": "a1b2c3d4-...",
"status": "ALERTE",
"findings": [
{
"description": "Conteneur 'app' s'exécute avec privileged: true",
"risk": "CRITICAL",
"framework": "DORA",
"lineReference": "securityContext.privileged",
"regulatoryReference": "DORA Art. 9 - Sécurité des systèmes ICT"
},
{
"description": "Bucket S3 'mon-bucket' accessible publiquement (acl: public-read)",
"risk": "HIGH",
"framework": "DORA",
"regulatoryReference": "DORA Art. 9 - Protection des données"
}
],
"summary": "L'audit de l'infrastructure version 1.4.2 a révélé deux non-conformités...",
"tokensConsumed": 3847,
"estimatedCostUsd": 0.048
}curl https://localhost:7xxx/health
# {"status":"healthy","service":"audit-sem-krnl"}Valeur framework |
Description |
|---|---|
DORA |
Digital Operational Resilience Act (UE 2022/2554) |
ACPR |
Autorité de Contrôle Prudentiel et de Résolution |
ISO27001 |
ISO/IEC 27001:2022 — Sécurité de l'information |
All |
Tous les référentiels indexés |
Les instructions des agents sont externalisées dans prompts/ au format YAML Semantic Kernel :
prompts/
├── auditeur_tech/
│ ├── analyze_kubernetes.yaml # Détection de privilèges excessifs K8s
│ ├── analyze_terraform.yaml # Détection de mauvaises configs cloud
│ └── analyze_shell.yaml # Analyse de scripts Shell
├── officer_conformite/
│ ├── check_dora.yaml # Comparaison avec articles DORA
│ ├── check_acpr.yaml # Comparaison avec directives ACPR
│ └── check_iso27001.yaml # Comparaison avec contrôles ISO 27001
└── synthese/
└── summarize_findings.yaml # Résumé exécutif pour RSSI
Tous les prompts utilisent temperature: 0.1 pour maximiser le déterminisme des résultats d'audit.
src/AuditSemKrnl.Api/appsettings.json :
{
"SemanticKernel": {
"AzureOpenAI": {
"Endpoint": "",
"ApiKey": "",
"DeploymentName": "gpt-4o",
"EmbeddingDeploymentName": "text-embedding-3-large"
},
"Anthropic": {
"ApiKey": "",
"Model": "claude-3-5-sonnet-20241022"
},
"Ollama": {
"Endpoint": "http://localhost:11434",
"Model": "mistral"
}
},
"Qdrant": {
"QdrantEndpoint": "http://localhost:6333",
"QdrantGrpcPort": 6334,
"CollectionName": "regulatory_docs",
"DenseVectorSize": 3072,
"RelevanceThreshold": 0.70,
"ChunkSize": 512,
"ChunkOverlap": 128
},
"Routing": {
"ComplexityTokenThreshold": 2000,
"ComplexKeywords": ["privilege escalation", "RBAC", "ClusterRoleBinding", "IAM"]
}
}Il n'existe pas de Microsoft.SemanticKernel.Connectors.Anthropic officiel sur NuGet. AnthropicChatCompletionService implémente IChatCompletionService en wrappant Anthropic.SDK et s'intègre nativement dans le système de services SK.
Chaque agent nécessite un modèle différent (GPT-4o, Claude, Mistral). SK ne permet pas de mixer les providers sur un kernel unique de manière fiable. KernelFactory crée trois kernels isolés, chacun avec ses propres plugins.
AgentGroupChat maintient l'historique de conversation en mémoire. L'enregistrer comme Singleton ferait partager l'historique entre toutes les requêtes HTTP concurrentes. La durée de vie Scoped garantit une instance propre par requête.
Sécurité by design : le modèle Mistral tourne en local (Ollama). Sans plugin, il ne peut pas appeler d'outils externes. Les données sensibles (fichiers de config Terraform, YAML) ne quittent jamais ce kernel vers des services cloud.
- Streaming : implémenter
GetStreamingChatMessageContentsAsyncdansAnthropicChatCompletionServicepour les réponses en temps réel - Sparse vectoriel natif : remplacer
SparseVectorEncoder(TF-IDF) par SPLADE via ONNX Runtime pour > 10k documents - Persistance des rapports : brancher
IDistributedCache(Redis) ou une base SQL pourGET /api/v1/audit/{correlationId} - GraphRAG : intégrer une base de graphes (Neo4j) pour modéliser les relations entre ressources infra
- Nouveaux référentiels : NIS2, PCI-DSS — il suffit d'ingérer les documents et d'ajouter les valeurs dans l'enum
ComplianceFramework - Tests d'intégration : brancher un Qdrant en mémoire et un serveur OpenAI mock pour les tests CI