Если бы Beatles пели не о любви, а о бухгалтерском учёте, то получилось бы что-то вроде: All you need is report. Всем от бухгалтерии интересно что-то узнать: налоговой — размер выручки, работнику — почему так мало начислили за прошлый месяц, а руководителю — начал ли хоть кто-то из хронических дебиторов рассчитываться с нами.
Мы занимаемся автоматизацией бухучёта с середины девяностых. Технологии менялись, но добыча нужных цифр всегда была процессом длительным, часто грязным (кто менял ленточки на Epson-ах или досыпал тонер в картриджи HP — знает) и дорогим: надо позвать программиста, объяснить ему, какой отчёт нужен, подождать пару недель и получить на выходе не то, что ожидалось.
ChatGPT изменил современный мир и то, как мы общаемся с компьютером. Пользователь больше не хочет продираться через минное поле выпадающих списков, чекбоксов и радиокнопок. Он хочет просто спросить обычным языком и получить ответ с текстом и цифрами, а если понадобится, то и с таблицами и графиками. Вопросы у пользователя могут быть как простыми: "Какое сальдо по расчётам с поставщиками на 1 октября?" или "покажи карточку счёта 50 за сентябрь", так и требующими рассуждений и анализа: "Почему упала прибыль в прошлом квартале, что происходит на рынке и что нам делать?" На простые вопросы модель обязана отвечать точно. На сложные — хотя бы обоснованно.
Мостиком между моделью и учётной базой служит Model Context Protocol (MCP) — открытый протокол, через который языковая модель подключается к внешним данным и инструментам. Тем, кто застал девяностые, проще всего представить его как ODBC для нейросетей: один разъём, к которому подключается что угодно. Дело за малым — написать сервер, который будет висеть на другом конце провода. О том, каким он должен быть, и пойдёт речь.
К конкретной учётной системе цикл не привязан. Схема данных условная, план счетов типовой — тот самый, по которому сдают экзамен в вузе (счёт 50 из примера выше — это, конечно, "Касса"). С терминологией сложнее: каждая система норовит назвать одно и то же по-своему. Документ где-то проводят, где-то отрабатывают в учёте, где-то формируют по нему проводки по типовой хозяйственной операции. Объекты аналитического учёта зовутся то субконто, то аналитиками, то аналитическими счетами, то классификаторами. Карточку счёта иногда величают картой, анализ счёта — шахматкой. Мы будем держаться языка учебника, а синонимы приводить там, где без них можно запутаться. Механизмы своей системы читатель, надеемся, узнает и без них.
План цикла
- Часть 1 (эта статья). Два пути: частный и универсальный сервер. Соблазн прямого доступа.
- Часть 2. Условная схема данных. Где ошибается модель.
- Часть 3. Архитектура сервера. Инструменты. Знание предметной области.
- Часть 4. Сложные вопросы. Чему нас научила разработка. Заключение.
Два пути: частный и универсальный сервер
Прежде чем браться за сервер, придётся выбрать дорогу. Их две.
Частный сервер — набор инструментов под конкретные вопросы: дай дебиторов, дай список сотрудников, дай начисления по сотруднику, дай остатки на складе. Он похож на кофейный автомат: нажал кнопку "капучино" — получил капучино.
Универсальный сервер устроен иначе. Его инструменты параметризованы не вопросами, а понятиями бухгалтерского учёта: счёт, период, объект аналитики, вид документа. Это уже не автомат, а бариста с кофемашиной, молоком и сиропами: из одних и тех же ингредиентов он соберёт и то, чего нет в меню.
Частный сервер подкупает простотой. Написать его легко, протестировать ещё легче. Для чат-бота, через который сотрудник узнаёт остаток своего отпуска, ничего другого и не нужно. Трудности обычно появляются на втором-третьем уточняющем вопросе.
Представим разговор с финансовым директором. В частном сервере есть инструменты get_employees(), get_accruals(employee) и get_revenue(period).
| Вопрос | Частный сервер | Универсальный сервер |
|---|---|---|
| Покажи начисления Иванову за сентябрь |
get_accruals("Иванов") — ответ получен |
Карточка счёта 70 "Расчёты с персоналом по оплате труда" с отбором по работнику |
| Сколько премий начислено отделу продаж в III квартале? | Инструмента для отдела и вида начисления нет. Модель вызывает get_accruals для каждого из 40 сотрудников и складывает 40 ответов в уме, путаясь в видах начислений |
Обороты по кредиту счёта 70 с группировкой по подразделению; вид начисления — из документов начисления заработной платы. Два вызова, итоги считает сервер |
| А по сравнению со II кварталом? | Ещё 40 вызовов | Тот же вызов со сравнением периодов |
| Как это соотносится с выручкой отдела? |
get_revenue об отделах не знает. Ответа нет |
Выручка по кредиту счёта 90 "Продажи" в разрезе подразделений — показатель из справочника показателей |
| Почему премии выросли, а выручка нет? | Ответа нет | Анализ счёта, документы начисления премий и их основания |
На первый вопрос оба сервера отвечают одинаково. На втором у частного сервера нет подходящего инструмента, и у разработчика остаётся два варианта. Можно дописать get_accruals_by_department_and_type, следом get_revenue_by_department, следом ещё десяток. Чем больше похожих инструментов, тем чаще модель выбирает не тот. А можно добавлять параметры: отбор по подразделению, группировку, период сравнения. Только инструмент с отбором по любому объекту аналитики, группировкой и сравнением периодов — это уже оборотно-сальдовая ведомость. Частный сервер, если его развивать, постепенно превращается в универсальный, только без единой схемы параметров.
Готовых ответов универсальный сервер при этом не лишается. Отчёт о дебиторской задолженности или расчётная ведомость по сотруднику входят в него как частные случаи: это те же учётные регистры с заданными параметрами. Разница в том, что, когда готового отчёта нет, у модели остаются инструменты, из которых его можно собрать.
Дальше речь пойдёт только об универсальном сервере. Но универсальный не значит "дай модели SQL, пусть разбирается". Почему — в следующем разделе.
Соблазн прямого доступа
Самый короткий путь к универсальному серверу — дать модели описание таблиц и инструмент для выполнения SQL-запросов. SQL современные модели пишут неплохо, схема базы известна. Что тут может пойти не так?
flowchart LR
U[Бухгалтер] -->|вопрос| L[Языковая модель]
L -->|SQL| S[MCP-сервер<br/>run_sql]
S --> D[(Учётная база)]
D --> S --> L -->|ответ| U
Рис. 1 Наивная архитектура: модель сама пишет запросы к учётной базе.
Первые ответы обычно выглядят убедительно. Проблема в том, что часть из них неверна, а по виду это не определить: запрос синтаксически корректен, выполняется без ошибок и возвращает правдоподобное число. Для бухгалтерии это хуже отказа. Если число хотя бы раз разошлось с оборотно-сальдовой ведомостью, бухгалтер начинает перепроверять каждый ответ, и пользы от сервера становится немного.
В чём же дело, если SQL модель знает не хуже иного программиста? Представьте толкового стажёра, которому выдали пароль от базы и ни слова не сказали о том, как в ней устроен учёт. SQL он знает, а про реформацию баланса слышит впервые. Модель находится в похожем положении, только работает быстрее и сомневается реже.
Где именно стажёр спотыкается, разберём во второй части. Возьмём условную схему хранения проводок и пройдём по ошибкам, которые модель делает чаще всего: двойной счёт на сложных проводках, свёрнутое сальдо там, где нужно развёрнутое, внутренние обороты в выручке, не тот объект аналитики, субсчета. У всех этих ошибок есть общая черта: запрос выполняется без замечаний и возвращает правдоподобное число.
Top comments (0)