RU ▾
Получить API-ключ

Создание чат-бота на базе LLM API: руководство по Python

Abliterated — это большие языковые модели, в которых убраны механизмы отказа, что позволяет модели отвечать на вопросы по спорным, взрослым или узкоспециализированным темам без срабатывания фильтра контента. Такой подход даёт разработчикам полный контроль над тоном и границей знаний модели, что особенно ценно для программирования, ролевых игр и креативного письма, где стандартные ограничения могут блокировать полезный вывод.

Блоки сообщений чата, перемещающиеся по кругу в центральный куб, иллюстрирующие цикл разговора чат-бота

Ключевые моменты

  • API не имеет состояния, поэтому ваш код владеет историей разговора и отправляет её при каждом запросе.
  • Потоковая передача, вызов функций и JSON-режим используют стандартные параметры OpenAI.
  • Обрезайте или суммируйте историю до того, как она приблизится к окну в 100 000 токенов.
  • 1 000 чатов по 2 000 входных и 400 выходных токенов стоят около $0,50.

Основная идея: чат-бот — это цикл и список

Языковая модель не запоминает ничего между запросами. Каждый вызов POST /v1/chat/completions независим. То, что кажется памятью, — это ваша программа, отправляющая предыдущие сообщения снова, каждое с ролью: system для постоянных инструкций, user для того, что ввел человек, assistant для предыдущих ответов модели.

Таким образом, вся архитектура проста. Храните список. Добавляйте сообщение пользователя. Отправляйте список. Добавляйте ответ. Повторяйте. Всё остальное в этом руководстве, от потоковой передачи до инструментов, — это вариация этого цикла.

API здесь совместимо с OpenAI, поэтому вы используете стандартный пакет openai. Установите базовый URL в https://api.abliterated.cc/v1, используйте свой ключ и установите модель в uncensored. Получите ключ на странице API-ключ; $0,50 бесплатного пробного баланса достаточно для сотен коротких тестовых сообщений.

Шаг 1: работающий консольный чат-бот

Установите SDK с помощью pip install openai и запустите это.

from openai import OpenAI

client = OpenAI(
    base_url="https://api.abliterated.cc/v1",
    api_key="YOUR_KEY",
)

messages = [
    {"role": "system", "content": "You are a concise, friendly support assistant for a bike shop."}
]

while True:
    text = input("you: ")
    if text.strip() in {"quit", "exit"}:
        break
    messages.append({"role": "user", "content": text})
    resp = client.chat.completions.create(
        model="uncensored",
        messages=messages,
        temperature=0.7,
    )
    answer = resp.choices[0].message.content
    messages.append({"role": "assistant", "content": answer})
    print("bot:", answer)

Это полноценный чат-бот. Системное сообщение задаёт его задачу, список хранит память, а temperature управляет степенью разнообразия ответов. Уменьшайте его до 0,2 для ботов фактологической поддержки, увеличивайте до 1,0 для творческих задач.

Напишите системный промпт, определяющий задачу

Системное сообщение — самое дешевое место для улучшения чат-бота. Делайте его кратким и конкретным. Укажите роль, аудиторию, длину и тон ответов, а также действия, если бот не знает ответа. Промпт вроде «Вы отвечаете на вопросы для веломагазина. Отвечайте не более чем тремя предложениями. Если вопрос о цене ремонта, скажите, что механик должен осмотреть велосипед» делает больше, чем страница общих добродетелей.

Две привычки окупаются. Во-первых, формулируйте правила в виде действий, а не установок: «задайте один уточняющий вопрос, если запрос неоднозначен», а не «будьте полезны». Во-вторых, тестируйте с помощью враждебных входных данных, таких как пустое сообщение, очень длинная вставка и запрос, не имеющий отношения к вашему продукту. Исправляйте промпт на основе того, что вы видите, а затем сохраняйте эти входные данные как список регрессионных тестов.

Поскольку системное сообщение отправляется при каждом ходе, каждое слово в нём оплачивается в каждом запросе. Промпт на 400 токенов в 1 000 чатах по 5 ходов добавляет 2 000 000 входных токенов, или 2 x $0,15 = $0,30. Это небольшая сумма, но она показывает, почему раздутые промпты накапливаются при большом объёме.

Шаг 2: потоковая передача ответа

Ожидание полного ответа кажется медленным в окне чата. Установите stream: true и выводите части по мере их поступления. Потоковая передача использует события, отправляемые сервером, и заканчивается блоком использования, поэтому вы можете регистрировать количество токенов.

from openai import OpenAI

client = OpenAI(base_url="https://api.abliterated.cc/v1", api_key="YOUR_KEY")
messages = [{"role": "user", "content": "Explain tire pressure in two sentences."}]

stream = client.chat.completions.create(
    model="uncensored",
    messages=messages,
    stream=True,
)
reply = ""
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        part = chunk.choices[0].delta.content
        reply += part
        print(part, end="", flush=True)
    if getattr(chunk, "usage", None):
        print("\ntokens:", chunk.usage.prompt_tokens, chunk.usage.completion_tokens)

Не забудьте добавить собранный ответ reply в историю, иначе следующий ход не будет знать, что сказал бот.

Шаг 3: управление контекстом до его роста

Окно составляет 100 000 токенов на промпт и ответ вместе. Большинство чат-ботов никогда не достигнут этого предела, но стоимость растет с историей, потому что вы платите за весь промпт при каждом ходе. Простая политика держит оба показателя под контролем:

  • Всегда сохраняйте системное сообщение.
  • Сохраняйте последние 10–20 сообщений дословно.
  • Когда список превышает выбранный вами лимит размера, например 8 000 оценочных токенов, отправьте самый старый блок модели с инструкцией «кратко перескажите этот разговор не более чем в 150 словах» и замените этот блок кратким пересказом в виде системного сообщения.

