Експертні системи: крок від математичного методу до обрання технологій

Серія статей: Експертні системи для R&D
Попередня стаття: Прикладна математика експертних систем: від правил і ймовірностей до графів, причинності та рішень
Наступна стаття: Архітектура експертної системи: від знання до доказового рішення

У попередній статті я почав із прикладної математики: логіка і правила, мережі Rete, системи підтримання істинності (Truth Maintenance Systems, TMS), коефіцієнти впевненості, байєсівські та марковські моделі, міркування за випадками (Case-Based Reasoning, CBR), графи, оптимізація, планування, причинність і пояснюваний штучний інтелект (Explainable AI, XAI). Це було зроблено навмисно: поки не зрозумієш, до якого математичного класу належить задача, вибір бібліотеки перетворюється на колекціонування модних назв. Тепер можна перейти до засобів реалізації — не одного «правильного» стека, бо його не існує. Сучасна експертна система різнорідна: знання живуть і в реляційній базі, і в графі, і в правилах, і у векторному індексі, і в документах, і в моделях, і в рушії політик доступу. Завдання архітектора — не знайти єдину платформу для всього, а чесно розкласти відповідальність між цими шарами.

Звідси й поділ на дві статті. Ця, технологічна, відповідає на питання «що існує і навіщо»: які класи інструментів є і яку задачу кожен закриває. Наступна, архітектурна стаття, відповідає на питання «як із цих засобів будується система»: як шари поєднуються, хто за який клас істини відповідає і за якими контрактами підсистеми обмінюються знаннями. Тут — каталог засобів; там — їхня композиція.

Я свідомо тримаю зв’язок із попередньою статтею: кожен клас інструментів тут закриває конкретний математичний клас задач звідти — рушії правил виконують логіку і Rete, графові бази матеріалізують графовий аналіз, векторні сховища дають пошук за схожістю, рушії політик — формальні обмеження доступу. Тому до кожного інструмента я ставлю одне питання: яку задачу експертної системи він реально закриває — і якій математиці відповідає? Список нижче великий, і спокуса «взяти все» велика, але інструмент сам по собі не робить систему експертною і доказовою — він лише дає місце, де відповідна математика працює під контролем версій, доступу та аудиту. Тому я групую засоби за класами задач, а не за модними назвами.

Ще один спосіб читати цю статтю — як історію того, як інженерія підлаштовувалася під форму задачі. Коли треба було надійно рахувати «так/ні», з’явилися правила, таблиці рішень і реляційні обмеження. Коли головним стали зв’язки, виросли графи. Коли треба було шукати слова в мільйонах документів, з’явилися інвертовані індекси. Коли треба стало порівнювати зміст, а не лише слова, знадобилися вектори й геометрія схожості. А коли моделі перетворилися на величезні множення матриць, апаратура навчилася виконувати ці множення паралельно. Тобто еволюція технологій тут не примха ринку, а відповідь на дуже конкретну математику: логіку, графи, ймовірності, оптимізацію і лінійну алгебру.

Мови програмування: сучасні універсальні мови як шари відповідальності


Перш ніж перелічувати конкретні мови, варто відповісти на просте питання: якого вони класу? Відповідь майже завжди однакова — це сучасні універсальні мови високого рівня з багатою екосистемою бібліотек і реальною кросплатформеністю. Систему знань на асемблері чи класичному BASIC сьогодні не пишуть, а історичні спеціалізовані мови штучного інтелекту — Lisp, Prolog, CLIPS — здебільшого відійшли в ніші, навчальні курси й окремі вбудовані рушії. Якщо погортати публічні репозиторії за запитом expert system на GitHub, картина та сама: домінують кілька універсальних мов, а не екзотика. Це не означає, що спадок мертвий: Prolog усе ще трапляється там, де природне логічне програмування, а CLIPS живе як вбудований рушій правил — але вже не як основна мова цілого продукту (до обох повернемося далі).

При виборі мови для конкретного шару важить не стільки синтаксис, скільки оточення: зрілість екосистеми, наявність готових бібліотек для математики й машинного навчання, можливість зв’язатися зі швидкими C/C++ ядрами через зовнішній інтерфейс функцій (Foreign Function Interface, FFI), передбачувана експлуатація і довгострокова підтримка. Окремо згадаю Jupyter Notebook — це не мова, а інтерактивне середовище навколо Python (та інших обчислювальних ядер), яке де-факто стало стандартним місцем для дослідження даних, прототипів моделей і відтворюваних експериментів. Тому далі я групую мови не за модою, а за тим, який шар відповідальності кожна закриває найкраще.

Python став стандартом для аналітики, прототипування, машинного навчання, обробки тексту, математичних моделей, конвеєрів пошуку з доповненням генерації (RAG) і швидких експериментів. Його сила — справді широка екосистема: NumPy, SciPy, pandas, scikit-learn, PyTorch, JAX, TensorFlow, NetworkX, PyMC, statsmodels, OR-Tools, FAISS, spaCy, Hugging Face. Практично: Python зручний для лабораторії знань, оцінювання, пошуку, класифікації, прототипів правил і пакетної обробки даних офлайн.

C і C++ лишаються фундаментом усього, що має працювати швидко й близько до заліза: високопродуктивні ядра, драйвери, embedded-системи, числові бібліотеки, рушії виконання моделей (inference runtimes) та інтеграція з апаратурою. Для теми цієї статті це не абстракція: саме на C/C++ написані ключові компоненти сучасного LLM-стека — llama.cpp і бібліотека тензорних обчислень GGML під ним, whisper.cpp для розпізнавання мовлення, обчислювальні ядра ONNX Runtime та OpenVINO. У світі експертних систем класичний рушій правил CLIPS (C Language Integrated Production System, рушій продукційних правил мовою C) написаний саме на C — і досі застосовується. Практично: C/C++ майже завжди присутні навіть тоді, коли продукт «на Python», бо найважчі бібліотеки виконуються нижче, у нативному коді.

Java і JVM-світ сильні в корпоративних системах. Там живуть Drools, Apache Jena, RDF4J, багато інтеграційних платформ і серверних застосунків. Для експертних систем це історично найбагатша екосистема: Drools (проєкт KIE) — канонічний рушій правил на алгоритмі Rete з власною мовою правил і підтримкою DMN; Apache Jena і RDF4J — для онтологій, RDF-сховищ і логічного виведення. Практично: якщо організація вже має Java-інфраструктуру, експертна система може вирости як набір сервісів із рушієм правил і службами знань без радикальної зміни культури.

C# і .NET важливі там, де організація живе в Microsoft-екосистемі: Windows, Active Directory, Office, SharePoint, внутрішні застосунки, промислові робочі місця. Для правил тут є свої рушії: NRules — Rete-рушій правил для .NET, а microsoft/RulesEngine — відкритий рушій правил від Microsoft, де правила задаються як JSON-вирази. Практично: це хороший вибір для інтеграції експертної системи з корпоративними процесами, настільними застосунками, внутрішніми сервісами і рольовим розмежуванням доступу (role-based access).

Go добре підходить для сервісного ядра, конекторів, CLI, агентів, черг, мережевої взаємодії і надійних серверних компонентів. Є тут і рушії правил: hyperjumptech/grule-rule-engine — рушій на алгоритмі Rete із власною мовою правил GRL. Практично: Go зручний для інфраструктурного шару, де важлива простота розгортання, легка багатопотоковість і передбачувана експлуатація.

Показово, що Ollama — один із найпоширеніших локальних LLM-рантаймів — написаний саме на Go. Це не випадковість: goroutines, низькі накладні витрати, єдиний бінарний файл без залежностей і крос-компіляція роблять Go природним вибором для системного шару AI-інфраструктури. Те саме стосується NATS сервера (повністю Go), Kubernetes, Docker і більшості сучасних cloud-native інструментів.

