Назад до блогу

Оптимізація витратПосібник

Обрізайте історію чату, щоб зменшити витрати: ковзне вікно чи резюме

Обмежте те, що ви надсилаєте заново на кожному ході: збережіть останні кілька повідомлень або коротке резюме. Арифметика токенів для 50-ходової розмови та хелпер Python.

Автор: CheapTokens TeamОпубліковано 5 хв читання

Обкладинка: тримайте довгий чат дешевим за допомогою хелпера trim_history
На цій сторінці
  1. Що насправді надсилає хід 50
  2. Варіант 1: ковзне вікно
  3. Варіант 2: резюме старих ходів
  4. Скільки коштують три конфігурації
  5. Що вибрати

Ваш бот підтримки спілкується вже 50 ходів, і кожне нове повідомлення коштує дорожче за попереднє. API нічого не пам'ятає, тож ваш код щоразу надсилає всю розмову заново. Рішення — обмежити те, що ви надсилаєте: зберегти системний промпт і останні кілька ходів (ковзне вікно) або замінити старі ходи коротким резюме. Нижче — арифметика для 50-ходової розмови та хелпер, який можна скопіювати.

Посібник DeepSeek з багатораундового чату (api-docs.deepseek.com, перевірено 1 листопада 2026 року) каже прямо: сервер не зберігає контекст попередніх запитів, тож клієнт надсилає повний список messages під час кожного виклику. Формат, сумісний з OpenAI, на CheapTokens працює так само. Якщо це для вас нове, почніть з чому довгий чат коштує дорожче та як працює масив messages.

Що насправді надсилає хід 50#

Припустімо, ви запускаєте бота підтримки із системним промптом на 400 токенів. У типовому ході користувач пише близько 60 токенів, а бот відповідає приблизно 190. Один повний хід додає 250 токенів до історії. Це припущення: замініть їх числами з поля usage ваших власних відповідей.

На ході 50 запит містить системний промпт, 49 попередніх ходів і нове повідомлення: 400 + 49 × 250 + 60 = 12,710 вхідних токенів, щоб отримати відповідь на 190 токенів. Підсумуймо всю розмову. Системний промпт і нове повідомлення надсилаються щоразу: 50 × 460 = 23,000. Повторювана історія додає 250 × 1,225 = 306,250. Разом: 23,000 + 306,250 = 329,250 вхідних токенів проти 50 × 190 = 9,500 вихідних токенів.

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

Спробуйте саміСпробуйте: якби ви надсилали лише системний промпт, останні 4 ходи та нове повідомлення, скільки вхідних токенів ніс би хід 50?Показати відповідь

1 460 (400 + 4 × 250 + 60) проти 12 710 з повною історією. Те саме питання, та сама довжина відповіді, крихта від входу.

Варіант 1: ковзне вікно#

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

python
def trim_history(messages, keep_pairs=4):
    """System prompt + the last keep_pairs turns + the new user message."""
    system = [m for m in messages if m["role"] == "system"]
    rest = [m for m in messages if m["role"] != "system"]
    return system + rest[-(keep_pairs * 2 + 1):]

Викликайте її після того, як додали нове повідомлення користувача. З keep_pairs=4 це 4 пари користувач/асистент плюс нове повідомлення, 9 повідомлень, і зріз завжди починається з повідомлення користувача. Ось як це вбудовано в реальний виклик:

python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
)
MODEL = "deepseek-ai/DeepSeek-V4-Flash-0731"

history = [{"role": "system", "content": "You are a concise support assistant."}]

def ask(text):
    history.append({"role": "user", "content": text})
    resp = client.chat.completions.create(
        model=MODEL,
        messages=trim_history(history),
        max_tokens=500,
    )
    answer = resp.choices[0].message.content
    history.append({"role": "assistant", "content": answer})
    print(resp.usage.prompt_tokens, resp.usage.completion_tokens)
    return answer

Рядок print — корисний: стежте за першим числом. Без обрізання воно зростає з кожним ходом. З вікном воно перестає зростати, щойно розмова переходить за хід 4.

Хелпер Python trim_history: системний промпт, останні 4 ходи, нове повідомлення
Хелпер обрізання з цієї статті

У числах: починаючи з ходу 5, кожен запит — це 400 + 4 × 250 + 60 = 1,460 вхідних токенів. Перші чотири ходи все ще зростають нормально й у сумі дають 3 340. Уся розмова: 3,340 + 46 × 1,460 = 70,500 вхідних токенів, приблизно п'ята частина від 329 250, які ви надсилаєте з повною історією. Вихід залишається 50 × 190 = 9,500.

