Ещё два года назад интеграция AI в продукт означала обращение к одному API-эндпоинту. Сегодня LLM стали полноценным архитектурным компонентом, который влияет на выбор базы данных, паттерны обработки данных и даже структуру команды разработки.
Новая реальность: LLM как компонент системы
Когда мы говорим об интеграции LLM в приложение, речь больше не идёт о простом POST /v1/chat/completions. Современные системы строятся вокруг нескольких ключевых паттернов, каждый из которых требует своего подхода к архитектуре.
Главное изменение — недетерминированность. В отличие от классических API, ответ LLM может отличаться при каждом вызове. Это фундаментально меняет подход к тестированию, мониторингу и обработке ошибок.
LLM — это не база данных и не микросервис. Это скорее стажёр-полиглот с фотографической памятью: невероятно способный, но требующий чёткого ТЗ и постоянной проверки результатов.
Паттерн 1 — RAG (Retrieval-Augmented Generation)
RAG остаётся самым популярным паттерном интеграции LLM в 2026 году. Идея проста: вместо того чтобы надеяться на знания модели, мы подаём ей релевантный контекст из нашей базы данных.
Архитектура RAG-пайплайна
Типичный RAG-пайплайн состоит из нескольких этапов:
- Ingestion — загрузка и чанкинг документов
- Embedding — векторизация через embedding-модель
- Storage — сохранение в векторную БД (pgvector, Qdrant, Pinecone)
- Retrieval — поиск релевантных чанков по запросу пользователя
- Generation — формирование ответа с учётом найденного контекста
# Минимальный RAG-пайплайн
from openai import OpenAI
import numpy as np
client = OpenAI()
def get_embedding(text: str) -> list[float]:
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
def search(query: str, documents: list, top_k: int = 3):
query_emb = np.array(get_embedding(query))
scores = []
for doc in documents:
doc_emb = np.array(doc["embedding"])
score = np.dot(query_emb, doc_emb)
scores.append((score, doc))
scores.sort(reverse=True, key=lambda x: x[0])
return [doc for _, doc in scores[:top_k]]
def generate(query: str, context_docs: list) -> str:
context = "\n\n".join(d["text"] for d in context_docs)
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": f"Контекст:\n{context}"},
{"role": "user", "content": query}
]
)
return response.choices[0].message.content
Ключевые решения
Выбор chunking-стратегии критически влияет на качество. Маленькие чанки (200-400 токенов) дают точный retrieval, но теряют контекст. Большие чанки (800-1200 токенов) сохраняют контекст, но снижают precision.
На практике лучше всего работает semantic chunking с overlap — разбивка по смысловым блокам с перекрытием в 10-15%. Это даёт баланс между точностью поиска и полнотой контекста.
Паттерн 2 — AI-агенты и цепочки вызовов
Агентный подход — следующий уровень сложности. Вместо одного вызова LLM мы строим цепочку: модель сама решает, какие инструменты вызвать, в каком порядке и когда остановиться.
# Простой агент с tool calling
tools = [
{
"type": "function",
"function": {
"name": "search_docs",
"description": "Поиск в документации",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "run_code",
"description": "Выполнить Python-код",
"parameters": {
"type": "object",
"properties": {
"code": {"type": "string"}
},
"required": ["code"]
}
}
}
]
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto"
)
Главная сложность агентов — управляемость. Модель может зациклиться, вызвать инструмент с некорректными параметрами или уйти в неожиданном направлении. Поэтому в продакшене критически важны:
- Лимит на количество итераций (обычно 5-10)
- Timeout на каждый шаг
- Валидация параметров tool call
- Логирование каждого шага для отладки
- Fallback-стратегия при ошибках
Паттерн 3 — Structured Output и типизация
Одно из ключевых улучшений 2025-2026 годов — нативная поддержка structured output. Вместо парсинга свободного текста мы получаем гарантированный JSON, соответствующий заданной схеме.
from pydantic import BaseModel
class CodeReview(BaseModel):
summary: str
issues: list[str]
severity: str # "low" | "medium" | "high"
suggestions: list[str]
response = client.beta.chat.completions.parse(
model="gpt-4o",
messages=[
{"role": "system", "content": "Ты — код-ревьюер."},
{"role": "user", "content": f"Проверь код:\n{code}"}
],
response_format=CodeReview
)
review = response.choices[0].message.parsed
print(f"Severity: {review.severity}")
print(f"Issues: {len(review.issues)}")
Это кардинально упрощает интеграцию: ответ LLM становится типизированным объектом, который можно безопасно использовать в бизнес-логике.
Инфраструктура: кэширование, мониторинг, fallback
LLM-вызовы дорогие и медленные. В продакшен-системе обязательны три инфраструктурных слоя:
Semantic cache — кэширование по смыслу запроса, а не по точному совпадению строки. Если пользователь задал вопрос, похожий на ранее обработанный, возвращаем кэшированный ответ. Экономия 30-50% вызовов.
Observability — каждый LLM-вызов должен логироваться: промпт, ответ, latency, token usage, стоимость. Инструменты: LangSmith, Helicone, Langfuse. Без этого отладка невозможна.
Fallback chain — если основная модель недоступна или отвечает слишком долго, переключаемся на запасную. Типичная цепочка: Claude Opus → GPT-4o → Claude Sonnet → cached response.
Практические рекомендации
После года работы с LLM-интеграциями в продакшене — главные выводы:
- Начинайте с простого. RAG с базовым chunking покрывает 80% юзкейсов. Не городите агентов, пока не упрётесь в ограничения.
- Тестируйте промпты как код. Eval-датасеты, автоматические прогоны, regression-тесты на качество ответов.
- Считайте деньги. Стоимость LLM-вызовов может быстро вырасти. Кэширование, batching, выбор правильной модели для задачи.
- Structured output — ваш друг. Везде, где можно, используйте типизированные ответы вместо свободного текста.
- Мониторьте всё. Latency, качество ответов, стоимость, ошибки. Без observability вы летите вслепую.
LLM-интеграция — это марафон, а не спринт. Архитектура должна быть готова к тому, что модели, API и best practices будут меняться каждые 3-6 месяцев.