Прежде чем писать код, спеку или задачу агенту, стоит ответить на вопрос, который почти никогда не задают вслух: из каких понятий состоит наша задача и как они связаны?
Раньше нестыковку понятий сглаживали люди: аналитик переспросит, разработчик догадается, тестировщик заметит. Агент не переспрашивает — он молча достраивает пробелы правдоподобными догадками. Если в вашей голове «заказ» один, в задаче описан другой, а в базе лежит третий, агент реализует четвёртый.
Эта статья — про весь путь: от «из чего состоит задача» до словаря, которым одинаково пользуются бизнес, команда и агент, и до графа, у которого можно спрашивать.
Система понятий есть у любого проекта
«Заказ состоит из позиций. У заказа есть владелец-клиент. Заказ оплачивается платежом. Отменённый заказ оплатить нельзя» — вот кусочек описания предметной области: четыре понятия, связи между ними и одно правило. Строгое имя этому — онтология, но за страшным словом стоит простая вещь: перечень понятий, их свойств, связей и правил.
Важно понять: такая система понятий есть у любого проекта — вопрос лишь, записана ли она. Команда в любом случае оперирует какими-то понятиями. Если они нигде не зафиксированы, они живут в головах, у каждого своей версией. Продакт под «заказом» понимает одно, разработчик — другое, поддержка — третье. Все три версии работают, пока не встретятся в одном спринте.
Первая ловушка — искать «истинную» модель домена. Её не существует. Что такое «клиент» для магазина? Для маркетинга — профиль с историей интересов. Для платежей — плательщик с реквизитами. Для доставки — получатель с адресом. Это три разные модели одного человека, и попытка сделать одну «универсальную» даст худшую из всех: раздутое понятие, неудобное каждому. Критерий качества модели — не истинность, а пригодность: помогает ли она решать задачи, ради которых построена, и одинаково ли её понимают все участники.
Второй практичный инструмент — замечать, на каком уровне вы сейчас говорите. Понятия выстраиваются в этажи. Внизу — конкретный заказ №4211 от Иванова на 3 400 ₽. Этажом выше — тип «заказ»: у любого есть номер, клиент, позиции, статус. Ещё выше — «бизнес-сущность»: заказы, клиенты, платежи — всё это сущности с жизненным циклом. На самом верху — соглашения о том, как мы вообще описываем сущности.
Половина запутанных споров в командах — это спор людей с разных этажей: один говорит про конкретный заказ («а вот тут заказ без клиента!»), второй — про тип («заказа без клиента не бывает по определению»). Умение сказать «мы на разных уровнях» экономит часы.
Модель понятий — не схема базы данных
Самая частая ошибка на старте: услышав задачу, инженер открывает редактор и рисует таблицы. Кажется, что это и есть моделирование. На деле пропущен целый этап, и его пропуск оплачивается позже переделками схемы и спорами «а что мы вообще имели в виду».
Это два разных вопроса. Модель понятий отвечает: какие понятия есть и как они связаны — заказ состоит из позиций, платёж относится ровно к одному заказу, отменённый заказ оплатить нельзя. Здесь нет ни слова про таблицы и индексы. Схема базы отвечает на другой вопрос: как это хранить — в каких таблицах, с какими ключами, что денормализовать ради скорости.
Одна и та же модель понятий ложится на десятки схем: нормализованную, денормализованную под чтение, документную, событийную. Выбор схемы — инженерный компромисс под нагрузку и запросы; выбор понятий — договорённость о смысле. Когда прыгаешь сразу в таблицы, договорённость о смысле принимается молча, как побочный эффект структуры хранения.
Отучающее упражнение: перестаньте считать, что понятию соответствует таблица.
- Одно понятие — несколько таблиц. «Заказ» живёт в
orders,order_lines,order_status_history— три таблицы, одно понятие. - Одна таблица — несколько понятий. В
usersчасто смешаны «учётная запись» (логин, пароль, роли) и «человек» (имя, контакты) — два понятия с разным жизненным циклом, склеенные структурой хранения. Отсюда растут боли вида «удалили пользователя — потеряли автора заказов». - Понятие без таблицы. «Просроченный платёж» — полноценное понятие с правилами, но в базе это условие
WHERE, а не таблица.
Если модель понятий не записана отдельно, схема базы становится моделью понятий — со всеми её компромиссами. Через год уже никто не помнит, что склейка «учётки» и «человека» была случайностью, и новые возможности строятся поверх неё как поверх осмысленного решения.
У этого есть и наружные следствия: API начинает повторять таблицы (наружу торчат user_id и джойны вместо понятий домена), каждая миграция под нагрузку превращается в пересмотр смысла, а спор «нужна ли отдельная таблица для X» маскирует нерешённый вопрос «а X — это вообще отдельное понятие или атрибут Y?».
С агентом это критично вдвойне: дайте ему только DDL — и он честно примет структуру хранения за модель домена, со всеми склейками и случайностями.
Как моделировать до таблиц — час честной работы, без тяжёлых инструментов:
- Выпишите понятия существительными из языка бизнеса: заказ, позиция, клиент, платёж, возврат.
- Свяжите их: «состоит из», «принадлежит», «относится к» — с кратностями (у заказа один клиент; у клиента много заказов).
- Запишите состояния и правила: какие статусы бывают, какие переходы разрешены, что запрещено («оплатить отменённый нельзя»).
- Проверьте на бизнесе: покажите модель тому, кто владеет задачей, — на этом уровне (в отличие от DDL) он способен найти ошибку.
Результат — страница текста или простая схема. Уже из неё проектируется структура хранения — осознанно, под запросы и нагрузку.
Как показывать модель человеку без технического образования
Четвёртый шаг — самое хрупкое место всей процедуры и единственное, что отличает записанную модель от выдуманной. Если владелец задачи кивнул, ничего не поняв, вы получили согласование, а не проверку.
Чего точно не делать: показывать схему с прямоугольниками и стрелками и спрашивать «всё верно?». Ответ будет «да» почти всегда — не из вежливости, а потому что человек не может проверить то, чего не читает. Кратности, ромбики и типы связей — это ваш язык, не его.
Что работает вместо этого — четыре приёма, в порядке силы.
1. Прочитать модель вслух обычными фразами. Каждую связь и каждое правило — одним предложением на человеческом языке, и ждать реакции на каждое:
«Заказ состоит из позиций. Позиция не бывает без заказа.»
«У заказа ровно один клиент. У клиента может быть много заказов.»
«Платёж относится ровно к одному заказу.»
«Отменённый заказ оплатить нельзя.»
«Заказ нельзя отменить после отгрузки.»
Такой список читается за две минуты, и именно на нём вылезает правда: «а, нет, у нас бывают заказы от неоформленных клиентов», «платёж бывает один на несколько заказов, когда платят за месяц сразу». Второе замечание меняет модель существенно, и никакая схема его бы не вытащила.
2. Спрашивать не «верно?», а «бывает ли иначе?». Вопрос «всё верно?» проверяет согласие. Вопрос «бывает ли, что платёж относится к двум заказам?» проверяет модель. Хорошая формулировка для каждой связи: «что должно случиться, чтобы это стало неправдой?»
3. Проверять на реальных случаях, а не на типовых. Попросите три-четыре настоящих примера, включая странные: самый крупный заказ за прошлый месяц, заказ, из-за которого был скандал, заказ, который пришлось править руками. Типовой заказ влезает в любую модель; ломают её именно эти.
4. Показывать таблицу состояний, а не диаграмму. Жизненный цикл в виде таблицы «из какого состояния — в какое — по какому событию — кто инициирует» читается без подготовки:
| Было | Стало | Событие | Кто |
|---|---|---|---|
| новый | оплачен | оплата прошла | клиент |
| новый | отменён | отмена | клиент или оператор |
| оплачен | отгружен | передали в доставку | склад |
| оплачен | отменён | отмена с возвратом | оператор |
| отгружен | — | отменить нельзя | — |
Пустые и странные клетки такой таблицы — лучший вопросник: «а вернуть деньги после отгрузки совсем нельзя?» — и выясняется, что можно, но это другой процесс, которого в модели нет вовсе.
Признак, что проверка состоялась, а не прошла формально: собеседник что-то поправил. Если после разговора модель не изменилась ни в одном месте, скорее всего его не поняли или не читали. Практический приём для этого случая: специально включить в список одно намеренно неверное утверждение и посмотреть, заметят ли. Заметили — проверка работает.
Когда схема базы уже стала моделью
Предыдущий раздел кончается предупреждением «не записали модель — ею станет схема хранения». Чаще всего читатель узнаёт об этом, когда уже поздно: схема живёт в бою, в ней склеены понятия, названия из середины двухтысячных, и трогать её страшно. Что делать в этом случае — отдельный разговор, и правильного ответа «переименовать всё» здесь нет.
Сначала — разделить два вопроса. Первый: как мы называем понятия. Второй: как мы их храним. Разъехавшаяся схема — это проблема первого вопроса, притворяющаяся проблемой второго. Пока они слиты, любая попытка «навести порядок» превращается в миграцию на выходные.
Дальше есть четыре хода, и они не альтернативы, а порядок предпочтения.
Ход 1: записать модель отдельно и зафиксировать соответствие. Самое дешёвое и самое недооценённое. Схему не трогают вовсе; рядом появляется страница с моделью понятий и таблицей перевода:
| Понятие | Где живёт в базе | Оговорки |
|---|---|---|
| Клиент | users (поля name, phone, email) | в той же таблице лежит учётная запись, это другое понятие |
| Учётная запись | users (поля login, password_hash, role) | удаление записи ломает авторство заказов, поэтому её не удаляют |
| Заказ | orders + order_lines + order_status_history | статус дублируется в orders.status для скорости |
| Просроченный платёж | таблицы нет, условие по payments.due_date | понятие есть в правилах, в схеме отсутствует |
Это документ на полчаса работы, и он закрывает главную боль: новый человек и агент перестают принимать структуру хранения за модель домена. Оговорки в третьей колонке — самое ценное, потому что именно они не видны из схемы.
Ход 2: чистые имена в новом коде, слой перевода на границе. Схема остаётся, но наружу и внутрь домена понятия идут под правильными именами: домен знает Customer, слой доступа к данным переводит его в строку users. Это обычное преобразование на границе, а не архитектурный подвиг. Правило: новый код никогда не повторяет имена из схемы наружу, а старый переписывают по мере того, как трогают. Плата — один слой преобразования, который надо писать; выигрыш — контракты эндпоинтов и код домена перестают зависеть от истории базы.
Ход 3: переименовать, но через совместимость. Там, где имя мешает всерьёз, переименование делают так же, как любое несовместимое изменение схемы: добавить новое, писать в оба, перевести читателей, удалить старое. Это несколько выкатов, и решение на такую работу принимается по цене, а не по красоте. Хороший критерий: переименование оправдано, если старое имя регулярно вызывает ошибку, а не просто раздражает.
Ход 4: терпеть и записать, что терпим. Совершенно законный ответ, если пометить его явно. Запись в память проекта: «users склеивает человека и учётную запись, разделять пока не будем, причина — цена миграции; при работе с авторством заказов помнить, что запись не удаляется». Такое терпение отличается от беспорядка ровно наличием записи.
Чего делать не стоит. Начинать с большой миграции «привести схему в соответствие с моделью» — самая дорогая и самая рискованная из возможных первых мер. И заводить второй, «правильный» справочник понятий, который никто не связал с базой: два документа, расходящиеся в разные стороны, хуже одного с оговорками.
Единый язык: словарь как контракт
Модель понятий бесполезна, если ею пользуетесь только вы. Её сила — когда одними и теми же словами говорят бизнес, код и агент. Эта практика называется ubiquitous language, «единый язык», и это самая недооценённая идея DDD: она не про архитектуру, а про то, как перестать терять смысл при переводах.
Посмотрите, сколько переводов проходит обычное требование. Бизнес говорит «клиент бросил корзину». Аналитик записывает «сессия с неоформленным заказом». Разработчик называет это abandoned_cart, а рядом в коде живёт pending_order — «это же почти то же самое». Тестировщик проверяет «незавершённую покупку». Четыре формулировки, и у каждой чуть-чуть свой смысл: почти то же самое, но «почти» здесь — источник дефектов.
Каждый перевод — точка потери смысла. Единый язык убирает переводы: слово из разговора с бизнесом попадает в модель, из модели — в код, из кода — в тесты и в задачу агенту, не меняясь по дороге. Отсюда практическое следствие: если для понятия в коде нет слова из языка бизнеса — это сигнал. Либо вы изобрели сущность, которой нет в домене, либо бизнес пользуется понятием, которого нет в вашей модели.
Вторая половина идеи, без которой первая не работает: не пытайтесь сделать один словарь на всю компанию. Мы уже видели на примере «клиента»: для маркетинга, платежей и доставки это три разных понятия. Это не беспорядок, который надо победить, а природа предметных областей. Решение — границы контекстов: единый язык действует внутри границы. Внутри контекста платежей «клиент» — всегда плательщик с реквизитами, и никаких других значений. На границе между контекстами происходит явный перевод: событие «заказ оформлен» превращается в «появился плательщик» — и этот перевод записан, а не происходит в чьей-то голове.
Работает всё это, только если словарь записан. Формат минимальный — глоссарий на страницу: термин (и имя в коде, если отличается, — но лучше, чтобы не отличалось), определение одним-двумя предложениями без «ну все же понимают», границы (в каком контексте действует, чем отличается от похожего термина соседнего контекста), состояния и правила, если у понятия есть жизненный цикл. Признак живого словаря: на ревью можно сказать «у нас нет понятия „подписка“, есть „регулярный платёж“» — и это аргумент, а не вкусовщина.
Для агента словарь — не справка, а контракт. Он называет сущности так же, как бизнес; не изобретает синонимов (Order, а не Purchase во второй сессии); соблюдает записанные правила состояний. Три сессии без словаря дают три несовместимых решения — не потому что модель плохая, а потому что каждый раз она заново угадывает вашу систему понятий. Словарь делает угадывание ненужным.
Где записан перевод между контекстами
«Перевод записан, а не происходит в чьей-то голове» — обещание, к которому нужен адрес. Записывается он в трёх местах одновременно, и у каждого своя роль.
В глоссарии — сам факт расхождения. У термина есть строка про границы: в каком контексте действует и чем отличается от похожего слова соседнего контекста. Выглядит это так:
## Плательщик (контекст платежей)
Тот, с чьих реквизитов списываются деньги. Имеет идентификатор
в платёжной системе и способ оплаты.
Границы: не то же самое, что «клиент» в контексте заказов.
Один клиент может иметь несколько плательщиков (личная карта
и карта компании); плательщик может не быть клиентом вовсе
(оплата за другого человека).
Эта запись стоит больше, чем определение: она предотвращает самую дорогую ошибку — молчаливое допущение, что один клиент равен одному плательщику.
В контракте события или запроса — форма перевода. Событие, уходящее за границу, говорит на языке того, кто его публикует, а принимающая сторона переводит его в свои понятия. Поле перевода видно прямо в контракте:
Событие OrderPlaced (контекст заказов)
orderId, customerId, amount, currency
Контекст платежей принимает его так:
customerId -> ищем или создаём Payer по customerId
amount + currency -> сумма к списанию
orderId -> назначение платежа
Важная деталь: контекст платежей не получает понятие «клиент» — он получает идентификатор, по которому создаёт своё понятие. Так и должно быть: событие переносит данные, а не модель.
В коде входящего адаптера — единственное место, где перевод исполняется. Не в сервисе домена и не «где придётся»: преобразование чужого представления в своё живёт на входе, в одном файле, и его можно прочитать целиком. Признак нарушения — чужие имена (customerId в контексте платежей) просочились внутрь домена: значит перевода нет, есть общая модель, и границы контекста фактически не существует.
Как это проверить в своём проекте за пять минут: поищите по коду одного контекста имена понятий другого. Нашлись глубоко внутри — граница проведена только на схеме, а не в коде.
Глубже: что заводить: страница, схема или графрасширенное
Три инструмента из этой статьи — глоссарий, записанная модель понятий и граф — не этапы зрелости, которые нужно пройти. Это ответы на разные объёмы, и брать надо самый дешёвый из подходящих. Развилка определяется наблюдаемыми признаками, а не размером компании.
| Признак | Что заводить |
|---|---|
| Один сервис, одна-две команды, понятий десятки | страница глоссария: термин, определение, оговорки |
| Есть жизненные циклы и правила переходов | глоссарий плюс таблицы состояний |
| Два и больше контекстов с расходящимися словами | глоссарий на контекст плюс записанные переводы на границах |
| Больше пяти-семи сервисов, вопросы «кого затронет изменение» | реестр в файлах: сервисы, владельцы, контракты, зависимости |
| Вопросы «кто владелец», «сколько у нас интеграций», «что зависит от этого контракта» задают регулярно | реестр с возможностью обхода связей |
| Десятки сервисов, несколько команд, обязательные ответы про персональные данные и влияние | граф знаний в отдельном хранилище |
Точнее всего развилку определяют вопросы, которые начали задавать, а не число сервисов:
- «Что такое X?» — хватает глоссария.
- «Как это работает?» — хватает документации и поиска по ней.
- «Кто владелец X?», «где используется Y?» — нужен реестр с точными фактами; поиск по документам здесь врёт правдоподобно.
- «Кого затронет изменение Z?», «через сколько систем проходят персональные данные?» — нужен обход связей, то есть граф.
Практическое правило перехода: не заводите граф, пока реестр в файлах не перестал справляться. Файлы дешевле в поддержке, читаются глазами, ревьюятся обычным порядком и уже дают ответы на точечные вопросы. Признак, что пора дальше: ответ на частый вопрос требует пройти по трём-четырём файлам руками, и это делают несколько раз в неделю.
И обратное правило, которое нарушают чаще: не заводите граф вместо глоссария. Граф связей без записанных определений понятий — это стрелки между словами, значение которых никто не зафиксировал. Определения всегда первыми.
Глубже: когда понятий много: граф знанийрасширенное
Граф знаний хранит связи явно — фактами вида «субъект → отношение → объект»: «payment-service — потребляет — orders-api», «orders-api — принадлежит — команде checkout». Узлы — понятия, рёбра — связи, а схема ограничивает допустимые типы узлов и отношений. По графу можно пройти от контракта к потребителям и их зависимостям, получив проверяемый ответ на вопрос «кого затронет изменение».
Связка графа с поиском по документам называется GraphRAG: модель выделяет сущности из вопроса, обходит связи и формулирует ответ по собранным фактам. Граф не заменяет обычный поиск для вопросов «что такое X» и не бесплатен: факты надо поддерживать актуальными, иначе он уверенно врёт. Поэтому сначала ведут реестр в файлах, а отдельное хранилище графа заводят по критериям из раздела выше.
Как граф заводят и кто его обновляет
Цена графа сосредоточена не в проектировании, а в поддержании — значит и разговор начинать надо с того, откуда берутся факты. Способов три, и они различаются именно этим.
Способ первый: факты в файлах репозитория. Самый дешёвый старт и самый частый в практике. У каждого сервиса рядом с кодом лежит файл-описание, а из них собирается общая картина:
name: payment-service
owner: team-checkout
consumes:
- orders-api
- customer-api
publishes:
- PaymentCaptured
- PaymentRefunded
stores: [payments, idempotency_keys]
personal_data: true
Обновление здесь решается организационно: файл лежит в том же репозитории, что и код, и меняется тем же изменением. Подключили новую зависимость — строка в файле появилась в том же запросе на слияние, и на ревью видно, если её забыли. Обход связей делается скриптом на десять строк или запросом к собранному из файлов набору. Слабое место — согласованность имён: orders-api и order-api в двух файлах дают разрыв в графе, поэтому список допустимых имён держат отдельным файлом и проверяют сборкой.
Способ второй: выводить факты из самой системы. Часть связей не нужно записывать руками — они уже есть в артефактах: список зависимостей сборки, описания эндпоинтов, настройки развёртывания, схемы событий в реестре схем, права доступа к базам. Скрипт, который читает их и складывает в граф, устаревает на порядок реже человека. Правило простое: что можно вывести — не записывают руками, потому что записанное руками расходится с реальностью, а выведенное — нет.
Способ третий: отдельное хранилище графа. Оправдано, когда фактов десятки тысяч, обходы глубокие, а источников много. Тогда у графа появляется свой конвейер наполнения: источники (файлы, артефакты сборки, реестры, ручной ввод), сборка, проверки целостности, публикация. Это уже инфраструктурная работа с владельцем, а не документ.
Кто обновляет — главный вопрос, и ответ на него организационный, а не технический. Три правила, без которых граф протухает за квартал:
- Факты о сервисе принадлежат команде сервиса. Центральная команда, которая описывает чужие сервисы, обречена: она узнаёт об изменениях последней.
- Обновление — часть изменения, а не отдельная задача. Строка в файле едет тем же коммитом. Отдельная задача «актуализировать реестр» означает, что реестр уже неактуален.
- У графа есть проверка свежести. Самое действенное: сборка падает, если сервис существует, а описания у него нет; отчёт показывает записи, которых давно не касались; связь, указывающая на несуществующий сервис, ловится проверкой целостности. Без этого нет способа отличить «не менялось, потому что стабильно» от «не менялось, потому что забыли».
Практический порядок для того, кто начинает: файл описания у каждого сервиса, список допустимых имён, проверка целостности в сборке. Этого хватает надолго, и из этого потом собирается настоящий граф, если он понадобится.
Что происходит при вопросе и чего граф не знает
Раздел выше говорит, что подграф подмешивается в контекст. Это пропускает два практических вопроса, на которые и спотыкаются первые внедрения.
Кто решает, какие сущности выделить из вопроса. Это делает сама модель, и делает не всегда верно. Порядок обычно такой: из вопроса выделяются упомянутые имена и типы (получается набор точек входа в граф), от них выполняется обход на заданную глубину, собранные факты попадают в контекст, и уже по ним формулируется ответ.
Ломается это в узнаваемых местах:
- Имя в вопросе не совпадает с именем в графе. Человек спрашивает «платежи», в графе
payment-service. Лечится списком синонимов у каждого узла — самая полезная и самая скучная часть работы. - Сущность не названа вовсе. «Кого затронет изменение контракта оплаты?» — какого именно контракта? Правильное поведение здесь — уточняющий вопрос, а не догадка.
- Глубина обхода выбрана неверно. Один шаг даёт неполный ответ, четыре — подграф размером в половину графа, который не влезает в контекст и топит важное. Практически глубину ограничивают двумя-тремя шагами и типами связей, по которым идти.
Что делать, когда факта в графе нет. Это главный вопрос, потому что от ответа на него зависит, можно ли графу верить. Плохой вариант очевиден: модель дополняет отсутствующий факт правдоподобным — и получается уверенная ложь, подкреплённая авторитетом «ответ по графу».
Что должно происходить вместо этого:
- Ответ «в графе этого нет» — правильный ответ, и он должен быть разрешён явно, иначе модель будет угадывать. Это формулируется в правилах: отвечать только по переданным фактам, отсутствие данных называть отсутствием данных.
- Ответ делится на две части: факты и рассуждение. Факты берутся из графа и снабжаются ссылкой на источник (файл, запись, дата обновления); вывод модели помечается как вывод. Тогда читающий видит, где кончается проверяемое.
- Дырки в графе — это задачи, а не шум. Вопрос, на который не нашлось фактов, стоит записывать: список таких вопросов и есть план наполнения графа, причём отсортированный по реальной потребности.
- Отдельно обрабатывается устаревший факт. Он опаснее отсутствующего, потому что выглядит как ответ. Поэтому дата обновления у записи — обязательное поле, и в ответе она видна: «владелец
payment-service— командаcheckout, запись от марта» позволяет читателю решить, верить ли.
Общее правило, которое стоит унести: граф отвечает за факты, модель — за формулировку, и смешивать их в ответе нельзя. Как только вывод модели подан как факт из графа, вся ценность графа теряется — проверять ответ снова приходится руками.
Коротко
- Модель понятий подходит под задачу, а не претендует на универсальную истину. Она не равна схеме базы: понятие может жить в нескольких таблицах, а таблица — склеивать несколько понятий.
- Порядок моделирования: понятия, связи с кратностями, состояния и правила, проверка на бизнесе, потом таблицы. Проверяют обычными фразами и реальными нестандартными случаями: собеседник должен что-то поправить.
- Единый язык сохраняет слова бизнеса в коде и тестах. Словарь действует внутри контекста, перевод между контекстами записан в глоссарии, контракте события и входящем адаптере.
- Если схема уже стала моделью, начинают с таблицы соответствия и оговорок, а не большой миграции. Новый код использует чистые имена через слой перевода, переименование делают совместимо и только по измеренной пользе.
- Инструмент выбирают по вопросам: «что такое X» — глоссарий, «кто владелец» — реестр в файлах, «кого затронет изменение» — граф. Граф не заменяет глоссарий и появляется, когда файлов уже мало.
- Факты графа принадлежат командам сервисов и обновляются с кодом. В ответе отделяют факты с источником и датой от вывода модели, отсутствие факта называют отсутствием, а не догадываются.
Что почитать дальше
- От модели к контракту — как превратить понятия в документ, по которому агент строит, а вы принимаете.
- Галлюцинации — почему пробелы в понятиях агент заполняет молча.
- Поиск по своим данным — как подмешивать знания в контекст модели.
- Use Case Pattern — методология, где спецификация домена живёт рядом с кодом.