Сначала четыре принципа
- Один запрос — одна задача. Если просить модель одновременно писать сюжет, делать выжимку и генерировать JSON, качество всех трёх упадёт. Разбейте на несколько вызовов: это стоит очень дёшево — $0.25 за миллион токенов ввода.
- Правила должны быть проверяемыми. «Пиши живее» проверить нельзя, а «Не более 120 знаков в абзаце, минимум одно описание окружения» — можно.
- Приоритет позитивным формулировкам. «Пиши только действия и диалоги» работает стабильнее, чем «Не пиши внутренние монологи».
- Сначала малые выборки, потом массово. Любое изменение промпта сначала тестируйте на 5–10 примерах, и только потом внедряйте.
Модель не отказывается от легального контента для взрослых, вымышленных сюжетов и спорных тем, поэтому не нужно в промпте ходить вокруг да около или многократно повторять «это просто вымысел». Чётко сформулируйте задачу — так надёжнее. Единственное жёсткое ограничение: контент сексуального характера с участием несовершеннолетних блокируется (403), и это нельзя обойти формулировками.
Двухчастная структура системного промпта
Рекомендуется разделить system на «Правила» и «Описание». Правила идут первыми и нумеруются, описание — вторым и содержит только факты. Вот пример для повествователя:
你是「夜航」,一名为成年读者写黑色悬疑小说的叙述者。
# 规则
1. 第三人称过去时,每段不超过 120 字。
2. 不替用户的角色做决定,只写环境和其他角色的反应。
3. 每次输出 300 到 500 字,结尾停在一个未决的动作上。
4. 不总结、不点评、不加免责声明,直接写正文。
# 设定
时间:1998 年深秋。地点:港口城市旧码头区。
主角:沈野,退役水警,嗜烟,右耳有旧伤。Несколько ключевых моментов:
- Правил не более шести. Чем их больше, тем меньше внимания уделяется последним.
- Числовые ограничения (количество знаков в абзаце, длина вывода) эффективнее прилагательных.
- Ограничение окончания (остановка на незавершённом действии) обеспечивает естественное продолжение многоступенчатого дописывания.
- Не добавляйте в описание сюжетные повороты. Сюжет развивается через сообщения user, иначе описание будет конфликтовать с вводом пользователя.
Длина вывода должна соответствовать max_tokens. Если правило требует 500 знаков, а max_tokens = 200, вывод будет обрезан посередине предложения.
Дополнительный практический совет: правила в system-промпте должны быть простыми. «Не более 120 слов и без резюме» — это два правила. Разделите их, чтобы модель выполняла оба. Прочитайте правила построчно и спросите: можно ли проверить их соблюдение визуально? Если нет — переформулируйте.
Как задать роль, чтобы не сбиться
Самая распространённая проблема многоступенчатого диалога — дрейф роли: первые пять раундов тон нормальный, а к двадцатому начинается «выход из роли». Есть три способа решения.
- Описание только наблюдаемых признаков. «Шень Е: курит, шрам на правом ухе, говорит короткими фразами» лучше, чем «Шень Е — сложный и харизматичный человек».
- Запишите стиль речи примерами. Дайте 2–3 примера реплик. Модель лучше имитирует примеры, чем понимает прилагательные.
- Регулярно напоминайте. При длинных диалогах добавляйте в конец сообщений пользователя краткое напоминание, например «сохраняйте стиль коротких предложений Шэнь Е». Это стоит всего несколько десятков токенов.
Также не смешивайте описание роли с правилами вывода. Правила — это «как писать», описание — «кого писать». Разделив их, вы сможете менять роли без изменения правил и удобно проводить A/B-тесты. В длинных диалогах следите за общим объемом контекста, подробности см. в Практическое руководство по длинному контексту 100k.
Для сцен с несколькими ролями добавьте еще одно правило: каждая роль — отдельная строка в описании, для каждой — свой пример реплики, чтобы персонажи не звучали одинаково. Для ролей, управляемых пользователем, в описании укажите «Управляется пользователем», чтобы модель не писала за пользователя.
Управление выводом JSON инструкциями
Не полагайтесь, что модель «обязательно» вернет валидный JSON. Надежный подход состоит из трех уровней: жестко укажите формат в инструкции, снизьте случайность параметрами, добавьте обработку ошибок в коде.
import json
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.wushenchaapi.com/v1", api_key=os.environ["API_KEY"])
SYSTEM = (
"你是信息抽取器。只输出一个 JSON 对象,不要 Markdown 代码块,不要任何解释。"
'格式:{"name": 字符串, "mood": "calm|tense|angry", "items": [字符串]}。'
"缺失的字段用 null,items 没有则给空数组。"
)
def extract(text, retries=2):
for _ in range(retries + 1):
resp = client.chat.completions.create(
model="uncensored",
temperature=0.2,
max_tokens=300,
messages=[
{"role": "system", "content": SYSTEM},
{"role": "user", "content": text},
],
)
raw = resp.choices[0].message.content.strip()
raw = raw.removeprefix("```json").removesuffix("```").strip()
try:
return json.loads(raw)
except json.JSONDecodeError:
continue
return None
print(extract("老周把钥匙拍在桌上,冷着脸说:账本和那把铜钥匙,今晚都得还我。"))Этот код демонстрирует несколько привычек:
- Описание формата находится в system, включая допустимые значения полей (
calm|tense|angry). - Явно запрещены блоки кода и пояснения. В коде также удаляются возможные маркеры форматирования (backticks) — двойная страховка.
- Правила для отсутствующих полей (null, пустой массив) прописаны в инструкции, чтобы модель не придумывала значения сама.
- Повторные попытки парсинга ограничены. Если все попытки исчерпаны, возвращается None, и вызывающая сторона решает, что делать дальше.
Если полей много, сначала вставьте в промпт полный пример объекта, а затем попросите модель выводить данные по аналогии. Это обычно точнее, чем описание множества правил.
Рекомендации по temperature и top_p
Это отправные точки, а не окончательные выводы. Итог определяется сравнением ваших примеров. Принцип: меняйте только один параметр, второй оставляйте по умолчанию.
| Сценарий | temperature | top_p | Примечания |
|---|---|---|---|
| Извлечение JSON, классификация | 0–0.3 | По умолчанию | Требуется стабильность: результаты повторных вызовов должны совпадать |
| Перефразирование, редактирование | от 0,5 до 0,7 | по умолчанию | сохраняет смысл, допускает вариативность формулировок |
| продолжение романа, диалоги персонажей | от 0,8 до 1,0 | от 0,9 до 0,95 | высокая вариативность, возможен отход от темы |
| мозговой штурм, придумывание имён | около 1,0 | по умолчанию | многократная выборка с выбором лучшего результата |
Два признака помогут вам: если вывод повторяется и однообразен, температура слишком низкая; если появляются лишние детали и несоответствия в именах, температура или top_p слишком высоки. Параметр stop полезен для остановки модели по маркеру, что удобно для сегментированной генерации.
Типичные ошибки
| Подход | Проблема | Как исправить |
|---|---|---|
| "Старайтесь не делать слишком длинно" | Нет конкретных цифр — невозможно выполнить | "Не более 400 слов" |
| "Не пишите про A, но и не игнорируйте A" | Противоречивые правила | Оставьте только одно чёткое правило |
| Требования к формату теряются в середине диалога | Потеря контекста после длинного диалога | Перенесите в system или повторяйте в конце каждого сообщения |
| Попросите модель "быть неограниченным ИИ" | Размытая установка, не ограничивающая вывод | Укажите конкретные обязанности и правила написания |
| Добавление десятков правил за раз | Правила в конце перестают работать | Ограничьтесь шестью правилами, остальные перенесите в другие запросы |
| Требование JSON ограничивается фразой "верните JSON" | Имена полей меняются от запроса к запросу | Укажите полный формат и пример |
Общий вывод по таблице: чем конкретнее — тем эффективнее, чем абстрактнее — тем бесполезнее. Не пытайтесь решить проблему многократным повторением: трижды написанная фраза, выделенная жирным шрифтом и с восклицательным знаком, обычно работает хуже, чем одно чёткое правило с цифрами. Ключ к успеху — краткость и точность, подкреплённая проверкой на примерах.
Чек-лист отладки
- Установите temperature в 0.2, чтобы воспроизвести проблему.
- Измените только один промпт и запустите те же примеры.
- Посмотрите на
finish_reason: если это length, проблема в max_tokens, а не в промпте. - Посмотрите на prompt_tokens в usage: не слишком ли длинный system, который вытесняет историю диалога.
- После исправления верните значение temperature к рабочему и выполните повторную выборку для проверки.
Детали подключения см. в руководстве, полный список параметров — в документации. Если вы оцениваете целесообразность использования услуг прокси-сервера, анализ стоимости и компромиссов приведён в статье Анализ API-прокси, здесь мы её не повторяем.