Co format schematu nie może powiedzieć twojemu agentowi: otwarty format wiedzy w BigQuery
Eksperyment z BigQuery i Gemini ADK: Pakiety w formacie Open Knowledge Format w Git umożliwiły agentowi to, czego nie potrafił schemat, stabilizowały odpowiedzi i naprawiły błąd GMV wynoszący 29%.
Źródła: **What Your Schema Cannot Tell Your Agent, Open Knowledge Format Can**
Plik z instrukcjami, którego nikt nie posiada
Gdy połączy się agent AI z magazynem danych, prędzej czy później pojawia się jakiś znajomy plik. Na początku to zaledwie trzy lub cztery uporządkowane linijki w instrukcjach systemowych: nie uwzględniać zwrotów w przychodach, rok budżetowy zaczyna się w kwietniu, ignorować wewnętrzne konta testowe. Z upływem miesięcy plik rozrasta się do około czterystu linijek. Jakiś inżynier ma odwagę go edytować, podczas gdy analityk, który faktycznie odpowiada za definicje metryk, nigdy nawet go nie widział.
Sam rozmiar to mniejszy problem. Najgorsze jest to, że nikt nie może tego utrzymywać. Ciąg instrukcji nie ma określonego właściciela, żadnego procesu przeglądania ani daty wygaśnięcia. Jeśli dział finansowy opublikuje nową politykę dotyczącą przychodów w styczniu, inżynier o tym nie dowie się, analityk nie ma możliwości zmiany kodu, a instrukcje same z siebie się nie wygaszają.
W takich warunkach tarcia wiedza staje się przestarzała lub nigdy nie zostaje ukończona, więc agent działa na podstawie niepełnych definicji i niewypowiedzianych założeń. Luki nie powodują widocznego awarii agenta – nie ma żadnego crashu, błędu składni SQL ani pustych wyników. Agent analizuje surowy schemat, domyśla się brakujących reguł biznesowych i przekazuje uporządkowany, wiarygodny wynik, który trafia na panel kierowniczy. Kłopot z konserwacją po cichu zamienia się w problem dokładności.
Format Otwartej Wiedzy (OKF) jest skierowany właśnie na tę lukę. Reguły biznesowe przestają istnieć w ciągach tekstowych lub nieprzejrzystych bazach wektorowych i stają się zwykłymi plikami Markdown z metadanymi YAML przechowywanymi w Git. Przynależność do konkretnego właściciela staje się jawna, zmiany są realizowane poprzez pull requests, a daty wygaśnięcia są widoczne zarówno dla analityków, jak i inżynierów.
Sceptycy twierdzą, że silniejsze modele oraz większe okna kontekstowe sprawiają, że jest to bezsensowne, ponieważ agent może otrzymać surowy schemat i dokumentację oraz samodzielnie wywnioskować definicje. Kompaktowy eksperyment sprawdził, jaki wpływ ma określenie kosztu niekompletnej wiedzy oraz jak zmieniają się efekty użycia metod OKF grounding. Wykorzystano w nim dwie tabele w BigQuery, jedno pytanie biznesowe oraz agenta stworzonego za pomocą Google’s Agent Development Kit (ADK) w różnych konfiguracjach wiedzy.
Szczegóły scenariusza testowego
Scenariusz odbywa się na platformie handlowej z tabelami orders i payments, a pytanie to takie, jakie menedżer produktu mógłby zadać w każdy poniedziałek rano.
Ile wartości GMV przyniosły płatności QRIS, które zostały pomyślnie zrealizowane, w regionie Jabodetabek w lipcu 2026 roku?
Trzy terminy wymagają objaśnienia. GMV oznacza łączną wartość towarów sprzedanych w określonym okresie. QRIS to krajowy standard płatności QR w Indonezji. Jabodetabek to nazwa regionu metropolitalnego Dżakarty: Dżakarta, Bogor, Depok, Tangerang i Bekasi.
Każda kolumna potrzebna do odpowiedzi na pytanie istnieje w magazynie, a mimo to schemat nie jest w stanie na nie odpowiedzieć. W pytaniu kryją się cztery decyzje, z których każdą podjął człowiek:
- Stan potwierdzający przybycie pieniędzy. Płatności zawierają zarówno
status_code, jak itransaction_status. Przyjemny dla oka kodstatus_code = 200informuje jedynie o pomyślnym wykonaniu żądania przez bramkę płatniczą, włączając przypadki anulowania. Środki trafiają na konto sprzedawcy wyłącznie wtedy, gdytransaction_status = 'settlement'. Wartości początkowe są skopiowane z publicznego API rzeczywistej bramki płatniczej. - Zamówienia, które mają znaczenie. Uiszczona płatność przypisana do zwróconego zamówienia to w rzeczywistości pieniądze wychodzące, a mimo to system nadal odnotowuje
settlementaż do zakończenia zwrotu. Dlatego filtr wymaga warunkuorders.order_status = 'completed', choć schemat w ogóle o tym nie wspomina. - Data i godzina, która określa okres. Proces rozliczeń opóźnia zamówienia o godziny, a nawet dni. Przeliczenie danych z lipca na podstawie
orders.order_tsdaje jeden wynik, natomiast przeliczenie na podstawiepayments.settlement_ts– inny, więc zamówienie z 31 lipca może zostać zaliczone do sierpnia. - Miasta, które tworzą region. Nie istnieje kolumna
region. Jabodetabek musi być podany jako trzynaście dokładnych wartości w kolumniedestination_city, aKotaorazKab.oznaczają dwa różne miejsca, a nie różne warianty pisowni.
Błąd w którymkolwiek z tych elementów nadal powoduje uruchomienie zapytania. Nie ma błędu, żadnego pustego wyniku ani śladu stosu wywołań – tylko dobrze sformatowana liczba skierowana do puli danych.
Kontekst versus wiedza
Oczywistym rozwiązaniem jest zapisanie czterech decyzji i przekazanie ich modelowi wraz ze schematem. Sprawny model może często na tej podstawie dojść do prawidłowej odpowiedzi. Robi to jednak ponownie w kolejnej sesji, a nic nie zmusza dwóch sesji do uzgodnienia wyników. Definicja GMV nie jest przechowywana nigdzie. Jest ponownie wyprowadzana na żądanie przez proces, który nie pamięta wczorajszego wniosku.
To rozróżnienie determinuje resztę argumentacji. Kontekst to materiał, nad którym model rozważa rozwiązania. Wiedza to wniosek, do którego ktoś już doszedł i który został zatwierdzony. Rozumowanie jest kosztowne w każdej sesji, natomiast pisany wniosek jest kosztowny tylko raz.
Gdzie więc to zapisać? Nie w ciągu tekstu zapytania, z powodów wymienionych powyżej. Baza danych lub magazyn wektorów umożliwiają przechowywanie danych, ale utrudniają ich przeglądanie, ponieważ analityk nie otworzy prośby o zmianę w magazynie embeddingów.
Czym jest OKF
Propozycją Google’a jest OKF, który jest celowo prosty w budowie. Plik pakietu to po prostu folder zawierający pliki Markdown z metadanymi w formacie YAML, udostępniany za pomocą Git, bez udziału bazy danych, magazynu wektorów ani SDK. Jeden plik opisuje jeden koncept, type to jedyna obowiązkowa kluczowa metadana, a zwykłe linki Markdown łączą te pliki, co przekształca pakiet w graf zamiast zwykłego zbioru plików.
Ważniejsze pytanie brzmi, co się w środku znajduje. Typy nie są rejestrowane centralnie, a użytkownicy muszą akceptować te, których nie rozpoznają, więc to, co następuje, to podstawowy słowniczek, a nie schemat. W dwóch opublikowanych przez Google przykładowych zestawach katalogi wyglądają w ten sposób:
tables/idatasets/określają, czym są dane: co oznacza każdy wiersz, jak łączą się kolumny, jakie wartości typu enum istnieją. Tę część można wywnioskować z magazynu danych, jak pokazuje następny krok.metrics/wyjaśnia, co oznacza dana liczba. Tutaj znajduje się GMV oraz jego cztery warunki.references/zawiera wiedzę specjalistyczną, która nie ma żadnych kolumn, taką jak trzynaście miast Jabodetabek, taxonomia produktów czy lista wewnętrznych kont testowych.policies/przechowuje dokument stanowiący źródło prawdy, do którego odnosi się definicja, na przykład zasady rozpoznawania przychodów, które dział finansowy publikuje co roku w styczniu.computations/orazattesters/zawierają zatwierdzony kod SQL służący do obliczania wskaźnika, wraz z kodem sprawdzającym, czy wynik rzeczywiście pochodzi z niego.skills/dokumentuje procedury: kiedy coś używać, jakie są jego warunki wstępne oraz jakie kroki należy wykonać.
Godny zaufania kryterium określające, czy coś powinno znaleźć się w pakiecie: czy zostałoby to wyjaśnione nowemu analitykowi w pierwszym tygodniu jego pracy? Jeśli tak, trafia do odpowiedniego pliku.
Wersja v0.2 specyfikacji dodaje trzy pola frontmatter, które bezpośrednio rozwiązują wspomniany problem. Pole sources wymienia źródła, z których pochodzi dany koncept. Pole verified gromadzi informacje o potwierdzeniach w formie {by, at}, a koncept bez takich informacji jest dosłownie oznaczony jako unverified. Pole stale_after wskazuje dokładny moment, po którym koncept uznawany jest za przestarzały. Wszystkie te elementy razem zapewniają określenie właściciela, mechanizm weryfikacji oraz termin ważności, przy czym wszystko to znajduje się w pliku, który analityk może edytować, a recenzent może przeczytać w ramach pull requesta.
Czy zespół może po prostu stworzyć własną strukturę folderów? Może to zrobić, ale wspólna terminologia umożliwia realizację zadań, których nie da się osiągnąć przy prywatnej strukturze. Opisany poniżej agent referencyjny może przygotować połowę zestawu, ponieważ już rozumie, czym jest koncepcja BigQuery Table. Recenzenci wiedzą, do czego zobowiązują ich podpisywanie komitetów jako verified. Format ten jest również neutralny pod względem dostawcy – nie jest powiązany z żadnym agentem, frameworkiem ani dostawcą modeli, więc wiedza napisana dziś przetrwa zmianę stacku.
Wielu czytelników uzna to za umiejętność agenta z dodatkowymi krokami, a pokrycie się tych pojęć jest na tyle duże, by wymagać rozważenia. OKF obejmuje type: Skill jako zwykły typ koncepcji. Przykładowy zestaw od Google zawiera plik skills/run-on-bq.md, w którym opisano, kiedy go używać, jakie są warunki wstępne i jakie kroki należy podjąć – co przypomina zwykłą umiejętność agenta. Umiejętności stanowią podzbiór OKF, a nie jego synonim. Umiejętność instruuje agenta, jak postępować, natomiast OKF wyjaśnia, co oznaczają poszczególne terminy, kto ostatnio je sprawdził oraz kiedy odpowiedź staje się nieaktualna. Jej użycie składa się z trzech kroków: wygenerowanie informacji, które magazyn już posiada, napisanie tego, co znają tylko ludzie, oraz umożliwienie agentowi przeczytania obu treści.
Pierwszy krok: wygenerowanie tego, co magazyn już wie
Gdy tabela w BigQuery jest już udokumentowana, nie ma potrzeby ręcznego wpisywania informacji. Repozytorium OKF zawiera agenta do tworzenia treści, a jeden polecenie przekształca zbiór danych z BigQuery w odpowiedni zestaw.
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
Wydaje dokument datasets/, dokument tables/ dla każdej tabeli oraz aktualizowane pliki index.md w całym systemie. Dodanie parametru --no-web pomija opcjonalny krok łączenia istniejącej dokumentacji, co sprawia, że pierwsze uruchomienie jest powtarzalne.
Wynik jest lepszy niż oczekiwano. Opisano strukturę każdej tabeli, znaleziono odpowiedni klucz łączenia oraz udokumentowano wartości z enum statusu. Rzadko tworzy się dokumenty typu Metric lub Reference, ponieważ źródło w BigQuery dostarcza jedynie informacje o zestawach danych i tabelach, a nic więcej. Granicą jest to, co magazyn danych już sam o sobie rejestruje.
Zanim założysz, że inne magazyny zachowują się w ten sam sposób, sprawdź to. Wartości stanu pojawiają się w generowanych dokumentach tylko dlatego, że ktoś umieścił je w opisach kolumn w DDL. Jeśli skierujesz generator na kolumny nieopisane i pozbawione dokumentacji, będzie miał o wiele mniej informacji do wykorzystania.
Generowane dokumenty mogą zawierać błędy, a eksperyment dostarcza przykładu. Podczas jednego przeprowadzenia utworzono wzorzec zapytania z sum(t2.total_price), ale takiej kolumny nie ma. Rzeczywista nazwa to gross_amount, która jest poprawnie podana w tabeli schematu tego samego pliku dwa akapity wcześniej. Uruchomienie tego fragmentu kończy się niepowodzeniem.
Wtedy właśnie przydaje się funkcja verified. Każdy wygenerowany dokument zawiera informację generated: {by: reference_agent/...}, ale nie ma żadnego zdarzenia verified, więc sam format oznacza go jako nierozpatrzony. Plik zbiorczy odnotowuje swoje własne źródło pochodzenia, a oddzielenie wyników maszyny od decyzji ludzkich nie wymaga żadnych zmian.
Druga część: zapisz to, co znają tylko ludzie
Dwa ręcznie napisane pliki w tym samym pliku zbiorczym i w tym samym formacie wystarczą, aby odnotować cztery ukryte decyzje. metrics/gmv.md jest z nich ważniejszy, a jego metadane pokazują, gdzie pola dotyczące zaufania znajdują swoje miejsce.
---
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
---
Ciało tekstu opisuje wszystkie cztery warunki i jasno stwierdza, że schemat nie spełnia żadnego z nich. Rozliczenie oznacza transaction_status = 'settlement', a wyraźnie nie status_code = 200. Zamówienia muszą mieć status order_status = 'completed' przy usunięciu ewentualnych odwołań. W tym okresie granicą jest settlement_ts, sprawdzanym przy użyciu przedziału półotwartego z wyraźnym odchyleniem +07: od 2026-07-01 00:00:00+07 włącznie do 2026-08-01 00:00:00+07 wyłącznie. Użycie operacji BETWEEN na zwykłych ciągach dat powoduje pominięcie ostatniego dnia oraz traktowanie każdego zapisu czasu jako UTC, przez co każda granica przesuwa się o siedem godzin.
Wartość stale_after faktycznie pełni istotną funkcję. Rozwiązuje problem związany z miesiącem styczniem w zaledwie jednej linii kodu: Dział finansowy co roku na początku stycznia aktualizuje zasady rozpoznawania przychodów, w wyniku czego definicja wygasa 31 stycznia 2027 roku, a system instruuje się, aby raportować te dane zamiast przedstawiać je jako aktualne.
Drugi plik, references/jabodetabek.md, wymienia trzynaście dokładnych wartości typu destination_city i ostrzega przed użyciem wyrażenia LIKE '%Jakarta%', które w ten sposób umykałoby uwadze przy ośmiu z tych wartości.
Tutaj współpraca staje się namacalna. Przedefiniowanie GMV odbywa się teraz poprzez prośbę o zmianę w pliku Markdown, którą może przejrzeć odpowiedni analityk, bez konieczności natychmiastowej edycji ani ponownego wdrożenia na serwerze. Przykładowy zestaw plików Google zachowuje nawet przestarzały plik gross-margin-legacy.md, dzięki czemu historyczne wyniki pozostają możliwe do odtworzenia – co pliki prostsze i oparte na prostych ciągach znaków nie potrafią zapewnić.
Akt trzeci: agent, który je odczytuje
Dla uzyskania dostępu do danych agent wykorzystuje zarządzany przez Google Cloud serwer MCP BigQuery pod adresem https://bigquery.googleapis.com/mcp, który zostaje uruchomiony po włączeniu API BigQuery dla projektu. Nie trzeba niczego instalować.
Jedynym dostosowanym kodem w projekcie jest narzędzie, które odczytuje plik.
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")}
A następnie definiuje się sam agent.
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],
)
Eksperyment przeprowadził następnie tę rozmowę.
Co tak naprawdę się stało
Agent, pytanie oraz dane wyjściowe pozostały takie same we wszystkich konfiguracjach. Zmienił się jedynie katalog, na który wskazywał OKF_BUNDLE, a każda konfiguracja była uruchamiana cztery razy.
Bez warstwy wiedzy, przy narzędziu do czytania wskazującym na pustą folder oraz jedynie z możliwością introspekcji schematu serwera MCP, cztery próby zadania tego samego pytania dały trzy różne wartości: IDR 371,251,000, 333,020,000 i 371,886,000. Najwyższa z nich przewyższała prawdę o 29%
Przy wygenerowanym pakuie i jednej próbie reference-agent enrich wartość przestała się zmieniać. Za każdym razem pojawiał się IDR 329,590,000 – wartość stabilna i powtarzalna, ale o 15% zbyt wysoka.
Dodanie dwóch ręcznie napisanych koncepcji dawało za każdym razem IDR 286,740,000. To jest poprawna wartość, a odpowiedź wskazuje koncepcje, na których się opierała.
Postęp jest prosty: dokładność odpowiedzi jest ograniczona przez precyzję dostarczonej wiedzy. Bez żadnej dodatkowej warstwy agent zgaduje brakującą logikę przy każdej próbie i błęduje, co może skutkować błędem sięgającym 29%. Kontekst schematu wywodzonego z maszyny eliminuje tę niestabilność i redukuje błąd do 15%, ale nie może samodzielnie sprostać zadaniu, ponieważ pozostała luka dotyczy polityki biznesowej, a nie aspektów technicznych. Tylko wyraźne, sprawdzone przez ludzi definicje niepisanych zasad, takie jak dokładne granice czasowe i statusy zamówień, pozwalają agentowi w sposób niezawodny osiągnąć wymaganą dokładność.
Najważniejszym czynnikiem wpływającym na dokładność okazał się ani dłuższy prompt, ani większy model – to ustrukturyzowana, kontrolowana wersjami wiedza specjalistyczna umieszczona w miejscu, gdzie kończy się schemat bazy danych.
Czego ten podejście nie naprawia
Ugruntowanie zależy od instrukcji, więc model może je zignorować, a nic nie przeszkadza w napisaniu zapytania SQL przed odczytaniem danego pojęcia. Każdy, kto chce mieć pewną gwarancję, może dodać ADK before_tool_callback, który odrzuca próbę wykonania zapytania, jeśli w danym etapie nie zostało odczytane żadne pojęcie.
Markdown również nie zawiera mechanizmu walidacji schematu. Analityk może wprowadzić błędnie sformatowane pojęcie, a OKF celowo pozostaje elastyczny, wymagając od użytkowników tolerowania nieznanych typów i uszkodzonych linków. Prawdziwa ochrona pochodzi z przeglądu kodu w ramach pull request, a także z flag verified i stale_after, które ujawniają nieprzeglądane lub wygasłe definicje agentowi podczas ich odczytywania. Specyfikacja określa poziomy zaufania jako sygnały doradcze, więc ich używanie zależy od osoby budującej agenta. Ten agent je odczytuje, a inne również muszą to robić.
Te wykresy również nie stanowią punktów odniesienia: jedno pytanie, jeden model i cztery próby na każdym ustawieniu z danych przygotowanych specjalnie po to, by zawierać pułapki. Pokazują one mechanizm, ale nic nie mówią o średnim wpływie OKF na dokładność.
Lekcja, która pozostaje aktualna
Mechanizm nadal ma wartość. Gdy agent przetwarzający dane popełnia błąd, odpowiedź nie wygląda błędnie. Żadne wyjątki nie są wyzwalane, żaden wynik nie wraca pusty, a nic nie zmienia koloru na czerwony. SQL jest poprawny, a wykres wygląda rozsądnie, więc trafia na slajd na długo przed tym, zanim ktoś zacznie w to wątpić.
Błędem, którego należy się najbardziej obawiać, nie jest ten nieregularny. Bez żadnych wskazówek agent zwracał każdorazowo nową liczbę, co łatwo było zauważyć w ciągu popołudnia. Mając połowę wiedzy, zwracał tę samą liczbę przy każdej próbie, jednocześnie mimochodem wspominając o alternatywie, której wyboru nie mógł uzasadnić. Stabilny wynik sprawia wrażenie dowodu poprawności, ale w rzeczywistości niczego nie dowodzi.
Lepszy model niekoniecznie jest tym, co zamknie tę luki. Jedno polecenie uchwyciło wszystko, co magazyn może powiedzieć o sobie, i zatrzymało się dokładnie w tym miejscu, gdzie zaczynają się decyzje ludzkie: data i godzina określająca okres, statusy zamówień, które je liczą, oraz status wskazujący na przybycie gotówki. To są wybory, a nie fakty, i powinny zostać zapisane raz przez osobę, która je podjęła, w miejscu, gdzie można będzie później do niej zwrócić się z pytaniami.
Dobrym punktem wyjścia jest projekt mniejszy rozmiarem. Wybierz metrykę, co do której zespół ciągle się kłóci, tę, w przypadku której dwa panel sterowania podają różne dane, a wszyscy wiedzą, do którego analityka zwrócić się o pomoc. Uruchom generator na odpowiadającym jej zbiorze danych, a następnie zapisz definicję wraz z jej autorem w jednym pliku i jednym pull requestu. To właśnie ta rozmowa stanowi rzeczywistą pracę. Format jedynie zapewnia jej miejsce przechowywania oraz zapis tego, kto się zgodził i kiedy wygasa jej ważność. Narzędzia są najmniej interesującymi elementami. Ciekawe jest to, co się dzieje, gdy analityk i inżynier muszą dojść do porozumienia co do jednego zdania i je zatwierdzić, oraz które ukryte decyzje okazują się ich własnymi.
Literatura pokrewna
- Praktyczne notatki: GUI do bazy danych, które również określa, co twojemu agentowi AI jest dozwolone — Szczegółowy przewodnik po Praktycznych notatkach: GUI do bazy danych, które również określa, co twojemu agentowi AI jest dozwolone: umowy, sprawdzania oraz miejsca na kod do wklejenia dla zespołów wdrażających ten wzorzec.
- Uruchamianie agentów Deep w lokalnym backendzie OpenSandbox — Jak połączyć OpenSandbox z agentami Deep za pomocą BaseSandbox, zsynchronizować kod asynchroniczny i synchroniczny oraz bezpiecznie uruchomić agent do analizy danych w lokalnej sandboxie Docker.