Для AI і LLM-інтеграції в Go є чималий набір бібліотек. Найпрактичніший вибір для роботи з Ollama і взагалі з будь-яким LLM-провайдером — sashabaranov/go-openai: він реалізує OpenAI-сумісний протокол, який Ollama підтримує нативно. Це означає, що один клієнт покриває Ollama, OpenAI, Mistral, Groq, Azure OpenAI і локальні сервери на базі llama.cpp — без зміни коду, лише через конфігурацію BaseURL. Для конкретних функцій Ollama (pull model, list models) є власний github.com/ollama/ollama/api, але в продакшн-стеку він потрібен рідше. tmc/langchaingo — Go-порт LangChain з chains, agents, tools і vector store connectors; корисний для прототипів. philippgille/chromem-go — in-process векторний store без окремого сервера, добре для тестів і standalone CLI. nlpodyssey/spago — нативна ML-бібліотека на Go: трансформери і вкладення (embeddings) без Python. Для виконання моделей у форматі ONNX є yalue/onnxruntime_go — Go-байндінги до ONNX Runtime, що дозволяє запускати класифікатори, ембедери і малі моделі безпосередньо в Go-процесі.

Для роботи з форматами даних і парсингу Go також має сильну екосистему. google/go-github і xanzy/go-gitlab — офіційні клієнти до GitHub і GitLab API, що закривають більшість сценаріїв збирання знань (knowledge acquisition) з репозиторіїв. andygrunwald/go-jira для Jira REST API. ledongthuc/pdf — нативний парсер PDF без CGO-залежностей. goccy/go-json, go-yaml/yaml, pelletier/go-toml, encoding/xml зі стандартної бібліотеки — для різноманітних форматів конфігурацій і обміну даними.

Для веб-шару і API в Go де-факто стандарт це gin-gonic/gin: швидкий HTTP-роутер з проміжними обробниками (middleware), валідацією, групуванням маршрутів і хорошою документацією.

Практика: Go-сервіс, який підключається до Ollama через ollama/api, читає знання з GitLab через go-gitlab, парсить PDF через ledongthuc/pdf, індексує фрагменти у векторному сховищі і відповідає на запити через Gin — це повноцінний бекенд збирання знань і пошуку без єдиного рядка
Python.

Rust потрібен там, де важливі безпека пам’яті, продуктивність, робота на краю мережі (edge) та embedded-пристроях, парсери, локальні агенти, обробка потоків і компоненти з високою надійністю. Прикладів із царини правил на Rust уже чимало: gorules/zen (Zen Engine) — рушій рішень у стилі DMN із ядром на Rust і прив’язками до C, Go, Node.js та Python; KSD-CO/rust-rule-engine — швидкий рушій правил із Rete, зворотним виведенням і тією ж мовою GRL, що й у grule. Поруч існує розсип невеликих навчальних реалізацій класичних експертних систем. Практично: Rust має сенс на межі системи — там, де збирання знань торкається пристроїв, телеметрії або критичних локальних компонентів.

Julia варта уваги для математичного ядра: оптимізація, диференціальні рівняння, симуляція, наукові моделі. Окремих експертних систем на Julia практично немає — це не її ніша; зате екосистема дає сильні будівельні блоки для математичного шару — наприклад, JuMP для математичної оптимізації та Catlab.jl для прикладної теорії категорій і формального подання знань. Практично: Julia рідко буває мовою для всієї системи в продакшні, але це сильний інструмент для науково-дослідницьких модулів.

TypeScript потрібен для інтерфейсів: графи простежуваності, досьє рішень, робочі місця експертів, панелі якості, черги перегляду. Але не лише інтерфейси: у світі JavaScript/TypeScript живуть і легкі рушії правил — cachecontrol/json-rules-engine задає правила як JSON, а GoRules дає візуальний редактор таблиць рішень поверх згаданого вище Zen Engine. Практично: експертна система не повинна бути тільки чатом; часто її головна цінність відкривається через хороший інженерний інтерфейс.

Мої практичні рішення. Серверне ядро я свідомо звів до Go — одна універсальна мова, єдиний бінарник і проста експлуатація, тим більше що потрібні мені NATS та Ollama теж на Go, тож ядро й оточення розмовляють однією мовою буквально. Важкий чисельний шар (OpenVINO та інші бібліотеки на C/C++) викликаю з Go через cgo: формально продукт «не на C++», але без нього він би не рахував. Python відсунув туди, де він найсильніший — допоміжні скрипти й експерименти, а не рантайм; інтерфейси для інженерів винесені в окремий шар на Vue + TypeScript.

Мови запитів: факти читаються не лише через API


SQL лишається центральною мовою доказової системи. Транзакції, схеми, зовнішні ключі, обмеження цілісності, віконні функції, рекурсивні узагальнені табличні вирази (Common Table Expressions, CTE), тип JSONB, повнотекстовий пошук, розширення pgvector у PostgreSQL — це реальні механізми роботи з фактами. Практично: саме SQL тримає записи погоджень, версії, базові зрізи (baselines), докази, журнал аудиту й результати спрацьованих правил — тобто все те, на що потім спирається висновок системи.

Cypher, Gremlin і GQL (Graph Query Language, новий стандарт ISO для графових запитів) потрібні для графових питань: що зачіпає зміна, який шлях від вимоги до тесту, де розрив простежуваності, які вузли критичні. Це прямий інструмент для графового аналізу з попередньої статті. Практично: графова мова дозволяє питати про зв’язки природніше, ніж з’єднання (JOIN) на десять таблиць у SQL.

SPARQL потрібен для RDF і онтологій — це мова запиту до тих самих семантичних графів, про які йшлося в розділі про графи й онтології попередньої статті. Практично: якщо знання інтегруються між доменами, стандартами й організаціями, SPARQL дає формальну мову запиту до семантичної моделі, а не до конкретної таблиці.

Datalog, логічні предметні мови (Domain-Specific Languages, DSL) і мови запиту правил корисні для політик, виведення і перевірок — це практичне продовження логіки й правил із математичного розділу.
Практично: ними зручно описувати правила простежуваності, доступу, перевірки несуперечливості й похідні факти, які система виводить сама, а не отримує готовими.

Мої практичні рішення. Робочим конем у мене лишається SQL: локально SQLite з FTS5/BM25, а коли векторів стало забагато — PostgreSQL із pgvector. Окремої графової мови на кшталт Cypher свідомо не вводив, бо граф простежуваності живе в реляційних таблицях, а обхід зв’язків написаний кодом — питань, що реально переростають SQL-JOIN, ще не набралося. SPARQL і RDF не використовую зовсім: семантичний шар стане виправданим лише при інтеграції знань між стандартами й організаціями, а доти це зайва формальність, що сповільнювала б розробку.

Rule engines і decision tables

Класичні рушії правил — CLIPS, Jess, Drools, OpenRules, Easy Rules, NRules, Experta, durable_rules, json-rules-engine — закривають задачу виконання правил над фактами. Це прямий інструмент для логіки й Rete з попередньої статті. Вони різні за мовою, зрілістю і моделлю інтеграції, але ідея спільна: правило має жити не як випадковий if у коді, а як керований артефакт із власною версією, рецензуванням і тестами.

Практичне застосування: вхідні контролі релізу (release gates), перевірки відповідності вимогам, правила допуску, сортування звернень (triage), автоматичні блокери, перевірка повноти й маршрутизація на перегляд.

Окремо варто назвати DMN (Decision Model and Notation — нотація моделювання рішень) і FEEL (Friendly Enough Expression Language — мова виразів, якою описують логіку всередині DMN-таблиць). DMN описує рішення через таблиці рішень, діаграми вимог до рішень і формальні вирази; FEEL — це сама мова цих виразів. Практично: DMN дуже корисний там, де рішення мають бути зрозумілі не лише програмісту, а й бізнесу, відповідальному за нормативну відповідність або системному інженеру. Наприклад, матриця «який рівень перегляду потрібен для зміни» часто краще живе як DMN-таблиця, ніж як код.