Грубой оценки одного токена на четыре английских символа достаточно для триггера. Данные usage в каждом ответе дают вам точное значение постфактум, поэтому вы можете откалибровать лимит.

Если вы обрезаете список, удаляя самые старые сообщения, удаляйте их парами user и assistant. Ответ без вопроса или вопрос без ответа сбивает модель с толку сильнее, чем вы ожидаете.

Шаг 4: предоставление боту инструментов

Вызов функций позволяет модели просить ваш код что-то сделать, например, найти заказ. Вы описываете функции с помощью tools, модель отвечает записью tool_calls, вы запускаете функцию и отправляете результат обратно как сообщение role: "tool".

import json
from openai import OpenAI

client = OpenAI(base_url="https://api.abliterated.cc/v1", api_key="YOUR_KEY")

tools = [{
    "type": "function",
    "function": {
        "name": "get_order_status",
        "description": "Look up the status of an order by its id",
        "parameters": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
        },
    },
}]

def get_order_status(order_id):
    return {"order_id": order_id, "status": "shipped"}

messages = [{"role": "user", "content": "Where is order A1042?"}]
resp = client.chat.completions.create(
    model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = resp.choices[0].message

if msg.tool_calls:
    messages.append(msg)
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = get_order_status(**args)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result),
        })
    final = client.chat.completions.create(
        model="uncensored", messages=messages, tools=tools
    )
    print(final.choices[0].message.content)

Всегда проверяйте аргументы перед выполнением чего-либо с побочными эффектами. Модель предлагает вызов; ваш код решает, выполнять ли его.

Шаг 5: структурированный вывод с JSON-режимом

Когда следующий шаг в вашей программе требует данных, а не текста, например сортировка сообщения по категории, используйте JSON-режим с response_format. Упомяните слово JSON и ожидаемые ключи в вашем промпте тоже.

curl https://api.abliterated.cc/v1/chat/completions \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "uncensored",
    "response_format": {"type": "json_object"},
    "messages": [
      {"role": "system", "content": "Reply with JSON: {\"intent\": string, \"urgent\": boolean}"},
      {"role": "user", "content": "My brakes squeal and I ride tomorrow morning."}
    ]
  }'

Всё равно оборачивайте json.loads в блок try. Если парсинг не удался, повторите попытку один раз, добавив текст ошибки; неудачные запросы не оплачиваются.

Заметки об ошибках, стоимости и развертывании

Обработка ошибок, повторных попыток и лимитов запросов

Реальные пользователи отправляют запросы в неудобные моменты, а сети выходят из строя. Оберните вызов, чтобы один плохой ответ не разрушил разговор.

import time
from openai import OpenAI, APIError, RateLimitError

client = OpenAI(base_url="https://api.abliterated.cc/v1", api_key="YOUR_KEY")

def chat(messages, tries=3):
    for attempt in range(tries):
        try:
            resp = client.chat.completions.create(
                model="uncensored", messages=messages, temperature=0.7
            )
            return resp.choices[0].message.content
        except RateLimitError:
            time.sleep(2 ** attempt)
        except APIError as exc:
            if attempt == tries - 1:
                raise
            time.sleep(1)
    return "Sorry, the assistant is busy. Please try again in a moment."

Полный ключ позволяет 8 параллельных запросов и 300 запросов в минуту; пробный ключ — 2 и 60. Если ваш бот обслуживает многих пользователей одновременно, поставьте запросы в очередь с лимитом воркеров ниже 8, а не полагайтесь на повторные запросы. Поскольку неудачные запросы не оплачиваются, повторные запросы не расходуют токены. Держите количество повторных запросов небольшим, чтобы при реальном сбое ошибка возникала быстро.

Также регистрируйте заголовок X-Request-Id с каждым ходом разговора. Когда ответ выглядит неправильно, id позволяет найти точный запрос позже.

Стоимость и развертывание

Цена составляет $0,15 за 1M входных токенов и $0,50 за 1M выходных токенов. Предположим, чат поддержки использует 2 000 входных токенов в сумме и 400 выходных токенов.

  • Вход: 0,002M x $0,15 = $0,0003
  • Выход: 0,0004M x $0,50 = $0,0002
  • Один чат: $0,0005, значит 1 000 чатов стоят $0,50

Посмотрите текущие тарифы и бонусы за пополнение на странице цен. Для продакшена храните ключ на сервере, а не в коде браузера, и следите за заголовком ответа X-Balance-USD, чтобы получить предупреждение до исчерпания баланса. Полный ключ позволяет 8 параллельных запросов и 300 запросов в минуту, поэтому ставьте запросы в очередь, а не отправляйте их все сразу. В документации перечислены все поддерживаемые параметры.

Вопросы и ответы

Нужен ли фреймворк для создания чат-бота?

Нет. SDK OpenAI и списка сообщений достаточно для работающего бота. Добавьте фреймворк позже, только если вам понадобятся его дополнительные возможности.

Запоминает ли API предыдущие сообщения?

Нет. Каждый запрос независим, поэтому ваш код должен отправлять историю каждый раз.

Как не допустить слишком большого объёма истории?

Сохраняйте системное сообщение и последние реплики, а старые сводите к краткой заметке, когда список превышает заданный вами размер.

Может ли бот вызывать мои собственные функции?

Да. Передайте инструменты и tool_choice, запустите функцию, когда в ответе появятся tool_calls, и верните результат как сообщение от инструмента.

Ваш ключ — в одном шаге от вас

Создайте аккаунт, скопируйте ключ, измените базовый URL. Это вся настройка.