LangGraph MCP as Node

Паттерн из практики: MCP-сервер подключается как отдельный узел графа, а не как инструмент, который LLM вызывает сама. Вызов решается программно (условным ребром), виден на диаграмме и не тратит токены на «решение модели». Типичное применение — fallback-цепочка RAG→MCP.

Суть

Обычно MCP-инструмент дёргает сама LLM через tool calling (Tool Calling). Здесь иначе: mcp_node — узел графа, в который ведёт условное ребро. Решение «звать MCP» принимает код (по состоянию), а не модель.

Зачем это нужно

  • Детерминизм и контроль: переход в MCP определяется явным критерием в state, а не «настроением» модели.
  • Экономия токенов: модель не тратит вызов на то, чтобы решить, нужен ли инструмент.
  • Наблюдаемость: MCP-вызов виден как узел на диаграмме графа — это часть бизнес-процесса, а не скрытая деталь внутри RAG-узла.

Как работает

  • Fallback-цепочка: rag_node делает retrieval и оценивает, нашёл ли ответ. Учебный критерий — overlap токенов вопроса и лучшего документа: если overlap < MIN_OVERLAP, ставит needs_mcp=True.
  • Условный переход: rag → final (нашли) или rag → mcp_node → final (не нашли). В проде вместо overlap — score/threshold, reranker, self-check/LLM-judge (Agentic RAG).
  • mcp_node: делает HTTP-запрос к MCP-серверу и кладёт ответ (mcp_answer) в state.
  • Это и есть «MCP как узел» в отличие от «MCP как tool»: оба валидны, выбор зависит от того, нужен ли программный контроль над вызовом.

Пример

rag_node ─(needs_mcp == False)→ final
         └(needs_mcp == True) → mcp_node → final     # внешний сервер как узел графа

Узел или инструмент, и чем заменить MIN_OVERLAP

Развилка — кто решает, вызывать ли внешний источник. Как узел MCP ставят, когда вызов обязателен и его место в потоке известно заранее: порядок фиксирован, трассировка предсказуема, модель не может «решить не ходить». Как инструмент — когда обращение уместно не всегда и выбор разумно оставить модели. Практическое следствие: обязательные шаги (обогащение контекста, обязательная проверка) делают узлом, опциональные (уточняющий поиск) — инструментом. Смешивать в одном пайплайне нормально.

Про порог «нашёл / не нашёл». Учебная метрика пересечения слов не переносится в прод: она ломается на синонимах и морфологии, а на русском особенно. Продакшен-замена — та же воронка, что в поиске: отобрать кандидатов, переранжировать cross-encoder'ом и принимать решение по его score (Reranking), а там, где нужен смысловой вердикт, а не ранг, — бинарная проверка судьёй (LLM as Judge). Порог в обоих случаях калибруется на размеченных примерах, а не берётся из учебного примера.

Связано с

  • MCP — здесь MCP подаётся как узел графа, а не как LLM-вызываемый инструмент
  • Tool Calling — альтернатива: MCP как tool, который выбирает сама модель
  • Agentic RAG — fallback «не нашли в базе → внешний источник» = шаг к agentic-retrieval
  • LangGraph Intent Router — MCP-узел = второй уровень маршрутизации из RAG-ветки