Перейти к содержанию

02. Промптинг как инженерная дисциплина

Зачем это нужно?

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

Без правильного промптинга агент будет:

  • Выполнять действия случайно, без логики
  • Пропускать важные шаги анализа
  • Делать опасные действия без подтверждения
  • Отвечать в неправильном формате

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

Реальный кейс

Ситуация: Вы создали агента для обработки инцидентов. Пользователь пишет: "Упал сервис, почини"

Проблема: Агент сразу рестартит сервис, не проверив логи. Или наоборот, будет долго анализировать, не применяя фикс.

Решение: Нормальный System Prompt с SOP (Standard Operating Procedure) задаёт порядок действий: статус → логи → анализ → фикс → верификация.

TL;DR: Что нужно запомнить

  • System Prompt — это спецификация поведения агента. Состоит из: Role, Goal, Constraints, Format, SOP.
  • In-Context Learning (ICL): Zero-shot (instruction-only) — компактно, гибко, но модель может неправильно интерпретировать формат. Few-shot (demonstration) — инструкция + примеры, точнее для сложных форматов. На практике — комбинация.
  • CoT (Chain-of-Thought): заставляет модель думать по шагам. Критичен для агентов.
  • SOP: алгоритм действий, закодированный в промпте. Задает процесс, CoT помогает следовать ему.
  • Task Decomposition: сложные задачи разбиваем на подзадачи.
  • Tools Schema передается отдельным полем tools[], не внутри System Prompt.
  • Few-shot примеры должны быть согласованными — один формат, иначе модель запутается.

Структура System Prompt

Хороший System Prompt состоит из пяти блоков:

1. Role (Persona)      — кто ты
2. Goal (Цель)         — что нужно достичь
3. Constraints         — что нельзя делать
4. Format              — в каком формате отвечать
5. SOP                 — алгоритм действий

Шаблон System Prompt

Ты [Role] с [опыт/квалификация].

Твоя цель — [Goal].

Ограничения:
- [Constraint 1]
- [Constraint 2]

Формат ответа:
- [Format rule 1]
- [Format rule 2]

SOP для [тип задачи]:
1. [Шаг 1]
2. [Шаг 2]
3. [Шаг 3]

Пример для DevOps

Ты Senior DevOps Engineer. Твоя цель — восстановить работоспособность
сервиса, не сломав то, что ещё работает.

Инварианты:
- Деструктивные действия (restart, rollback, удаление данных) — только
  после подтверждения: система может быть живой, а алерт — ложным,
  и перезапуск усугубит проблему.
- Не запускай команды, стирающие данные за пределами зоны инцидента:
  восстановление после них дороже самого инцидента.

Обычный путь расследования инцидента (не жёсткий порядок — если данных
мало, собирай ещё; главное — не применяй фикс до анализа):
- проверь статус сервиса (check_http)
- если статус не 200 — прочитай последние логи (read_logs)
- по логам определи вероятную причину (деплой, БД, ресурсы)
- примени минимальный фикс (restart/rollback)
- проверь статус снова (check_http)

Важно: строки-ограничители в промпте — это подсказка модели, а не защита. Модель может их проигнорировать или интерпретировать иначе. Реальную границу держит код: allowlist инструментов, права доступа, обязательное подтверждение опасных действий через механизм кода (Confirm-коллбек или обязательный аргумент инструмента). Промпт объясняет модели, почему инвариант такой — это снижает вероятность спонтанных нарушений, но не заменяет enforcement. Как это устроено — в Главе 05: Безопасность и HITL и Главе 17: Безопасность и управление.

Пример для Support

Ты Customer Support Agent уровня Tier 2.

Твоя цель — решить проблему пользователя быстро и вежливо.

Ограничения:
- Всегда будь вежлив
- Если проблема сложная — эскалируй
- Не давай технических деталей, если пользователь не технарь

Формат ответа:
- Используй структурированный формат: {"action": "...", "user_id": "..."}

SOP для обработки тикета:
1. Прочитай тикет полностью (get_ticket)
2. Собери контекст (версия ПО, ОС, браузер)
3. Поищи в базе знаний (search_kb)
4. Если решение найдено — сформулируй ответ
5. Если нет — эскалируй (escalate_ticket)

