Числата се разчитат с код, не с модел
Един и същ модел преписва „1 849,97“ съвършено вярно и превръща „3,6 kW“ в 3600. Разликата не е в трудността, а в операцията: преписването е копиране, превръщането е тълкуване. Само едното е безопасно — затова моделът копира числата, а кодът ги смята.
01Какво ще научиш
- Защо преписването на число е безопасно, а превръщането му в число — не.
- Какви са трите източника на тиха грешка при български формат на числата.
- Как се сверява запис с няколко реда код: сбор, ДДС, валута, порядък, суров низ.
- Защо парите се смятат с десетична аритметика, а не с обикновено дробно число.
02Преди да започнеш
- Python 3 (достатъчни са стандартните модули; ползваме
decimal). - Модел, който вече връща записи като JSON — например по схемата от предишния урок.
- Примерите с числа и кодът тук са измислени: ООД „Пример“, ЕИК 000000000, суми в евро. Не са взети от реални документи.
03Стъпки
-
Преписване срещу превръщане
Когато моделът върне числото като низ, точно както стои в документа, той просто го е пренесъл. Тази операция е надеждна — същият модел я прави вярно дори върху разкривен текст от OCR. Когато обаче трябва да върне число, някой трябва да реши къде е десетичният знак, кое е разделител на хиляди и кое е мерна единица. Това е тълкуване и тук моделът греши.
Входът Какво излиза Какво е станало 3,6 kW3600 запетаята е взета за разделител на хиляди 742,31857423185 запетаята е премахната изцяло 250 kWh250000 интервалът преди единицата е взет за хиляди „хиляда двеста тридесет и четири евро и 56 цента“ 1204,56 изписаното с думи е сглобено с една сгрешена цифра Примерите са измислени, но формите на грешка са тези, които се срещат при български формат: десетична запетая, интервал за хиляди, единица след числото и сума с думи.
⚠️Размерът не е гаранцияВ наши вътрешни проби модел от 3,3 GB даде 10 от 10 на същия списък, а модел от 5,2 GB сгреши 5 от 10 — устойчиво, при всяко пускане. Не е повторено за това издание. Изводът за работата е същият: не се решава с по-голям модел, а с друго разпределение на труда. -
Тихата грешка
Грешките от превръщане имат общо свойство: нищо не се оплаква. Няма изключение, няма предупреждение. Излиза число, което изглежда точно като число. Разликата между 1 234,56 и 1 204,56 е една цифра и нищо в отговора не подсказва коя е вярната.
Клас А — шумен провал. Моделът връща каша, невалиден JSON или отказва. Следващата стъпка не може да прочете входа и спира.
Клас Б — тих провал. Моделът връща правдоподобен запис със сгрешено число. Минава всяка проверка за форма, влиза в регистъра и се открива месеци по-късно — ако изобщо се прави сверка.⛔ПравилотоМоделите се оценяват по класа на провала, не по обща оценка. Модел от клас Б е по-опасен от модел от клас А със същия резултат в таблицата.✅Разделението на трудаМоделът чете, разпределя и намира. Кодът смята, превръща и сверява. Числото пътува през веригата като низ колкото може по-дълго и става число на едно-единствено място — в код, с тест. -
Сверката с код
Всеки извлечен запис с числа минава през няколко реда проверки. Те не поправят нищо — вдигат флаг. Поправката е работа на човек, защото поправка без поглед е същата грешка с друг автор.
Парите се смятат с
Decimal, не сfloat: двоичното дробно число не може да представи точно 2,675 иround(2.675, 2)връща 2,67, докато десетичното закръгляване „половината нагоре“ дава 2,68. При пускане в пясъчника видяхме точно това.python · четири проверки върху измислен записfrom decimal import Decimal, ROUND_HALF_UP RATE = Decimal("1.95583") # фиксиран курс: лева за 1 евро CENT = Decimal("0.01") def to_dec(raw): # единственото място, където низ става число s = raw.replace(" ", "").replace(" ", "").replace(",", ".") return Decimal(s) def eur_to_bgn(eur): return (eur * RATE).quantize(CENT, rounding=ROUND_HALF_UP) def check(r): flags = [] rows = [to_dec(x["amount_raw"]) for x in r["rows"]] net, vat, total = to_dec(r["net_raw"]), to_dec(r["vat_raw"]), to_dec(r["total_raw"]) if sum(rows) != net: flags.append("сборът на редовете не съвпада с нетото") if (net * Decimal("0.20")).quantize(CENT, ROUND_HALF_UP) != vat: flags.append("ДДС не е 20% от нетото") if net + vat != total: flags.append("нето + ДДС не е равно на общото") if eur_to_bgn(total) != to_dec(r["total_bgn_raw"]): flags.append("сумата в лева не отговаря на фиксирания курс") return flags record = { # ООД „Пример“, ЕИК 000000000 — измислен запис "rows": [{"amount_raw": "1 000,00"}, {"amount_raw": "541,64"}], "net_raw": "1 541,64", "vat_raw": "308,33", "total_raw": "1 849,97", "total_bgn_raw": "3 618,23", } print(check(record)) # [] — няма флаговеИзпълнение в пясъчник: измисленият запис излиза без флагове. Ако вторият ред е сгрешен на „514,64“ (разменени цифри), проверките вдигат флаг „сборът на редовете не съвпада с нетото“ и документът остава непипнат.
Четирите задължителни проверки
1. Пресмятане наново. Съберѝ редовете и сравни с обявеното. 2. Превръщане от кода. Всяко производно число (ДДС, сума в друга валута) се пресмята пак, никога не се приема наготово. 3. Проверка за порядък. Стойност, която се различава от съседите си с хиляда пъти, е заподозряна по презумпция. 4. Суровият низ остава. До всяко превърнато число стои текстът, от който е дошло — иначе сверката после е невъзможна.
ℹ️Защо флаг, а не поправкаАвтоматичната поправка изглежда като услуга и се държи като загуба на следа. Флагът струва на човека десет секунди и оставя документа такъв, какъвто е.✅Сверено на 01.10.2026: ставка, курс, закръгляванеСтандартната ставка по чл. 66, ал. 1 от ЗДДС е 20 на сто. Фиксираният курс е 1 EUR = 1,95583 BGN (решение на Съвета на ЕС от 8 юли 2025 г., съобщено от ЕЦБ). Официалният калкулатор на evroto.bg (Министерство на финансите) прилага този курс и математическото правило за закръгляване — до цент, „половината нагоре“, както в кода по-горе. Намалените ставки по ЗДДС (например 9 %) тук не се разглеждат: проверката е за документи със стандартна ставка. -
И електронната таблица лъже
Правилото не е само за езикови модели — то е за числата изобщо. Същата тиха грешка живее и в таблиците, които хората попълват от години, само че там никой не я търси.
При преглед на месечни протоколи, водени в електронна таблица, излязоха три сгрешени формули. Едната умножаваше количеството по празна клетка — формулата е валидна, резултатът е нула. Друга носеше твърдо вписана стара тарифа от предишна година. Никоя не даде грешка. Един ред показваше 3,03 за 50 кубика три месеца подред — и минаваше, защото никой не сравняваше количеството със сумата.
Проверката е същата като при модела: сборът на редовете срещу обявеното общо и количество срещу сума по тарифа. Две сравнения, които се пишат веднъж и се пускат върху целия архив, а не само върху последния месец.
⚠️УроцитеПроверявай формулите, не само стойностите: отвори файла на ниво формула, иначе виждаш точно това, което таблицата иска да ти покаже. И общото правило: колона, която се смята, не се въвежда и не се извлича — смята се при четене, от код, върху суровите стойности. Ако хранилището не умее изчислима колона (а повечето прости хранилища не умеят), сметката живее в изгледа, не в таблицата.
04Проверка
1. Коя операция е безопасна за езиков модел?
2. Кой клас грешка е по-опасен?
3. Какво прави сверката, когато сборът на редовете не съвпада с общото?
4. Защо суровият низ се пази до превърнатото число?
05Какво следва
06Източници
- Python: модулът decimal — десетична аритметика и закръгляване (
ROUND_HALF_UP). - Python: дробните числа и техните ограничения — защо
round(2.675, 2)дава 2,67. - Закон за данъка върху добавената стойност — чл. 66, ал. 1: стандартна ставка 20 на сто (сверено на 01.10.2026).
- ЕЦБ, 8 юли 2025 г. — България влиза в еврозоната на 1 януари 2026 г.; курсът е фиксиран на 1,95583 лв. за 1 евро.
- evroto.bg (Министерство на финансите) — калкулатор по фиксирания курс и математическото правило за закръгляване.
- Собствени наблюдения на КАГАМИ при извличане на суми от български документи и при преглед на таблици (не са повторени за това издание). Кодовите примери в урока са с измислени данни.