Мої практичні рішення. Готовий рушій я не брав, а написав власне ядро правил на Go з прямим і зворотним виведенням і чергою активацій (по суті спрощений Rete). Вирішальним був не сам рушій, а дисципліна: кожне правило — окремий артефакт із життєвим циклом чернетка → затверджене → виведене з обігу, версією і перевіркою коректності, а не захована в коді умова. DMN-шару й візуального редактора для доменних експертів свідомо ще немає — вони стануть виправданими, коли правила почнуть активно обговорювати люди поза командою розробки.

Семантичний веб, онтології і машини виведення (reasoners)


RDF, OWL, SPARQL, SHACL, ShEx, SKOS — це не музейні технології. Це інструменти для формальної роботи зі смислом, типами і допустимими зв’язками — пряме продовження розділу про графи й онтології з попередньої статті, тільки вже у вигляді конкретних стандартів і форматів. RDF (Resource Description Framework — модель опису ресурсів) описує знання трійками: суб’єкт, предикат, об’єкт. Практично: це зручно для інтеграції різних джерел, де одна сутність може приходити з кількох систем.

OWL (Web Ontology Language — мова онтологій вебу) додає онтологічну логіку: класи, властивості, обмеження, наслідування. Це той самий формальний шар виведення, що в попередній статті стояв поруч із логікою і правилами. Практично: OWL допомагає виводити нові факти і знаходити суперечності в доменній моделі.

SHACL (Shapes Constraint Language — мова обмежень на форму графа) і ShEx (Shape Expressions — вирази форми) потрібні для перевірки графів на коректність. Якщо OWL більше про логічні наслідки, то SHACL ближчий до практичного питання «чи має кожна вимога безпеки відповідального, метод верифікації і посилання на доказ». Практично: SHACL може бути контролем якості (quality gate) для графа знань.

SKOS (Simple Knowledge Organization System — проста система організації знань) потрібен для таксономій, контрольованих словників, синонімів і класифікацій. Практично: SKOS добре підходить для глосаріїв, доменних словників, зіставлення абревіатур, термінів стандартів і назв компонентів.

Інструменти: Protégé для моделювання онтологій; Apache Jena і RDF4J для JVM-стека; GraphDB, Stardog, RDFox як промислові RDF-сховища; HermiT, Pellet, ELK як OWL-машини виведення. Практично: цей стек має сенс, якщо вам потрібна формальна семантика, сумісність між системами і перевірка знань, а не просто граф для навігації.

Мої практичні рішення. Цього шару в мене немає зовсім, і це свідомо: те, що формально дали б OWL чи SHACL — виведення фактів і перевірку несуперечливості — уже закриває власний рушій правил, тільки іншим інструментом. Повноцінна онтологія коштувала б більше, ніж дає, доки знання не інтегрують між організаціями з різними схемами. Найближчим кандидатом бачу не повний RDF, а контрольований словник у стилі SKOS — зіставлення синонімів і термінів стандартів знадобиться раніше за все інше.

Реляційні, документні, графові, пошукові і векторні сховища

Тут варто зробити перемикання. Досі йшлося про мови — чим саме формулювати запит до фактів. Цей розділ уже про інше: про самі сховища і їхнє призначення — де які знання фізично живуть і яку задачу кожен тип бази закриває найкраще. Одна й та сама мова (наприклад, SQL) може обслуговувати кілька різних сховищ, тож далі я групую не за синтаксисом, а за роллю в системі.

Причина, чому сховищ так багато, теж не в моді. Дані мають різну математичну форму. Таблиця добре тримає факти і обмеження цілісності: хто, коли, що затвердив. Граф добре тримає досяжність і ланцюги залежностей: що з чим пов’язано. Пошуковий індекс добре тримає слова і частоти. Векторне сховище добре тримає точки в багатовимірному просторі й шукає найближчих сусідів. Часовий ряд добре тримає функцію від часу. Коли це розкласти так, вибір бази даних перестає бути питанням смаку: ви просто дивитеся, яку форму має знання.

PostgreSQL, Microsoft SQL Server, Oracle, MySQL потрібні для транзакційних фактів: хто затвердив, коли, яку версію, з яким статусом. Для експертної системи це місце, де живе доказова основа: статуси вимог і їхні базові зрізи, погодження і відхилення, журнал аудиту висновків, результати спрацьованих правил.

Практичний приклад: коли система каже «реліз заблоковано», саме реляційна база має показати, який факт, якої версії і за яким правилом дав цей висновок — і витримати це під час перевірки роками згодом.

MongoDB, Couchbase та інші документні бази корисні для гнучких артефактів зі змінною структурою, які важко звести до однієї схеми. Для бази знань це, наприклад, імпортовані профілі компонентів від різних постачальників, де набір полів щоразу інший, або сирі вимоги з різних інструментів до нормалізації. Практичний приклад: листи якості від десятка вендорів, у яких немає двох однакових форматів, простіше прийняти як документи, а структуру наводити вже на етапі видобування фактів. Застереження: без схемної дисципліни документне сховище швидко стає смітником, куди складають усе підряд.

Neo4j, JanusGraph, TigerGraph, ArangoDB, Amazon Neptune потрібні там, де головна цінність — самі зв’язки. Для експертної системи це класична простежуваність: вимога → архітектурне рішення → тест → дефект → доказ.
Практичний приклад: питання «що зламається, якщо змінити цю вимогу» або «які вимоги безпеки лишилися без доказу» — це обхід графа на кілька рівнів углиб, який у реляційній моделі перетворюється на десяток вкладених з’єднань, а в графі читається природно. Саме граф матеріалізує той аналіз впливу змін і ланцюги доказів, про які йшлося в математичному розділі попередньої статті.

OpenSearch, Elasticsearch, Solr і Lucene потрібні для повнотекстового пошуку, фасетної навігації, пошуку за ідентифікаторами і підсвічування збігів. Для бази знань це шар, який знаходить потрібний фрагмент документа за словами користувача, коли той не знає точної назви. Практичний приклад: інженер шукає «перегрів роз’єму при вібрації», а система має знайти відповідні пункти стандарту, звіти про дефекти й рішення, навіть якщо там ужиті інші формулювання. Важливо: пошуковий індекс дає кандидатів і ранжування, але не є джерелом транзакційної істини — знайдене ще треба зіставити з фактами.

FAISS, HNSWlib, Annoy, ScaNN, pgvector, Qdrant, Milvus, Weaviate, Pinecone потрібні для векторного (семантичного) пошуку — пошуку за змістом, а не за словами. Для експертної системи це основа етапу пошуку в RAG: знайти фрагменти знань, близькі за смислом до запиту, навіть коли немає жодного спільного слова. Практичний приклад: запит «чи можна цей компонент у медичному виробі» має підняти релевантні вимоги і кейси, описані зовсім іншою термінологією. Критичне застереження: результат вектора — це кандидат на джерело, а не доказ. Без походження даних (provenance), контролю доступу і зафіксованої версії ембедера векторний індекс небезпечний, бо впевнено повертає схоже замість правильного.

TimescaleDB, InfluxDB, Prometheus із мовою запитів PromQL (Prometheus Query Language) потрібні для часових рядів — даних, у яких головне вимір часу. Для експертної системи це не стільки про метрики самого сервера, скільки про знання, що змінюється в часі: тренд частоти дефектів по компоненту, динаміка покриття вимог тестами від релізу до релізу, деградація якості пошуку після зміни ембедера. Практичний приклад: правило «ризик зростає, якщо частота критичних дефектів у вузлі збільшується три релізи поспіль» потребує саме часового ряду, а не одного поточного значення — інакше система бачить лише зріз, а не тенденцію.

