Промпт как микросервис: Как разбить сложную задачу на пайплайн LLM
В мире разработки программного обеспечения давно принят принцип: сложные системы строятся из простых, независимых компонентов. Микросервисная архитектура, Unix-конвейеры, функциональные пайплайны — везде одна идея: делай одно дело хорошо, передавай результат дальше.
Тот же принцип применим к работе с LLM. Когда задача слишком сложна для одного промпта — её нужно разбить на пайплайн промптов, каждый из которых решает ровно одну подзадачу. Это и есть концепция промпта как микросервиса.
Проблема: почему один большой промпт не работает
Представьте задачу: «Проанализируй этот договор, найди риски для покупателя, переведи ключевые пункты на простой язык и сформируй список вопросов для юриста».
Это четыре совершенно разных когнитивных операции:
- Юридический анализ (что написано в договоре)
- Оценка рисков (что из этого опасно)
- Адаптация текста (как объяснить простыми словами)
- Генерация вопросов (что уточнить у эксперта)
Когда всё это сваливается в один промпт, модель пытается решить все задачи одновременно. Результат получается смешанным: юридический анализ мешается с объяснениями, риски недооцениваются, вопросы для юриста звучат расплывчато.
Исследования (в том числе открытые работы Anthropic и OpenAI) показывают: LLM работают значительно лучше, когда задача декомпозирована и каждый шаг сфокусирован на одном типе когнитивной нагрузки.
Решение: пайплайн промптов
Принцип прост: вместо одного гигантского промпта создаётся цепочка, где:
- Каждый промпт получает на вход чётко определённые данные
- Выполняет одну конкретную трансформацию
- Отдаёт на выход структурированный результат, который становится входом следующего промпта
Для нашего примера с договором пайплайн выглядит так:
Промпт 1 (Извлечение) → JSON со структурой договора
Промпт 2 (Анализ рисков) → JSON с рисками и их серьёзностью
Промпт 3 (Адаптация языка) → Текст на «человеческом» языке
Промпт 4 (Генерация вопросов) → Список вопросов для юриста
Пример на Python: анализ конкурентного рынка
Рассмотрим практический пример: автоматический конкурентный анализ по описанию продукта.
import openai
import json
client = openai.OpenAI(
base_url="https://aineron.ru/api/v1",
api_key="ak_ваш_ключ"
)
def run_prompt(system: str, user: str, model="gpt-4o-mini") -> str:
# Базовый блок пайплайна - один промпт.
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": system},
{"role": "user", "content": user}
],
temperature=0.3,
)
return response.choices[0].message.content
# ШАГ 1: Извлечь характеристики продукта
product_description = '''
SaaS-платформа для управления задачами малого бизнеса.
Интеграция с 1С, мобильное приложение, от 590 руб/мес.
'''
features = run_prompt(
system="Извлеки ключевые характеристики продукта в JSON: "
"{category, target_audience, key_features: [], price_range, integrations: []}. "
"Только JSON, без пояснений.",
user=product_description
)
features_data = json.loads(features)
# ШАГ 2: Определить конкурентов (на основе характеристик)
competitors = run_prompt(
system="Ты эксперт по российскому SaaS-рынку. "
"Назови 5 главных конкурентов для продукта с такими характеристиками. "
"Ответ в JSON: [{name, website, positioning}]. Только JSON.",
user=json.dumps(features_data, ensure_ascii=False)
)
competitors_data = json.loads(competitors)
# ШАГ 3: Найти слабые места конкурентов
weaknesses = run_prompt(
system="Проанализируй слабые стороны каждого конкурента с точки зрения "
"малого бизнеса в России. Для каждого укажи 2-3 конкретных слабости. "
"JSON: [{competitor_name, weaknesses: []}]. Только JSON.",
user=json.dumps(competitors_data, ensure_ascii=False)
)
weaknesses_data = json.loads(weaknesses)
# ШАГ 4: Сформировать позиционирование
positioning = run_prompt(
system="Ты стратег-маркетолог. На основе анализа слабостей конкурентов "
"предложи уникальное позиционирование нашего продукта. "
"Текст 3-4 предложения для лендинга.",
user=f"Наш продукт: {product_description}
Слабости конкурентов: {json.dumps(weaknesses_data, ensure_ascii=False)}"
)
print("=== УНИКАЛЬНОЕ ПОЗИЦИОНИРОВАНИЕ ===")
print(positioning)
Ключевые паттерны промпт-пайплайнов
1. Паттерн «Структурирование → Трансформация»
Первый промпт всегда извлекает структуру из неструктурированного текста. Это самый важный шаг — он превращает «сырые данные» в формат, с которым следующие промпты могут работать надёжно.
Правило: промпт-экстрактор должен возвращать только JSON без каких-либо пояснений. Это делает парсинг предсказуемым и позволяет легко обрабатывать ошибки.
2. Паттерн «Специализация модели»
Разные шаги пайплайна могут использовать разные модели. Простые задачи (извлечение, форматирование) — дешёвые и быстрые модели (GPT-4o mini, Mistral Small). Сложные аналитические шаги — более мощные (GPT-4o, Claude 3.5 Sonnet).
def pipeline(text):
# Дешёвая модель для извлечения структуры
structure = run_prompt(..., model="gpt-4o-mini")
# Мощная модель для глубокого анализа
analysis = run_prompt(..., model="claude-3-5-sonnet")
# Снова дешёвая для финального форматирования
report = run_prompt(..., model="gpt-4o-mini")
return report
Такой подход снижает стоимость пайплайна в 3–5 раз по сравнению с использованием GPT-4o на всех шагах, без потери качества результата.
3. Паттерн «Параллельный разведчик»
Когда несколько шагов пайплайна независимы друг от друга, их можно выполнять параллельно:
import asyncio
async def parallel_pipeline(document: str):
# Запускаем три независимых анализа одновременно
results = await asyncio.gather(
async_prompt("Найди все даты и дедлайны", document),
async_prompt("Найди все денежные суммы и обязательства", document),
async_prompt("Найди все стороны договора и их роли", document),
)
dates, amounts, parties = results
# Объединяем и синтезируем
summary = await async_prompt(
"Создай краткое резюме договора",
f"Даты: {dates}
Суммы: {amounts}
Стороны: {parties}"
)
return summary
Это снижает время выполнения в N раз, где N — количество параллельных ветвей.
4. Паттерн «Критик-исправитель»
Один из самых мощных паттернов: один промпт генерирует результат, второй — критикует его, третий — исправляет на основе критики.
draft = run_prompt("Напиши email клиенту о задержке проекта", context)
critique = run_prompt(
"Найди проблемы в этом письме: тон, конкретность, следующие шаги",
draft
)
final = run_prompt(
f"Перепиши письмо, исправив следующие проблемы:
{critique}",
draft
)
Этот паттерн особенно эффективен для задач, где качество важнее скорости: деловая переписка, технические описания, маркетинговые тексты.
Когда разбивать на пайплайн, а когда нет
Пайплайн оправдан, когда:
- Задача содержит принципиально разные когнитивные операции (извлечение + анализ + генерация)
- Выходные данные одного шага нужны как точные входные данные для следующего
- Важна отслеживаемость: вы хотите видеть промежуточные результаты и понимать, где пайплайн сбился
- Задачу нужно масштабировать: обработать тысячи документов
Один промпт достаточен, когда:
- Задача простая и однородная («переведи этот текст на английский»)
- Скорость важнее качества («напиши быстрый черновик»)
- Стоимость критична, а результат «достаточно хороший»
Работа с ошибками в пайплайне
Главная уязвимость пайплайна — сбой на промежуточном шаге. Если второй промпт вернул невалидный JSON, весь пайплайн упадёт. Решение: явная обработка ошибок на каждом шаге.
def safe_json_prompt(system: str, user: str, retries: int = 3) -> dict:
for attempt in range(retries):
result = run_prompt(system, user)
try:
return json.loads(result)
except json.JSONDecodeError:
if attempt == retries - 1:
raise ValueError(f"Не удалось получить JSON после {retries} попыток")
# Добавляем явное указание на ошибку в следующую попытку
system += "
ВНИМАНИЕ: Возвращай ТОЛЬКО валидный JSON без каких-либо пояснений."
return {}
Практические советы по построению пайплайнов
- Начинайте с конца. Опишите идеальный финальный результат, потом идите назад — что нужно для предпоследнего шага, что для него — и так до входных данных.
- Один выход — один формат. Промпт-экстракторы — всегда JSON. Промпты-трансформаторы — всегда один конкретный текстовый формат. Не смешивайте.
- Логируйте промежуточные результаты. При отладке это сэкономит часы — вы сразу увидите, на каком шаге пайплайн начал давать некорректные результаты.
- Версионируйте промпты. Храните промпты как код (в файлах или базе данных), не хардкодьте в функциях. Это позволяет итерировать и откатываться.
- Тестируйте граничные случаи на каждом шаге. Что будет, если входные данные пустые? Слишком длинные? На другом языке?
Заключение
Промпт как микросервис — это не просто метафора. Это архитектурный принцип, который позволяет строить надёжные, масштабируемые и поддерживаемые системы на основе LLM.
Разбивая сложные задачи на цепочку специализированных промптов, вы получаете:
- Более высокое качество результата
- Отслеживаемость и отлаживаемость
- Возможность оптимизировать стоимость, выбирая разные модели для разных шагов
- Параллелизацию там, где это возможно
Попробуйте этот подход на вашей следующей сложной задаче с нейросетями — и вы удивитесь, насколько более управляемым становится процесс.
Для экспериментов с пайплайнами используйте API aineron.ru — доступ к GPT-4o, Claude 3.5 и другим моделям без VPN, с оплатой в рублях.