TL;DR
OAR — приём для агента-координатора, который собирает данные у нескольких владельцев (отделов, команд, систем). Владельцы выполняют запросы к своим таблицам, но сами таблицы не отдают. Перед опросом агент пишет черновик SQL-запроса только по схеме, то есть по названиям таблиц и колонок, без строк данных. Затем он считает, сколько владельцев нужно для этого запроса. Если один, он опрашивает всех параллельно («веер», fan-out). Если два и больше, он идёт по цепочке («эстафета», relay): опрашивает владельцев по очереди и подставляет полученные значения в следующий запрос. Черновик никогда не выполняется. Это один дополнительный вызов модели.
Главная находка: способ опроса решает, ответит агент вообще или нет. Когда нужные таблицы у одного владельца, веер точнее. Когда вопрос требует связать таблицы разных владельцев, веер почти не отвечает: от 0 до 4% верных ответов, а эстафета даёт до 48%. Причина простая. Чтобы выбрать нужные строки у второго владельца, нужны ключи (например, номера заказов) от первого. При параллельном опросе владельцы друг друга не видят, и ключи до второго не доходят. Агент в итоге пишет «не могу ответить». Если у эстафетного планировщика спрятать уже полученные строки, его точность падает с 23% до 5%.
Метод решает проблему тремя шагами: черновик по схеме, подсчёт владельцев, выбор протокола. Правило без настраиваемых параметров: ≥2 владельцев → эстафета, иначе веер. Обучение не нужно. На PeopleJoin OAR обошёл оба фиксированных протокола, а на BIRD остался почти вровень с лучшим.
Схема метода
ШАГ 1: Агент пишет ОДИН черновик SQL по публичной схеме (без данных) → SQL или пусто
ШАГ 2: Считает владельцев, чьи таблицы есть в FROM/JOIN → число κ
ШАГ 3: Маршрут:
κ ≥ 2 → ЭСТАФЕТА (по очереди, ключи передаются дальше)
κ = 1 / пусто → ВЕЕР (параллельно, каждый отвечает сам)
Шаг 1 и шаг 2 можно сделать в одном промпте. Шаг 3 — это уже работа самого агента с инструментами.
Эстафета (оригинал): планировщик на каждом шаге видит вопрос, схемы, все прошлые запросы и строки. Сначала он берёт небольшой отфильтрованный набор ключей. Потом вставляет эти значения в запрос следующему владельцу. Максимум 5 запросов, до 20 строк в каждом.
Веер (оригинал): каждый владелец получает вопрос и свою схему, пишет один локальный запрос и возвращает до 100 строк. Потом отдельный вызов собирает ответ.
Пример применения
Задача: Вы руководитель аналитики на маркетплейсе. Агент-аналитик ходит в два отдела. Логистика держит таблицу deliveries (order_id, pvz_city, delay_days). Маркетинг держит таблицу promos (order_id, promo_code). Ни тот, ни другой не выгружает таблицы целиком, они только выполняют запросы. Вопросы приходят разные:
- «Сколько заказов в Казани доставлено с опозданием больше 3 дней?» — нужна только логистика.
- «Какая средняя задержка доставки у заказов с промокодом ВЕСНА25?» — нужны обе таблицы.
Промпт (координатор):
Ты — координатор данных. Владельцы данных выполняют запросы к своим таблицам,
но строки не отдают. Тебе доступен публичный каталог (только названия таблиц и колонок).
Владелец "Логистика": deliveries(order_id, pvz_city, delay_days)
Владелец "Маркетинг": promos(order_id, promo_code)
Шаг 1. По вопросу напиши ОДИН черновик SQL-запроса по каталогу.
Черновик НЕ выполняй и никому не отправляй. Если безопасного запроса нет — оставь пустым.
Шаг 2. Выпиши таблицы из FROM и JOIN и укажи владельца каждой.
Посчитай число разных владельцев: κ.
Шаг 3. Если κ ≥ 2 → протокол RELAY. Если κ = 1, черновик пуст или таблица неизвестна → FAN-OUT.
Опрашивай владельцев по очереди, максимум 5 запросов, до 20 строк в каждом.
Сначала получи небольшой отфильтрованный набор ключей у первого владельца.
Подставь эти значения в запрос следующему владельцу.
Отвечай только по реально полученным строкам.
Отправь вопрос каждому владельцу параллельно. Каждый сам пишет один локальный запрос.
Собери ответ из результатов. Если данных нет — скажи, что вопрос неотвечаем.
Вопрос: Какая средняя задержка доставки у заказов с промокодом ВЕСНА25?
Сначала покажи шаги 1–3, затем действуй по выбранному протоколу.
Результат: Модель покажет черновик SQL с JOIN двух таблиц, список владельцев (Логистика и Маркетинг) и κ = 2. Она выберет эстафету. Сначала запросит у маркетинга order_id заказов с нужным промокодом, потом вставит эти номера в запрос к логистике. Для первого вопроса про Казань черновик будет использовать одну таблицу, κ = 1, и агент выберет веер.
Почему это работает
Слабость. Параллельный опрос быстрый и надёжный на простых вопросах, но владельцы друг друга не видят. Если для ответа нужно связать данные двух владельцев, ключи от одного до другого не доходят. Модель не может заранее придумать эти ключи, поэтому и «планирование всех запросов вперёд» почти не помогает: оно решает лишь малую часть проблемы.
Сила. Модель хорошо читает схему и пишет по ней SQL. Структуру вопроса видно уже по черновику: сколько таблиц и чьи они. Это не требует данных, а значит, ничего приватного не раскрывается. Модель также умеет брать значения из прошлого ответа и вставлять в следующий запрос.
Как метод это использует. Черновик показывает «владельческую структуру» вопроса за один вызов. По ней жёсткое правило выбирает протокол. Эстафету запускают только там, где она реально нужна, поэтому на простых вопросах агент не теряет точность веера.
Рычаги управления: - Лимит запросов и строк (5 запросов × 20 строк) → увеличь для длинных цепочек и больших выборок, уменьши для экономии токенов. - Условие маршрута (κ ≥ 2) → замени на своё: например, «есть JOIN между таблицами разных владельцев». - Пустой черновик → веер → поменяй на «спросить уточнение у человека», если не хочешь, чтобы агент гадал. - Инструкция «сначала возьми небольшой набор ключей» → это ядро эстафеты, не убирай.
Шаблон промпта
Ты — координатор данных. Владельцы выполняют запросы к своим таблицам,
но строки не отдают. Тебе виден только публичный каталог.
Владелец "{владелец_1}": {таблица_1}({колонки})
Владелец "{владелец_2}": {таблица_2}({колонки})
{...другие владельцы}
Шаг 1. Напиши ОДИН черновик SQL по каталогу для вопроса. Не выполняй его.
Если безопасного запроса нет — оставь пустым.
Шаг 2. Выпиши таблицы из FROM и JOIN, укажи владельца каждой, посчитай число
разных владельцев: κ.
Шаг 3. κ ≥ 2 → RELAY. Иначе → FAN-OUT.
Опрашивай владельцев по очереди, не более {число_запросов} запросов,
до {число_строк} строк в каждом. Сначала получи небольшой отфильтрованный
набор ключей, затем подставь эти значения в запрос следующему владельцу.
Отвечай только по реально полученным и неусечённым строкам.
Отправь вопрос каждому владельцу параллельно. Каждый пишет один локальный запрос.
Собери ответ из результатов. Если ответа нет в данных — скажи, что вопрос неотвечаем.
Вопрос: {вопрос}
Сначала покажи шаги 1–3, затем действуй по выбранному протоколу.
Подставляйте: реальных владельцев, таблицы и колонки из каталога, лимиты запросов и строк, вопрос пользователя.
🚀 Быстрый старт — вставь в чат:
Вот шаблон OAR (маршрутизация по владельцам данных). Адаптируй под мою задачу: {твоя задача}.
Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит, кто какие таблицы и колонки держит и какие инструменты есть у агента для запросов к владельцам. Эти сведения нужны, чтобы корректно посчитать число владельцев в черновике. Она возьмёт паттерн из шаблона и соберёт промпт под вашу систему.
Ограничения
⚠️ Нужны владельцы с отдельными таблицами. Метод про данные, которые лежат у разных владельцев и доступны только через их запросы. Если у агента одна база без разделения, выбирать нечего.
⚠️ Нужна настройка инструментов. Сам по себе промпт не опросит владельцев. Агенту нужны инструменты вроде «выполнить запрос у владельца X». В статье это реализовано кодом.
⚠️ Абсолютная точность невысокая. Даже лучший вариант на BIRD даёт около трети верных ответов. OAR выбирает лучший протокол, но не делает вопросы лёгкими.
⚠️ Только SQL-вопросы. Проверено на запросах к таблицам. Перенос на документы, поиск или произвольные инструменты авторы не проверяли.
⚠️ Повтор эстафетой после «не знаю» не всегда полезен. На BIRD он добавляет 0,7–2,2 пункта, а на PeopleJoin чаще вредит: там отказ веера нередко и есть правильный ответ на неотвечаемый вопрос.
⚠️ Приватность. Значения-ключи, которые координатор вставляет в запрос, видит следующий владелец. Авторы это допускают как условие.
⚠️ Черновик ошибается. Он находит 59–91% многовладельческих вопросов. Пропущенные идут в веер и там почти всегда проваливаются, но это не катастрофа: потери относительно лучшего протокола небольшие.
Как исследовали
Автор смоделировал организации, где таблицы разложены по владельцам. Владельцы публикуют схему, но не строки. Взяли два набора: PeopleJoin (имитация компаний с сотрудниками) и BIRD, где таблицы раздавали владельцам по хешу имени, то есть случайно и независимо от вопроса. Это важный ход: так можно отделить влияние «сколько таблиц в запросе» от влияния «у скольких владельцев они лежат». Тестировали три семейства моделей (DeepSeek, Claude Sonnet, Qwen) в 13 настройках, включая 600 вопросов, которых не было при разработке правила.
Результат оказался резким. Для JOIN двух таблиц на BIRD веер отвечал на 32–44% вопросов, когда обе таблицы у одного владельца, и на 1–4%, когда у двух. Эстафета в обоих случаях давала 22–39%. Структура запроса та же, меняется только распределение владельцев, значит решает именно владение. На PeopleJoin есть обратный случай: когда строки одной таблицы разбиты между двумя владельцами и JOIN не нужен, веер снова выигрывает. Удивляет, что решает не число владельцев, а именно JOIN через границу.
Механизм проверили прямо. Взяли эстафетного планировщика и выключили ему видимость полученных строк. Точность на двухвладельческих вопросах упала с 23,2% до 5,1%, а на одновладельческих осталась прежней. Вариант «заранее спланировать все запросы» тоже провалился (около 7%): ключи заранее не угадаешь. Практический вывод: эстафета выигрывает за счёт того, что читает результат предыдущего шага, а не за счёт большего числа вызовов.
Адаптации и экстраполяции
🔧 Техника: убрать «Сначала покажи шаги 1–3» → дешевле, но без контроля
Если маршрут уже стабильно выбирается правильно, замените последнюю строку на «Шаги 1–3 выполни молча и сразу действуй по выбранному протоколу». Токенов меньше, но проверить, правильно ли посчитан κ, станет труднее.
🔧 Техника: добавить в RELAY отчёт по каждому шагу → отладка
Добавьте: «После каждого запроса выведи: какому владельцу ушёл запрос, какие значения ты в него подставил и откуда они взяты». Так видно, передаются ли ключи. В статье именно «успешные эстафеты передают ключи» отличало верные ответы от неверных.
Экстраполяция (не из статьи): принцип для субагентов. Статья такого не проверяла, но идея переносится: если подзадача B нужна значение из результата подзадачи A, их нельзя запускать параллельно. Фрагмент для инструкции оркестратору в Claude Code или Cursor (проверьте на своих задачах):
Перед запуском субагентов выпиши для каждой подзадачи: какие входные значения ей нужны.
Если значение должно прийти из результата другой подзадачи — запускай их по очереди
и передавай конкретные значения в запрос. Независимые подзадачи запускай параллельно.
Ресурсы
- Работа: Fan Out or Relay? Ownership-Aware Routing for LLM Agents Querying Siloed Tables
- Автор: Zhexi Feng, Department of Electrical and Computer Engineering, University of California San Diego
- Датасеты: PeopleJoin (Jhamtani et al., 2025), BIRD (Li et al., 2023b), Spider (Yu et al., 2018)
- Родственные идеи: ReAct (Yao et al., 2023), параллельные вызовы функций (Kim et al., 2024), полусоединения в распределённых БД (Bernstein & Chiu, 1981)
