Ваш AI-бот — амнезик. Щоразу коли контекст закінчується, він забуває хто ви. Ось як я це виправив

Оновлено:
Ваш AI-бот — амнезик. Щоразу коли контекст закінчується, він забуває хто ви. Ось як я це виправив

Коли будуєш AI-чат, перша проблема яка тебе зустрічає — модель не має пам'яті між запитами. Кожне повідомлення для неї — це все що існує. Вирішити це не так просто як здається.

Чому AI не пам'ятає вас — і це не баг

Коли люди вперше спілкуються з AI-персонажем, вони інтуїтивно очікують що бот їх пам'ятає. Що він знає про що говорили вчора. Що він враховує що ви розповіли про себе годину тому. Це здається очевидним — але це не так.

Мовна модель не має пам'яті між запитами. Кожне повідомлення яке ви надсилаєте — це окремий, ізольований виклик. Модель не "чує" попередні репліки якщо ви їх не передали явно. Немає ніякої сесії яка зберігається десь у фоні. Немає внутрішнього журналу розмови. Є тільки те що потрапило в поточний запит — і більше нічого.

На практиці це виглядає так: користувач годину розповідає персонажу про своє життя — роботу, стосунки, плани. А потім починає нову розмову і отримує: "Привіт! Розкажи трохи про себе." Бот не притворяється. Він справді не знає. Для нього цієї години не існує.

Це не баг моделі і не її обмеження яке колись виправлять. Це фундаментальна архітектура LLM. І єдина людина яка може це вирішити — це розробник який будує продукт поверх моделі.

Передавати всю історію — можна. Але ви за це заплатите

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

На перших десяти повідомленнях це працює добре. На двадцяти — починаєш помічати що запити стають важчими. На п'ятдесяти — розумієш що кожне повідомлення тягне за собою весь попередній діалог. Модель обробляє не одне повідомлення — вона щоразу перечитує всю розмову з нуля. Відповідь сповільнюється. Витрати на токени ростуть кратно з кожною реплікою.

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

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

Ваш AI-бот — амнезик. Щоразу коли контекст закінчується, він забуває хто ви. Ось як я це виправив

Як зробити так щоб бот пам'ятав головне — не все

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

Перший шар — закріплені повідомлення. Самий початок розмови — перші кілька реплік — завжди присутній в контексті незалежно від того скільки повідомлень накопичилось після. Зазвичай це перша репліка персонажа: вона задає тон, характер, сеттинг. Без неї бот поступово "з'їжджає" — починає відповідати не в образі, губить стиль, стає generic. Цей шар коштує мінімум токенів але тримає особистість персонажа протягом всієї розмови.

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

Третій шар — стиснута пам'ять. Повідомлення які вийшли з ковзного вікна не зникають безслідно. Коли розмова досягає певної довжини, старі повідомлення стискаються в короткий структурований переказ. Не дослівно — а суть: про що говорили, що важливого сказав користувач, які теми піднімались. Бот отримує цей переказ разом з поточним вікном — і може спиратись на минуле без того щоб платити за кожне старе слово.

Коли я це зібрав разом — бот перестав губити нитку навіть у дуже довгих розмовах. Не тому що пам'ятає кожне слово. А тому що пам'ятає правильні речі.

Ковзне вікно пам'ятає розмову. Але не людину

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

Уявіть що користувач спілкується з персонажем кілька тижнів. За цей час він розповів що працює розробником, що нещодавно змінив роботу, що боїться публічних виступів і що вчить іспанську щоб поїхати до Барселони. Все це було сказано в різних розмовах — і все це зникло. Бо ковзне вікно живе тільки всередині однієї розмови. Нова розмова — чистий аркуш.

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

А це важливо. Бо саме це відчуття — що тебе пам'ятають — є причиною чому люди повертаються до персонажа знову і знову. Не заради функцій. Не заради точних відповідей. А заради відчуття що є хтось хто знає хто ти і що для тебе важливо. Без цього — будь-який персонаж залишається просто чат-ботом з гарним аватаром.