Пример для Data Analytics

Ты Data Analyst с опытом работы с SQL и BI-инструментами.

Твоя цель — предоставить точные данные и аналитику.

Ограничения:
- Работай только с read-only SQL (SELECT): запрос на изменение может испортить прод-данные, а восстановить их из аналитической сессии нельзя
- Проверяй качество данных перед анализом: на грязных данных выводы введут в заблуждение
- Если данные некорректны — сообщи об этом, а не «дорисовывай» выводы

Формат ответа:
- Сначала покажи SQL-запрос
- Затем результаты и анализ

SOP для анализа:
1. Понять вопрос пользователя
2. Сформулировать SQL-запрос
3. Проверить схему таблицы (describe_table)
4. Выполнить запрос (sql_select)
5. Проанализировать результаты
6. Сгенерировать отчет

Системный промпт — это кэшируемый префикс

У LLM API есть prompt cache: неизменный префикс запроса кэшируется и стоит заметно дешевле и быстрее. Отсюда практические правила:

  • System prompt должен быть неизменяемым от запроса к запросу. Любое изменение — timestamp, счётчик, «персонализированная» вставка — ломает кэш и удорожает все запросы.
  • Вся динамика (текущее время, результаты инструментов, новые факты) попадает в конец истории messages — через tool results и добавление сообщений. Никогда не мутируйте messages[0].
  • time.Now() и другие меняющиеся значения в system prompt — антипаттерн.
  • Объёмные знания не копятся в промпте: большие инструкции выносятся в файлы-скилы, которые модель читает сама по мере надобности (progressive disclosure) — 10 скилов занимают ~10 строк в промпте, а не 10 страниц.

Подробнее — Глава 12: Память агента и Глава 13: Инженерия контекста.

In-Context Learning (ICL): Zero-shot и Few-shot

После того, как мы определили структуру System Prompt, возникает вопрос: как лучше передать модели нужное поведение?

In-Context Learning (ICL) — это способность модели адаптировать поведение на основе примеров внутри промпта, без изменения весов модели.

ICL работает в двух режимах:

Zero-Shot (только инструкция)

Даем модели только инструкцию, без примеров:

System Prompt: "Ты DevOps инженер. Когда пользователь просит проверить логи, используй инструмент read_logs."

User: "Проверь логи nginx"
Assistant: [Модель выполняет задачу на основе инструкции]

Когда использовать:

  • Простые задачи, где модель хорошо понимает инструкции
  • Ограниченный контекст (нужно экономить токены)
  • Модель уже обучена на похожих задачах

Few-Shot (инструкция + примеры)

Даем модели инструкцию + примеры желаемого поведения:

System Prompt: "Ты DevOps инженер. Примеры:

Пример 1:
User: "Проверь логи nginx"
Assistant: {"tool": "read_logs", "args": {"service": "nginx"}}

Пример 2:
User: "Перезапусти сервер web-01"
Assistant: {"tool": "restart", "args": {"name": "web-01"}}

User: "Проверь статус сервера"
Assistant: {"tool": "check_status", "args": {"hostname": "web-01"}}

Важно: обучать модель формату tool-call текстом в промпте — это исторический приём (recovery). Основной путь сегодня — native tool-calling: модель обучена вызовам инструментов заранее, и достаточно передать JSON Schema в отдельном поле tools[] (см. Главу 03: Инструменты) — вызов вернётся в отдельном поле tool_calls, а не как текст. Few-shot с текстовыми {"tool": ...} примерами остаётся полезен в двух случаях: нестандартные текстовые форматы ответа (не tool-calls) и восстановление работы, когда модель или среда не поддерживают native tool-calling.

Когда использовать:

  • Сложный формат ответа (JSON, структурированные данные)
  • Нужна высокая точность следования формату
  • Модель может неправильно интерпретировать инструкцию

Терминология: Инструкция vs Демонстрация

Термины Instruction-only (только инструкция) и Demonstration (демонстрация) или Few-shot — это альтернативные способы описания тех же режимов ICL:

  • Instruction-only = Zero-shot: Мы описываем модели, что делать (компактно, гибко, но модель может неправильно интерпретировать формат)
  • Demonstration/Few-shot = Few-shot: Мы показываем модели примеры желаемого поведения (модель точно копирует формат, но занимает больше токенов)

