Техники эффективного взаимодействия с LLM: zero-shot, few-shot, chain-of-thought, ReAct, prompt templates
Искусство формулировать запросы к LLM для получения точных и релевантных ответов
Prompt Engineering — это навык формулирования запросов к языковой модели таким образом, чтобы получать максимально точные и полезные ответы. Для Python-разработчика это критически важный навык, так как качество промпта напрямую влияет на качество работы вашего приложения.
Почему это важно:
Zero-shot — самый простой подход: вы даёте модели только задачу, без примеров.
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "user", "content": "Переведи на французский: Hello, World!"}
]
)
print(response.choices[0].message.content)
# Output: Bonjour, le monde!Когда использовать:
Ограничения:
Few-shot — вы предоставляете несколько примеров input-output перед задачей. Это показывает модели паттерн, который нужно продолжить.
messages = [
{"role": "user", "content": """
English: Hello → French: Bonjour
English: World → French: Monde
English: Good morning → French: Bonjour matin
English: Thank you → French: ?
"""}
]Пример с кастомным форматом:
from langchain import PromptTemplate
template = PromptTemplate.from_template("""
Примеры классификации тональности:
Текст: "Отличный продукт, очень доволен!" → Тональность: позитивная
Текст: "Ужасное качество, не рекомендую" → Тональность: негативная
Текст: "Нормально, ничего особенного" → Тональность: нейтральная
Текст: "{text}" → Тональность:
""")
prompt = template.format(text="Быстрая доставка, товар как в описании")
# Модель продолжит паттерн: позитивнаяКогда использовать:
Best practices:
Chain-of-Thought — техника, при которой модель генерирует промежуточные шаги рассуждения перед финальным ответом. Особенно эффективна для математических и логических задач.
Добавьте фразу "Let's think step by step" (или "Давай рассуждать пошагово"):
messages = [
{"role": "user", "content": """
Вопрос: У меня 5 яблок, я съел 2, потом купил 3. Сколько яблок осталось?
Давай рассуждать пошагово.
"""}
]
# Модель сгенерирует:
# Рассуждение:
# 1. Было 5 яблок
# 2. Съел 2: 5 - 2 = 3
# 3. Купил 3: 3 + 3 = 6
# Ответ: 6 яблокПокажите примеры с рассуждениями:
messages = [
{"role": "user", "content": """
Вопрос: Роберт стоит 5-м в очереди слева и 7-м справа. Сколько человек в очереди?
Рассуждение: Если Роберт 5-й слева, значит слева от него 4 человека.
Если он 7-й справа, значит справа от него 6 человек.
Всего: 4 + 1 (Роберт) + 6 = 11 человек.
Ответ: 11
Вопрос: Поезд едет 60 км/ч. Какое расстояние он проедет за 2.5 часа?
Рассуждение: Расстояние = Скорость × Время.
60 км/ч × 2.5 ч = 150 км.
Ответ: 150 км
Вопрос: В комнате 3 лампы. Каждая потребляет 60 Вт. Сколько Вт потребляют все лампы?
Рассуждение:
"""}
]Почему это работает:
Интеграция с FastAPI:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class ReasoningRequest(BaseModel):
question: str
@app.post("/reason")
async def reason(request: ReasoningRequest):
response = client.chat.completions.create(
model="gpt-4",
messages=[{
"role": "user",
"content": f"{request.question}\nДавай рассуждать пошагово."
}]
)
return {"answer": response.choices[0].message.content}ReAct — паттерн для задач, требующих взаимодействия с внешними инструментами. Модель циклически генерирует:
Question: Какая погода в городе, где находится штаб-квартира Apple?
Thought: Мне нужно найти город, где находится штаб-квартира Apple
Action: search(query="Apple headquarters location")
Observation: Apple Park находится в Купертино, Калифорния
Thought: Теперь нужно узнать погоду в Купертино
Action: get_weather(location="Cupertino, CA")
Observation: 22°C, ясно, влажность 45%
Thought: У меня есть вся информация для ответа
Answer: В Купертино (где находится штаб-квартира Apple) сейчас 22°C, ясно.
from langchain.agents import initialize_agent, Tool
from langchain.llms import OpenAI
from langchain.utilities import SerpAPIWrapper
llm = OpenAI(temperature=0)
search = SerpAPIWrapper()
tools = [
Tool(
name="Search",
func=search.run,
description="Полезно для поиска фактов в интернете"
),
Tool(
name="Weather",
func=get_weather, # ваша функция
description="Полезно для получения текущей погоды"
)
]
agent = initialize_agent(
tools,
llm,
agent="zero-shot-react-description",
verbose=True
)
result = agent.run("Какая погода в городе, где находится штаб-квартира Apple?")from fastapi import FastAPI, BackgroundTasks
from pydantic import BaseModel
import httpx
app = FastAPI()
class AgentRequest(BaseModel):
query: str
class WeatherTool:
async def get_weather(self, location: str) -> str:
async with httpx.AsyncClient() as client:
resp = await client.get(
f"https://api.weather.com/{location}"
)
return resp.json()["description"]
class SearchTool:
async def search(self, query: str) -> str:
async with httpx.AsyncClient() as client:
resp = await client.get(
"https://api.serper.dev/search",
params={"q": query}
)
return resp.json()["organic"][0]["snippet"]
@app.post("/agent")
async def run_agent(request: AgentRequest):
tools = {"weather": WeatherTool(), "search": SearchTool()}
# ReAct цикл
thoughts = []
for _ in range(5): # максимум 5 итераций
# Генерируем thought + action
response = client.chat.completions.create(
model="gpt-4",
messages=[{
"role": "user",
"content": f"Question: {request.query}\n" +
"\n".join(thoughts) +
"\nThought:"
}]
)
thought = response.choices[0].message.content
thoughts.append(f"Thought: {thought}")
# Парсим action и выполняем
if "Action:" in thought:
action = parse_action(thought)
observation = await tools[action.name](**action.args)
thoughts.append(f"Observation: {observation}")
else:
return {"answer": thought, "steps": thoughts}
return {"answer": thoughts[-1], "steps": thoughts}Prompt templates — параметризированные шаблоны для генерации промптов. Позволяют переиспользовать структуру промпта с разными данными.
from langchain import PromptTemplate
template = """Ты — опытный {role}. Твоя задача — {task}.
Контекст:
{context}
Вопрос пользователя: {question}
Ответ:"""
prompt = PromptTemplate(
template=template,
input_variables=["role", "task", "context", "question"]
)
formatted = prompt.format(
role="Python разработчик",
task="объяснить концепцию",
context="FastAPI — современный веб-фреймворк",
question="Что такое dependency injection?"
)code_review_template = PromptTemplate.from_template("""
Ты — senior Python разработчик. Проведи код-ревью следующего кода.
Код:
```python
{code}Проанализируй:
Формат ответа:
### FastAPI endpoint с шаблоном
```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
app = FastAPI()
class CodeReviewRequest(BaseModel):
code: str = Field(..., description="Код для ревью")
language: str = Field(default="python")
@app.post("/code-review")
async def code_review(request: CodeReviewRequest):
prompt = code_review_template.format(
code=request.code
)
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
temperature=0.2 # низкая температура для консистентности
)
return {"review": response.choices[0].message.content}
1. Роль/Персона
"Ты — опытный Python разработчик..."
2. Задача
"Твоя задача — написать функцию..."
3. Контекст
"Эта функция будет использоваться в..."
4. Ограничения
"Не используй внешние библиотеки..."
5. Формат вывода
"Ответ должен быть в формате JSON..."
6. Примеры (few-shot)
"Пример входа: {...}, пример выхода: {...}"
prompt = """
Ты — senior Python разработчик с 10-летним опытом (роль).
Твоя задача — оптимизировать следующий SQL-запрос (задача).
Контекст: Запрос выполняется 1000+ раз в секунду в production (контекст).
Ограничения:
- Не меняй логику запроса
- Используй только стандартные индексы
- Объясни каждое изменение (ограничения)
Формат ответа:
1. Оптимизированный запрос
2. Список изменений
3. Ожидаемое улучшение (формат)
Исходный запрос:
{query}
"""| Ошибка | Как исправить |
|---|---|
| Слишком общий промпт | Добавьте контекст и ограничения |
| Нет формата вывода | Явно укажите ожидаемый формат |
| Слишком много инструкций | Разбейте на несколько шагов |
| Нет примеров для niche tasks | Добавьте few-shot примеры |
| Игнорирование CoT для сложных задач | Используйте "рассуждай пошагово" |
import asyncio
from typing import List
async def test_prompts(
prompts: List[str],
test_cases: List[dict]
) -> dict:
results = {p: [] for p in prompts}
for case in test_cases:
tasks = [
client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": p.format(**case)}]
)
for p in prompts
]
responses = await asyncio.gather(*tasks)
for prompt, response in zip(prompts, responses):
results[prompt].append(
evaluate_response(response, case["expected"])
)
return {
prompt: sum(scores) / len(scores)
for prompt, scores in results.items()
}Prompt Engineering — итеративный процесс. Начинайте с простого zero-shot, добавляйте few-shot примеры для сложных задач, используйте CoT для рассуждений и ReAct для работы с инструментами. Тестируйте разные варианты и измеряйте качество.
В следующей теме вы изучите Function Calling — как интегрировать внешние API и инструменты с LLM.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.