Мої практичні рішення. Набір сховищ я тримаю навмисно скромним: транзакційну основу й документні артефакти веде одна реляційна база — локально SQLite, а векторний і ключовий індекс переїхали в PostgreSQL із pgvector, єдине авторитетне векторне сховище. Окрему графову базу, MongoDB і сховище часових рядів свідомо не заводив: граф живе в реляційних таблицях, напівструктуроване — у JSONB-полях, тренди — у тих самих таблицях. Кожен новий тип сховища додаю тоді, коли наявний перестає справлятися, а не «щоб було».

Ймовірнісні, оптимізаційні і планувальні бібліотеки

Цей клас інструментів матеріалізує три математичні розділи з попередньої статті: роботу з невизначеністю, оптимізацію і планування. Об’єднує їх те, що відповідь тут не «знайти факт», а «порахувати найкраще рішення за умов».

Для ймовірнісного програмування корисні PyMC, Stan, Pyro, NumPyro, TensorFlow Probability. Для експертної системи вони потрібні там, де відповідь має чесно нести міру невпевненості, а не ховати її в одному числі. Практичний приклад: оцінка «наскільки ми впевнені, що компонент відповідає вимозі, маючи три непрямі докази і один суперечливий» — це апостеріорний розподіл, а не бінарне «так/ні». Так система може сказати «ймовірно відповідає, але доказова база слабка» замість оманливо впевненого вердикту.

Для ймовірнісних графових моделей (probabilistic graphical models) дивляться на pgmpy, pomegranate, hmmlearn. Це інструменти для байєсівських мереж, прихованих марковських моделей (Hidden Markov Models, HMM) і діагностики — тих самих, що в математичному розділі відповідали за поширення підозр причинами. Практичний приклад: один симптом — «тест періодично падає» — може мати кілька причин (дефект коду, нестабільний стенд, помилка в самому тесті); байєсівська мережа дає контрольований спосіб оновлювати ймовірність кожної причини в міру надходження нових свідчень, замість здогадок.

Для оптимізації: Google OR-Tools, SciPy optimize, CVXPY, Pyomo, JuMP, комерційні розв’язувачі на кшталт Gurobi або CPLEX. Для бази знань це задачі вибору найкращого варіанта під обмеженнями. Практичний приклад: вибрати мінімальний набір тестів, що покриває всі змінені вимоги в межах бюджету часу, — класична задача оптимізації, а не ручний відбір «на око». Так само сюди лягає планування дефіцитних ресурсів: стендів, ліцензій, людей.

Для розв’язання задач з обмеженнями (constraint solving): MiniZinc, Choco, OR-Tools CP-SAT, OptaPlanner/Timefold. Це випадок, коли рішення мусить одночасно задовольнити десятки жорстких правил. Практичний приклад: скласти план верифікації, де певні тести вимагають конкретного обладнання, не можуть іти паралельно, мають передумови й дедлайни, — людині вручну це майже не під силу, а розв’язувач обмежень знаходить припустимий розклад або доводить, що його немає. Окремо зазначу, що саме ці засоби я розглядаю як кандидатів для планування ієрархічної структури робіт (Work Breakdown Structure, WBS) у проєктній і програмній діяльності: декомпозиція робіт із залежностями, ресурсними обмеженнями і вікнами часу — це та сама задача з обмеженнями, тільки в управлінському, а не суто верифікаційному контексті.

Для планування дій штучного інтелекту (AI planning): інструменти на основі мови опису задач планування (Planning Domain Definition Language, PDDL), Fast Downward, планувальники на ієрархічних мережах задач (Hierarchical Task Network, HTN). Для експертної системи це шар, який перетворює діагноз на план дій. Практичний приклад: висновок «бракує доказу для вимоги безпеки» має стати послідовністю кроків — «запусти ось ці тести, збери результати, запроси погодження відповідального, онови оцінку ризику», — і саме планувальник будує таку послідовність із наявних дій і їхніх передумов.

Пошук із доповненням генерації (RAG), оркестрація і оцінювання

Навколо пошуку з доповненою генерацією (Retrieval-Augmented Generation, RAG) виросла ціла екосистема фреймворків — найвідоміші LangChain і LlamaIndex. Вони прискорюють склеювання пошуку, шаблонів промптів та інструментів у єдиний робочий процес. Тему RAG я тут навмисно тримаю стримано: вона зараз модна й гаряча, тож важливіше не перелічити всі бібліотеки, а зафіксувати одну думку — оркестраційний фреймворк керує послідовністю викликів, але сам по собі не є джерелом доказовості, і плутати його з експертною системою не варто.

Окремий клас — інструменти оцінювання якості RAG і поведінки моделі (наприклад, Ragas чи promptfoo). Практично: вони допомагають міряти якість пошуку, обґрунтованість відповіді джерелами (groundedness) і частоту вигадок (hallucination rate). Для бази знань це не косметика: саме оцінювання відрізняє доказовий пошук від правдоподібного. Практичний приклад: новий ембедер може підняти середній бал, але водночас просісти на домені функціональної безпеки — без вимірювання по доменах цього не видно, і система почне впевнено помилятися саме там, де ціна помилки найвища.

Головне правило, яке я виніс із попередньої статті: RAG без еталонного набору (gold set), без походження джерел, без контролю доступу і без політики відмови (refusal policy, коли система чесно каже «доказів недостатньо») — це пошук із красивим інтерфейсом. RAG з оцінюванням і журналом аудиту вже може бути корисним компонентом доказової системи. Сам по собі RAG доказовості не дає — він лише доставляє контекст, а відповідальність за істинність лишається на шарах фактів, правил і політик.

Мої практичні рішення. Жодного з цих фреймворків я не брав — конвеєр пошуку зібраний нативно на Go: коли ядро вже на Go, тягнути окремий Python-оркестратор заради склеювання викликів сенсу мало. Пошук гібридний (щільні вектори плюс FTS5/BM25) з переранжуванням кандидатів і обов’язковими посиланнями на джерела. Найціннішим вважаю політику відмови з кодами причин і власний інструмент перевірки якості пошуку на доменному еталонному наборі — їх усе одно довелося б робити руками.

Обслуговування і локальне виконання моделей

ONNX Runtime, OpenVINO, TensorRT, TVM, TFLite, Core ML, DirectML потрібні для виконання моделей (inference) у різних середовищах. Практично: вони дозволяють запускати класифікатори, ембедери, оптичне розпізнавання тексту (Optical Character Recognition, OCR), розпізнавання іменованих сутностей (Named Entity Recognition, NER) або менші мовні моделі локально, контрольовано і швидко.

Для обслуговування великих мовних моделей важливі vLLM, llama.cpp, Ollama, Hugging Face Text Generation Inference, NVIDIA Triton Inference Server, KServe, BentoML, Ray Serve. Практично: це відповідає на питання, як саме обслуговувати модель — локально, на власному залізі (on-prem), у Kubernetes, на GPU чи CPU, із пакетною обробкою запитів (batching), з реєстром моделей і обмеженнями на затримку.

Якщо зняти маркетинг, модель під час виконання робить дуже багато простих числових кроків: множить матриці, додає вектори, нормалізує числа, рахує увагу між токенами і щоразу вибирає найімовірніше продовження. Саме тому рантайми існують не як «обгортки навколо AI», а як перекладачі між математикою моделі й конкретним залізом. Один і той самий ембедер можна запустити на CPU, GPU або NPU, але ефективність буде різною, бо схема обчислення різна: універсальна послідовність інструкцій, масовий паралелізм або спеціалізовані матричні блоки. Для доказової системи це важливо не лише через швидкість: різні рантайми, квантизації й токенізатори можуть трохи змінювати числову поведінку, а отже їх треба фіксувати як частину доказу.