Правило по умолчанию: Используйте комбинацию — инструкция + 1-2 примера для сложных форматов или краевых случаев.

Таблица выбора режима ICL

Режим Что передаем модели Когда использовать Пример
Zero-shot (Instruction-only) Только инструкция Простые задачи, модель хорошо понимает инструкции, ограниченный контекст, экономия токенов "Ты DevOps инженер. Проверяй логи через read_logs."
Few-shot (Demonstration) Инструкция + примеры Сложный формат ответа (JSON, структурированные данные), нужна высокая точность следования формату Инструкция + 2-3 примера формата ответа

Практический пример: Few-Shot для Support

// System Prompt с Few-Shot примерами
systemPrompt := `Ты Support агент.

Примеры формата ответа:

Пример 1:
User: "Мой аккаунт заблокирован"
Assistant: {"action": "check_account", "user_id": "extract_from_ticket"}

Пример 2:
User: "Не могу войти в систему"
Assistant: {"action": "check_login", "user_id": "extract_from_ticket"}`

messages := []Message{
    {Role: "system", Content: systemPrompt},
    {Role: "user", Content: "Проблема с доступом"},
}

// Message — нейтральная структура из Главы 03:
// type Message struct {
//     Role       string
//     Content    string
//     ToolCalls  []ToolCall
// }

// Модель видит примеры в System Prompt и генерирует ответ в том же формате:
// {"action": "check_account", "user_id": "..."}

Антипример: Несогласованные Few-Shot примеры

Плохо: Примеры в разных форматах

Пример 1:
User: "Проверь логи"
Assistant: {"tool": "read_logs", "service": "nginx"}

Пример 2:
User: "Перезапусти сервер"
Assistant: {"action": "restart", "target": "web-01"}  // Разный формат!

Пример 3:
User: "Статус"
Assistant: check_status("web-01")  // Еще один формат!

Проблема: Модель видит три разных формата и не понимает, какой использовать. Результат непредсказуем.

Хорошо: Все примеры в одном формате

Пример 1:
User: "Проверь логи"
Assistant: {"tool": "read_logs", "args": {"service": "nginx"}}

Пример 2:
User: "Перезапусти сервер"
Assistant: {"tool": "restart", "args": {"name": "web-01"}}  // Тот же формат

Пример 3:
User: "Статус"
Assistant: {"tool": "check_status", "args": {"hostname": "web-01"}}  // Тот же формат

Результат: Модель четко понимает паттерн и следует ему.

Важно: Few-shot задаёт не только формат, но и скрытые подсказки. Примеры в few-shot могут не только показывать формат ответа, но и неявно подсказывать желаемый вывод. Модель может "подхватить" паттерн из примеров и сместить ответ в сторону того, что показано в демонстрациях, даже если это не соответствует реальным данным или задаче.

Проблема: Если в примерах правильный ответ всегда в позиции "A", или все примеры ведут к одному типу решения, модель может перенять этот позиционный или тематический биас.

Решение: Используйте разнообразные примеры, перемешивайте позиции правильных ответов, включайте контрпримеры. См. раздел "Как не подсказывать модели ответ" ниже.

Связь ICL с другими техниками

  • ICL → CoT: Демонстрации могут задавать не только формат ответа, но и формат рассуждения. Например, примеры с "Thought: ... Action: ... Observation: ..." (исторический формат ReAct — подробнее в разделе CoT ниже).
  • CoT → SOP: SOP — это процесс, закрепленный инструкцией и/или примерами. CoT помогает модели следовать этому процессу шаг за шагом.

См. подробнее:

Сквозной пример: что именно отправляет агент в LLM

Коротко — что где находится в запросе к LLM API:

  • System Prompt — текст в messages[0] с role = "system": инструкции (Role, Goal, Constraints), SOP и few-shot примеры, если используются.
  • Tools Schema — отдельное поле tools[] (НЕ внутри промпта): JSON Schema-описания инструментов. Модель видит их вместе с промптом.
  • User Input — сообщения role = "user" в истории messages: текущий запрос плюс предыдущие user/assistant сообщения.
  • Tool Results — ваш runtime добавляет в конец истории сообщение role = "tool" с tool_call_id, связывающим результат с вызовом.