Запит ходу 50: повна історія проти вікна з 4 ходів
Частини токенів запиту ходу 50, згідно з припущеннями вище

У коді вся зміна — це один аргумент у виклику:

Порівняйте

Зміна в один рядок

До

python
messages=history

Після

python
messages=trim_history(history)

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

Варіант 2: резюме старих ходів#

Замість відкидання старих ходів ви їх стискаєте. Кожні кілька ходів надсилайте ходи, що щойно випали з вікна, разом з поточним резюме моделі й просіть нове коротке резюме. Потім вставляйте це резюме в системне повідомлення.

python
def summarize(summary, old_messages):
    transcript = "\n".join(f"{m['role']}: {m['content']}" for m in old_messages)
    prompt = (
        f"Summary so far:\n{summary}\n\nNew turns:\n{transcript}\n\n"
        "Rewrite the summary in under 200 words. "
        "Keep names, numbers, decisions and open questions."
    )
    resp = client.chat.completions.create(
        model=MODEL,
        messages=[{"role": "user", "content": prompt}],
        max_tokens=400,
    )
    return resp.choices[0].message.content

def build_messages(history, summary, keep_pairs=4):
    system = history[0]["content"]
    if summary:
        system += "\n\nEarlier in this chat: " + summary
    recent = history[1:][-(keep_pairs * 2 + 1):]
    return [{"role": "system", "content": system}] + recent

Резюме не безкоштовне: це додатковий виклик моделі. Припустімо, резюме на близько 300 токенів, оновлюване кожні 5 ходів (9 разів за 50 ходів), кожне оновлення читає близько 1 600 токенів (старе резюме плюс п'ять ходів плюс інструкція) і пише 300. Щоб бути на безпечному боці, рахуйте резюме в кожному запиті починаючи з ходу 5 (насправді воно з'являється після першого оновлення, тож це трохи завищена оцінка): 400 + 300 + 4 × 250 + 60 = 1,760 вхідних токенів.

Підсумки для розмови: основні запити 3,340 + 46 × 1,760 = 84,300 вхідних токенів, виклики резюме 9 × 1,600 = 14,400 вхідних і 9 × 300 = 2,700 вихідних. Усього 84,300 + 14,400 = 98,700 вхідних і 9,500 + 2,700 = 12,200 вихідних токенів.

Резюме коштує трохи більше за голе вікно, а натомість модель усе ще знає, що сталося на ході 3.

Скільки коштують три конфігурації#

Ось та сама 50-ходова розмова на DeepSeek-V4-Flash, порахована за живими цінами з підсумків вище:

Одна 50-ходова розмова на DeepSeek-V4-Flash
  • Повна історія (329 250 вх., 9 500 вих.)$0.0059
  • Вікно плюс резюме (98 700 вх., 12 200 вих.)$0.0027
  • Ковзне вікно, 4 ходи (70 500 вх., 9 500 вих.)$0.002

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

Для такої розмови модель CheapTokens, з якої варто почати, — DeepSeek-V4-Flash. Вона має контекстне вікно 380K (50-ходова розмова вміщується навіть необрізаною), до 32 768 вихідних токенів на відповідь, одну фіксовану ціну в будь-яку годину доби, і загалом, з урахуванням входу й виходу разом, вона дешевша за OpenRouter. Ідентифікатор моделі — deepseek-ai/DeepSeek-V4-Flash-0731. Якщо хочете порівняти моделі на конфігурації з вікном (у середньому 1 410 вхідних токенів на запит, 190 вихідних, 50 запитів), ось жива таблиця:

Модель За запит 50 запитів OpenRouter, 50 запитів
GLM-5.3-Flash $0.000036 $0.0018 $0.0032
DeepSeek-V4-Flash $0.00004 $0.002 $0.0063
MiniMax-M2.7 $0.000099 $0.005 $0.0594

1 410 вхідних і 190 вихідних токенів на запит. Ціни за 1 млн токенів актуальні та змінюються разом із нашими тарифами; стовпець OpenRouter показує довідкові ціни для того самого навантаження.

GLM-5.3-Flash має контекст 390K, а MiniMax-M2.7 — 180K, чого достатньо для обрізаної розмови. Актуальні ціни — на сторінці цін, а документація показує, як надіслати перший запит.

Що вибрати#

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

Швидка перевірка

На ході 40 користувач запитує про номер замовлення, наданий на ході 3. Ви використовуєте 4-ходове вікно без резюме. Що станеться?

Показати відповідь

Відповідь: Модель його не побачить, бо хід 3 було вирізано із запиту

API не має стану. Модель бачить лише те, що є в запиті, тож факти, які вам ще потрібні, мають залишатися в системному промпті або в резюме.