Три інструменти заслуговують окремого опису, бо з’являються в будь-якій дискусії про локальне виконання великих мовних моделей.

llama.cpp — бібліотека і рантайм на C/C++ для виконання GGUF-моделей на CPU і GPU без Python та CUDA як обов’язкової залежності. Підтримує Apple Metal, CUDA, Vulkan, OpenCL, AVX2/AVX-512. Дає дуже низьке споживання пам’яті завдяки агресивній квантизації (Q2—Q8, IQ-формати). Має вбудований HTTP-сервер з OpenAI-сумісним інтерфейсом. Ліцензія: MIT — повністю вільне використання, включно з комерційним. llama.cpp — найнижчий рівень стеку: якщо потрібен максимальний контроль над пам’яттю, виконання на CPU на краю мережі (edge) або вбудовування моделі у власний бінарний файл, це найкращий вибір.

Ollama — надбудова над llama.cpp (і частково над іншими рантаймами) із зручним CLI, REST API, реєстром моделей у форматі Modelfile і автоматичним управлінням ваговими файлами. Написаний на Go. Підтримує CUDA, Metal, ROCm. Надає OpenAI-сумісний інтерфейс /api/chat, /api/generate, /api/embeddings. Ліцензія: MIT для сервера; окремі моделі мають власні ліцензії (Llama, Mistral, Gemma тощо). Ollama — правильний вибір для локального розгортання на робочій станції або сервері, де важлива простота операцій і не потрібен Kubernetes.

vLLM — бібліотека і сервер на Python для високопродуктивного виконання великих мовних моделей на GPU. Ключова інновація — PagedAttention: ефективне управління кешем ключів і значень (KV-cache), що дозволяє обслуговувати набагато більше паралельних запитів на одному GPU, ніж проста реалізація. Підтримує CUDA, ROCm, TPU. Надає OpenAI-сумісний інтерфейс. Ліцензія: Apache 2.0. vLLM виправданий там, де є велике навантаження і GPU: спільний сервер для команди, промислова кінцева точка (endpoint) для кількох інстансів ES або пакетне виконання для масової обробки документів.

Практичне порівняння для R&D-контексту:

Критерійllama.cppOllamavLLM
Мова реалізаціїC/C++Go + llama.cppPython
Основний режимCPU + GPUCPU + GPU, desktopGPU, high-throughput
Складність розгортаннянизьканайнижчасередня
Throughput при навантаженніпомірнийпомірнийнайвищий
ЛіцензіяMITMITApache 2.0
OpenAI-compatible APIвбудованийвбудованийвбудований
Edge / embeddedтакніні

Для експертної системи обслуговування моделей не є центром. Але воно критичне для відтворюваності: треба знати версію моделі, токенізатор, параметри генерації, квантизацію, рантайм і цільове залізо — інакше той самий запит завтра дасть іншу відповідь, і доказовість розсиплеться.

Мої практичні рішення. Велику генерацію я не обслуговую сам, а делегую локальному рантайму Ollama через OpenAI-сумісний інтерфейс. А ось ембединги (саме вони — гаряча точка, бо їх рахуються мільйони) я нещодавно перевів на прямий шлях через OpenVINO: окремий тонкий Go-SDK обгортає рантайм OpenVINO і рахує вектори нативно в процесі експертної системи, з автоматичним вибором пристрою за принципом «спершу NPU, далі GPU, потім CPU». До цього той самий шлях ішов через ONNX Runtime; я свідомо звів усе до одного бекенда, щоб не тримати два майже однакові кодові шляхи й мати єдину, передбачувану числову поведінку. Те, як саме цей рантайм вбудовується в загальну архітектуру — оркестрація, маршрутизація моделей, межа між «розумним клієнтом» і сервером виведення, — це вже тема наступної, архітектурної статті.

Оптичне розпізнавання, парсинг і документне ingestion

Збирання знань (knowledge acquisition) часто ламається не на моделі, а на вході: PDF, скани, Word, таблиці, діаграми, листи постачальників, старі архіви. Тут є сенс використати Apache Tika для видобування тексту з різних форматів і Tesseract для розпізнавання сканів, а за потреби — хмарне розпізнавання тексту на кшталт AWS Textract або Azure Document Intelligence.

Практичне застосування: перетворити документи на придатні до пошуку фрагменти (chunks), зберігши структуру — таблиці, заголовки, сторінки, якорі, мову, якість розпізнавання, версію джерела. Якщо вхідний конвеєр зіпсував текст, навіть найкращий пошук знайде лише зіпсований текст.

Окремо важливі парсери коду, Markdown, експортів вимог, формату обміну вимогами ReqIF (Requirements Interchange Format), журналів і тестових звітів. Експертна система має бачити не просто «текст», а тип артефакту: вимога, тест, дефект, рішення, таблиця, код, трасування стека, вимірювання.

Мої практичні рішення. Цей шар один із найбільш «вистражданих», бо саме на збиранні знань губиться найбільше якості. Конвеєр нативний на Go: він нормалізує різні формати у фрагменти зі збереженням структури й типу артефакту, а для коду використовує структурний парсинг через tree-sitter, щоб бачити функції й типи, а не рядки. Відкритий фронт — не нові парсери, а наскрізна простежуваність походження, щоб кожен фрагмент ніс посилання на джерело, версію й спосіб видобування; це більше дисципліна метаданих, ніж новий інструмент.

Інженерні стандарти інтеграції знань

Для дослідно-конструкторської роботи (R&D) важливі не лише AI-інструменти. Часто знання вже живе у стандартизованих форматах, і їх є сенс приймати як є, а не переписувати вручну.

OSLC (Open Services for Lifecycle Collaboration — відкритий стандарт зв’язування артефактів життєвого циклу) допомагає пов’язувати артефакти управління життєвим циклом застосунків і виробів: вимоги, тести, зміни, дефекти. Практично: це шлях до простежуваності між інструментами без ручного копіювання.
ReqIF (Requirements Interchange Format — формат обміну вимогами) застосовують для обміну вимогами між системами і постачальниками. Практично: якщо вимоги приходять як ReqIF, їх варто імпортувати зі збереженням ідентифікаторів, атрибутів, зв’язків і базового зрізу (baseline).

SysML (Systems Modeling Language — мова системного моделювання) описує систему як модель. Практично: блоки, інтерфейси, стани і вимоги з такої моделі можуть стати частиною графа знань.
FMI (Functional Mock-up Interface — стандарт обміну імітаційними моделями) і підходи цифрових двійників (digital twin) корисні там, де доказ приходить із симуляцій. Практично: експертна система може пов’язувати вимогу не лише з тестом, а й з результатом симуляції та моделлю середовища.

Найчастіше знання доводиться витягувати не з абстрактних стандартів, а з конкретних робочих інструментів, і кожен має свій формат та спосіб експорту. Polarion ALM віддає вимоги через експорт у ReqIF і власний REST API, тож звідти варто брати дані зі збереженням ідентифікаторів, зв’язків і базового зрізу. Jira має REST API і вивантаження в CSV для задач, дефектів і статусів. GitLab і GitHub надають REST і GraphQL API (а також події через вебхуки) для звернень (issues), запитів на злиття (merge/pull requests), оглядів коду й конвеєрів CI/CD. Для бази знань саме ці інтерфейси — основне джерело «живих» артефактів інженерії, тож проєктувати збирання знань варто насамперед під них, а не під ідеальний стандарт.

Мої практичні рішення. Стандарти я підключаю «за потребою джерела», а не списком. Найдалі просунуті вимоги: ReqIF підтримую, бо саме в ньому вони приходять із Polarion ALM, а дані з GitLab і GitHub тягну через їхні API як найжвавіше джерело артефактів. Простежуваність тримаю у власній типізованій моделі, а не через формальний OSLC; SysML і FMI поки поза обсягом — системних і симуляційних доказів у конвеєр ще не надходить.