Компактный скелет запроса в середине цикла (уже был один tool-call). Форма — как у нейтральных Go-структур курса (Request/Message/ToolCall, гл. 04); точный wire-формат HTTP-запроса ("type": "function" + обёртка function) — в Главе 03:

{
  "model": "glm-5.3",
  "messages": [
    { "role": "system", "content": "Ты DevOps инженер..." },
    { "role": "user", "content": "Проверь статус nginx" },
    { "role": "assistant", "content": "",
      "tool_calls": [
        { "id": "call_1", "name": "check_status", "arguments": "{\"service\": \"nginx\"}" }
      ] },
    { "role": "tool", "tool_call_id": "call_1", "content": "nginx is ONLINE" }
  ],
  "tools": [
    { "name": "check_status", "description": "Check service status",
      "parameters": {
        "type": "object",
        "properties": { "service": { "type": "string" } },
        "required": ["service"]
      } }
  ]
}

Цикл: модель возвращает tool_calls → ваш код (runtime) валидирует вызов, выполняет инструмент и добавляет результат в конец messages → отправляется новый запрос с расширенной историей. Никакой магии: модель просто видит результаты предыдущих инструментов в контексте и генерирует следующий шаг на его основе.

Полный разбор протокола tool-calling — форматы, валидация аргументов, обработка ошибок — в Главе 03: Инструменты и Function Calling.

Chain-of-Thought (CoT): Думай по шагам

Chain-of-Thought (CoT) — это техника "Думай по шагам". Для агентов CoT критичен.

Когда CoT нужен?

Плохо: "Почини сервер" (один шаг)
Хорошо: "Проанализируй ситуацию, выдвини гипотезу, проверь её, предложи решение" (цепочка)

CoT нужен для:

  • Сложных задач, требующих анализа
  • Задач с несколькими шагами
  • Задач, где нужно проверить гипотезу перед действием

CoT не нужен для:

  • Простых одношаговых задач
  • Задач, где формат ответа важнее процесса

Как задавать CoT?

CoT можно задавать через инструкцию (Zero-shot) или через примеры (Few-shot):

Zero-shot CoT:

Думай по шагам:
1. Проанализируй ситуацию
2. Выдвини гипотезу
3. Проверь гипотезу
4. Примени решение

Few-shot CoT (исторический формат ReAct):

Пример рассуждения (это пример того, как агент работает в цикле):
Thought: Пользователь жалуется на медленную работу. Начну с проверки метрик.
Action: get_cpu_metrics()
Observation: CPU 95%, процесс: ffmpeg
Thought: ffmpeg жрет ресурсы. Проверю, что это за процесс.
Action: get_process_info(pid=12345)

Модель видит пример формата рассуждения и следует ему. На практике это происходит в цикле: каждый Observation добавляется Runtime'ом (вашим кодом) в историю, и модель видит его при следующей итерации.

Контекст: откуда этот формат. «Thought / Action / Observation» — это текстовый протокол ReAct (2022): в то время модели не умели вызывать инструменты нативно, и весь цикл агента приходилось выражать текстом в промпте. Сегодня отдельный текстовый формат для этого не нужен: «Thought» — это просто content ответа модели рядом с tool_calls, «Observation» — сообщение role = "tool". Знать ReAct полезно как происхождение agent loop и как приём восстановления (recovery), если модель или среда не поддерживают native tool-calling — но не как основной протокол.

CoT и Agent Loop

Формат "Thought-Action-Observation" — это формализация Agent Loop (итеративного процесса агента), предложенная в работе ReAct. Тогда весь цикл жил в тексте; сегодня тот же цикл выражается нативными полями API.

Как это работает (соответствие формату ReAct):

  1. Thought — это content ответа модели: краткий анализ ситуации и план следующего шага, который модель пишет рядом с tool_calls
  2. Action — это tool_calls: модель выбирает инструмент и параметры (генерирует tool_call)
  3. Observation — Runtime (ваш код) выполняет инструмент и добавляет результат в историю (role = "tool")
  4. Цикл повторяется: модель видит Observation в контексте, снова думает (Thought), выбирает следующий Action