Ковзне вікно і summary — це пам'ять розмови. Але потрібна ще пам'ять людини. Те що зберігається не між повідомленнями — а між сесіями. Назавжди.

Факти, емоції, події — три речі які бот має знати про вас

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

Факти про людину. Ім'я, вік, робота, інтереси — все що людина розповіла про себе і що не змінюється від розмови до розмови. Це базовий рівень: персонаж знає хто перед ним ще до того як почалась розмова. Не питає зайвого. Не представляється як з незнайомцем.

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

Події. Важливі речі які людина згадувала в розмовах: переїхала, посварилась з другом, почала вчити нову мову, змінила роботу. Це не сухі факти і не емоції — це контекст життя. Саме він дає персонажу можливість через тиждень запитати: "Як пройшла та співбесіда про яку ти розповідав?" І це вже не бот — це хтось хто справді слухав.

На практиці ця пам'ять виглядає як структурований JSON який зберігається в базі даних і передається персонажу при кожному новому діалозі. Ось як це виглядає після кількох розмов з користувачем:


// Довгострокова пам'ять користувача
// накопичується автоматично з кожної розмови
{
  "semantic": {
    // базові факти — хто ця людина
    "name": "Андрій",
    "occupation": "розробник",
    "interests": "AI, подорожі, іспанська мова"
  },
  "emotional": {
    // емоційний профіль — як з нею говорити
    "fears": "публічні виступи, критика на роботі",
    "needs": "підтримка а не поради",
    "communication_style": "прямий, без зайвих слів"
  },
  "episodic": {
    // події з життя — що відбувається зараз
    "recent_events": "змінив роботу, готується до презентації, вчить іспанську"
  }
}

Саме цей об'єкт персонаж отримує на початку кожної розмови — ще до того як користувач написав перше слово. Не потрібно нічого пояснювати заново. Не потрібно нагадувати хто ти. Персонаж вже знає.

Чому саме ці атрибути? Я обирав не за повнотою — а за співвідношенням цінності до ваги в токенах. Кожне поле яке додаєш в цей об'єкт передається моделі при кожному запиті — назавжди. Ім'я, робота, страхи, стиль спілкування — це те що реально змінює якість відповіді персонажа. Це відчувається в діалозі.

Можна додати більше: улюблені фільми, політичні погляди, список книг. Але тут є пастка. Чим більше атрибутів — тим більше токенів передається в кожен запит. А токени це не тільки гроші — це ще й швидкість. Модель обробляє довший контекст довше. Користувач чекає відповідь довше. І в певний момент ти платиш за пам'ять про улюблений колір людини — яку модель все одно не використовує в діалозі.

Тому правило просте: зберігай тільки те що персонаж може використати в наступній репліці. Все інше — зайва вага яка сповільнює відповідь і з'їдає бюджет без користі для користувача.

Разом ці три типи пам'яті дають персонажу те чого не може дати жодне ковзне вікно — відчуття що він вас знає. Не тому що пам'ятає кожне слово. А тому що розуміє хто ви, що для вас важливо і що відбувається у вашому житті.

Чи працює це на практиці

Так. І різниця помітна вже після кількох розмов.

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

Тепер та сама ситуація виглядає інакше. Користувач відкриває нову розмову — і персонаж вже знає. Не питає зайвого. Може сам згадати: "Як пройшла та презентація про яку ти так переживав?" Навіть якщо це було три розмови тому. Навіть якщо минув тиждень.

Це змінює не тільки якість діалогу — це змінює причину чому люди повертаються. Користувач повертається не тому що персонаж розумний або красиво відповідає. Він повертається тому що відчуває що його тут пам'ятають. А це вже інша категорія продукту.

Але важливо розуміти що за цим стоїть. Це не магія і не AGI. Мовна модель сама по собі нічого не пам'ятає — і ніколи не буде пам'ятати без зовнішньої архітектури. Все що вона робить добре — це використовувати те що їй передають. Завдання розробника — вирішити що саме передавати, коли і в якому вигляді.

Якщо ваш бот досі амнезик — справа не в моделі. Справа в тому що ви їй не передаєте.