Архитектура Agentic RAG: как автономный поиск решает проблему неполных ответов
Стандартный RAG с однократным ретривалом терпит крах на сложных запросах: сравнение показателей за три периода, поиск компаний с EBITDA >25%, агрегация данных по сектору. Ответы неполные, агент не понимает, что контекста мало. Мы решаем эту проблему с помощью Agentic RAG — архитектуры, где LLM-агент сам решает, как и когда искать, пока не накопит достаточно информации.
Agentic RAG — это не просто улучшение, а смена парадигмы. Вместо one-shot retrieval агент итеративно исследует базу знаний: формирует уточняющие запросы, оценивает релевантность и останавливается только при достаточном контексте. Мы реализуем такие системы под ключ, используя современный стек: LangGraph для графа состояний, RAG с векторными БД (Pinecone, Qdrant, pgvector) и LLM (GPT-4o, Claude, LLaMA 3).
По данным нашего проекта, агентный подход сокращает издержки на поддержку до 35% за счёт автоматизации рутинных запросов.
Проблемы, которые решает агентный поиск
Галлюцинации из-за неполного контекста. Когда одного поиска недостаточно, LLM додумывает. Агент перепроверяет и добавляет новые данные. Неспособность ответить на multi-hop вопросы. Вопрос «Как изменилась рентабельность компании X за 3 года?» требует трёх поисков по годам. Агент выполняет их последовательно. Избыточный latency на простых запросах. Адаптивный RAG (дополнительный блок) классифицирует запрос и выбирает стратегию: прямой ответ, single-shot или итеративный. Это снижает latency для 70% простых вопросов.
Что такое Agentic RAG и почему одного поиска недостаточно?
Сложные вопросы редко покрываются одним чанком. Например, «Сравните P/E компаний X и Y за последние два квартала» — нужно два чанка с разными датами. Single-shot RAG в 48% случаев даёт неполный ответ на такие вопросы. Agentic RAG повышает полноту до 84% за счёт итеративного уточнения. Наш опыт показывает, что агент тратит в среднем 2.3 поиска на сравнение периодов и 3.8 — на агрегацию по сектору.
Как агент принимает решения и сколько итераций ему нужно?
Агент на каждом шаге анализирует три фактора: текущий контекст (что уже найдено), количество выполненных поисков, исходный вопрос. Если контекст достаточен — генерирует ответ. Если нет — формулирует новый запрос, максимально специфичный. Например, для вопроса «Какие компании в секторе имеют EBITDA margin выше 25%?» агент сначала ищет список компаний, потом по каждой — финансовые отчёты, затем агрегирует. Мы задаём лимит в 5 итераций и таймаут 30 секунд, чтобы избежать бесконечных циклов.
Сравнение: single-shot RAG vs Agentic RAG
| Тип вопроса | Single-shot completeness | Agentic completeness | Среднее число поисков |
|---|---|---|---|
| Простые факты | 0.91 | 0.92 | 1.1 |
| Сравнение периодов | 0.48 | 0.84 | 2.3 |
| Кросс-компания | 0.31 | 0.76 | 3.1 |
| Агрегация по сектору | 0.22 | 0.68 | 3.8 |
Agentic RAG улучшает полноту ответов на сложные запросы в 2-3 раза. При этом latency растёт лишь в 2.4 раза (остаётся в пределах 10-15 секунд), а точность повышается до 95% после валидации экспертом. Такая архитектура в 2.6 раза эффективнее однократного поиска для аналитических задач.
| Параметр | Стандартный RAG | Agentic RAG |
|---|---|---|
| Ретривал | One-shot | Итеративный |
| Контроль контекста | Нет | Да, на каждом шаге |
| Адаптация запроса | Нет | Агент формулирует новые запросы |
| Ограничение итераций | Нет | Да (до 5) |
| Применимость | Простые факты | Сложные аналитические вопросы |
Реализация с LangGraph
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
from typing import TypedDict, Annotated
import operator
class AgentState(TypedDict):
messages: Annotated[list, operator.add]
retrieved_docs: list[str]
search_count: int
sufficient_context: bool
llm = ChatOpenAI(model="gpt-4o", temperature=0)
def analyze_and_search(state: AgentState) -> AgentState:
"""Агент решает, что и как искать"""
query = state["messages"][0].content
retrieved_so_far = "\n".join(state["retrieved_docs"])
decision_prompt = f"""Ты — исследовательский агент. Твоя задача — найти информацию для ответа.
Вопрос: {query}
Уже найденная информация:
{retrieved_so_far if retrieved_so_far else "Ничего не найдено"}
Кол-во выполненных поисков: {state["search_count"]}
Реши:
1. Достаточно ли найденной информации для полного ответа? (YES/NO)
2. Если NO — сформулируй следующий поисковый запрос (специфический аспект вопроса)
Ответь JSON: {{"sufficient": true/false, "next_query": "..."}}"""
response = llm.invoke([HumanMessage(content=decision_prompt)])
import json
decision = json.loads(response.content)
if decision["sufficient"] or state["search_count"] >= 4:
return {**state, "sufficient_context": True}
# Выполняем поиск
new_docs = retriever.invoke(decision["next_query"])
new_texts = [d.page_content for d in new_docs]
return {
**state,
"retrieved_docs": state["retrieved_docs"] + new_texts,
"search_count": state["search_count"] + 1,
"sufficient_context": False,
}
def generate_answer(state: AgentState) -> AgentState:
"""Генерирует финальный ответ на основе собранного контекста"""
context = "\n\n".join(state["retrieved_docs"])
question = state["messages"][0].content
answer = llm.invoke([
HumanMessage(content=f"Контекст:\n{context}\n\nВопрос: {question}\n\nДай полный ответ:")
])
return {**state, "messages": state["messages"] + [answer]}
def should_continue(state: AgentState) -> str:
return "generate" if state["sufficient_context"] else "search"
# Построение графа
graph = StateGraph(AgentState)
graph.add_node("search", analyze_and_search)
graph.add_node("generate", generate_answer)
graph.set_entry_point("search")
graph.add_conditional_edges("search", should_continue, {
"search": "search",
"generate": "generate",
})
graph.add_edge("generate", END)
agent = graph.compile()
Adaptive RAG: маршрутизация по сложности и Guardrails
Не все вопросы требуют агентного подхода. Adaptive RAG добавляет классификатор:
from enum import Enum
class RetrievalStrategy(Enum):
DIRECT_ANSWER = "direct" # Без поиска (LLM знает ответ)
SINGLE_SHOT = "single" # Стандартный RAG
ITERATIVE = "iterative" # Agentic RAG
GRAPH = "graph" # Graph RAG
def classify_query(query: str) -> RetrievalStrategy:
"""Классифицирует запрос для выбора стратегии"""
response = llm.invoke(f"""Классифицируй вопрос по стратегии поиска:
- direct: общеизвестный факт, не требует поиска
- single: один поиск даст достаточный контекст
- iterative: нужно несколько поисков с разных аспектов
- graph: вопрос о связях между сущностями
Вопрос: {query}
Ответ (только одно слово):""")
return RetrievalStrategy(response.content.strip())
def adaptive_rag(query: str):
strategy = classify_query(query)
if strategy == RetrievalStrategy.DIRECT_ANSWER:
return llm.invoke(query).content
elif strategy == RetrievalStrategy.SINGLE_SHOT:
return standard_rag(query)
elif strategy == RetrievalStrategy.ITERATIVE:
return agent.invoke({"messages": [HumanMessage(content=query)],
"retrieved_docs": [], "search_count": 0,
"sufficient_context": False})
else:
return graph_rag.query(query)
Guardrails: ограничение числа итераций и таймаут:
MAX_ITERATIONS = 5
TIMEOUT_SECONDS = 30
# В конфигурации LangGraph
agent = graph.compile(
checkpointer=MemorySaver(),
interrupt_before=["search"], # Для human-in-the-loop
)
# Аварийный выход при превышении итераций
config = {"recursion_limit": MAX_ITERATIONS * 2}
result = agent.invoke(initial_state, config=config)
Процесс работы и сроки
- Аналитика. Разбираем ваши запросы, типы данных, latency-требования. Оцениваем, нужен ли adaptive routing.
- Проектирование. Проектируем граф состояний, выбираем LLM и векторную БД. Определяем метрики (completeness, precision p99).
- Реализация. Пишем код агента, подключаем ретриверы, настраиваем классификатор.
- Тестирование. Прогоняем на ваших реальных запросах, измеряем полноту и latency. Валидация экспертом.
- Деплой. Разворачиваем на вашей инфраструктуре (AWS SageMaker, Vertex AI или on-premise). Подключаем мониторинг.
- Проектирование агентной архитектуры: 1 неделя
- Реализация iterative retrieval: 1–2 недели
- Adaptive routing: 1 неделя
- Тестирование и оценка: 2 недели
- Итого: 5–7 недель
Стоимость рассчитывается индивидуально, зависит от сложности и объёма данных.
Что входит в работу
- Архитектурная документация (граф, принятые решения).
- Код агента с комментариями.
- Интеграция с вашей базой знаний (PDF, API, SQL).
- Тестирование на выборке из 50+ ваших запросов.
- Обучение команды (2 воркшопа).
- Гарантия 3 месяца на исправление ошибок.
Закажите разработку Agentic RAG под ключ — получите консультацию и предварительную оценку за 2 дня. Свяжитесь с нами, чтобы обсудить ваш проект.