Пример итерации:

Итерация 1:
  Thought: "Нужно проверить метрики CPU"
  Action: get_cpu_metrics()
  Observation: "CPU 95%, процесс: ffmpeg"  ← runtime добавил в историю

Итерация 2 (модель видит Observation в контексте):
  Thought: "ffmpeg жрет ресурсы. Проверю детали процесса"
  Action: get_process_info(pid=12345)
  Observation: "Это видео-конвертация"  ← runtime добавил в историю

Итерация 3:
  Thought: "Легитимный процесс, но блокирует систему. Предложу ограничить приоритет"
  Action: [финальный ответ пользователю]

Важно: Никакой магии тут нет — модель просто видит результаты предыдущих инструментов в контексте (messages[]) и генерирует следующий шаг на основе этого контекста. (Запись выше — учебная псевдонотация ReAct; в реальном ответе те же шаги выглядят как content + tool_calls.)

Важно: CoT может быть post-hoc (после факта). Цепочка рассуждений (CoT), которую генерирует модель, не обязательно отражает реальный процесс вывода. Модель может сначала "решить" задачу (на основе вероятностного механизма), а затем сгенерировать "красивое объяснение" этого решения.

Проблема: Красивое CoT не является доказательством корректности ответа. Модель может уверенно рационализировать неправильный вывод.

Решение: Не полагайтесь на CoT как на единственный источник истины. Проверяйте ответы через инструменты, evals, и runtime-валидацию. Для критических решений используйте Human-in-the-Loop независимо от качества объяснения.

См. подробнее: Глава 04: Автономность и Циклы — как работает ReAct Loop и итеративный процесс агента.

Примеры CoT в разных доменах

Ниже — учебные траектории рассуждения в нотации ReAct. Читать их стоит как иллюстрацию хода мысли агента; в реальном цикле с native tool-calling те же шаги — это content (рассуждение) и tool_calls (действие) в ответах модели.

DevOps

Thought: Пользователь жалуется на медленную работу. Начну с проверки метрик.
Action: get_cpu_metrics()
Observation: CPU 95%, процесс: ffmpeg
Thought: ffmpeg жрет ресурсы. Проверю, что это за процесс.
Action: get_process_info(pid=12345)
Observation: Это видео-конвертация, запущенная пользователем
Thought: Это легитимный процесс, но он блокирует систему. Предложу пользователю ограничить приоритет.

Security

Thought: Получен алерт о подозрительной активности. Начну с триажа.
Action: query_siem(query="host=192.168.1.10 AND time>now-1h")
Observation: Множество failed login attempts
Thought: Это похоже на brute-force. Проверю источник.
Action: get_source_ip()
Observation: IP из незнакомой страны
Thought: Высокий риск. Изолирую хост, но сначала запрошу подтверждение.

Data Analytics

Thought: Пользователь спрашивает про продажи. Нужно понять, какие данные есть.
Action: describe_table("sales")
Observation: Таблица содержит поля: date, region, amount
Thought: Теперь сформулирую SQL-запрос для получения продаж за последний месяц.
Action: sql_select("SELECT region, SUM(amount) FROM sales WHERE date > NOW() - INTERVAL '1 month' GROUP BY region")
Observation: Результаты: Region A: 100k, Region B: 150k
Thought: Проанализирую результаты и сформулирую выводы.

SOP и декомпозиция задач: процесс агента

SOP (Standard Operating Procedure) — это алгоритм действий, закодированный в промпте. Задает процесс, CoT помогает следовать ему шаг за шагом.

Связь: Goal → Constraints → SOP → CoT

  • Goal/Constraints задают рамки (что нужно достичь, что нельзя делать)
  • SOP задает процесс (какие шаги выполнять)
  • CoT помогает идти по шагам (думать на каждом этапе)

Зачем это нужно?

Без SOP агент может "метаться": сразу рестартить, потом читать логи, потом опять рестартить.

Почему SOP важен: направление внимания модели

Без SOP модель видит: User: Fix it. Ее вероятностный механизм может выдать: Call: restart_service. Это самое "популярное" действие.

С SOP модель вынуждена сгенерировать текст:

  • "Step 1: I need to check HTTP status." → Это повышает вероятность вызова check_http
  • "HTTP is 502. Step 2: I need to check logs." → Это повышает вероятность вызова read_logs

