Усі уроки · 44
Розмова з моделлю: токени, контекст і семплінг
Як влаштована розмова з LLM: як текст перетворюється на токени, чому переповнюється контекстне вікно і що насправді роблять temperature, top-p та top-k.
Усе — це токени
Чому це важливо? Тому що токени — це одиниця, в якій вам виставляють рахунок, одиниця, в якій вимірюється межа пам’яті, і причина, з якої «хитрий» промпт інколи коштує вдесятеро дорожче, ніж ви розраховували. П’ятнадцять хвилин над цим уроком перетворять загадкові рахунки на просту арифметику.
Модель читає токени — фрагменти тексту з фіксованого словника, в англійській зазвичай завдовжки 3–4 символи. Літер і слів вона не бачить. Слово «artificial» може виявитися одним токеном або двома — це залежить від токенізатора (невеликої програми, яка розбиває текст на токени до того, як його побачить модель). Код, неанглійські мови та рідкісні символи часто розбиваються на токени менш ефективно — а отже, коштують дорожче й швидше заповнюють контекст.
Три практичні наслідки:
- Платити доводиться за токени — і за те, що ви надсилаєте, і за те, що отримуєте.
- Контекстне вікно — усе, що модель «бачить» в одному запиті, її робоча пам’ять, — вимірюється в токенах. У сучасних моделей воно становить від 8 тис. (приблизно обсяг цього уроку) до мільйона й більше.
- Між запитами модель нічого не пам’ятає. Те, що здається пам’яттю, — це ваш застосунок, який щоразу заново надсилає всю розмову. Модель бачить ваш промпт як один суцільний потік: системні інструкції, повідомлення користувача, результати інструментів, її власні попередні відповіді — усе це зліплено в токени. Жодної чарівної пам’яті за межами цього вікна немає.
Контекстне вікно — це бюджет
Уявіть контекстне вікно як бюджет, з якого витрачають усі частини вашого застосунку. Ось реалістичний приклад роботи агента — натисніть «запуск» і подивіться, як бюджет заповнюється:
Дві речі, які інженери знову й знову дізнаються на гіркому досвіді:
- Кожне повідомлення знову надсилає все. Розмова з 10 реплік коштує дорожче, ніж 10 реплік у токенах: ви платите за «трикутник» історії, що повторюється й щоразу росте. Тому «простий чат-бот» може коштувати дорого.
- Довгий контекст — це не безкоштовна пам’ять. Моделі помітно гірше використовують інформацію, сховану в середині величезного контексту («загублене посередині», lost in the middle). Більше контексту не означає кращих відповідей.
Коли вікно заповнюється, застосунки виконують стиснення контексту (compaction): коротко переказують старі репліки й відкидають деталі, лишаючи тільки важливе. Ви вчинили б так само, передаючи колезі довге листування з клієнтом: замість 200 повідомлень написали б «клієнт замовив двічі, просить повернути гроші за одне замовлення». (Позначте в симуляції вище пункт «симулювати стиснення контексту»: громіздкий результат інструмента зникне, а закріплений системний промпт залишиться. Закономірність така: інструкції мають пережити стиснення, а зайвий обсяг — ні.)
Семплінг: як вибирається один токен
На виході модель видає ймовірність для кожного токена зі свого словника (повного списку відомих їй фрагментів — їх десятки тисяч). Щось має перетворити ці ймовірності на один конкретний вибір. Це і є семплінг — кидок зваженого кубика, — і ви визначаєте, як саме цей кубик зміщено.
Головний параметр — температура (temperature). Перш ніж вибрати токен, модель ділить свої «сирі» оцінки на температуру. Висока температура «згладжує» розподіл: малоймовірні токени стають імовірнішими — ніби кубик перебалансували так, що всі грані майже рівні. Низька температура, навпаки, «загострює» його: найімовірніший токен стає майже неминучим. За температури 0 модель завжди вибирає єдиний найімовірніший токен, тож той самий вхід дає той самий результат.
Поекспериментуйте:
low temperature → the sharp token wins almost always (safe, repetitive). high temperature → the distribution flattens, unusual tokens get a real chance (creative, risky).
Виставте температуру 0,0 і натисніть «вибрати» кілька разів: щоразу випадатиме «Париж». Тепер підніміть її до 1,5 і знову поклацайте: почнуть з’являтися «Лондон» і «місто». Жодне зі значень не «правильне» — це різні інструменти:
- Низька температура (0,0–0,3) — вилучення даних, класифікація, код, усе, де потрібна найімовірніша відповідь, причому детерміновано (той самий вхід — той самий результат, щоразу).
- Середня (0,5–0,8) — звичайний чат і письмо: трохи різноманіття без нісенітниці.
- Висока (від 1,0) — мозковий штурм, творча генерація — коли ви хочете несподіванок.
Решта параметрів — коротко:
- top-p (ядерний семплінг) — замість фіксованої кількості кандидатів модель залишає найменший набір токенів, імовірності яких у сумі дають p (наприклад, 0,9), і вибирає вже з них. Це можна розуміти як «відкинути довгий хвіст безглуздих варіантів». Його часто використовують разом із температурою; багато команд просто задають температуру, а top-p лишають за замовчуванням.
- top-k — модель залишає лише k найімовірніших токенів, а решту ігнорує. Це грубіший варіант top-p.
- штраф за повторення (repetition penalty) — невеликий «поштовх», через який уже використані токени стають менш імовірними; він зменшує зациклення в довгих відповідях. Одні API дають ним керувати, інші ні.
- max tokens — жорстка межа довжини відповіді. Задавайте її завжди: це ваша страховка від некерованого рахунку.
При цьому творчість у відповідях задає промпт, а температура лише додає шуму: вона визначає, як далеко від битої стежки забреде семплер.
Практичне правило
Починайте з температури 0,2–0,7 — залежно від того, наскільки творче завдання. Змінюйте top-p і top-k лише тоді, коли є конкретна причина: у більшості API вже задано розумні значення за замовчуванням.
Кожен параметр на цій сторінці — температура, top-p, max tokens — змінює лише продовження в одному виклику. Сам виклик лишається незмінним: на вході текст, на виході продовження. Чат, агенти, інструменти та RAG — це той самий виклик, тільки з іншим текстом на вході. Ця думка — основа всього курсу: Один виклик: як насправді працює будь-який ШІ-продукт.
Спробуйте: скопіюйте цю конфігурацію
З цієї конфігурації я починаю будь-який новий проєкт. Скопіюйте її, змініть назву моделі й змінюйте по одному числу за раз — ніколи по два, інакше ви не зрозумієте, яке з них допомогло.
response = client.chat.completions.create(
model="gpt-4o-mini",
# 0.2 для фактів і вилучення даних, 0.7 для текстів і чату
temperature=0.2,
# жорстка межа довжини відповіді — задавайте її завжди
max_tokens=500,
# не чіпайте це без причини
# top_p=0.9,
# n=1, # скільки альтернативних відповідей згенерувати
messages=[
{"role": "system", "content": "You are a concise assistant."},
{"role": "user", "content": "Your question here."},
],
)Два параметри, які легко пропустити й які варто знати: n=1 означає «дай мені
одну відповідь» — збільште його, щоб отримати кілька варіантів на вибір (і
заплатити за кожен). А seed, де він підтримується, робить семплінг
відтворюваним: той самий seed, той самий вхід — той самий результат; це зручно,
коли ви налагоджуєте промпт.
Потокова передача: чому відповідь з’являється по словах
Модель генерує по одному токену, зліва направо: вона не може почати останнє слово, доки не написала всі попередні. API вміють надсилати вам кожен токен у момент, коли він створений, — це і є потокова передача (streaming). Сама відповідь не змінюється, змінюється лише доставка. Порівняйте із завантаженням файла: він лишається того самого розміру, є індикатор прогресу чи ні, але з індикатором очікування здається набагато коротшим.
Ось і весь трюк: користувач починає читати вже за кілька сотень мілісекунд, хоча на готовий блок тексту довелося б чекати кілька секунд. Для будь-якого інтерактивного інтерфейсу потокова передача — обов’язковий мінімум.
Кешування промпта: платимо один раз за контекст, що повторюється
Зверніть увагу на симуляцію вище: системний промпт і документи надсилаються знову з кожним запитом, і щоразу ви платите повну ціну. Багато провайдерів пропонують кешування промпта (prompt caching): ви позначаєте незмінну частину (стабільний початок промпта — системний промпт, прикріплені документи, описи інструментів), і провайдер використовує вже виконану роботу замість того, щоб повторювати її. Читання з кешу зазвичай коштує малу частку звичайної ціни вхідних токенів і скорочує час до першого токена.
Спочатку стабільне, потім змінне. Якщо у вашому промпті чергуються змінний і незмінний вміст, кеш не спрацює. Помістіть великий незмінний блок на початок, а справжнє повідомлення користувача — в кінець.
Перевірте себе
Ваш чат-бот з кожним новим повідомленням надсилає всю історію з 40 повідомлень. Що в цьому разі відбувається на боці провайдера?
Вам потрібно, щоб той самий вхід завжди давав той самий результат. Що ви задаєте?
Ваш застосунок ставить запитання користувача першим, а величезну незмінну базу знань — другою. У чому практичний мінус?