Оболонки експертних систем і середовища з малим обсягом коду

Історично експертні системи часто будували не з нуля, а в оболонках (expert-system shells): CLIPS, Jess, KEE, ART, а пізніше — платформи правил і рішень. Сучасний аналог — середовища з малим обсягом коду (low-code): візуальні платформи правил, редактори DMN-таблиць, редактори онтологій, рушії робочих процесів, інструменти створення і ведення політик.

Практична користь оболонок не в тому, що вони «магічно роблять штучний інтелект». Вони дають доменним експертам місце, де можна бачити і змінювати правила без переписування серверного коду. Для дослідно-конструкторської роботи це буває критично: інженер із функційної безпеки має переглядати вхідні контролі безпеки, відповідальний за нормативну відповідність — матрицю політик, відповідальний за вимоги — перевірки якості.

Ризик теж очевидний. Якщо шар із малим обсягом коду не має контролю версій, рецензування, тестів і журналу аудиту, він перетворюється на темну кімнату, де правила змінюються швидше, ніж команда встигає зрозуміти наслідки. Тому навіть «безкодові» правила в доказовій системі мають проходити той самий життєвий цикл: чернетка, рецензування, тест, затвердження, публікація, виведення з обігу.

Практичне застосування: оболонки і середовища з малим обсягом коду корисні для створення, ведення і рецензування правил, але виконання в продакшні має бути контрольованим. Інакше система стане зручною, але недоказовою.


Мої практичні рішення. Готової оболонки чи платформи з малим обсягом коду (low-code) в мене немає — правила живуть у власному ядрі на Go, а їх створення і ведення йде через CLI та API. Життєвий цикл, перевірку коректності й «сухий прогін» я зробив дисципліною самого рушія, а не запозичив із зовнішньої оболонки. Чого ця конструкція не дає — місця, де доменний експерт поза командою міг би сам змінити правило; тримаю це питання відкритим, доки правил не стане достатньо багато для активного обговорення.

Практична мапа 15 класів технологій

Великий список назв легко створює ілюзію, що треба взяти все. Не треба. Варто починати від питань.

Якщо проблема у правилах — рушій правил або DMN. Якщо у зв’язках — графова база або RDF. Якщо у тексті — пошуковий рушій і збирання знань. Якщо у схожості — векторний індекс. Якщо у доступі — рушій політик. Якщо у походженні даних — інструменти родоводу. Якщо у невизначеності — ймовірнісний шар. Якщо у плануванні — оптимізатор або планувальник.

Добра архітектура не та, де найбільше інструментів. Добра архітектура та, де кожен інструмент має чітку математичну і доменну причину існування.

Щоб не лишати цей огляд на рівні «ось багато назв», зведу його до практичної мапи. Це спосіб швидко зрозуміти, який клас технологій відповідає за який біль експертної системи.

1. Рушії правил і Rete-сімейство. Потрібні, коли рішення має бути відтворюваним і пояснюваним: вхідний контроль релізу, блокер нормативної відповідності, правило допуску, контрольний список безпеки. Практичний сигнал: якщо команда сперечається, чому щось заблоковано, правило має бути окремим артефактом, а не умовою в коді.

2. Підтримання істинності (TMS). Потрібне, коли висновки залежать від змінних припущень. Практичний сигнал: якщо відкликання дозволу-винятку (waiver) має автоматично відкликати готовність до релізу, вам потрібна логіка TMS, навіть якщо реалізована вона через граф і події.

3. Коефіцієнти впевненості і керування певністю. Потрібні, коли відповідь не бінарна. Практичний сигнал: якщо експерт каже «схоже, але не достатньо доказів», система має вміти зберегти цю градацію, а не перетворювати її на «так/ні».

4. DMN і таблиці рішень. Потрібні, коли правила мають читати і погоджувати не лише розробники. Практичний сигнал: якщо відповідальний за нормативну відповідність, керівник проєкту або системний інженер мають розуміти логіку рішення, таблиця рішень часто краща за код.

5. Семантичний веб: RDF, OWL, SHACL, SKOS. Потрібні, коли важлива формальна модель домену. Практичний сигнал: якщо треба перевірити, що всі вимоги безпеки мають доказ і відповідального, SHACL ближче до задачі, ніж черговий запит до моделі.

6. Ймовірнісні графові моделі. Потрібні для діагностики і ризиків. Практичний сигнал: якщо падіння тесту є симптомом кількох можливих причин, байєсівська мережа або прихована марковська модель (HMM) дають контрольований спосіб оновити підозри.

7. Міркування за випадками (CBR). Потрібне там, де минулий досвід цінніший за абстрактне правило. Практичний сигнал: якщо команда повторює ті самі помилки, варто структурувати випадки, а не писати ще один PDF із засвоєними уроками.

8. Оптимізація і розв’язання задач з обмеженнями. Потрібні для розкладів, ресурсів, портфелів і тестових планів. Практичний сигнал: якщо вручну складають план, який має десятки обмежень, це вже задача для розв’язувача (solver).

9. Планування дій (AI planning). Потрібне, коли система має запропонувати послідовність дій. Практичний сигнал: якщо відповідь «бракує доказів» має перетворитися на план «запусти ці тести, запроси це погодження, онови цю оцінку ризику», потрібен шар планування.

10. Повнотекстовий і векторний пошук. Потрібні, коли знання розкидані по документах. Практичний сигнал: якщо користувач не знає точну назву документа, але описує проблему людською мовою, потрібен гібридний пошук.

11. Пояснюваний ШІ (XAI) і моніторинг моделей. Потрібні, коли рішення моделі впливає на інженерний процес. Практичний сигнал: якщо оцінка ризику змінює пріоритет роботи, треба пояснити, що на неї вплинуло, і перевірити калібрування.

12. Обслуговування моделей. Потрібне, коли модель стає компонентом продакшну. Практичний сигнал: якщо модель має угоду про рівень обслуговування (SLA), політику доступу, версію і відкат, це вже не блокнот для експериментів, а сервіс.

13. Розпізнавання тексту, парсинг і збирання знань. Потрібні, коли джерела не є чистим текстом. Практичний сигнал: якщо знання приходить у PDF, сканах, таблицях, ReqIF або тестових звітах, збирання стає частиною доказовості.

14. Рушії політик доступу. Потрібні, коли є конфіденційність, межі замовника або експортні обмеження. Практичний сигнал: якщо документ не можна показати всім, то його фрагменти, вектори й зведення теж не можна показати всім.

15. Походження, родовід даних та інженерні стандарти. Потрібні, коли треба відтворити відповідь. Практичний сигнал: якщо аудитор питає «звідки це взялося», система має показати джерело, парсер, спосіб розбиття на фрагменти, ембедер, пошук, модель, правило і рішення політики доступу.

Де ці класи розгорнуті детально. Ця стаття фокусується на класах із власними секціями вище: мови програмування, мови запитів, рушії правил і таблиці рішень (пп. 1, 4), семантичний веб (п. 5), сховища і пошук (п. 10), оптимізація і планування (пп. 8, 9), RAG, обслуговування моделей (п. 12), розпізнавання і збирання знань (п. 13), рушії політик доступу (п. 14) і походження даних (п. 15). Математичну основу решти класів — підтримання істинності (п. 2), коефіцієнти впевненості (п. 3), ймовірнісні графові моделі (п. 6), міркування за випадками (п. 7) і пояснюваний ШІ (п. 11) — я розглянув у попередній статті про прикладну математику, а їх місце в архітектурі — у наступній.