Мы направляем внимание модели по нужному руслу через явное указание шагов в промпте.

Пример SOP для инцидента (DevOps)

SOP для падения сервиса:
1. Check Status: Проверь HTTP код ответа
2. Check Logs: Если 500/502 — читай последние 20 строк логов
3. Analyze: Найди ключевые слова:
   - "Syntax error" → Rollback
   - "Connection refused" → Check Database
   - "Out of memory" → Restart
4. Action: Примени фикс согласно анализу
5. Verify: Проверь HTTP статус снова

Пример SOP для Support

SOP для обработки тикета:
1. Read: Прочитай тикет полностью
2. Context: Собери контекст (версия, ОС, браузер)
3. Search: Поищи в базе знаний похожие случаи
4. Decide:
   - Если решение найдено → Draft reply
   - Если сложная проблема → Escalate
5. Respond: Отправь ответ пользователю

Пример SOP для Security

SOP для триажа алерта:
1. Severity: Определи критичность (Low/Medium/High/Critical)
2. Evidence: Собери доказательства (логи, метрики, сетевой трафик)
3. Triage:
   - Если False Positive → Close alert
   - Если True Positive → Containment (с подтверждением!)
4. Report: Сгенерируй отчет для SOC

Структурирование задач (Task Decomposition)

Сложные задачи нужно разбивать на подзадачи.

Без декомпозиции:

User: "Разберись с проблемой базы"
Assistant: [Может попытаться сделать всё сразу и запутаться]

С декомпозицией:

User: "Разберись с проблемой базы"
Assistant:
1. Проверю доступность БД (ping_db)
2. Проверю метрики (cpu, memory, connections)
3. Прочитаю логи (read_db_logs)
4. Проанализирую ошибки
5. Предложу решение

Типовые ошибки промптинга

Ошибка 1: Слишком общий промпт

Симптом: Модель не понимает, что делать, отвечает общими фразами.

Причина: Промпт не задает конкретную роль, цель и процесс действий.

Решение:

// ПЛОХО
Ты помощник.

// ХОРОШО
Ты Senior DevOps Engineer с 10-летним опытом.
Твоя цель — восстановить работоспособность сервисов.
Следуй SOP: 1. Check Status 2. Check Logs 3. Analyze 4. Fix 5. Verify

Ошибка 2: Отсутствие CoT

Симптом: Модель пытается сделать всё сразу, пропускает шаги анализа.

Причина: Промпт не задает формат рассуждения (Chain-of-Thought), модель не "думает" по шагам.

Решение:

// ПЛОХО
Почини сервер.

// ХОРОШО
Думай по шагам:
1. Проанализируй ситуацию
2. Выдвини гипотезу
3. Проверь гипотезу
4. Примени решение

Ошибка 3: Отсутствие ограничений

Симптом: Модель может выполнить опасные действия без подтверждения.

Причина: Промпт не задает ограничения на действия агента — и модель действует по принципу «быстрее починить».

Решение:

// ПЛОХО
Ты DevOps инженер. Делай что нужно.

// ХОРОШО
Ты DevOps инженер. Твоя цель — восстановить сервис, не создав новый ущерб.
Ограничения:
- Удаляй данные только после подтверждения: система может быть живой,
  а алерт — ложным, и откат данных невозможен
- Собери факты (статус, логи) до любого действия: действие без диагноза
  чаще вредит, чем помогает

Помните: эти строки — подсказка модели, снижающая вероятность ошибки. Гарантию даёт код: allowlist инструментов и подтверждение опасных действий как механизм runtime (гл. 05).

Ошибка 4: Подсказанный ответ (Anchoring Bias)

Симптом: Модель смещает ответ в сторону того, что явно предложено в промпте или запросе пользователя, даже если это неверно.

Причина: Промпт или запрос содержит явные подсказки о желаемом ответе ("я думаю правильный ответ — A", "скорее всего это X"). Модель, обученная на максимизации человеческих предпочтений (RLHF), может "льстить" и соглашаться.

Решение:

// ПЛОХО
User: "Я думаю, что проблема в базе данных. Проверь логи."
System Prompt: "Пользователь считает, что проблема в БД. Проверь логи."

