Aller au contenu principal

G-14 Corrélation d'audit

Brique G de la plateforme Enterprise AI. Technologie : composant développé qui relie les événements d'audit, les décisions de politique, les spans et les événements d'usage par trace_id, run_id et request_id. Version anglaise : ../../en/bricks/G-14-audit-correlation.md.

Rôle et responsabilités​

  • Répondre à ce qui s'est passé dans ce run : décisions, outils, modèles, données, coût, sur une seule chronologie.
  • Fournir des dossiers de preuves pour les incidents et les audits.

Position et interactions​

  • Chemin d'exécution : chemin de lecture, investigation.
  • Utilisé par la vue run du portail, les incidents (brique A) et les auditeurs.

Part open source et part développée​

  • Open source réutilisé : Aucun : jointures SQL et ClickHouse.
  • Développé dans la plateforme : Composant complet : clés de corrélation, API de chronologie, export avec empreintes.

Modèle de données​

Tables du Control Plane (schéma eai) : eai.audit_events, eai.telemetry_spans, eai.usage_events.

Chaque table de tenant a tenant_id uuid NOT NULL et une politique de sécurité au niveau des lignes sur current_setting('app.tenant_id', true)::uuid. ClickHouse usage_events est la réplique analytique de eai.usage_events (ReplacingMergeTree par event_id).

Contrat : contracts/v1/audit-event.schema.json (schema_version 1.0). L'usage est toujours remonté avec contracts/v1/usage-event.schema.json, l'audit avec contracts/v1/audit-event.schema.json.

Endpoints d'API​

MéthodeChemin
GET/v1/audit/correlations
GET/v1/telemetry/traces/{trace_id}
GET/v1/finops/usage

GET /v1/audit/correlations​

Réponse :

{
"items": [
{
"trace_id": "4bf92f35...",
"run_id": "run_42",
"events": 14,
"actions": [
"policy.evaluate",
"tool.invoke",
"model.invoke"
],
"decisions": {
"allow": 13,
"deny": 1
}
}
],
"next_cursor": null
}

GET /v1/telemetry/traces/{trace_id}​

Réponse :

{
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"spans": [
{
"span_id": "00f067aa0ba902b7",
"name": "gen_ai.chat",
"duration_ms": 1820
}
],
"links": {
"opik": {
"url": "https://<opik-host>/default/redirect/projects?name=eai-platform",
"metadata_key": "eai.trace_id",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
}
}

GET /v1/finops/usage​

Réponse :

{
"items": [
{
"event_id": "use_01J",
"usage_type": "llm",
"cost": {
"measured": {
"amount": "0.000228",
"currency": "USD"
}
}
}
],
"next_cursor": null
}

Exemples SDK​

Python​

from enterprise_ai import EnterpriseAI

client = EnterpriseAI(base_url="<control-plane-url>", api_key="eai_live_xxx", tenant="acme", locale="fr")
client.audit.correlations(run_id="run_42")
client.telemetry.trace("4bf92f3577b34da6a3ce929d0e0e4736")
client.finops.usage(agent_id="agt_ap", limit=100)

TypeScript​

import { EnterpriseAI } from "@enterprise-ai/sdk";

const client = new EnterpriseAI({ baseUrl: "<control-plane-url>", apiKey: process.env.EAI_API_KEY, tenant: "acme", locale: "fr" });
await client.audit.correlations({ run_id: "run_42" });
await client.telemetry.trace("4bf92f3577b34da6a3ce929d0e0e4736");
await client.finops.usage.list({ agent_id: "agt_ap", limit: 100 });

Java​

EnterpriseAIClient client = EnterpriseAIClient.builder()
.baseUrl("<control-plane-url>")
.apiKey(System.getenv("EAI_API_KEY"))
.tenant("acme")
.locale(Locale.FRENCH)
.build();
client.audit().correlations(Map.of("run_id", "run_42"));
client.telemetry().trace("4bf92f3577b34da6a3ce929d0e0e4736");
client.finops().listUsage(Map.of("agent_id", "agt_ap", "limit", 100));

Go​

client, err := eai.NewClient(eai.WithBaseURL("<control-plane-url>"), eai.WithAPIKey(os.Getenv("EAI_API_KEY")), eai.WithTenant("acme"), eai.WithLocale("fr"))
if err != nil { log.Fatal(err) }
ctx := context.Background()
_, err = client.Audit.Correlations(ctx, eai.WithQuery(map[string]string{"run_id": "run_42"}))
_, err = client.Telemetry.Trace(ctx, "4bf92f3577b34da6a3ce929d0e0e4736")
_, err = client.FinOps.ListUsage(ctx, eai.WithQuery(map[string]string{"agent_id": "agt_ap"}), eai.WithLimit(100))

Configuration​

Variables d'environnement : EAI_CLICKHOUSE_URL, EAI_S3_BUCKET.

Clés de valeurs Helm (deploy/helm/enterprise-ai/values.yaml) : controlPlane.config.

Sécurité​

  • Respecte la RLS et l'habilitation de l'appelant : un contenu de classification supérieure est résumé, pas affiché.
  • Tenant déduit de l'identifiant d'accès, jamais du corps ; sécurité au niveau des lignes PostgreSQL sur chaque table de tenant.
  • Chaque mutation et décision de politique écrit un événement d'audit chaîné par hachage (G-11).
  • Les erreurs utilisent l'enveloppe standard avec message_i18n en et fr.

Observabilité​

  • Latence des requêtes de corrélation.
  • traceparent W3C propagé, X-Request-Id renvoyé, les spans portent eai.tenant_id, eai.project, eai.agent_id, eai.run_id.

Unités FinOps​

  • La corrélation affiche le coût mesuré par run issu des Usage Events.

Modes de défaillance et résilience​

  • Ids de trace absents (clients anciens) -> repli sur request_id et fenêtre temporelle.

Adoption autonome​

Les auditeurs l'utilisent en lecture seule via le rôle auditor.

Tests d'acceptation​

  1. Un run avec un appel d'outil refusé affiche decisions allow=13 deny=1 et l'usage correspondant.

Accès audités et preuves de décision (section 11 du kit)​

  • Chaque lecture, recherche, vérification et export de l'audit (/v1/audit/*, /v1/evidence/*) écrit une ligne eai.audit_access_log (qui, quelle requête, combien de résultats) et son propre événement audit.read, audit.search, audit.verify, audit.export.read ou audit.payload.decrypt.
  • Les décisions du PEP enregistrées dans l'événement portent decision_id, policy_id, policy_version, engine, reasons, obligations et, lorsque le moteur la fournit, bundle_revision.
  • Les champs data (classification, DLP, masquage, sortie) et versions (modèle, prompt, outil, skill, plugin) sont chaînés avec l'événement.

Feuille de route d'implémentation