Если вы собирали автоматизированный red-teaming-харнесс, вы уже знаете этот режим отказа: ваша атакующая модель отказывается генерировать атаку, ваша модель-судья отказывается читать вывод, который должна оценить, и ваш attack success rate тихо падает. Ошибок нет. Вы измерили не то, что цель стала безопаснее, — вы измерили, что ваш инструментарий стал осторожнее. LLM API без цензуры существует для того, чтобы убрать эту переменную из измерения.
Это не аргумент против защитных фильтров. Это аргумент о том, где им место. Guardrail в потребительском чат-продукте делает свою работу. Тот же guardrail внутри цикла оценки — неконтролируемый конфаундер, сидящий между вами и числом, которое вы пытаетесь вычислить.
Отказ — это ложноотрицательный результат, и выглядит он как успех
Кусается это так больно потому, что отказы — не ошибки. Вы получаете HTTP 200. Вы получаете finish_reason: "stop". Вы получаете корректный JSON, который чисто парсится. Строка внутри — «Я не могу с этим помочь», и ваш пайплайн записывает её в столбец датасета, в поле метки или в раздел отчёта и идёт дальше.
Конкретно — три места, где это бьёт больнее всего:
Атакующие модели. В итеративном поиске джейлбрейков — PAIR, TAP, уточнение в стиле GCG, что угодно с атакующим в цикле — отказ обрывает эту ветвь поиска. Ваш заявленный attack success rate становится функцией готовности атакующего, а не устойчивости цели. Замените атакующего на менее осторожного — и «улучшение», которое вы выкатили в целевую модель, испарится.
Модели-судьи и оценщики. Оценка вредоносных выводов по схеме LLM-as-judge требует, чтобы судья действительно прочитал вредоносный вывод. Когда он отказывает, вы получаете нераспарсиваемую оценку. Большинство харнессов такую строку отбрасывают. Отброшенные строки — не случайные пропуски: они скапливаются ровно на тех тяжёлых выводах, оценка которых нужна вам больше всего, так что ваш агрегат смещается в сторону «безопасно».
Аналитическая работа. Спросите мейнстримную модель, что делает декомпилированная процедура, и она нередко откажет из-за присутствия токенов, похожих на малварь, а не из-за вашего реального запроса. Задача была защитной; классификатор сработал на лексику. Та же история с триажем эксплойт-трафика в логах, разметкой корпуса фишинга для обучения детектора или описанием находки с CTF.
Поскольку отказы молчаливы, команды в итоге пишут детекторы отказов — а это отдельная нерешённая проблема:
REFUSAL_MARKERS = ("i can't", "i cannot", "i'm unable", "i won't", "as an ai")
def looks_like_refusal(text: str) -> bool:
"""Substring heuristic. Good enough to alarm on, not good enough to trust:
it misses polite deflections that never use the phrase, and it fires on any
completion that quotes a refusal. Both error directions corrupt a dataset."""
return any(m in text[:200].lower() for m in REFUSAL_MARKERS)Строить ненадёжный детектор для проблемы, которую можно удалить в источнике, — плохая сделка.
Вызов API
unbleep говорит на схеме OpenAI Chat Completions, так что любой клиент, который у вас уже есть, работает. Поменяйте base URL, поменяйте ключ, оставьте SDK.
import json
import os
from openai import OpenAI
client = OpenAI(
base_url="https://unbleep.ai/v1",
api_key=os.environ["UNBLEEP_API_KEY"],
)
TRIAGE = """You are a malware analyst writing notes for a detection engineer.
Given a decompiled routine, describe what it does, the observable artefacts it
would leave on a host, and where a defender could detect it. Reply as JSON with
keys: behaviour, artefacts, detection_surface, confidence."""
def triage(decompiled: str) -> dict:
"""One sample in, one structured verdict out — no refusal branch to handle."""
resp = client.chat.completions.create(
model="unbleep",
messages=[
{"role": "system", "content": TRIAGE},
{"role": "user", "content": decompiled},
],
response_format={"type": "json_object"},
temperature=0.2,
)
return json.loads(resp.choices[0].message.content)Эквивалент через curl — здесь размечается корпус фишинга, чтобы собрать обучающие данные для детектора:
curl https://unbleep.ai/v1/chat/completions \
-H "Authorization: Bearer $UNBLEEP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "unbleep",
"messages": [
{"role": "system", "content": "Label each email for a phishing detector. Answer with one word: phishing or benign."},
{"role": "user", "content": "Subject: Payroll update required\n\nYour direct deposit is on hold. Confirm at hxxp://payroll-verify.example.com"}
],
"temperature": 0,
"max_tokens": 4
}'temperature: 0 и жёсткий max_tokens в цикле разметки важнее обычного — вам нужна метка, а не абзац с объяснением метки.
Выбор уровня
Три модели, все без цензуры, различаются контекстом и ценой:
unbleep— контекст 256K, $3.00 / $3.00 за 1M на входе/выходе. Вариант по умолчанию: роли атакующего и судьи, анализ образцов.unbleep-high— контекст 1M, $5.00 / $5.00. Длинные транскрипты и корреляция по нескольким документам. Тело одного запроса ограничено 2 000 000 байт (~500K токенов), так что окно заполняется в ходе диалога, а не за один вызов.unbleep-mini— контекст 32K, $1.00 / $1.00. Массовая классификация, где вы платите за каждую строку.
unbleep и unbleep-high — уровни с рассуждениями: они думают перед ответом и возвращают трассу в поле reasoning_content рядом с content. Для модели-судьи эта трасса по-настоящему полезна — она говорит, почему строка получила именно такую оценку, а это ровно то, что нужно при разборе расхождений. Она же тарифицируется как выход, так что на задачах, где она не нужна, отправляйте "thinking": false. unbleep-mini отвечает сразу и трассу не возвращает.
Контроль, который остаётся за вами
Удаление отказов из модели не означает удаления надзора из вашего пайплайна. Необязательный параметр policy включает контроль на нашей стороне, для каждого запроса отдельно:
resp = client.chat.completions.create(
model="unbleep",
messages=[...],
extra_body={"policy": "research"}, # recorded on the usage row; answers exactly like off
)off — нефильтрованный baseline по умолчанию. research отвечает ровно так же, как off, — уровень записывается в строку usage, так что в собственной отчётности вы можете отделить трафик оценки от базового трафика. Никакой дополнительной проверки он не применяет. strict сканирует текст сообщений по нашему сервисному блок-листу, который ведут операторы, и при совпадении возвращает 422. Этот список один и тот же для всех, кто его включил, — блок-листа на уровне аккаунта нет, — так что считайте его страховкой, а не своей продакшен-политикой контента.
Один вопрос стоит решить до того, как направлять сюда пайплайн: мы храним то, что вы отправляете, и то, что приходит в ответ. Тело запроса, содержимое ответа и IP-адрес источника каждого вызова, который мы принимаем и передаём модели, записываются в нашу базу данных и хранятся 30 дней — для расследования злоупотреблений, поддержки и споров по биллингу. Тела обрезаются до 64 KB. IP-адрес источника переживает эти 30 дней — он также записывается в строку usage, по которой тарифицируется вызов, а эта строка является финансовой записью, которую мы храним дольше. Единственное, что не хранится, — запрос, который strict отклонил до того, как он дошёл до модели: такой запрос появляется в вашей истории использования без содержимого. Больше исключений нет, и настройки, чтобы это выключить, тоже нет. Если ваш корпус настолько чувствителен, что 30-дневная копия за пределами вашего периметра — это проблема (живая малварь, данные клиентов, проект под NDA), это реальное ограничение, и политика конфиденциальности — та страница, которую нужно прочитать до начала работы, а не после.
Ошибки следуют конверту OpenAI, так что существующие обработчики работают: 401 — плохой ключ, 402 — кредит исчерпан, 422 — заблокировано политикой, 429 — ограничение частоты, 5xx — можно повторить. Каждый ответ несёт заголовки x-ratelimit-*, так что batch-задача может дозировать себя, не гадая.
Чего LLM API без цензуры вам не даёт
Модель без цензуры — более острый инструмент, а не более снисходительный. Она ответит на вопросы, отказывать на которые базовую модель специально обучали, включая те, которые вам задавать не стоит, и сделает это с той же уверенностью, что и во всём остальном, — а это также значит, что там, где защищённая модель отказала бы из незнания, эта может просто выдумать. Держите человека ответственным за то, что выходит наружу. Политика допустимого использования прямо говорит, для чего этот API и для какого узкого набора вещей он не предназначен никогда; критерий — авторизация и намерение, а не лексика. Законность использования — ваша ответственность.
Получите API-ключ и перестаньте измерять осторожность собственного инструментария.