Реализация Prompt Engineering для AI-системы
Prompt Engineering — дисциплина конструирования входных данных для LLM с целью получения предсказуемых, качественных результатов. Включает структурирование запросов, управление контекстом, выбор техник (CoT, Few-Shot, ReAct), настройку параметров и итеративную калибровку.
Ключевые принципы
Конкретность: неточные инструкции дают неточные результаты. «Напиши хорошее описание» → неопределённо. «Напиши описание товара 80-100 слов, акцент на пользе для покупателя, тон дружелюбный, избегай технического жаргона» → измеримо.
Роль и контекст: «Ты — [роль]. Твоя задача — [цель]. Контекст: [условия].»
Формат вывода: явное указание формата устраняет неоднозначность. JSON, markdown, numbered list — всё должно быть прописано.
Ограничения: что НЕ делать так же важно, как что делать.
Базовые техники
from openai import OpenAI
client = OpenAI()
# Базовая структура промпта
SYSTEM_PROMPT = """Ты — {role}.
Задача: {task_description}
Правила:
{rules}
Формат ответа:
{output_format}"""
# Инстанциирование под конкретный use case
support_prompt = SYSTEM_PROMPT.format(
role="ассистент клиентской поддержки SaaS-продукта",
task_description="Отвечай на вопросы клиентов о продукте, помогай решать технические проблемы",
rules="""- Используй только информацию из предоставленной базы знаний
- Если ответа нет — честно скажи и предложи создать тикет
- Не обещай функциональность, которой нет в продукте
- Тон: дружелюбный, профессиональный""",
output_format="Ответ до 200 слов. При необходимости — нумерованный список шагов.",
)
def query_llm(user_message: str, system: str = None, **kwargs) -> str:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": system or "You are a helpful assistant."},
{"role": "user", "content": user_message},
],
temperature=kwargs.get("temperature", 0.1),
max_tokens=kwargs.get("max_tokens", 1000),
)
return response.choices[0].message.content
Управление галлюцинациями
ANTI_HALLUCINATION_ADDENDUM = """
ВАЖНО: Отвечай только на основе предоставленного контекста.
Если информации нет в контексте — скажи: "У меня нет данных по этому вопросу."
Не домысливай и не делай предположений.
Если уверен < 80% — укажи степень неопределённости.
"""
# Верификация ответа
async def answer_with_verification(question: str, context: str) -> dict:
answer = query_llm(
f"Контекст:\n{context}\n\nВопрос: {question}",
system=f"Ты — аналитик. {ANTI_HALLUCINATION_ADDENDUM}",
)
# Самопроверка
verification = query_llm(
f"Исходный ответ: {answer}\n\nВопрос: Все ли утверждения в ответе подтверждены контекстом? Ответь JSON: {{\"verified\": bool, \"unsupported_claims\": [...]}}",
temperature=0,
)
return {"answer": answer, "verification": json.loads(verification)}
A/B тестирование промптов
class PromptABTest:
def __init__(self, variants: dict[str, str]):
self.variants = variants
self.results = {name: [] for name in variants}
def run_test(self, test_inputs: list[str], judge_prompt: str) -> dict:
"""Сравнивает варианты промптов на тестовых входах"""
for input_text in test_inputs:
outputs = {}
for name, prompt in self.variants.items():
output = query_llm(input_text, system=prompt)
outputs[name] = output
# LLM-as-judge
comparison = query_llm(
f"""Сравни два ответа на запрос: "{input_text}"
Вариант A: {outputs[list(outputs.keys())[0]]}
Вариант B: {outputs[list(outputs.keys())[1]]}
{judge_prompt}
Верни JSON: {{"winner": "A"|"B"|"tie", "reason": "..."}}""",
temperature=0,
)
result = json.loads(comparison)
winner = result["winner"]
if winner != "tie":
winning_name = list(self.variants.keys())[0 if winner == "A" else 1]
self.results[winning_name].append(1)
return {
name: sum(wins) / len(test_inputs)
for name, wins in self.results.items()
}
# Пример использования
ab_test = PromptABTest({
"v1": "Отвечай кратко и по делу.",
"v2": "Ты — эксперт. Давай структурированные ответы с примерами.",
})
results = ab_test.run_test(
test_inputs=customer_questions[:50],
judge_prompt="Какой ответ полезнее для клиента? Оцени по точности и ясности.",
)
Практический совет: итеративный процесс
Хороший промпт — результат итераций, не единственного написания. Типичный процесс:
- Baseline prompt → тест на 20 примерах → замеряем качество
- Добавляем конкретику → тест → замеряем улучшение
- Добавляем Few-Shot примеры → тест
- Тонкая настройка ограничений → финальный тест
Каждая итерация — измеримое улучшение или откат. Без метрик — prompt engineering превращается в интуитивное гадание.
Сроки
- Базовый промпт для конкретного use case: 1–3 дня
- A/B тестирование с eval-сетом: 3–5 дней
- Production-промпт с верификацией: 1 неделя







