Головна › Архітектура корпоративної аналітики
Агентні міркування, консенсус і розгортання
Агент, що міркує над типізованою онтологією, а не над текстом; п'ять рівнів оркестрації інструментів; Raft, Paxos і CRDT для спільного стану; доставка в ізольовані та відрізані контури.
У цьому напрямі
Четвертий шар платформи — це там, де дані нарешті ухвалюють рішення. Але агент, який отримує текстові фрагменти, вигадує ідентифікатори; агент, який отримує типізовані об'єкти онтології, — ні. Цей модуль розбирає три речі, що визначають, чи виживе агентна система в продакшні: нейросимволічний поділ обов'язків між ймовірнісною моделлю та детермінованим рушієм онтології, інженерія поверхні інструментів та її оркестрація з бюджетом, і механіка спільного стану — коли потрібен Raft із кворумом, коли достатньо CRDT без координації, і що саме перетинає повітряний зазор під час доставки у відрізане середовище.
Агент над онтологією, а не над текстом
Типовий RAG-агент отримує фрагменти тексту і здогадується, про які сутності йдеться. Агент над онтологією отримує типізовані об'єкти зі стабільною ідентичністю, типізованими зв'язками та закритим переліком дій. Різниця не косметична — вона змінює три властивості системи одразу.
- Обґрунтованість. Кожне твердження прив'язане до
object_id, а не до абзацу. Модель не може назвати номер вагона, якого немає в сховищі об'єктів, бо вона його ніколи не друкує — вона повертає посилання, яке видало сховище. - Авторизація стає вирішуваною. ABAC-політика застосовується до об'єкта та властивості до того, як модель щось побачить. Політика, записана в промпті, — це побажання; політика в фільтрі онтології — це гарантія.
- Записи валідуються. Модель не змінює стан. Вона пропонує типізовану дію, символьний шар перевіряє передумови і або комітить, або відхиляє з кодом причини.
Нейросимволічний поділ обов'язків. Ймовірнісна половина (LLM) перетворює намір на кандидатний план. Детермінована половина (онтологія + рушій правил) тримає типи, кардинальності, інваріанти та дозволи. Правило, яке не має винятків: інваріанти ніколи не живуть у ймовірнісній половині. Модель, що на 97% дотримується правила «не переспрямовувати вантаж під митним арештом», порушує його раз на тридцять три виклики. І це не проблема якості моделі: 97% — уже добрий показник. Просто інваріант не є статистичною величиною, і 99,9% його теж не задовольняють.
Практична форма поверхні інструментів. search_objects(object_type, filter) повертає масив посилань на об'єкти, а не markdown. get_links(id, link_type) обходить граф. propose_action(action_type, object_id, params, idempotency_key) ініціює двофазний запис. Векторний пошук тут лише звужує кандидатів; вирішує предикат онтології.
Що змінюється в пошуку. Питання перестає бути «який чанк найсхожіший» і стає «які об'єкти задовольняють предикат». Векторний пошук лишається, але як груба відсічка кандидатів; далі працює обхід зв'язків: замість зшивати три абзаци з різних документів, агент робить два кроки get_links і отримує точну відповідь на багатокроковий запит. Побічний ефект приємний і вимірюваний: типізована відповідь на десять об'єктів — це кількасот токенів замість десяти чанків по 400.
Двофазний запис. Пропозиція → валідація → шлюз (політика або людина) → коміт із ключем ідемпотентності. Ключ обов'язковий: агент, який повторив крок після таймауту, не повинен переспрямувати вантаж двічі. Кожен коміт лишає в журналі актора, тип дії, id об'єкта та версію онтології — це і є аудиторський слід, який регулятор може перевірити, жодного разу не читаючи промптів.
# Ontology-grounded tool surface. The model never receives prose:
# every result is a typed object reference it can cite and act on.
TOOLS = [
{
'name': 'search_objects',
'description': 'Find objects of ONE ontology type. Returns object refs, not text.',
'input_schema': {
'type': 'object',
'properties': {
'object_type': {'enum': ['Shipment', 'Wagon', 'Customer']},
'filter': {'type': 'object'},
'limit': {'type': 'integer', 'maximum': 50}
},
'required': ['object_type', 'filter']
}
},
{
'name': 'propose_action',
'description': 'Propose a typed write. Symbolically validated before commit.',
'input_schema': {
'type': 'object',
'properties': {
'action_type': {'enum': ['reroute_shipment', 'flag_wagon']},
'object_id': {'type': 'string'},
'params': {'type': 'object'},
'idempotency_key': {'type': 'string'}
},
'required': ['action_type', 'object_id', 'idempotency_key']
}
}
]
def commit(proposal, actor, ontology, policy, log):
# The deterministic half owns types, invariants and permissions.
obj = ontology.get(proposal['object_id'])
if obj is None or obj.type not in ACTION_DOMAIN[proposal['action_type']]:
return Rejected('TYPE_MISMATCH', retryable=False)
if not policy.permits(actor, proposal['action_type'], obj):
return Rejected('FORBIDDEN', retryable=False) # never decided in a prompt
for inv in ontology.invariants(proposal['action_type']):
if not inv(obj, proposal['params']):
return Rejected('PRECONDITION_FAILED:' + inv.name, retryable=True)
# ordered, replicated, and idempotent by key (see E8.3)
return log.append(proposal, key=proposal['idempotency_key'])
На практиці
Оператор вантажних залізничних перевезень підключив агента до сховища на 4,2 млн об'єктів (вантажі, вагони, митні статуси). Базовий текстовий RAG правильно прив'язував сутність у 61% запитів «які вантажі під ризиком»; після переходу на типізовані інструменти — 94%. Вигадані номери вагонів упали з 12% відповідей до нуля, бо жоден ідентифікатор більше не генерувався моделлю.Антипатерн
Серіалізувати весь підграф у промпт як JSON-текст і покластися на модель у виконанні з'єднань. Це роздуває контекст, повертає вигадані ідентифікатори і — найгірше — переносить авторизацію в промпт, де її неможливо застосувати примусово.Оркестрація інструментів: п'ять рівнів і бюджет
Здатність агента користуватися інструментами вимірюється не бінарно. Корисно розкласти її на п'ять рівнів — і побачити, що продакшн-збої майже ніколи не на першому.
- Рівень 1 — один виклик. Правильно обрати інструмент і сформувати аргументи.
- Рівень 2 — фіксована послідовність. Пронести результат через кілька кроків без втрати.
- Рівень 3 — паралелізація. Кілька незалежних читань одночасно.
- Рівень 4 — умовні та вкладені виклики. Вихід одного кроку формує схему наступного. Саме тут ламається більшість систем.
- Рівень 5 — самокорекція. Розпізнати порожній чи помилковий результат і перепланувати, а не переказати помилку користувачеві як факт.
Головний важіль — не промпт, а поверхня інструментів. Опис інструмента — це його контракт; він важливіший за розмір моделі. Тримайте до ~20 інструментів на одного агента: далі точність вибору просідає, і лікується це маршрутизацією до вужчих під-агентів, а не більшою моделлю. Помилки мають бути дієвими: code, message, retryable. Помилка «щось пішло не так» гарантує, що рівня 5 не буде ніколи.
Опис — це контракт, а не документація. Іменуйте інструменти за типом об'єкта, яким вони оперують (shipment.search, wagon.flag), а не за дією взагалі. Два інструменти з описами, що перетинаються, гарантують плутанину, і жоден промпт її не розводить. Коли поверхня росте, розділяйте не всередині агента, а між агентами: маршрутизатор передає під-агентові явно сформульоване завдання й потрібні посилання на об'єкти, а не спільний блокнот міркувань — інакше контекст росте, а точність падає.
Бюджет — це архітектура, а не налаштування. Цикл ReAct (думка → дія → спостереження) без обмежень на кроки, час і вартість — це аварія зі спінером. Досягнення межі не має завершуватися вигаданою відповіддю: воно має ескалювати з накопиченим трейсом.
Маршрутизація за корисністю. Очікувана цінність виклику = P(корисно) × цінність − вартість (затримка + токени + гроші). Дешеві детерміновані інструменти (фільтр онтології, точковий lookup) йдуть першими; дорогі генеративні — останніми. Паралелити можна лише читання без побічних ефектів; записи серіалізуються через реплікований журнал (E8.3).
Вимірювання. Точність кінцевої відповіді приховує крихкість. Міряйте окремо: точність вибору інструмента, валідність аргументів, частку успішного відновлення після помилки. Агент із 92% кінцевої точності та 40% відновлення розсиплеться при першій деградації API. Причина проста: офлайн-набір складається зі здорових викликів, тож рівень 5 у ньому просто не трапляється — його треба перевіряти окремо, навмисно ламаючи інструменти.
# Bounded ReAct loop with utility-guided tool selection.
# An unbounded agent loop is an outage with a spinner on it.
BUDGET = {'steps': 8, 'seconds': 25.0, 'usd': 0.05}
def expected_utility(tool, trace):
# value of the information, minus what the call actually costs us
return tool.p_useful(trace) * tool.value - (
tool.latency_s * LATENCY_PRICE + tool.usd
)
def run(goal, tools, model, budget=BUDGET):
trace = []
spent = {'steps': 0, 'seconds': 0.0, 'usd': 0.0}
while spent['steps'] < budget['steps']:
# cheap deterministic tools outrank expensive generative ones;
# the slice keeps the visible surface under the selection cliff
allowed = sorted(tools, key=lambda t: -expected_utility(t, trace))[:9]
step = model.plan(goal, trace, allowed)
if step.kind == 'final':
return step.answer, trace
obs = call(step.tool, step.args) # typed result, never raw prose
if obs.error and obs.error.retryable:
# actionable error -> level 5 recovery is possible
trace.append(Observation(obs.error.code, obs.error.message))
else:
trace.append(obs)
spent['steps'] += 1
spent['usd'] += obs.usd
spent['seconds'] += obs.latency_s
if spent['usd'] > budget['usd'] or spent['seconds'] > budget['seconds']:
return escalate(goal, trace), trace # hand over the trace, do not spin
return escalate(goal, trace), trace
На практиці
Агент реагування на інциденти мав 34 інструменти в одній пласкій поверхні. Після розділення на три маршрутизовані під-агенти по ≤9 інструментів точність вибору інструмента зросла з 71% до 96%, медіанна кількість кроків упала з 11 до 6, а вартість опрацювання одного інциденту — з $0.84 до $0.31. Модель не змінювали жодного разу.Антипатерн
Виставити один узагальнений інструментrun_query(sql) замість типізованих дій. Модель пише синтаксично валідний, але семантично хибний SQL, авторизація на рівні окремої дії стає неможливою, а точність вибору інструмента неможливо виміряти — інструмент один.Спільний стан: консенсус, CRDT і відрізані контури
Дві різні задачі постійно плутають. Узгодження єдиного впорядкованого журналу (Raft, Paxos) дає лінеаризовність, потребує кворуму і жертвує доступністю під час розділення мережі. Збіжність без координації (CRDT) завжди доступна на запис і зливається детерміновано, але не вміє виражати інваріант «схвалення має бути рівно одне».
Raft у трьох рядках. Вибори лідера з термами, реплікація журналу, коміт за більшістю. Щоб пережити f відмов, потрібно 2f+1 вузлів: п'ять витримують дві. Лідер — це водночас стеля пропускної здатності й підлога затримки (один RTT до більшості). Кладіть у нього те, де важливий порядок: журнал дій агента, версію онтології, версію бандла політик.
Paxos чи Raft — та сама арифметика. Multi-Paxos і Raft дають однакові гарантії й однакову вимогу більшості; Raft виграє зрозумілістю реалізації та явним поняттям лідера, тому його простіше правильно експлуатувати. Що не варто класти в консенсус: високочастотну телеметрію, метрики та проміжні спостереження агента. Лідер миттєво стає вузьким місцем, а порядок цих подій нікому не потрібен — їм місце в потоковому конвеєрі, а не в реплікованому журналі.
CRDT там, де операції комутативні. OR-Set для тегів і міток (add-wins із tombstone), LWW-регістр для нотаток тріажу, лічильники. Критична деталь: LWW за настінним годинником при розбіжності 400 мс тихо втрачає редагування. Використовуйте гібридний логічний годинник — він зберігає причинність і не залежить від NTP. Жорсткіша синхронізація тут не рятує: вона звужує вікно втрат, не усуваючи саму можливість.
Відрізані та ізольовані контури. В анклав заходить підписаний бандл: ваги моделі, версія онтології, бандл політик, маніфест інструментів — усе з контент-хешем і перевіркою підпису при завантаженні. Назовні через односторонній канал виходить лише підписана дельта.
- Анклав має власну Raft-групу, ніколи не спільну з ядром. Одна група через повітряний зазор означає, що будь-яке від'єднання зупиняє записи всього флоту.
- Локальні коміти типізовані як
local_commitзі сферою повноважень local. Анклав, що комітить «глобально авторитетні» рішення власним кворумом, — це split-brain з красивою назвою. - Повернення на зв'язок — це злиття, а не push. CRDT-стан зливається автоматично. Дії з журналу переграються як пропозиції і перевіряються заново проти поточної версії політик: поки анклав був темним, політика могла змінитися.
- Розбіжність версій. Кожна дія несе
ontology_version; при неспівпадінні — відхилення з кодом, ніколи не тиха підгонка. Закріплення версії має сенс рівно настільки, наскільки система готова через нього зупинитися.
# fleet.yaml - one core Raft group plus disconnected enclaves with CRDT state
core:
consensus: raft
members: [core-1, core-2, core-3, core-4, core-5] # 2f+1 -> tolerates f=2
linearizable_state:
- action_log # order matters: who approved what, and first
- ontology_version
- policy_bundle_version
enclaves:
- id: fwd-17
connectivity: intermittent # observed dark window: 6-72h
consensus: raft # SEPARATE group - never spans the air gap
members: [fwd-17a, fwd-17b, fwd-17c]
authority_scope: local # commits are 'local_commit', never global
convergent_state: # coordination-free, always writable
annotations: or-set # add-wins, tombstoned removes
triage_notes: lww-register
clock: hybrid-logical # NOT wall clock: 400ms skew loses edits
bundle: # the only thing that crosses the diode, one way
pins:
ontology_version: 2026.31.0
policy_bundle: sha256:9f3c1d...
model: local-8b-q4:sha256:1a77e0...
signature: minisign
verify_on_load: true # refuse to start on a hash mismatch
reconnect:
annotations: merge # CRDT: deterministic, no human in the loop
actions: replay_as_proposals # re-validated against the CURRENT policy
on_version_skew: reject # never silently coerce a stale action
На практиці
Логістичне розгортання на 40 передових вузлах із вікнами автономності 6–72 год: Raft-група з 5 вузлів у ядрі та CRDT-шар анотацій на кожному вузлі. Після відновлення зв'язку 18 400 анотацій злилися за 40 секунд без жодного ручного розв'язання конфліктів, тоді як 260 дій запису переграли як пропозиції — і 11 із них відхилив рушій політик, переглянутий, поки вузли були відрізані.Антипатерн
Розтягнути одну Raft-групу через повітряний зазор і покластися на LWW за настінним годинником між вузлами з розбіжністю 400 мс. Перше зупиняє записи всього флоту при кожному від'єднанні; друге тихо втрачає редагування, і виявляється це лише під час аудиту.Джерела, з яких виведено напрям
- Agentic Reasoning for Large Language Mod
- A Survey on Large Language Model based A
- A Relational Neural Network Database Mod
- ReAct Synergizing Reasoning and Acting in Language
- ETOM A Five Level Benchmark for Evaluating Tool Orchestrat
- The Evolution of Tool Use in LLM Agents From Single Tool C
- Utility Guided Agent Orchestration for Efficient LLM Tool
- Reinforcement Learning for LLM based Multi Agent Systems t
- A Survey on Consensus Algorithms in Bloc
- A Deep Dive into Blockchain Consensus Pr
- A Security Reference Architecture for Bl
- A comparative study on consensus mechani
Пройти інтерактивно
У кожного напряму є питання, картки з інтервальним повторенням і облік прогресу. Для них потрібен акаунт — безкоштовний і на одну хвилину.
Відкрити інтерактивний трек Створити безкоштовний акаунт