Ця мапа також показує, чому «взяти LlamaIndex плюс векторну базу» не є архітектурою експертної системи. Це закриває один або два класи задач із п’ятнадцяти. Решта все одно повернеться у вигляді доступу, походження даних, правил, життєвого циклу, оцінювання і людського перегляду.

Три уроки технологічного шару

Урок 1. Інструмент обирають під клас задачі, а не навпаки

Це головна теза статті: кожен інструмент тут закриває конкретний математичний клас задач із попередньої. Вибір, що починається з назви («візьмімо LlamaIndex», «поставмо Neo4j»), а не з питання «який клас задачі я закриваю», майже завжди веде до стека, де половина можливостей не потрібна, а потрібного бракує.

Практичний приклад: команда ставить графову базу, бо «у нас є зв’язки», а реально питань, що переростають SQL-JOIN, у неї одиниці — і виходить окремий сервіс під задачу, яку закривала б рекурсивна CTE. І навпаки: пошук «за схожістю» намагаються робити повнотекстовим індексом, бо він уже є, доки не впираються в те, що це задача векторного, а не лексичного класу.

Практичний висновок: спершу назвіть клас задачі (правила, зв’язки, схожість, невизначеність, планування), і лише тоді — інструмент. Інструмент без свого класу задачі — це мода, а не вибір.

Урок 2. Фреймворк не компенсує відсутність моделі знань

Можна зібрати RAG на модному стеку за тиждень. Але якщо документи не мають статусів, відповідальних, базових зрізів (baselines) і позначок доступу, фреймворк лише швидше підніме на поверхню хаос: система впевнено відповідатиме з чернеток, прострочених версій і документів, які користувачу взагалі не можна показувати.

Практичний висновок: перед вибором оркестраційної бібліотеки треба описати доменну модель, життєвий цикл і джерела істини. Жоден фреймворк не додасть доказовості тому, що її не має на рівні даних.

Урок 3. Інструмент треба оцінювати за тим, як він ламається

При виборі технології питання не лише «що ця бібліотека робить добре», а й «як вона поводиться, коли щось іде не так». Чи видно помилку збирання знань, чи можна відтворити той самий пошук завтра, чи зберігається версія моделі, чи поважає інструмент контроль доступу, чи можна провести аудит.

Практичний приклад: непомітно оновили модель-ембедер — середній бал пошуку навіть підріс, але вчорашня відповідь на той самий запит сьогодні не відтворюється, бо ніхто не зафіксував версію моделі. Інструмент «працював добре» на демонстрації й тихо зламав доказовість у продакшні.

Практичний висновок: у доказовій системі зріла поведінка при збоях і відтворюваність важливіші за красиву демонстрацію — і це повноцінний критерій вибору, а не другорядна деталь.

Висновок

Технологічний стек експертної системи — це не список покупок. Це спосіб матеріалізувати прикладну математику: правила виконує рушій правил, онтології живуть у RDF/OWL, графи — у графовому сховищі, невизначеність — у ймовірнісному шарі, планування — у розв’язувачах, текст — у пошуку і збиранні знань, схожість — у векторному індексі, доступ — у рушії політик, походження даних — у шарі родоводу.

У цій логіці стає зрозуміло, чому технології розвивалися саме так. Не тому, що хтось вирішив ускладнити життя інженерам, а тому, що різні задачі мають різну форму: булева логіка просить правила, залежності просять граф, невизначеність просить ймовірність, пошук за змістом просить вектори, а великі мовні моделі просять апаратуру, здатну швидко рахувати лінійну алгебру. Коли це видно, назви інструментів перестають бути шумом: за кожною з них стоїть відповідь на питання «яку роботу ми хочемо доручити машині».

Мовна модель займає важливе, але не центральне місце. Вона допомагає працювати з мовою, узагальненням, діалогом і чернетками. Доказовість народжується в інших шарах: факти, правила, версії, джерела, зв’язки, політики і аудит.

Спойлер наступної статті. Ця стаття відповіла на питання «що є і навіщо» — каталог класів інструментів і їхня математика. Наступна відповідає на «як це стає системою»: не новий список засобів, а те, як зібрати вже названі шари в одну доказову архітектуру — межі відповідальності, потоки знань, контракти між підсистемами, топології, залізо й життєвий цикл. Саме там знайдуть своє місце й наскрізні властивості системи — доступ, походження даних, контракти та калібрування якості.

Питання до читачів.
Стаття була про вибір засобів, тож і питання поставлю практичні — такі, що змушують перевірити власний стек на чесність. Згрупував їх за трьома релевантними ролями, бо рішення «що взяти» кожна з них зважує своєю міркою: розробник питає «яка мова й бібліотека», інженер зі знань — «де фізично живе знання і якою мовою його питати», а аналітик — «чи лишається висновок доказовим і відтворюваним». Цікаво почути ваші відповіді в коментарях.

Питання очима розробника-практика:

1. Спираючись на розділ про мови програмування як шари відповідальності: які шари своєї системи ви свідомо тримали б на одній універсальній мові (скажімо, Go для ядра й конекторів), а де готові викликати важкі C/C++ ядра через зовнішній інтерфейс функцій чи cgo — і де ця межа у вас уже розмилася?
2. Спираючись на «Мої практичні рішення» щодо рушія правил: ви взяли б готовий рушій (Drools, NRules, grule) чи написали б власне ядро правил — і за яким критерієм проходить ця межа «зробити vs узяти»?

Питання очима інженера зі знань і даних:

1. Спираючись на розділ про сховища і мови запитів: які з ваших питань насправді реляційні, які — графові, а які — векторні, і чи не закриваєте ви пошук «за схожістю» повнотекстовим індексом лише тому, що він уже є?
2. Спираючись на розділ про розпізнавання, парсинг і збирання знань: де у вашому вхідному конвеєрі (PDF, ReqIF, експорти трекерів) найчастіше губиться якість — і чи несе кожен фрагмент посилання на джерело, версію й спосіб видобування?

Питання очима системного аналітика доказовості:

1. Спираючись на розділ про RAG і оцінювання: чи є у вас еталонний набір (gold set) і політика відмови — чи RAG лишається «пошуком із красивим інтерфейсом», що впевнено відповідає з чернеток і прострочених версій?
2. Спираючись на Урок 3 «інструмент оцінюють за тим, як він ламається»: чи відтворите ви вчорашню відповідь після тихого оновлення ембедера — і що у вас зафіксовано як частину доказу: версія моделі, токенізатор, квантизація, рантайм?

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось4
До обраногоВ обраному2
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Ця стаття — чудовий системний каталог математичних абстракцій для верхнього рівня системи. Але давайте спустимося на рівень нижче — туди, де експертна система реально взаємодіє зі своїм шаром інференсу (Inference Layer), коли їй потрібно прорахувати мільйони ембедингів для RAG чи запустити локальну модель для класифікації ризиків.
Я як соло-розробник, який самотужки пилить децентралізовану open-source платформу для периферійного інференсу (FogAI), бачу, що саме на цьому стику зазвичай ловиться найболючіший хардверний оверхед. Поділюся тим, як вимоги до стабільності та латентності шару інференсу ламають наївний дизайн «на коліні»:
1. Пастка монолітного cgo проти ізоляції рантаймів
Автор пише, що викликає OpenVINO з Go через cgo прямо в одному процесі. Для лабораторії це ок. Але якщо цей інференс-рантайм є фундаментом для експертної системи — це міна уповільненої дії. Будь-який segfault у нативній C++ лібі OpenVINO моментально ховає весь ваш сервісний бекенд з усіма правилами та базами знань. Оскільки я пишу проєкт один, мені критично важливо мати ізоляцію, тому у себе на шарі інференсу я чітко розділив вузли:
Type A (In-Process JNI/Panama): Тільки для CRITICAL priority запитів (наприклад, sensor fusion для DSA) із жорстким SLA (0–10 мс). Нативні рушії (MNN/ONNX Runtime) вантажаться прямо в JVM-процес неблокуючого бекенду на Kotlin/Java 21 (Vert.x). Оверхед ~20—50 µs.
Type B (Out-of-Process gRPC): Для важких моделей та GPU-інференсу (TensorRT). Вони винесені в окремі нативні C++ сервіси. Впав важкий рантайм під навантаженням — експертне ядро цього навіть не помітить.
2. Реальний Zero-Copy Pipeline проти копіювання пам’яті
Коли експертна надбудова починає пачками згодовувати тексти шару інференсу для генерації векторів, ви миттєво впираєтеся в аллокації. Якщо ганяти байти з мережевого буфера шлюзу в пам’ять мови, а звідти копіювати в нативний тензор — слабке периферійне залізо (Edge/Fog) просто задихнеться.
Я у себе реалізував чесний Zero-Copy пайплайн на стику шлюзу та інференс-ядра:
HTTP Request (Netty off-heap) → nioBuffer() → DirectByteBuffer → JNI Pointer → Native Tensor (MNN/ONNX)
Через JNI передається лише покажчик. Нативний двигун будує свій Tensor view прямо поверх цього буфера. Жодного проміжного копіювання байтів у рантаймі.
3. Пріоритезація на рівні виконання, а не черг застосунку
Академічні Марковські процеси рішень (MDP/POMDP) для планування дій — це красиво. Але на рівні реального заліза інференс-шару потрібен жорсткий детермінізм. Замість математичного оверінжинірингу я впровадив у FogAI багаторівневі пріоритетні черги з плануванням Earliest Deadline First (EDF). Якщо від експертного ядра прилітає критичний сигнал від датчиків — шар інференсу просто витісняє (preempts) фоновий батчінг аналітики прямо на рівні виконання обчислювальних ядер.
Резюме: Високорівнева логіка експертної системи — це круто, але вона важить стільки, скільки важить її інференс-шар. Якщо ваш бекенд лягає від паніки в C++ лібі, а вектори копіюються в пам’яті туди-сюди — уся «експертність» помре на рівні заліза. Кому цікаво подивитися в мій погляд на побудову нативного інференсу без Python-оверхеду — код відкритий: github.com/NickZt/FogAi

Пане Ніколай, я погоджуюся з Вашим висновком про

чудовий системний каталог математичних абстракцій

Бо стаття навмисно створювалася абстрактною.
Ви, як практик, це легко помітили, і це нормально. Я роблю навмисну обфускацію, балансуючи між дозволеністю від NDA та технологічною зацікавленістю в контенті. Звісно, щось-десь не ідеально виходить. Щось більш практичне — буде далі.

1. Пастка монолітного cgo проти гібридної топології вузлів

Хороше зауваження! Так, зараз більш лабораторний підхід, можливості і умови.
Окремі процеси-воркери і комунікація через NATS вже закладені в роадмапі, зараз це на рівні goroutines.

2. Пекло копіювання пам’яті та реальний Zero-Copy Pipeline

Поки не з мережевого, а з файлового буфера. Згоден що буде залипати, і може бути фатальним на звичайних amd64
З ONNX зараз прощаюсь, відпрацювавши приямий OpenVINO

3. Реальне планування: Чому Earliest Deadline First (EDF) б’є академічний POMDP

Небагато про Марківські процеси, але згадати було варто.

Резюме: Мапа технологій у статті правильна, але диявол завжди в обробці пам’яті та ізоляції процесів.

Згоден. Ба більше, ви забігли дещо наперед до розгляду практичних рішень і їх оцінки, що я планую робити трохи пізніше. Тому що фахівців в цій галузі мало. А Ваш релевантний досвід буде дуже корисним надалі як у коментарях до моїх статей, так і у Ваших власних. Про що дякую наперед!

Ба більше, ви забігли дещо наперед до розгляду практичних рішень і їх оцінки

Сорян, не втерпів. Коли бачиш такий глибокий та рідкісний для нашого простору розбір, інженерний азарт просто бере гору, тому й забіг трохи наперед.
Момент із навмисною обфускацією через NDA повністю розумію в індустрії автомобільної електроніки та embedded-систем без цього ніяк.
Ваше рішення рухатися в бік окремих воркерів та NATS — абсолютно правильний вектор для забезпечення fault tolerance. Горутини на ранніх етапах дійсно дають швидкий старт, але в продакшні це міна уповільненої дії при першій же паніці нативних ліб.
Щодо фокусу на прямому OpenVINO — для зрізання абстракцій та оптимізації під конкретний кремній це логічний крок. Але, судячи з мого досвіду, через кілька специфічних граблів ви все одно прийдете до парадигми «нехай квітнуть усі квіти». У суворому R&D мультирушійність та збереження альтернатив (того ж ONNX Runtime чи MNN) критично необхідні хоча б як інструмент крос-верифікації. Коли після чергового апдейту драйверів або специфічної квантизації числова поведінка тензорів на NPU тихо «попливе», саме незалежний альтернативний рантайм виступить еталоном і врятує від тижнів нічного дебагу «привидів» у залізі.
Насправді диявол обробки пам’яті, ізоляції та верифікації числових результатів- це якраз те, що відрізняє працююче Edge/Fog рішення від лабораторного концепту. З величезним інтересом чекаю на вашу наступну, архітектурну статтю. Буде дуже цікаво подивитися, як плануєте розрулювати контракти та походження даних (provenance) між шарами. Успіхів!

Будьте обережні у Ваших очікуваннях, я наперед про це кажу. Я можу зовсім не розглянути саме Ваші гарячі проблеми, бо я з ними не спіткався. Мені не хочеться розчарувати експертів, навпаки, я намагаюся ініціювати нових авторів до створення власних цікавих статей, або до конструктивних коментарів існуючих статей, бо я теж хочу повчитися. Цікавих тем тут вистачить усім.

Зверніть увагу, що я намагаюсь надати моїм статтям навчально-пізнавальний контекст, і саме тому мої перші три статті були «довгою преамбулою» для розкриття проблем, а стаття про математичний апарат майже проігнорована коментаторами — ну бо вона не токсично-флеймова для загальної публіки, нема бажаючих згадувати «вишмат».

Пане Миколо, я вас умоляю! Взагалі-то, я й сам зазвичай беруся за код чи за перо суто заради інженерної цікавості щоб розібратися в чомусь новому, систематизувати досвід і повчитися у колег. Тому ваш навчально-пізнавальний підхід то саме те, чого катастрофічно бракує нашому медіапростору.
За ту вашу минулу статтю про вишмат... Ой, у мене було там шо написати! Але я тримав свою цінну думку глибоко в собі, щоб не душнити завчасно й не перевантажувати тред. :) Наприклад, шо той хвалений чистий POMDP у великих системах не живе від слова «зовсім» — чисто обчислювальний параліч. Наскільки ж приємніше і чистіше працювати з нормальним Марковським процесом (MDP). Тим більше, шоб ви розуміли, Марковські процеси мені викладав професор... на прізвище Марковський! І це було тоді, коли в нас ішли лаби по Прологу, і він вважався топчиком для рекомендаційних систем. Ну карма чистої води, де ви ще таке побачите?
Тож не переживайте про «невиправдані очікування». Те, що ви підіймаєте пласти реальної прикладної математики та логіки в архітектурі — ото вже величезний плюс і чудовий тригер для тих, хто в темі. Прям вайбом старих курсів Ng повіяло ) А те, шо народ на DOU проігнорував математику заради легкого флейму... Та де ви бачили, шоб люди добровільно згадували теорвер замість того, шоб просто покидатися капцями?
Короче, у наступних статтях стримуватися більше не буду.

Підписатись на коментарі