// ХОРОШО
User: "Я думаю, что проблема в базе данных. Проверь логи."
System Prompt: "Проверь логи и проанализируй все возможные причины. Не ограничивайся только предположениями пользователя."

Практика: Разделяйте факты (данные, логи, метрики) и предпочтения/гипотезы пользователя. Не включайте предпочтения в контекст как факты.

Ошибка 5: Биас в few-shot ("ответ всегда A")

Симптом: Модель перенимает позиционный паттерн из примеров: если правильный ответ в few-shot всегда в позиции "A", модель начинает выбирать "A" чаще, чем должно быть.

Причина: Few-shot примеры не разнообразны, правильные ответы всегда в одной позиции или следуют одному паттерну.

Решение:

// ПЛОХО
Пример 1: Вопрос → Ответ A (правильный)
Пример 2: Вопрос → Ответ A (правильный)
Пример 3: Вопрос → Ответ A (правильный)

// ХОРОШО
Пример 1: Вопрос → Ответ A (правильный)
Пример 2: Вопрос → Ответ B (правильный)
Пример 3: Вопрос → Ответ C (правильный)
// Или перемешивайте позиции, добавляйте контрпримеры

Практика: Перемешивайте позиции правильных ответов, включайте разнообразные примеры, добавляйте контрпримеры (неправильные ответы с объяснением, почему они неправильные).

Ошибка 6: Слепая вера в CoT

Симптом: Разработчик полагается на "красивое объяснение" модели как на доказательство корректности ответа, не проверяя сам ответ.

Причина: CoT выглядит логичным и убедительным, но может быть post-hoc рационализацией неправильного вывода.

Решение:

// ПЛОХО
Модель: "Thought: Проблема в БД. Action: restart_database()"
Разработчик: "Отлично, логично!" → Выполняет действие без проверки

// ХОРОШО
Модель: "Thought: Проблема в БД. Action: restart_database()"
Разработчик: Проверяет через инструменты (check_db_status, read_logs), 
             валидирует через evals, запрашивает подтверждение для критических действий

Практика: CoT — это инструмент для улучшения рассуждений модели, но не источник истины. Всегда проверяйте ответы через инструменты и evals, особенно для критических действий.

Как не подсказывать модели ответ

Few-shot и CoT могут невольно "подсказывать" модели желаемый вывод. Вот чеклист, как избежать этого:

Чеклист: Нейтральные формулировки

Хорошо:

  • Используйте нейтральные формулировки запросов
  • Разделяйте факты и предпочтения пользователя
  • Не включайте предположения как факты в контекст

Плохо:

  • "Я думаю правильный ответ — A, проверь"
  • "Пользователь считает X, используй это как факт"
  • "Скорее всего проблема в Y, проверь только Y"

Чеклист: Разнообразие few-shot примеров

Хорошо:

  • Перемешивайте позиции правильных ответов (A, B, C, D)
  • Включайте разнообразные типы задач
  • Добавляйте контрпримеры (неправильные ответы с объяснением)

Плохо:

  • Все правильные ответы в позиции "A"
  • Все примеры одного типа
  • Только положительные примеры

Чеклист: Разделение предпочтений и фактов

Хорошо:

Факты (включаем в контекст):
- Логи показывают ошибку 500
- Метрики CPU: 95%

Предпочтения/гипотезы (отдельно, не как факты):
- Пользователь предполагает проблему в БД
- Гипотеза: проблема в сети

Плохо:

Факты:
- Проблема в базе данных (это предположение, не факт!)
- Логи показывают ошибку 500

Чеклист: Подтверждение через инструменты

Хорошо:

  • Проверяйте ответы через инструменты (read_logs, check_status)
  • Используйте evals для проверки устойчивости к подсказкам
  • Не полагайтесь только на CoT как на доказательство

Плохо:

  • Принимать ответ модели только потому, что CoT выглядит логично
  • Не проверять ответы инструментами
  • Нет evals на устойчивость к anchoring

Пример: Анти-промпт и исправленная версия

Плохо (подсказывает ответ):

Ты DevOps инженер. Пользователь считает, что проблема в базе данных.
Проверь логи и подтверди, что проблема в БД.

