Ce que le schéma ne peut pas dire à votre agent : le format de connaissance ouverte sur BigQuery
Une expérience BigQuery et Gemini ADK : les bundles au format Open Knowledge dans Git ont permis à un agent d’obtenir ce que le schéma ne pouvait pas fournir, stabilisant les réponses et corrigeant une erreur de GMV de 29 %.
Sources: **What Your Schema Cannot Tell Your Agent, Open Knowledge Format Can**
Le fichier de prompts qui appartient à personne
Lorsqu’on connecte un agent IA à un entrepôt de données, un fichier bien connu apparaît tôt ou tard. Au début, il s’agit de trois ou quatre lignes bien organisées dans le prompt système : exclure les remboursements des revenus, l’année fiscale commence en avril, ignorer les comptes de test internes. Avec le temps, ce fichier passe à environ quatre cents lignes. Un seul ingénieur ose le modifier, tandis que l’analyste qui est en réalité responsable des définitions des métriques ne l’a jamais vu.
La taille colossale n’est que le moindre des problèmes. Ce qui est vraiment problématique, c’est que personne ne peut en assurer la maintenance. Une chaîne de prompts ne possède ni propriétaire désigné, ni processus d’évaluation, ni date d’expiration. Si le service financier publie une nouvelle politique de revenus en janvier, l’ingénieur n’en entend jamais parler, l’analyste n’a aucun moyen de modifier le code, et le prompt ne périt jamais de lui-même.
Sous ce type de friction, les connaissances deviennent obsolètes ou ne sont jamais achevées, ce qui oblige l’agent à fonctionner avec des définitions incomplètes et des hypothèses non exprimées. De plus, ces lacunes ne provoquent pas de défaillances visibles de la part de l’agent : pas d’erreur de crash, pas d’erreur de syntaxe SQL, pas de résultats vides. L’agent examine le schéma brut, devine les règles métier manquantes et fournit une donnée propre et crédible qui finit sur le tableau de bord des dirigeants. Un problème de maintenance mineur se transforme ainsi discrètement en problème d’exactitude.
Le format Open Knowledge Format (OKF) vise précisément ce type de lacune. Les règles métier cessent d’exister sous forme de chaînes de texte ou dans des bases de données vectorielles opaques pour devenir des fichiers Markdown simples avec des métadonnées YAML stockées dans Git. La propriété des données devient explicite, les modifications passent par des demandes de fusion, et les dates d’expiration sont visibles tant pour les analystes que pour les ingénieurs.
Les sceptiques affirment que des modèles plus puissants et des fenêtres de contexte plus larges rendent cela inutile, puisque l’agent peut être doté du schéma brut et des documents pour qu’il infère lui-même les définitions. Une expérience concise a testé cela en chiffrant le coût d’une connaissance incomplète ainsi que les changements induits par une base de connaissances OKF. Elle a utilisé deux tables BigQuery, une seule question métier, et un agent développé avec le Google Agent Development Kit (ADK) dans plusieurs configurations de connaissances.
Le scénario de test
Le contexte est un marché en ligne disposant d’une table orders et d’une table payments, ainsi que d’une question que un responsable produit pourrait poser chaque lundi matin.
Quel a été le chiffre d’affaires total généré par les paiements QRIS qui ont été traités avec succès dans la région de Jabodetabek au cours du mois de juillet 2026 ?
Trois termes nécessitent une explication. GMV signifie Valeur brute des marchandises, c’est-à-dire la somme des biens vendus sur une période donnée. QRIS est la norme nationale de paiement par QR en Indonésie. Jabodetabek désigne la région métropolitaine de Jakarta : Jakarta, Bogor, Depok, Tangerang et Bekasi.
Toute colonne nécessaire à la réponse à la question existe bien dans le stockage, mais le schéma ne parvient pas à y répondre. Quatre décisions sont implicites dans la question, et chacune a été prise par une personne :
- Le statut qui prouve que l’argent est bien arrivé. Les paiements comportent à la fois un
status_codeet untransaction_status. Le codestatus_code = 200, bien que courant, indique simplement que l’appel au passerelle de paiement a réussi, y compris en cas d’annulation. Les fonds n’atteignent le compte du commerçant que lorsquetransaction_status = 'settlement'. Les valeurs de référence sont tirées de l’API publique d’une véritable passerelle de paiement. - Les commandes qui comptent. Un paiement réglé associé à une commande retournée représente en réalité de l’argent qui revient, pourtant le système continue de le classer comme
settlementjusqu’à ce que le remboursement soit traité. Le filtre nécessite doncorders.order_status = 'completed', or le schéma ne le mentionne jamais. - La date qui détermine la période. Le règlement est retardé par rapport aux commandes de plusieurs heures, voire de jours. En analysant le mois de juillet à l’aide de
orders.order_ts, on obtient un total, tandis qu’en utilisantpayments.settlement_ts, on en obtient un autre ; ainsi, une commande du 31 juillet peut être classée dans le mois d’août. - Les villes qui composent la région. Il n’existe pas de colonne
region. Jabodetabek doit être indiqué sous forme de treize valeurs exactes dedestination_city, etKotaversusKab.désignent deux lieux distincts et non des variantes d’orthographe.
Même une erreur sur l’un de ces éléments génère néanmoins une requête qui s’exécute. Il n’y a ni erreur, ni résultat vide, ni trace d’erreur, seulement un nombre bien formaté destiné à être utilisé dans le jeu.
Contexte versus connaissance
La solution évidente consiste à noter les quatre décisions et à les fournir au modèle accompagnées du schéma. Un modèle compétent peut souvent en déduire la bonne réponse. Cependant, il le fait à nouveau lors de la session suivante, et rien ne force deux sessions à être concordantes. La définition du GMV n’est stockée nulle part ; elle est dérivée à nouveau sur demande par un processus qui ne se souvient pas des conclusions du jour précédent.
Cette différence explique le reste du raisonnement. Le contexte est le matériel sur lequel le modèle raisonne. La connaissance, quant à elle, est une conclusion déjà atteinte et approuvée par quelqu’un. Le raisonnement doit être effectué à chaque session, tandis qu’une conclusion écrite n’a besoin d’être produite qu’une seule fois.
Où est-ce que cela est écrit, alors ? Pas dans la chaîne de requête, pour les raisons mentionnées ci-dessus. Une base de données ou un stockage vectoriel offre de l’espace de stockage mais rend les révisions impossibles, car un analyste n’ouvrira pas de demande de fusion contre un stockage d’embeddings.
Qu’est-ce que OKF ?
La proposition de Google est OKF, et elle est délibérément simple. Un bundle n’est rien de plus qu’un dossier contenant des fichiers Markdown avec du frontmatter YAML, partagé via Git, sans aucune base de données, stockage vectoriel ou SDK impliqué. Un fichier décrit un concept, type est la seule clé de frontmatter obligatoire, et des liens Markdown ordinaires relient les fichiers, ce qui transforme le bundle en un graphe plutôt qu’en un simple ensemble de fichiers.
La question plus pertinente est de savoir ce qui se trouve à l’intérieur. Les types ne sont pas enregistrés de manière centralisée, et les utilisateurs doivent accepter ceux qu’ils ne reconnaissent pas ; par conséquent, ce qui suit constitue un vocabulaire de base et non un schéma. Dans les deux ensembles d’exemples publiés par Google, les dossiers ont l’aspect suivant :
tables/etdatasets/indiquent de quoi sont composés les données : ce que représente chaque ligne, comment les colonnes sont liées, quels valeurs d’enum existent. Cette partie peut être déduite du entrepôt de données, comme le montre l’étape suivante.metrics/explique la signification d’un nombre. Le GMV et ses quatre conditions y sont mentionnés.references/contient des connaissances sectorielles qui ne correspondent à aucune colonne, telles que les treize villes de Jabodetabek, une taxonomie des produits ou la liste des comptes de test internes.policies/stocke le document de référence vers lequel pointe une définition, par exemple la règle de reconnaissance des revenus que Finance réédite chaque janvier.computations/etattesters/conservent le SQL approuvé pour une métrique, ainsi que le code permettant de vérifier que le résultat provient bien de celui-ci.skills/recense les procédures : quand utiliser telle ou telle méthode, ses prérequis et ses étapes.
Un critère fiable pour déterminer si quelque chose doit faire partie d’un ensemble : serait-il expliqué à un nouvel analyste durant sa première semaine ? Si oui, il est placé dans un fichier.
La version v0.2 de la spécification ajoute trois champs de frontmatter qui traitent directement le problème initial. sources indique à partir de quoi un concept a été dérivé. verified collecte les événements de validation sous la forme de {by, at} ; un concept ne contenant aucun de ces événements est explicitement marqué unverified. stale_after indique le moment précis après lequel un concept est considéré comme obsolète. Ensemble, ils fournissent un responsable, un point de contrôle de révision et une date d’expiration, tout cela au sein d’un fichier que l’analyste peut modifier et que le réviseur peut lire dans le cadre d’une demande de fusion.
Un équipe pourrait-elle simplement inventer sa propre structure de dossiers ? C’est possible, mais un vocabulaire commun permet des choses que une structure privée ne peut pas offrir. L’agent de référence décrit ci-après peut rédiger la moitié d’un ensemble de documents, car il comprend déjà ce qu’est le concept de BigQuery Table. Les reviewers savent à quoi ils s’engagent en apposant leur signature sur des commits marqués verified. Ce format est également neutre par rapport aux fournisseurs, n’étant lié à aucun agent, framework ou fournisseur de modèles, de sorte que les connaissances acquises aujourd’hui survivent à un changement de stack.
De nombreux lecteurs penseront qu’il s’agit d’une compétence d’agent comportant des étapes supplémentaires, et la superposition est suffisamment réelle pour être prise en compte. OKF inclut type: Skill simplement comme un type de concept. Le fichier d’exemple de Google contient skills/run-on-bq.md structuré en sections intitulées « Quand l’utiliser », « Prérequis » et « Étapes », ce qui ressemble à n’importe quelle compétence d’agent ordinaire. Les compétences constituent un sous-ensemble d’OKF et non un synonyme. Une compétence indique à un agent comment agir, tandis qu’OKF explique la signification des termes, qui les a vérifiés pour la dernière fois et quand la réponse devient obsolète. Son utilisation se décompose en trois étapes : générer ce que le entrepôt connaît déjà, écrire ce que seuls les humains savent, et permettre à l’agent de lire les deux.
Étape un : générer ce que l’entrepôt connaît déjà
Lorsqu’une table BigQuery est déjà documentée, la partie fastidieuse n’a pas besoin d’être rédigée manuellement. Le répertoire OKF inclut un agent de création, et une seule commande permet de convertir un ensemble de données BigQuery en un fichier de bundle.
uv tool install git+https://github.com/GoogleCloudPlatform/open-knowledge-format
reference-agent enrich \
--source bq \
--dataset "$PROJECT_ID.marketplace" \
--out bundles/generated \
--no-web
Il génère un document datasets/, un document tables/ pour chaque table, ainsi que des fichiers index.md mis à jour en permanence. L’ajout de l’option --no-web permet de sauter l’étape facultative de fusion avec la documentation existante, ce qui rend la première exécution reproductible.
Le résultat est meilleur que prévu : il décrit la structure de chaque table, identifie la clé de jointure appropriée et documente l’enum des états. Il produit rarement des documents Metric ou Reference, car une source BigQuery ne fournit que des concepts de dataset et de table, rien de plus. Les limites dépendent de ce que le entrepôt enregistre déjà à son sujet.
Vérifiez ceci avant de supposer qu’un autre entrepôt se comporte de la même manière. Les valeurs d’état apparaissent dans les documents générés uniquement parce que quelqu’un les a inscrites dans les descriptions des colonnes du DDL. Si l’on dirige le générateur vers des colonnes nues et non documentées, il dispose de beaucoup moins d’informations.
Les documents générés peuvent contenir des erreurs, et l’exemple suivant en est la preuve. Une exécution a produit un schéma de requête avec sum(t2.total_price), mais aucune colonne de ce nom n’existe. Le nom réel est gross_amount, qui est indiqué correctement dans la propre table de schéma du même fichier, deux sections plus tôt. L’exécution de ce fragment échoue.
C’est là que l’option verified s’avère utile. Chaque document généré indique generated: {by: reference_agent/...} mais ne mentionne aucun événement verified, ce qui fait que le format même le marque comme non examiné. Un ensemble de documents enregistre sa propre provenance, et il n’y a aucune différence à séparer les résultats générés par la machine des décisions prises par des humains.
Deuxième étape : noter ce que seuls les humains savent
Deux fichiers manuscrits dans le même ensemble et au même format suffisent pour consigner les quatre décisions cachées. metrics/gmv.md est le plus important d’entre eux, et son frontmatter montre où les champs de confiance trouvent leur place.
---
type: Metric
title: Gross Merchandise Value (GMV)
description: Total settled transaction value for a period in IDR, excluding cancellations, reversals and returns.
tags: [metrics, finance, headline-metric]
generated: { by: human:analytics-lead@marketplace.example, at: 2026-09-01T09:00:00+07:00 }
verified:
- { by: human:analytics-lead@marketplace.example, at: 2026-09-01T11:00:00+07:00 }
stale_after: 2027-01-31T00:00:00+07:00
---
Le texte précise les quatre conditions et indique clairement que le schéma n’en fournit aucune. Un règlement signifie transaction_status = 'settlement' et non expressément status_code = 200. Les commandes doivent être en order_status = 'completed' avec les annulations supprimées. Pour cette période, settlement_ts constitue la date limite, vérifiée à l’aide d’une plage demi-ouverte incluant un décalage explicite de +07 : de 2026-07-01 00:00:00+07 inclus jusqu’à 2026-08-01 00:00:00+07 exclus. Une comparaison BETWEEN sur des chaînes de dates simples fait perdre la dernière journée et traite chaque timestamp comme étant en UTC, de sorte que chaque limite se déplace de sept heures.
La valeur stale_after effectue réellement du travail concret. Elle résout le problème de janvier en une seule ligne : Finance réédite les règles de reconnaissance des revenus chaque janvier, de sorte que la définition prend fin le 31 janvier 2027, et l’agent est invité à communiquer cette information plutôt que de présenter la valeur comme étant à jour.
Le deuxième fichier, references/jabodetabek.md, énumère les treize chaînes exactes de destination_city et met en garde contre l’utilisation de LIKE '%Jakarta%', qui manquerait discrètement huit d’entre elles.
Ici, la collaboration devient concrète. Redéfinir le GMV consiste désormais en une demande de modification adressée à un fichier Markdown que l’analyste en charge peut examiner, sans nécessiter de modification immédiate ni de déploiement backend. Le bundle d’exemples de Google conserve même un fichier obsolète gross-margin-legacy.md afin que les résultats historiques restent reproductibles, ce que des chaînes simples et rapides ne permettent pas.
Troisième acte : l’agent qui les lit
Pour accéder aux données, l’agent utilise le serveur MCP géré par Google Cloud pour BigQuery à l’adresse https://bigquery.googleapis.com/mcp ; ce dernier s’active automatiquement une fois que l’API BigQuery est activée pour le projet. Aucun déploiement n’est nécessaire.
Le seul code personnalisé présent dans le projet est un outil qui lit un fichier.
def read_okf_concept(path: str) -> dict:
"""Read one OKF concept document from the local bundle.
Args:
path: Path relative to the bundle root, for example "metrics/gmv.md".
Call this with "index.md" first to find which concepts exist.
Returns:
'status', and on success 'content' with the raw markdown and frontmatter.
"""
doc = (OKF_BUNDLE / path.lstrip("/")).resolve()
# `path` is model-controlled. Block anything that escapes the bundle.
if not doc.is_relative_to(OKF_BUNDLE) or not doc.is_file():
return {"status": "error", "error": f"no concept at {path}"}
return {"status": "success", "content": doc.read_text(encoding="utf-8")}
Ensuite, l’agent lui-même est défini.
bigquery_mcp = McpToolset(
connection_params=StreamableHTTPConnectionParams(
url="https://bigquery.googleapis.com/mcp",
headers={
"Content-Type": "application/json",
"Accept": "application/json, text/event-stream",
},
sse_read_timeout=300.0,
),
header_provider=_bigquery_auth_header,
tool_filter=[
"list_dataset_ids",
"list_table_ids",
"get_dataset_info",
"get_table_info",
"execute_sql_readonly",
],
)
root_agent = Agent(
name="marketplace_analytics_agent",
model="gemini-3.7-flash",
instruction=(
"Before writing any SQL: call read_okf_concept('index.md'), then read every "
"concept the question touches: the metric, the payment status, the geography.\n"
"Use only the filters, joins and period cuts those concepts define. "
"Never invent a status value or a metric formula.\n"
f"Run queries with execute_sql_readonly, passing projectId='{PROJECT_ID}'.\n"
"Show the SQL you ran, and name the concepts you relied on."
),
tools=[read_okf_concept, bigquery_mcp],
)
L’expérience a ensuite mené cette conversation.
Que s’est-il vraiment passé
L’agent, la question et les données de départ sont restés identiques d’une configuration à l’autre. Seul le répertoire vers lequel pointait OKF_BUNDLE a changé, et chaque configuration a été exécutée quatre fois.
En l’absence d’une couche de connaissances, avec un outil de lecture pointant vers un dossier vide et ne pouvant compter que sur l’auto-découverte du schéma du serveur MCP, quatre exécutions de la même question ont donné trois valeurs différentes : IDR 371 251 000, 333 020 000 et 371 886 000. La valeur la plus élevée dépassait la vérité de 29 %.
Avec le paquet généré et une seule exécution de reference-agent enrich, la valeur n’a plus fluctué. IDR 329 590 000 est apparu à chaque fois, de manière stable et reproductible, mais toujours 15 % trop élevé.
En ajoutant les deux concepts manuscrits, IDR 286 740 000 est obtenu à chaque fois. C’est la valeur correcte, et la réponse indique les concepts sur lesquels elle s’appuie.
La progression est évidente : la précision des réponses dépend de la justesse des connaissances fournies. Sans couche supplémentaire, l’agent devine la logique manquante à chaque tour, ce qui entraîne des erreurs allant jusqu’à 29 % de manière aléatoire. Le contexte de schéma dérivé par la machine élimine cette instabilité et réduit les erreurs à 15 %, mais il ne peut pas accomplir la tâche seul, car le manque restant concerne les politiques commerciales et non des aspects techniques. Seules des définitions explicites, revues par des humains, pour les règles non écrites — telles que les limites exactes des timestamps et les états des commandes — permettent à l’agent d’atteindre ce chiffre avec fiabilité.
Le facteur le plus important pour améliorer la précision s’est avéré être ni une instruction plus longue ni un modèle plus puissant. Il s’agit plutôt de connaissances sectorielles structurées et gérées par version, placées là où s’arrête le schéma de base de données.
Ce que cette approche ne résout pas
Le fonctionnement repose sur des instructions, de sorte qu’un modèle est libre de les ignorer, et rien n’empêche d’écrire du SQL avant que un concept ne soit lu. Quiconque souhaite une garantie stricte peut ajouter une fonction ADK before_tool_callback qui rejette une demande de requête si aucun concept n’a été lu au cours de ce tour.
Markdown ne propose pas non plus de validation de schéma. Un analyste pourrait introduire un concept mal formaté, et OKF reste délibérément permissif, exigeant que les utilisateurs tolèrent des types inconnus et des liens morts. La véritable protection provient de l’examen du code lors des demandes de fusion, ainsi que des attributs verified et stale_after, qui exposent aux agents les définitions non examinées ou expirées lorsqu’ils les lisent. La spécification qualifie ces niveaux de confiance de signaux consultatifs, de sorte que leur utilisation dépend de celui qui construit l’agent. L’agent ici les lit, et d’autres doivent en faire de même.
Ces chiffres ne constituent pas non plus des références : une seule question, un seul modèle et quatre exécutions par configuration sur des données préparées pour contenir les pièges. Ils montrent un mécanisme sans rien dire de l’effet moyen de OKF sur la précision.
La leçon qui tient
Le mécanisme conserve sa valeur. Lorsqu’un agent de traitement des données se trompe, la réponse ne semble pas erronée. Aucune exception n’est déclenchée, aucun résultat n’est vide, et rien ne vire au rouge. La requête SQL est valide et le graphique semble raisonnable, ce qui fait qu’il est présenté bien avant que quiconque ne le mette en doute.
L’erreur à craindre le plus n’est pas celle qui est erratique. Sans aucune orientation, l’agent renvoyait un nouveau nombre à chaque exécution, ce qui est facile à détecter en une après-midi. Avec la moitié des connaissances, il renvoyait le même nombre à chaque exécution tout en mentionnant négligemment une alternative qu’il ne pouvait justifier. Un résultat stable donne l’impression d’une preuve de correction, mais ne prouve rien.
Un meilleur modèle n’est pas nécessairement ce qui comble l’écart. Une seule commande recueillait tout ce que l’entrepôt pouvait dire de lui-même et s’arrêtait précisément là où commencent les décisions humaines : la date et l’heure délimitant une période, les états des commandes à prendre en compte, le statut indiquant l’arrivée de l’argent. Ce sont des choix et non des faits, et ils doivent être enregistrés une seule fois par celui qui les a pris, à un endroit où cette personne pourra être interrogée ultérieurement.
Un bon point de départ est un projet de petite envergure. Choisissez la métrique sur laquelle l’équipe se dispute constamment, celle pour laquelle deux tableaux de bord donnent des résultats différents et pour laquelle tout le monde sait à quel analyste s’adresser. Exécutez le générateur sur l’ensemble de données correspondant, puis écrivez la définition ainsi que le nom de son responsable dans un seul fichier et une seule demande de fusion. C’est cette discussion qui constitue le véritable travail ; le format ne sert qu’à lui donner une structure et à enregistrer qui a donné son accord et quand il prend fin. Les outils sont la partie la plus ennuyeuse. Ce qui est intéressant, c’est ce qui se passe lorsque un analyste et un ingénieur doivent se mettre d’accord sur une phrase et la valider, ainsi que les décisions cachées qui se révèlent être les leurs.
Lectures complémentaires
- Notes pratiques : Un GUI de base de données qui gère également les permissions de votre agent IA — Guide pas à pas des Notes pratiques : Un GUI de base de données qui gère également les permissions de votre agent IA : contrats, vérifications et emplacements de code prêts à l’emploi pour les équipes utilisant ce modèle.
- Exécuter des agents Deep dans un backend OpenSandbox local — Comment intégrer OpenSandbox dans des agents Deep via BaseSandbox, relier du code synchrone et asynchrone, et exécuter en toute sécurité un agent d’analyse de données dans un sandbox Docker local.