Чего не может рассказать схема вашему агенту: формат открытых знаний в BigQuery
Эксперимент с BigQuery и Gemini ADK: пакеты в формате Open Knowledge Format в Git предоставили агенту то, чего не могла схема, обеспечили стабильность ответов и устранили ошибку GMV в 29%.
Источники: **What Your Schema Cannot Tell Your Agent, Open Knowledge Format Can**
Файл инструкций, которым никто не владеет
Подключив ИИ-агента к хранилищу данных, рано или поздно появляется какой-то стандартный документ. Сначала это три-четыре аккуратных строки в системных инструкциях: не учитывать возвраты средств в доходы, финансовый год начинается в апреле, игнорировать внутренние тестовые счета. С течением месяцев объем файла увеличивается до примерно четырехсот строк. Только один инженер находит в себе смелость его редактировать, в то время как аналитик, который на самом деле отвечает за определения метрик, даже никогда его не видел.
Огромный размер — это ещё не самая серьезная проблема. Гораздо хуже то, что никто не может им управлять. У строки с инструкциями нет указанного владельца, нет процедуры проверки и нет срока действия. Если в январе финансовый отдел опубликует новую политику расчета доходов, инженер об этом никогда не узнает, у аналитика нет возможности изменить код, и инструкции сами по себе не истекают срока действия.
Под воздействием такого трения знания становятся устаревшими или так и не формируются полностью, поэтому агент работает на основе неполных определений и негласных предположений. Пробелы в данных не приводят к очевидным сбоям у агента: нет сбоев, ошибок синтаксиса SQL и пустых результатов. Агент анализирует исходную структуру данных, догадывается о отсутствующих бизнес-правилах и предоставляет аккуратные, достоверные данные, которые в итоге попадают на панель управления руководства. Незначительные неудобства при техническом обслуживании тихо превращаются в проблемы с точностью данных.
Формат открытых знаний (OKF) направлен именно на устранение этих пробелов. Бизнес-правила больше не хранятся в строках команд или непрозрачных векторных хранилищах, а представляют собой обычные файлы Markdown с YAML-метаданными, хранящимися в Git. Принадлежность данных становится явной, изменения вносятся через pull-запросы, а сроки действия данных видны как аналитикам, так и инженерам.
Скептики утверждают, что более мощные модели и большие окна контекста делают это бессмысленным, поскольку агенту можно просто передать сырые схемы и документы, позволяя ему самостоятельно выводить определения. В компактном эксперименте оценивалась стоимость неполных знаний и влияние метода OKF grounding. Для этого использовались две таблицы BigQuery, один бизнес-вопрос, а также агент, созданный с помощью инструментария Google Agent Development Kit (ADK), в различных условиях наличия знаний.
Сценарий теста
Сеттинг — онлайн-рынок с таблицами orders и payments, а вопрос — тот, который менеджер по продуктам мог бы задать в любое понедельник утром.
Какой объем общего объема продаж (GMV) был получен от успешно завершенных платежей QRIS в регионе Jabodetabek в июле 2026 года?
Три термина требуют пояснения. GMV означает валовую стоимость товаров — сумму проданных товаров за определенный период. QRIS — это национальный стандарт оплаты через QR-коды в Индонезии. Jabodetabek — это название метрополитенского региона Джакарты: Джакарта, Богор, Депок, Тангеранг и Бекаси.
Все необходимые для ответа на вопрос колонки существуют в базе данных, однако схема не может на него ответить. В вопросе скрыты четыре решения, каждое из которых было принято человеком:
- Статус, подтверждающий поступление денег. Оплаты имеют как
status_code, так иtransaction_status. Привлекательный кодstatus_code = 200указывает лишь на успешный вызов шлюза, включая случаи отмены. Средства попадают на коммерческий счет только тогда, когдаtransaction_status = 'settlement'. Значения, используемые в примерах, взяты из публичного API реального платежного шлюза. - Значимые заказы. Урегулированный платеж, привязанный к возвращенному заказу, представляет собой деньги, возвращаемые обратно, однако система продолжает отмечать его как
settlementдо момента обработки возврата. Поэтому фильтр требует условияorders.order_status = 'completed', хотя в схеме этого не указано. - Временная метка, определяющая период. Урегулирование платежей отстает от заказов на часы или даже дни. Если разделить данные июля по параметру
orders.order_ts, получится одна сумма, а если по параметруpayments.settlement_ts— другая; поэтому заказ от 31 июля может попасть в август. - Города, составляющие регион. Столбца
regionне существует. Название Jabodetabek должно быть указано как тринадцать точных значений в колонкеdestination_city, аKotaиKab.обозначают два разных места, а не варианты написания.
Любая ошибка в любом из этих элементов всё равно приводит к выполнению запроса. Ошибок нет, результата тоже нет, трассировки стека тоже — есть только корректно сформированное число, предназначенное для обработки.
Контекст против знаний
Очевидным решением является запись четырех решений и передача их модели вместе со схемой. Способная модель часто может на основе этого прийти к правильному ответу. Но она делает это снова в следующей сессии, и ничто не обязывает две сессии соглашаться друг с другом. Определение GMV нигде не хранится; оно воссоздается по мере необходимости процессом, который не помнит вчерашних выводов.
Это различие лежит в основе всего аргумента. Контекст — это материал, над которым размышляет модель. Знания — это вывод, к которому кто-то уже пришел и который был одобрен. Размышления требуются снова и снова в каждой сессии, тогда как написанный вывод требуется лишь один раз.
Но где это записывается? Не в строке запроса, по вышеуказанным причинам. База данных или хранилище векторов обеспечивают хранение, но исключают возможность ревью, поскольку аналитик не будет создавать pull request к хранилищу эмбеддингов.
Что такое OKF
Предложение Google — это OKF, и оно преднамеренно простое. Пакет представляет собой просто папку с файлами Markdown с YAML-метаданными, которые обмениваются через Git, без участия базы данных, хранилища векторов или SDK. Один файл описывает одну концепцию, type — единственный обязательный ключ метаданных, а обычные ссылки Markdown соединяют файлы, превращая пакет в граф вместо простого набора файлов.
Более важный вопрос — что находится внутри. Типы не регистрируются централизованно, и потребители вынуждены мириться с теми, которые им не знакомы, поэтому приведённый ниже список является лишь начальным словарём, а не схемой. В двух опубликованных Google примерах пакетов структура каталогов выглядит следующим образом:
tables/иdatasets/указывают, что представляют собой данные: что означает строка, как соединяются столбцы, какие значения enum существуют. Эту информацию можно получить из хранилища, как покажет следующий пример.metrics/объясняет, что означает тот или иной числовой показатель. Сюда входят показатель GMV и четыре условия, связанные с ним.references/содержит доменные знания, у которых нет соответствующих столбцов, такие как список из тринадцати городов Jabodetabek, таксономия продуктов или перечень внутренних тестовых учётных записей.
policies/ хранит документ, являющийся источником истины, на который указывает определение, например, правило учета доходов, которое отдел финансов переиздает каждый январь.computations/ и attesters/ содержат утвержденные SQL-запросы для определенных метрик, а также код для проверки того, действительно ли результат получен с их помощью.skills/ фиксирует процедуры: когда использовать тот или иной инструмент, каковы его предпосылки и каковы шаги выполнения.Честный критерий для определения того, следует ли что-то включать в пакет: будет ли это объяснено новому аналитику в течение его первой недели? Если да, то это попадает в файл.
Версия v0.2 спецификации добавляет три поля frontmatter, направленные непосредственно на решение вышеуказанной проблемы. Поле sources указывает, от чего был заимствован тот или иной концепт. Поле verified собирает события подтверждения в формате {by, at}; концепт, не имеющий таких событий, отмечается как unverified. Поле stale_after указывает абсолютный момент, после которого концепт считается устаревшим. Все эти элементы вместе обеспечивают наличие ответственного лица, процедуры проверки и срока действия, причем всё это находится в одном файле, который аналитик может редактировать, а рецензент — читать в рамках pull request.
Может ли команда просто придумать собственную структуру папок? Может, но общий словарь терминов позволяет делать то, чего невозможно с использованием индивидуальной структуры. Описанный ниже агент-референт может составить половину пакета, поскольку он уже понимает, что такое концепция BigQuery Table. Рецензенты знают, к каким последствиям приводит подтверждение статуса verified для коммитов. Этот формат также нейтрален с точки зрения поставщика, не связан ни с одним агентом, фреймворком или поставщиком моделей, поэтому знания, записанные сегодня, останутся актуальными даже при смене технологической стек-структуры.
Многие читатели сочтут это навыком агента с дополнительными шагами, и их совпадение настолько значительно, что требует рассмотрения. В OKF понятие type: Skill рассматривается лишь как один из типов концепций. В примерном наборе Google файл skills/run-on-bq.md структурирован по разделам «Когда использовать», «Предусловия» и «Шаги», что напоминает обычный навык агента. Навыки являются подмножеством OKF, а не синонимом. Навык указывает агенту, как действовать, в то время как OKF объясняет значение терминов, кто последний их проверял и когда ответ устаревает. Его использование включает три шага: сгенерировать информацию, известную складу, записать то, что известно только людям, и позволить агенту прочитать оба вида информации.
Первый шаг: сгенерировать информацию, уже известную складу
Если таблица в BigQuery уже задокументирована, не нужно вручную вводить информацию. В репозитории OKF имеется агент для создания контента, и один командный приказ преобразует набор данных BigQuery в соответствующий набор.
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
Он генерирует документ datasets/, документ tables/ для каждой таблицы, а также обновляемые файлы index.md во всех разделах. Использование параметра --no-web позволяет пропустить необязательный шаг объединения существующей документации, что делает первую запуск процесса воспроизводимым.
Результат оказался лучше ожидаемого. В нем описывается структура каждой таблицы, находится подходящий ключ соединения и документируется перечень состояний. Редко генерируются документы типа Metric или Reference, поскольку источником данных является BigQuery, который предоставляет только информацию о наборах данных и таблицах, и ничего больше. Границы определяются тем, что хранилище уже само о себе записало.
Проверьте это, прежде чем предполагать, что другие склады ведут себя аналогично. Значения статуса появляются в генерируемых документах только потому, что кто-то указал их в описаниях столбцов DDL. Если направить генератор на простые, не задокументированные столбцы, у него будет гораздо меньше информации для работы.
Генерируемые документы могут содержать ошибки, и в качестве примера можно привести один из экспериментов. В одном из запусков был сгенерирован шаблон запроса с sum(t2.total_price), но такого столбца не существует. Фактическое название — gross_amount, которое указано в таблице схемы того же файла на две секции ранее. Попытка выполнения этого фрагмента завершается ошибкой.
Именно здесь проявляется преимущество статуса verified. Каждый сгенерированный документ содержит запись generated: {by: reference_agent/...}, но не содержит события verified, поэтому сам формат отмечает его как непроверенный. Пакет документов фиксирует собственное происхождение, а разделение результатов работы машины и человеческих решений не требует никаких изменений.
Второй этап: запишите то, что знают только люди
Два рукописных файла в одном пакете и в одном формате достаточны для фиксации четырех скрытых решений. metrics/gmv.md является более важным из них, и его метаданные показывают, где находятся поля, отвечающие за уровень доверия.
---
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
---
В тексте подробно описаны все четыре условия, причем прямо указано, что схема не обеспечивает ни одного из них. Завершение операции означает transaction_status = 'settlement', а не status_code = 200. Заказы должны иметь статус order_status = 'completed' без возможности отмены. Для данного периода критерием является settlement_ts; проверка проводится в диапазоне с открытым концом с явным смещением +07: от 2026-07-01 00:00:00+07 включительно до 2026-08-01 00:00:00+07 исключительно. Использование оператора BETWEEN с обычными строками дат приводит к тому, что последний день не учитывается, и все временные метки рассматриваются как UTC, поэтому каждая граница смещается на семь часов.
Значение stale_after выполняет реальную работу. Оно решает проблему, связанную с январем, всего в одной строке: каждый год в январе отдел финансов переиздает правила учета доходов, в результате чего определение устаревает 31 января 2027 года, и агенту указывается сообщать об этом вместо того, чтобы представлять данные в текущем виде.
Во втором файле references/jabodetabek.md перечислены тринадцать точных строк destination_city, и в нем предупреждается о использовании оператора LIKE '%Jakarta%', который незаметно пропустит восемь из них.
Здесь сотрудничество становится осязаемым. Переопределение показателя GMV теперь осуществляется с помощью pull request к файлу в формате Markdown, который может просмотреть ответственный аналитик, без необходимости немедленной правки и без переразвертывания серверной части. В примерном наборе файлов Google даже сохраняется устаревший файл gross-margin-legacy.md, чтобы исторические результаты оставались воспроизводимыми, чего невозможно сделать с помощью простых и мгновенных строк.
Третий этап: агент, который их читает
Для получения доступа к данным агент использует управляемый сервер MCP BigQuery от Google Cloud по адресу https://bigquery.googleapis.com/mcp; он активируется автоматически после включения API BigQuery для проекта. Никаких дополнительных компонентов развертывать не требуется.
Единственным пользовательским кодом в проекте является инструмент для чтения файла.
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")}
Затем определяется сам агент.
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],
)
Затем в рамках эксперимента был запущен этот диалог.
Что на самом деле произошло
Агент, вопрос и исходные данные оставались неизменными во всех конфигурациях. Изменялась только директория, на которую указывал OKF_BUNDLE, причем каждая конфигурация тестировалась четыре раза.
При отсутствии слоя знаний, когда инструмент для анализа ссылался на пустую папку и единственным источником информации служила возможность самопроверки схемы сервера MCP, четыре запуска одного и того же вопроса дали три разных значения: IDR 371,251,000, 333,020,000 и 371,886,000. Максимальное значение превышало истинное на 29%.
При использовании сгенерированного пакета данных и одного запуска операции reference-agent enrich значения стали стабильными: каждый раз получалось IDR 329,590,000, что является точным показателем, однако он все равно превышал истинное значение на 15%.
При добавлении двух вручную определенных концепций каждый раз получалось IDR 286,740,000. Это соответствует истинному значению, причем в ответе указываются концепции, на которые была сделана ставка.
Логика развития проста: точность ответов определяется степенью точности предоставленных знаний. При отсутствии дополнительных слоев агент на каждом шаге догадывается о недостающей логике, в результате чего погрешность достигает 29% и колеблется. Схема контекста, получаемая с помощью машин, устраняет эту нестабильность и снижает погрешность до 15%, но она не может справиться с задачей в одиночку, поскольку оставшийся пробел связан с бизнес-политикой, а не с техническими аспектами. Только четкие, проверенные человеком определения неписаных правил, такие как точные временные рамки и статусы заказов, позволяют агенту надежно достигать желаемого уровня точности.
Оказалось, что главным фактором повышения точности является не более длинный запрос и не более мощная модель, а структурированные знания доменной области с контролем версий, размещенные там, где заканчивается схема базы данных.
Что не решает этот подход
Функционирование системы зависит от инструкций, поэтому модель может проигнорировать их, и ничто не мешает писать SQL до того, как будет прочитано соответствующее понятие. Тот, кто хочет получить строгую гарантию, может добавить функцию ADK before_tool_callback, которая отклоняет запрос, если за данный ход понятие не было прочитано.
Markdown также не предусматривает проверки структуры данных. Аналитик может внести некорректное понятие, и OKF намеренно сохраняет такой подход, требуя от пользователей терпимости к неизвестным типам и недействующим ссылкам. Настоящая защита обеспечивается путем проверки кода в рамках pull request, а также использованием атрибутов verified и stale_after, которые выявляют непроверенные или устаревшие определения, когда агент их читает. В спецификации уровни доверия обозначаются как рекомендательные сигналы, поэтому использование этих сигналов остается на усмотрение того, кто создаёт агента. Данный агент их читает, и другим тоже необходимо это делать.
Эти показатели также не являются эталонами: один вопрос, одна модель и четыре запуска для каждой настройки на специально подготовленных данных, содержащих «ловушки». Они демонстрируют механизм, но ничего не говорят об среднем влиянии OKF на точность.
Урок, который остается актуальным
Этот механизм по-прежнему имеет ценность. Когда агент обрабатывает данные неправильно, ответ не выглядит неверным: не срабатывают никакие исключения, результат не возвращается пустым, и ничего не становится красным. SQL-запрос валиден, а график выглядит разумным, поэтому он попадает на слайд задолго до того, как кто-либо начнёт сомневаться в нём.
Самой опасной ошибкой является не случайная. Без каких-либо указаний агент возвращал каждый раз новое число, что легко заметить за один день. При наличии лишь половины необходимых знаний он возвращал одно и то же число при каждом запуске, одновременно упоминая альтернативу, которую не мог обосновать. Стабильный результат кажется доказательством правильности, но на самом деле ничего не доказывает.
Лучшая модель не обязательно является тем фактором, который закрывает пробел. Одна команда сумела зафиксировать всё, что склад может сказать о себе, и остановилась именно там, где начинаются человеческие решения: временная метка, определяющая период, статусы заказов, указывающие на их состояние, а также статус, означающий поступление денег. Это выборы, а не факты, и их следует записать один раз тем, кто их принял, в месте, где позже можно будет обратиться к этому человеку.
Хороший способ начать — с проекта меньшего масштаба. Выберите тот показатель, по которому команда постоянно спорит, тот, по которому два дашборда дают противоречивые данные, и по поводу которого все знают, к какому аналитику обратиться. Запустите генератор на соответствующем наборе данных, затем запишите определение вместе с именем ответственного лица в один файл и в один запрос к репозиторию. Сама дискуссия и является основной работой; формат лишь предоставляет структуру для хранения информации, указывает, кто согласился, и отмечает срок действия. Инструменты — это самая скучная часть. Интересно то, что происходит, когда аналитику и инженеру необходимо прийти к единому мнению и зафиксировать его, а также какие скрытые решения оказываются их собственными.
Связанные материалы
- Практические заметки: интерфейс для работы с базой данных, который также определяет, что разрешено вашему ИИ-агенту — Подробное руководство по использованию Практических заметок: интерфейса для работы с базой данных, который также определяет, что разрешено вашему ИИ-агенту: шаблоны контрактов, проверок и блоки кода для команд, внедряющих эту модель.
- Запуск Deep Agents в локальной среде OpenSandbox Backend — Как подключить OpenSandbox к Deep Agents через BaseSandbox, синхронизировать и асинхронно выполнять код, а также безопасно запускать агент для анализа данных в локальной среде Docker Sandbox.