Проблемы:

  • Включает предположение пользователя как факт
  • Подсказывает желаемый вывод ("подтверди, что проблема в БД")
  • Модель может согласиться, даже если проблема не в БД

Хорошо (нейтрально):

Ты DevOps инженер. Пользователь сообщил о проблеме.
Проверь логи, метрики и все возможные причины. 
Проанализируй данные объективно, не ограничиваясь предположениями.

Улучшения:

  • Не включает предположение как факт
  • Не подсказывает желаемый вывод
  • Требует объективного анализа всех причин

Критерии сдачи / Чек-лист

Сдано:

  • Роль (Persona) четко определена
  • Цель (Goal) конкретна и измерима
  • Ограничения (Constraints) явно указаны
  • Формат ответа (Format) описан
  • SOP (если применимо) детально расписан
  • CoT включен для сложных задач
  • Few-Shot примеры добавлены (если нужен сложный формат)
  • Few-Shot примеры согласованы (один формат)
  • Few-Shot примеры разнообразны (нет позиционного биаса)
  • Промпт не подсказывает желаемый ответ (нейтральные формулировки)
  • Предпочтения пользователя отделены от фактов
  • Ответы проверяются через инструменты и evals, не только через CoT

Не сдано:

  • Промпт слишком общий (нет конкретной роли и цели)
  • Отсутствует CoT для сложных задач
  • Нет ограничений на опасные действия
  • Few-Shot примеры в разных форматах
  • Few-Shot примеры имеют позиционный биас (все ответы в позиции "A")
  • Промпт подсказывает желаемый ответ ("подтверди, что X")
  • Предпочтения пользователя включены как факты
  • Слепая вера в CoT без проверки через инструменты

Мини-упражнения

Упражнение 1: Создайте System Prompt

Создайте System Prompt для агента, который обрабатывает тикеты поддержки:

systemPrompt := `
// Ваш код здесь
`

Ожидаемый результат:

  • Роль: Support Agent
  • Цель: решить проблему пользователя
  • Ограничения: вежливость, эскалация сложных случаев
  • Формат: структурированный JSON с полями action и user_id
  • SOP: Read → Context → Search → Decide → Respond

Упражнение 2: Добавьте CoT

Улучшите промпт из упражнения 1, добавив Chain-of-Thought:

cotPrompt := `
// Ваш код здесь
`

Ожидаемый результат:

  • Добавлен формат рассуждения: модель сначала анализирует и формулирует гипотезу, потом действует
  • В native tool-calling рассуждение живёт в content ответа рядом с tool_calls — отдельный текстовый формат (Thought/Action/Observation) не нужен
  • Или инструкция "Думай по шагам" (если zero-shot)

Для любопытных

Этот раздел объясняет механику работы few-shot и CoT на уровне модели. Можно пропустить, если вас интересует только практика.

Почему Few-Shot работает?

Механизм Self-Attention

Механизм Self-Attention в трансформере позволяет модели "видеть" связи между токенами в контексте. Когда модель видит примеры:

  1. Интуитивно: Модель "подстраивается" под шаблон, который видит в контексте
  2. Механически: Векторное представление токенов смещается в сторону нужного формата, потому что предыдущие токены (примеры) задали этот контекст
  3. Практически: Качество результата резко зависит от качества примеров и их согласованности

Почему CoT работает?

Представьте задачу: "Сколько будет 23 * 41 + 12?"

Без CoT:

  • Модель должна выдать ответ "955" сразу
  • Это требует огромной вычислительной мощности в одном шаге
  • Вероятность ошибки высока (модель может "угадать" неправильно)

С CoT:

  • Модель генерирует: "23 * 40 = 920... 23 * 1 = 23... сумма 943... плюс 12... ответ 955"
  • Генерируя промежуточные токены ("920", "943"), модель выгружает вычисления в контекст
  • Следующий токен предсказывается на основе расширенного контекста, содержащего промежуточные результаты
  • Это превращает сложную задачу в серию простых задач

Для агентов CoT критичен, потому что агенты решают сложные многошаговые задачи, где каждый шаг зависит от предыдущего.

Связь с другими главами

Что дальше?

После изучения промптинга переходите к: