Підготовка до співбесіди з LLM та генеративного ШІ
Запитання для співбесіди інженера з LLM та генеративного ШІ
15 відібраних запитань для співбесіди з LLM та генеративного ШІ, згрупованих за рівнем досвіду. Використовуйте їх для повторення основ, практичних компромісів та міркувань щодо продакшену на старшому рівні.
1Поясніть токенізацію підслів (subword tokenization) та чому її віддають перевагу над токенізацією на рівні слів або на рівні символів у сучасних мовних моделях.
Токенізація підслів — це гібридний підхід до сегментації тексту, який розбиває текст на морфологічні фрагменти змінної довжини або підрядки на основі частотності (такі як 'un', 'break', 'able'), а не на цілі слова чи окремі символи. Алгоритми, такі як Byte-Pair Encoding (BPE), WordPiece та Unigram LM, вивчають словник фіксованого розміру з навчального корпусу, де часті слова залишаються цілими як окремі токени, тоді як рідкісні або невидимі слова розкладаються на відомі одиниці підслів.
Токенізація підслів віддається перевазі в сучасних мовних моделях, оскільки вона забезпечує баланс між розміром словника, довжиною послідовності та стійкістю до виходу за межі словника (out-of-vocabulary - OOV). Чиста токенізація на рівні слів вимагає надмірно великого словника (що призводить до величезних матриць вбудовування) і все ще страждає від OOV-токенів, які зіставляються із загальними токенами '[UNK]'. Навпаки, чиста токенізація на рівні символів усуває проблеми OOV, але дає дуже довгі послідовності, які різко збільшують обчислювальну складність у механізмах уваги (які масштабуються квадратично відносно довжини послідовності) та розбавляють семантичну щільність на один токен.
Токенізація підслів досягає оптимального компромісу, зберігаючи керовані довжини послідовностей, практичні розміри словника (зазвичай від 32k до 128k токенів) та нульові показники OOV (особливо в поєднанні з байтовими резервними варіантами).
2Поясніть різницю між статичними векторними представленнями слів, контекстуальними векторними представленнями та прихованими станами трансформера.
Статичні векторні представлення слів (word embeddings), контекстуальні векторні представлення та приховані стани трансформера представляють прогресивну еволюцію того, як текстові представлення захоплюють значення та синтаксичний контекст.
1. **Статичні векторні представлення слів** (наприклад, Word2Vec, GloVe, FastText) призначають єдиний, фіксований вектор кожному токену словника незалежно від його контексту в реченні. У цій парадигмі багатозначні слова, такі як 'bank' (річковий берег проти фінансового банку) або 'apple' (фрукт проти технологічної компанії), мають ідентичні векторні представлення в усіх контекстах, покладаючись на статичну таблицю пошуку.
2. **Контекстуальні векторні представлення** (наприклад, ранні ELMo, BERT-представлення токенів або векторні представлення речень з Bi-Encoders) створюють представлення, де вектор для токена є динамічною функцією його навколишнього контексту. У BERT або ELMo 'bank' у 'river bank' отримує зовсім інший вектор представлення, ніж 'bank' у 'deposit money at the bank'.
3. **Приховані стани трансформера** відносяться до проміжних векторних представлень, що генеруються на кожному окремому шарі трансформерної мережі під час прямого проходу. Враховуючи вхідні векторні представлення токенів на шарі 0, кожен наступний шар трансформера застосовує механізми самоусвідомлення (self-attention) та прямого поширення (feed-forward transformations), створюючи послідовність векторів прихованого стану h_l на шарі l. Хоча приховані стани останнього шару діють як високорівневі контекстуальні векторні представлення, нижні та середні приховані стани захоплюють низькорівневі синтаксичні, лексичні та структурні ознаки. Таким чином, приховані стани трансформера охоплюють повний вертикальний континуум пошарових представлень по всій мережі.
import torch
from transformers import AutoTokenizer, AutoModel
tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
model = AutoModel.from_pretrained('bert-base-uncased', output_hidden_states=True)
text1 = 'River bank'
text2 = 'Bank deposit'
inputs1 = tokenizer(text1, return_tensors='pt')
inputs2 = tokenizer(text2, return_tensors='pt')
with torch.no_grad():
out1 = model(**inputs1)
out2 = model(**inputs2)
# Layer 0 (Input token embeddings - static lookup before self-attention)
static_bank_1 = out1.hidden_states[0][0, 2] # 'bank'
# Layer 12 (Final contextual hidden state after all attention layers)
contextual_bank_1 = out1.hidden_states[12][0, 2]
contextual_bank_2 = out2.hidden_states[12][0, 1]
print('Cosine similarity of bank in different contexts (Layer 12):',
torch.cosine_similarity(contextual_bank_1, contextual_bank_2, dim=0).item())
3Поясніть, що являє собою простір вбудовування (embedding space) і як інтерпретується косинусна подібність для текстових вбудовувань.
Простір вбудовування (embedding space) — це безперервний, багатовимірний векторний простір R^d, куди дискретні текстові сутності (слова, речення або документи) відображаються таким чином, що семантичні, синтаксичні або реляційні подібності відповідають геометричній близькості та спрямованим зв'язкам. У цьому просторі відстані та кути відображають семантичну пов'язаність.
Косинусна подібність вимірює косинус кута тета між двома векторами u та v, що обчислюється як:
```
Cosine Similarity(u, v) = (u . v) / (||u|| ||v||)
```
Косинусна подібність інтерпретується в текстових вбудовуваннях наступним чином:
- **Діапазон та орієнтація**: Вона виробляє скалярне значення, типово обмежене в [-1, 1] (або [0, 1] для невід'ємних вбудовувань). Значення, близьке до 1.0, вказує на те, що два вектори вказують практично в одному напрямку, відображаючи високу семантичну подібність або тематичну відповідність. Значення, близьке до 0.0, передбачає ортогональність (семантичну незалежність або непов'язаність), а негативні значення вказують на протилежні орієнтації.
- **Інваріантність масштабу**: На відміну від евклідової відстані (відстань L2) або скалярного добутку, косинусна подібність нормалізує за довжиною вектора. У текстових вбудовуваннях величина вектора іноді може корелювати з довжиною послідовності, частотою токенів або специфічністю термінів. Зосереджуючись виключно на спрямованому вирівнюванні, косинусна подібність ізолює семантичну орієнтацію від відмінностей у величині вектора.
4Поясніть різницю між вбудовуваннями токенів (token embeddings), позиційними вбудовуваннями (positional embeddings) та сегментними або типовими вбудовуваннями (segment or type embeddings) у вхідних даних трансформера.
У вхідних даних трансформера (зокрема, в архітектурах типу BERT) вхідне представлення для кожного токена зазвичай формується шляхом по елементного додавання трьох різних векторів вбудовувань:
1. **Вбудовування токенів (Token Embeddings)**: Перетворюють дискретні ідентифікатори токенів словника на щільні вектори, що представляють основну семантичну та лексичну ідентичність токенів.
2. **Позиційні вбудовування (Positional Embeddings)**: Вводять інформацію про порядок токенів та їх послідовний індекс у представлення, компенсуючи той факт, що само-увага є інваріантною до перестановок (permutation-invariant).
3. **Сегментні (або типові) вбудовування (Segment or Token Type Embeddings)**: Розрізняють різні текстові діапазони або речення, упаковані в одну вхідну послідовність (наприклад, Речення A проти Речення B у задачах парної класифікації або відповіді на питання).
Комбінування цих вбудовувань забезпечує єдиний щільний вхідний тензор, який кодує значення токена, його позицію та групування послідовностей, перш ніж передати його до першого шару трансформера.
import torch
import torch.nn as nn
vocab_size, max_seq_len, num_segments, d_model = 30522, 512, 2, 768
tok_embed = nn.Embedding(vocab_size, d_model)
pos_embed = nn.Embedding(max_seq_len, d_model)
seg_embed = nn.Embedding(num_segments, d_model)
input_ids = torch.tensor([[101, 7592, 102, 2023, 102]]) # Token IDs
type_ids = torch.tensor([[0, 0, 0, 1, 1 ]]) # Segment IDs (Sentence A vs B)
positions = torch.arange(input_ids.size(1)).unsqueeze(0) # Indices: [0, 1, 2, 3, 4]
# Final representation is the element-wise sum
input_rep = tok_embed(input_ids) + pos_embed(positions) + seg_embed(type_ids)
print(input_rep.shape)
5Поясніть механізм уваги (attention) для встановлення зв'язків між токенами в послідовності, включно з запитами, ключами, значеннями та багатоголовою увагою.
Увага (attention) — це механізм, який дозволяє токенам у послідовності динамічно направляти інформацію та зважувати релевантність усіх інших токенів на основі контекстного співставлення. Лінійні проєкції перетворюють вхідні дані кожного токена на три вектори: - Запит (Q): Представляє, яку інформацію шукає поточний токен. - Ключ (K): Представляє, які атрибути або вміст токен пропонує для співставлення із запитами. - Значення (V): Містить фактичне інформаційне навантаження, яке потрібно агрегувати. У масштабованій увазі за точковим добутком (scaled dot-product attention) оцінки уваги обчислюються шляхом множення Запитів і Ключів ($Q K^T$), масштабовані на $\frac{1}{\sqrt{d_k}}$ для запобігання зникненню градієнта у великих вимірах, та нормалізовані за допомогою функції softmax. Кінцевий результат — це зважена сума Значень: $$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$ Багатоголова увага (Multi-Head Attention, MHA) проєктує $Q$, $K$ і $V$ у кілька незалежних представницьких підпросторів (голів) паралельно. Це дозволяє моделі одночасно звертати увагу на різні типи зв'язків (наприклад, синтаксична структура, кореференція, далекі залежності) між позиціями. Вихідні дані голів конкатеновані та лінійно проєктуються назад до виміру моделі.
6Поясніть, як маскування уваги (attention masking) відрізняється для причинних декодерів (causal decoders) порівняно з двонаправленими кодерами (bidirectional encoders) і яку поведінку воно дозволяє або запобігає.
Маскування уваги контролює, яким токенам дозволено звертатися до інших токенів, встановлюючи логіти уваги (оцінки перед softmax) на $-\infty$ для недозволених пар, забезпечуючи, щоб їхня вага уваги після softmax була строго 0.
1. **Причинні декодери** (наприклад, GPT, LLaMA): Використовують нижньотрикутну причинну (авторегресійну) маску. Токен на позиції $i$ може звертатися лише до позицій $j \le i$. Це запобігає зверненню до майбутніх токенів, уможливлюючи авторегресійну по-токенну генерацію під час виведення та запобігаючи витоку міток майбутніх токенів під час паралельного навчання.
2. **Двонаправлені кодери** (наприклад, BERT): Не використовують причинну маску; кожен токен може звертатися до всіх минулих та майбутніх токенів у послідовності. Вони використовують маски заповнення (padding masks), щоб запобігти зверненню дійсних токенів до порожніх токенів `[PAD]` у пакетних послідовностях. Двонаправлена увага створює багаті всебічні контекстні представлення, ідеальні для завдань розуміння, але запобігає прямій однопрохідній авторегресійній генерації тексту.
import torch
import torch.nn.functional as F
scores = torch.randn(3, 3)
# Create upper-triangular mask for future positions
causal_mask = torch.triu(torch.ones(3, 3, dtype=torch.bool), diagonal=1)
# Mask future positions with -inf before softmax
masked_scores = scores.masked_fill(causal_mask, float('-inf'))
atten_weights = F.softmax(masked_scores, dim=-1)
print(atten_weights)
7Поясніть різницю між архітектурами трансформерів лише з енкодером, лише з декодером та енкодер-декодером для мовних завдань.
Три основні архітектури трансформерів принципово відрізняються патернами маскування уваги та цільовими операційними завданнями: 1. Лише енкодер (наприклад, BERT (Bidirectional Encoder Representations from Transformers), RoBERTa): Використовує двонаправлену само-увагу, де кожен токен може взаємодіяти з усіма іншими токенами в послідовності одночасно. Він створює багаті контекстуальні представлення для всієї вхідної послідовності, що робить його ідеальним для класифікації, екстрактивного відповіді на питання (QA - Question Answering) та представлення ознак. Він не може природним чином генерувати авторегресивний текст. 2. Лише декодер (наприклад, GPT-3 (Generative Pre-trained Transformer 3), Llama, Mistral): Використовує каузальну (односпрямовану) само-увагу, де токен $i$ може взаємодіяти лише з токенами в позиціях $j \le i$. Він навчається авторегресивно за допомогою передбачення наступного токена і служить стандартною архітектурою для генеративних мовних моделей, генерації коду та відкритого діалогу. 3. Енкодер-декодер (наприклад, T5 (Text-To-Text Transfer Transformer), BART (Bidirectional and Auto-Regressive Transformers)): Поєднує двонаправлений енкодер з авторегресивним каузальним декодером. Окрім каузальної само-уваги до згенерованих токенів, декодер використовує шари перехресної уваги, які запитують вихідні представлення енкодера. Ця архітектура спеціально розроблена для завдань перетворення послідовності в послідовність, таких як переклад та узагальнення.
8Поясніть, як вибір токенізаторів (побайтових, Юнікод-орієнтованих та багатомовних) впливає на якість моделі, вартість та справедливість у різних мовах.
Вибір дизайну токенізатора — як-от побайтова сегментація проти Юнікод-орієнтованої сегментації та розподіл багатомовного словника — безпосередньо впливає на подальшу якість моделі, вартість інференсу/навчання та лінгвістичну справедливість. З точки зору вартості та справедливості, токенізатори, навчені переважно на корпусах англійської мови або мовах з латинським алфавітом, виділяють більшість словникових записів на англійські слова та морфеми. Отже, англійська мова досягає високої компресії (наприклад, близько 1.3 токена на слово), тоді як нелатинські алфавіти (наприклад, арабська, деванагарі, тайська, китайська) або мови з низькими ресурсами часто фрагментуються на кілька субслів або сирі байти UTF-8 (часто від 3 до 6 токенів на слово). Ця невідповідність часто називається «податком на токени» або «дисбалансом коефіцієнта фертильності»: користувачі, що не є англомовними, платять значно більше за одиницю семантичного вмісту в рахунках за API, швидше вичерпують ліміти контекстного вікна та страждають від вищої затримки. З точки зору якості, побайтові токенізатори (як-от Byte-level BPE (Byte Pair Encoding) у GPT-2/GPT-4 або SentencePiece з байтовим резервом у LLaMA) повністю уникають збоїв через нерозпізнані символи та помилок out-of-vocabulary (UNK), оскільки будь-який дійсний рядок UTF-8 розкладається на байтові токени. Однак надмірна байтова фрагментація погіршує якість представлення, оскільки трансформер повинен витрачати шари на повторне поєднання байтових фрагментів у семантичні концепції перед виконанням високорівневого міркування. Збільшення розміру багатомовного словника (наприклад, розширення з 32k до 128k+ токенів) збалансовує коефіцієнти фертильності та покращує продуктивність у подальших завданнях для різних мов, ціною помірно більшого вхідного/вихідного шару вбудовування.
9Які артефакти токенізації (tokenization artifacts) з'являються в числовому міркуванні, генерації коду або рідкісному тексті Unicode, і як спеціалізовані токенізатори (tokenizers) можуть їх зменшити?
Артефакти токенізації виникають, коли субслівні токенізатори непослідовно розділяють структурований, числовий або рідкісний текст, заважаючи моделі розпізнавати основну семантичну або синтаксичну закономірність. Основні артефакти включають:
1. Артефакти числового міркування: Стандартні токенізатори BPE (Byte Pair Encoding), навчені на загальному тексті, розділяють числа на довільні фрагменти довжиною залежно від частоти (наприклад, '12345' може бути токенізовано як ['12', '345'], тоді як '12346' як ['123', '46']). Це непослідовне групування порушує вирівнювання за розрядами (одиниці, десятки, сотні) та перешкоджає арифметичним міркуванням.
2. Артефакти генерації коду: Відступи (провідні пробіли/таби) та багатосимвольні оператори (наприклад, '==', '!=', '->') часто розділяються нерівномірно на межах пробілів, що призводить до помилок відступів, роздутих кількостей токенів у глибоко відступленому коді та пошкоджень синтаксису.
3. Артефакти рідкісних символів Unicode та Emoji: Багатобайтові послідовності UTF-8 (наприклад, складні emoji з розділювачами нульової ширини або рідкісні скрипти) розділяються на токени сирих байтів, які не несуть індивідуального семантичного значення, спричиняючи галюцинації символів або пошкоджене відображення гліфів при генерації.
Спеціалізовані токенізатори зменшують ці артефакти за допомогою адаптованих правил попередньої токенізації та обмежень словника:
* Розділення цифр: Забезпечення токенізації окремих цифр (наприклад, розділення кожної цифри `0-9` за допомогою регулярного виразу на окремий токен) гарантує одноманітне представлення за розрядами для математичних міркувань.
* Виділені токени для пробілів/відступів: Додавання явних токенів для багатопробільних відступів (наприклад, 2, 4, 8 пробілів) та збереження ключових слів/операторів мови програмування.
* Попередня токенізація за регулярними виразами / резервні варіанти на рівні байтів: Використання розділювачів за регулярними виразами (таких як регулярні вирази GPT-4/tiktoken), які розділяють розділові знаки, літери та цифри на строгі категорії до обчислення BPE-злиттів, запобігаючи злиттям між категоріями (наприклад, запобігаючи злиттю 'a=10' в один токен).
import tiktoken
# tiktoken cl100k_base (GPT-4 / ChatGPT) enforces digit and whitespace handling
enc = tiktoken.get_encoding('cl100k_base')
num1 = '12345'
num2 = '12346'
code_indent = ' def foo():'
print('Tokens num1:', [enc.decode([t]) for t in enc.encode(num1)])
print('Tokens num2:', [enc.decode([t]) for t in enc.encode(num2)])
print('Tokens code:', [enc.decode([t]) for t in enc.encode(code_indent)])
10Розгляньте компроміси уваги в довгому контексті (long-context attention trade-offs), включаючи квадратичну повну увагу, увагу з ковзним вікном (sliding-window attention), розріджену або глобальну увагу, вартість KV-кешу та розмивання уваги (attention dilution).
Масштабування уваги до довгих вікон контексту має компроміси між обчисленнями, використанням пам'яті та точністю моделі:
1. Квадратична повна увага проти уваги з ковзним вікном / розрідженої уваги: Стандартна повна увага масштабується квадратично ($O(N^2)$) за обчисленнями та пам'яттю активацій залежно від довжини послідовності $N$. Увага з ковзним вікном (локальна) обмежує увагу фіксованою областю $W$, зменшуючи складність до $O(N \cdot W)$, але вимагає кількох шарів для поширення інформації між віддаленими токенами. Розріджені або глобальні моделі уваги поєднують локальні вікна з вибраними глобальними токенами-якорями для збереження масштабування $O(N)$ при забезпеченні далекосяжного зв'язку.
2. Вартість пам'яті KV-кешу: Під час авторегресивної генерації ключі та значення для всіх попередніх токенів кешуються, щоб уникнути надлишкових обчислень. Пам'ять KV-кешу масштабується лінійно з довжиною послідовності ($O(B \cdot L \cdot H_{KV} \cdot D \cdot N)$). Для дуже довгих контекстів (32k–128k+ токенів) KV-кеш споживає десятки гігабайт відеопам'яті GPU за пакет, обмежуючи максимальний розмір пакета та пропускну здатність пам'яті.
3. Розмивання уваги (Lost-in-the-Middle): Зі збільшенням контексту знаменник softmax підсумовує десятки тисяч токенів, тонко розподіляючи масу ймовірності по нерелевантному контексту. Це збільшення ентропії розмиває гостроту уваги, погіршуючи здатність моделі надійно згадувати конкретну інформацію, вбудовану в середину довгих підказок.
11Порівняйте MHA, MQA та GQA і поясніть, як вони впливають на пам'ять кешу ключів-значень (KV-cache) та пропускну здатність декодування.
Механізми Багатоголової уваги (MHA — Multi-Head Attention), Багатозапитної уваги (MQA — Multi-Query Attention) та Групованої запитної уваги (GQA — Grouped-Query Attention) відрізняються тим, як голови Ключа ($K$) та Значення ($V$) розподіляються між головами Запиту ($Q$):
1. **Багатоголова увага (MHA)**: Має однакову кількість голів $Q$, $K$ та $V$ ($H_Q = H_{KV}$, співвідношення 1:1). Кожна голова запиту працює зі своїми незалежними представленнями ключів/значень. Хоча цей механізм є виразним, він вимагає кешування окремих KV-матриць для кожної голови.
2. **Багатозапитна увага (MQA)**: Використовує кілька голів $Q$ ($H$), але лише 1 спільну голову $K$ та 1 спільну голову $V$ (співвідношення $H:1$). Це зменшує розмір кешу ключів-значень (KV-cache) у $H$ разів, але може призвести до незначної втрати якості або нестабільності під час навчання.
3. **Групована запитна увага (GQA)**: Групує голови $Q$ у $G$ розділів, де кожна група використовує одну голову $K$ та одну голову $V$ (наприклад, 8 голів $Q$ на одну голову KV). GQA пропонує оптимальний компроміс, відновлюючи майже всю якість моделювання MHA, зберігаючи при цьому переваги MQA щодо пам'яті.
**Вплив на KV-cache та пропускну здатність декодування:**
Авторегресивна генерація токенів (декодування) обмежена пропускною здатністю пам'яті, оскільки графічний процесор (GPU) повинен передавати весь кеш ключів-значень (KV-cache) з пам'яті з високою пропускною здатністю (HBM — High-Bandwidth Memory) до вбудованої статичної оперативної пам'яті (SRAM — Static Random-Access Memory) для кожного згенерованого токена. Зменшуючи кількість KV-голів у $H/G$ разів (наприклад, у $4\times$ до $8\times$ у GQA або $32\times+$ у MQA):
* **Обсяг пам'яті KV-cache** пропорційно зменшується, що дозволяє використовувати значно більші розміри пакетів обслуговування у відеопам'яті (VRAM) графічного процесора.
* **Обсяг трафіку читання з HBM** на токен суттєво зменшується, що значно підвищує пропускну здатність декодування токенів.
# Model with 32 Query Heads
num_q_heads = 32
mha_kv_heads = 32 # 1:1 ratio
gqa_kv_heads = 8 # 4:1 ratio (4 query heads per KV head)
mqa_kv_heads = 1 # 32:1 ratio (1 shared KV head)
print(f"KV Cache Size Relative to MHA:")
print(f"MHA: {mha_kv_heads / mha_kv_heads * 100:.1f}%")
print(f"GQA: {gqa_kv_heads / mha_kv_heads * 100:.1f}%")
print(f"MQA: {mqa_kv_heads / mha_kv_heads * 100:.1f}%")
12Поясніть, як FlashAttention прискорює точні обчислення уваги (attention), не змінюючи її вихідних даних.
FlashAttention прискорює обчислення уваги, роблячи алгоритм обізнаним щодо вводу/виводу (IO-aware) — мінімізуючи трафік читання та запису пам'яті між повільною високошвидкісною пам'яттю графічного процесора (HBM) та швидкою вбудованою пам'яттю SRAM, замість того, щоб намагатися зменшити загальну кількість арифметичних операцій FLOP. Стандартний механізм уваги матеріалізує проміжні матриці оцінок уваги та ймовірностей розміром N x N у HBM, що викликає значне вузьке місце пропускної здатності пам'яті. FlashAttention долає це за допомогою трьох ключових механізмів: 1. Розбиття на тайли (Tiling): Він розбиває матриці Query, Key та Value на блоки, які повністю поміщаються у вбудовану пам'ять SRAM графічного процесора. 2. Онлайн Softmax: Він обчислює softmax інкрементно по блоках, відстежуючи поточні максимуми та суми нормалізатора, оновлюючи часткові вихідні дані без потреби у повній матеріалізованій матриці N x N у пам'яті. 3. Точний перерахунок: Під час зворотного проходу він не читає збережені проміжні матриці уваги з HBM; натомість він перераховує їх на льоту в SRAM із збережених поточних статистичних даних. Оскільки не використовуються апроксимації, низькорангові факторизації або евристики відкидання токенів, вихідні дані є математично точними з точністю до числової точності з плаваючою комою, при цьому зменшуючи обсяг пам'яті HBM з O(N^2) до O(N).
13Розробіть детерміновані агентні робочі процеси, використовуючи планувальники, скінченні автомати, DAG (спрямовані ациклічні графи), типізований проміжний стан, обмежені повторні спроби та верифікацію результатів інструментів замість відкритих агентних циклів.
Відкриті агентні цикли (наприклад, неконтрольовані автономні цикли ReAct) у продакшені часто страждають від недетермінованого розгалуження, нескінченних циклів, надмірного споживання токенів та дрейфу стану. Детермінований агентний робочий процес замінює вільні цикли структурованим, спостережуваним потоком керування: 1. **Скінченні автомати та DAG.** Потік керування визначається як явний спрямований ациклічний граф або скінченний автомат (наприклад, LangGraph, Temporal, AWS Step Functions). Переходи вузлів залежать від явних умов та типізованих результатів, а не від відкритих рішень моделі. 2. **Типізований проміжний стан.** Стан, що розділяється між вузлами, моделюється суворими схемами (наприклад, моделями Pydantic або dataclasses). Вузли виконують валідовані операції читання та запису, запобігаючи дрейфу схеми або некоректному стану. 3. **Планувальники.** Структуровані планувальники заздалегідь видають обмежений план (наприклад, впорядкований список кроків, підкріплених переліченнями) або вибирають з обмеженого набору дійсних переходів станів, а не вільно приймають рішення щодо наступних дій без обмежень. 4. **Верифікація результатів інструментів.** Вихідні дані, повернені інструментами, детерміновано валідуються за схемами та бізнес-правилами перед оновленням стану або передачею на наступні кроки LLM. 5. **Обмежені повторні спроби та запасні варіанти.** Кожен крок встановлює явні бюджети повторних спроб, експоненційні відстрочки, таймаути та запасні переходи (наприклад, ескалація до перегляду людиною або запуск безпечного утримання), щоб гарантувати завершення.
from pydantic import BaseModel
from typing import Optional, Literal
class WorkflowState(BaseModel):
user_query: str
extracted_id: Optional[str] = None
verification_status: Literal["PENDING", "VERIFIED", "FAILED"] = "PENDING"
retry_count: int = 0
max_retries: int = 3
def execute_validation_node(state: WorkflowState) -> WorkflowState:
if state.retry_count >= state.max_retries:
state.verification_status = "FAILED"
return state
try:
result = call_verification_service(state.extracted_id)
state.verification_status = "VERIFIED" if result.is_valid else "FAILED"
except Exception:
state.retry_count += 1
return state
14Розробіть політику впевненості та утримання для асистента на базі великої мовної моделі (LLM), який відповідає на запитання в регульованих сферах.
У регульованих сферах (таких як охорона здоров'я, банківська справа, юриспруденція та комплаєнс) неправильні відповіді тягнуть за собою регуляторні санкції, юридичну відповідальність та ризики для безпеки. Надійна політика впевненості та утримання поєднує багатосигнальну калібровану оцінку впевненості, порогові значення відповіді з рівневою градацією та детерміновані робочі процеси ескалації:
1. **Багатосигнальна калібрована оцінка впевненості:** сирі лог-ймовірності (logprobs) LLM часто є некаліброваними для запитів, що виходять за межі предметної області. Оцінка впевненості повинна синтезувати кілька незалежних сигналів:
* **Оцінка обґрунтованості пошуку:** семантична подібність та впевненість повторного ранжування отриманих фрагментів доказів.
* **Виведення на рівні твердження (NLI - Natural Language Inference):** моделі виведення природною мовою, що перевіряють, чи кожне вилучене твердження випливає з отриманого вихідного контексту.
* **Семантична ентропія / Самоузгодженість:** вимірювання семантичної узгодженості між кількома згенерованими вибірками.
* **Лог-ймовірності токенів моделі:** мінімальні та середні лог-ймовірності для ключових іменованих сутностей та фактичних токенів.
2. **Багаторівнева політика утримання:**
* **Висока впевненість (Оцінка >= Високий поріг):** безпосередньо надавати згенеровану відповідь з вбудованими посиланнями.
* **Середня впевненість / Неоднозначність (Низький поріг <= Оцінка < Високий поріг):** надавати консервативну відповідь з явними застереженнями, відмовами від відповідальності або запитувати у користувача уточнюючі деталі.
* **Низька впевненість / Поза межами компетенції (Оцінка < Низький поріг):** жорстке утримання зі стандартизованим повідомленням про відмову.
3. **Ескалація та можливість аудиту відповідності:**
* **Детермінована ескалація:** утримання або критичні розбіжності автоматично направляються до черг «людина в циклі» (HITL) або систем тікетів для агентів з повним контекстом.
* **Журнал аудиту та походження:** повна телеметрія — включаючи хеші запитів, ідентифікатори отриманих документів, оцінки окремих компонентів впевненості та остаточні рішення щодо маршрутизації — повинна реєструватися для можливості регуляторного аудиту.
15Розробіть стратегію маршрутизації моделей, яка вибирає між малими, середніми та великими моделями на основі складності запиту, вартості, ризику та вимог до якості.
Виробнича архітектура маршрутизації моделей спрямовує вхідні запити до малих (наприклад, 1B–8B малих мовних моделей (SLM)), середніх (наприклад, моделей 14B–70B) та великих (наприклад, передових або великих моделей «суміш експертів» (MoE)) рівнів, балансуючи складність, затримку, ризик та обчислювальні витрати. Робочий процес маршрутизації зазвичай поєднує статичні правила, прогностичну маршрутизацію та динамічні каскади відмови:
1. **Детерміновані/статичні політики-ворота:** фільтрують запити за рівнем клієнта, жорсткими угодами про рівень обслуговування (SLA) щодо затримки, регуляторним/доменним ризиком (наприклад, медична діагностика або юридичне документообіг спрямовується безпосередньо до моделей вищого рівня) або простими завданнями, що відповідають правилам (наприклад, базові регулярні вирази/форматування до малих моделей).
2. **Прогностична маршрутизація за складністю:** швидкий, легковажний класифікатор (такий як пошук подібності вбудованих векторів, перехресний кодер або маршрутизатор малих мовних моделей (SLM)) оцінює складність запиту, глибину міркування та неоднозначність домену, щоб заздалегідь вибрати найрентабельніший рівень.
3. **Каскади динамічного виконання та ескалації:** надіслати запит спочатку до меншої моделі та оцінити впевненість вихідних даних (за допомогою лог-ймовірностей/ентропії токенів, валідності структурованої схеми або перевірок захисних механізмів). Якщо впевненість нижче порогу або перевірка не вдається, маршрутизатор ескалує до середньої або великої моделі. Ключові компроміси системи включають накладні витрати на затримку маршрутизатора проти економії обчислювальних ресурсів, бюджети часу очікування відмови при піках трафіку та безперервну оцінку (наприклад, тіньову оцінку для відстеження зміщення якості вихідних даних між рівнями).
class DynamicModelRouter:
def __init__(self, small_client, medium_client, large_client, classifier, guardrail):
self.small = small_client
self.medium = medium_client
self.large = large_client
self.classifier = classifier
self.guardrail = guardrail
async def route_and_execute(self, request):
# 1. Deterministic Risk Gate
if request.risk_level == "high" or request.domain in ["legal", "medical_compliance"]:
return await self.large.generate(request.prompt)
# 2. Predictive Complexity Classifier
complexity = self.classifier.predict_complexity(request.prompt) # 0.0 to 1.0
if complexity < 0.35:
response = await self.small.generate(request.prompt)
if self.guardrail.is_acceptable(response):
return response
return await self.medium.generate(request.prompt) # Fallback
if complexity < 0.75:
response = await self.medium.generate(request.prompt)
if self.guardrail.is_acceptable(response):
return response
return await self.large.generate(request.prompt) # Fallback
# 3. High complexity frontier execution
return await self.large.generate(request.prompt)