Автор: Александр Казанцев, руководитель отдела документации и контента
В последних версиях OpenWebUI появились Skills, и я решил сразу же их «пристроить» в дело. Одной из задач их применения виделась валидация ссылок, которые чат-бот техподдержки отдает в своем ответе: модель должна отвечать на вопросы по документации, строить корректные ссылки на статьи и не выдумывать несуществующие эндпоинты и URL.
|
AI-платформа Готовые серверы с LLM и инструментами для ИИ и машинного обучения. |
Казалось бы, простая задача — всего лишь нужно написать в системном промте: «Проверяй ссылки перед отправкой». Но модель не умеет делать HTTP-запросы. Она — текстогенератор, а не браузер. Поэтому встал вопрос:
«Как гарантировать, что сгенерированная ссылка действительно работает, а не ведёт на 404?»

В текущей экосистеме OpenWebUI мы имеем три уровня абстракции:
System Prompt — инструкция для LLM
Skills — пост-процессоры ответа
MCP Tools — исполняемый код для действий «в реальном мире»
В этой статье я подробно разберу, чем они отличаются, как взаимодействуют и как на их примере построить надёжный пайплайн валидации URL. Весь код — из продакшена, с обработкой ошибок и логированием.
Карта территории: три слоя архитектуры
Прежде чем погружаться в код, давайте визуализируем, как данные проходят через систему:

Каждый слой решает свою задачу. Давайте разберём их по порядку.
Уровень 1: System Prompt — «Должностная инструкция»
System Prompt — это текстовый контекст, который «настраивает» поведение языковой модели до генерации ответа. Это не код, не конфигурация — это естественный язык, который модель интерпретирует как правила поведения. В нашем случае это правила для поиска информации в базе знаний, ее интерпретация, форматирование.
Кратко стартовый блок и блок формирования ссылок выглядят вот так:
<role> ИИ-ассистент техподдержки HOSTKEY. Задача: помогать по серверам, панели Invapi и документации hostkey.ru. </role> <rules> ### ? ЯЗЫК (приоритет №1) - Отвечай ТОЛЬКО на русском, независимо от языка вопроса. ### ? ССЫЛКИ — АЛГОРИТМ (строго) Файл в базе: `<раздел>@<тема>@ru.md` Преобразование: 1. Убрать `@ru.md` → `faq@network_settings` 2. Разбить по `@` → `["faq","network_settings"]` 3. Собрать URL: `https://hostkey.ru/documentation/faq/network_settings/` </rules>
Что делает System Prompt в данном случае:
Задаёт роль, тон и тематику ответов;
Обучает модель правильно формировать ссылки по бизнес-алгоритму;
Запрещает нежелательные действия (код, выдуманные URL);
Чего он не делает:
Не проверяет, существует ли ссылка на самом деле;
Не делает сетевые запросы;
Не знает о существовании инструмента url_fetch_mcp — для модели это «магия».
System Prompt — это должностная инструкция сотрудника. Он знает, как надо писать отчёт, но не может выйти в поле и проверить данные.
Уровень 2: Skills — «Редактор с лупой»
Новый инструментарий Skills в OpenWebUI — это декларативные скрипты (в формате Markdown с метаданными YAML), которые выполняются после генерации ответа моделью. Они могут:
Парсить и анализировать текст ответа
Вызывать внешние инструменты (MCP Tools)
Модифицировать вывод перед отправкой пользователю
Если системный промт привязан к конкретной модели, то Skills могут вызываться и использоваться различными моделями совместно. Достаточно указать их при создании кастомной модели в Workspace. Мы реиспользуем этот Skills, например, в переводчиках, чтобы не было «битых» ссылок.

В нашем случае нам нужен валидатор ссылок, то есть Skill url-validator-with-mcp.
--- name: url-validator-with-mcp description: Validates URLs via url_fetch_mcp, removes invalid ones, and ensures proper formatting for valid links. version: "2.0" tags: [validation, urls, links, mcp, formatting, self-check] requires_tools: ["url_fetch_mcp"] --- # ? URL Validator Skill (MCP-backed + Format Enforcement) ## ? Purpose After generating a response containing URLs: 1. **Validate accessibility** using `url_fetch_mcp` 2. **Remove entirely** invalid/unreachable links (text + URL) 3. **Enforce proper formatting** for valid links 4. Output ONLY the cleaned, validated response — no logs, no commentary
Обратите внимание на секцию между ---. В ней вы описываете ваш Skills и задаете, какие Tools он должен использовать. Также не забудьте включить Skills в настройках группы для ваших пользователей.

Разберемся, как же работает наш Skill:
Он находит URL в сгенерированном ответе (выполняет его парсинг);
Вызывает MCP Tools url_fetch_mcp передавая его последовательно найденные URL;
Применяет бизнес-правила: удалять битые ссылки, форматировать рабочие;
Возвращает «чистый» ответ без мета комментариев.
Чего не делает наш Skill:
Не учит модель, как строить ссылки (это задача системного промта);
Не заменяет логику бизнес-правил;
Не работает автономно и требует подключённого MCP Tool, так как сам он не может пройти по ссылке и проверить ответ от сервера.
Skill — это редактор, который проверяет черновик. Он не пишет за автора, но исправляет ошибки, удаляет нерабочие ссылки и приводит текст к стандарту.
Уровень 3: MCP Tools — «Курьер с прибором»
MCP (Model Context Protocol) Tools — это внешний исполняемый код (обычно Python/Node.js), который выполняет конкретные действия: запросы к API, проверка URL, работа с файлами. В OpenWebUI есть три механизма использования: встроенные инструменты, Toos и внешние MCP Tools.
В нашем случае у нас есть уже специальный сервер mcpo (MCP over HTTP) от тех же разработчиков, на котором расположен инструментарий работы с Invapi, поэтому добавить туда еще один было не проблемой. Но никто не запрещает тот же код реализовать через внутренние Tools OpenWebUI.
По факту сам инструмент — это простой fastmcp-сервер и код на питоне, который и проверяет ссылки. mcpo же в данном случае — это «переводчик» между миром локальных CLI-инструментов (MCP) и миром веб-приложений (OpenWebUI).
Плюсом mcpo будет интерактивная документация в swagger-формате, доступная в /docs.

Наш MCP инструмент носит имя check_url()
from __future__ import annotations import logging, re, sys, time from dataclasses import asdict, dataclass from typing import Optional from urllib.parse import urlparse import requests logging.basicConfig( stream=sys.stderr, level=logging.INFO, format='%(asctime)s [URL_FETCH] %(levelname)s: %(message)s' ) logger = logging.getLogger(__name__) DEFAULT_TIMEOUT = 5.0 USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)..." @dataclass class URLCheckResult: valid: bool url: str normalized_url: Optional[str] = None status_code: Optional[int] = None response_time_ms: Optional[float] = None error: Optional[str] = None content_type: Optional[str] = None final_url: Optional[str] = None ssl_valid: Optional[bool] = None def to_dict(self) -> dict: return {k: v for k, v in asdict(self).items() if v is not None} def check_url( url: str, timeout: float = DEFAULT_TIMEOUT, follow_redirects: bool = True, check_ssl: bool = True ) -> dict: """MCP Tool: Проверка доступности URL""" logger.info(f"MCP call: check_url(url='{url}')") # 1. Валидация формата is_valid, err = _validate_url_format(url) if not is_valid: return URLCheckResult(valid=False, url=url, error=err).to_dict() # 2. HTTP-запрос с обработкой ошибок try: response = requests.head(url, timeout=timeout, allow_redirects=follow_redirects, verify=check_ssl) # ... анализ ответа ... return URLCheckResult(valid=True, status_code=response.status_code, response_time_ms=...).to_dict() except requests.exceptions.Timeout: return URLCheckResult(valid=False, error="Таймаут").to_dict() except requests.exceptions.SSLError: return URLCheckResult(valid=False, error="SSL-ошибка").to_dict() # ... другие исключения ...
Что наш валидатор делает:
Реально «стучится» по ссылке (сетевой запрос);
Возвращает структурированный результат: status_code, response_time_ms, error;
Обрабатывает исключения: таймауты, SSL-ошибки, редиректы;
Также его можно использовать не только в нашем конкретном Skill, но и в других, а также напрямую в моделях.
Но так как это просто код на Python, то он не знает контекст диалога (кто пользователь, о чём вопрос), не принимает решений: «удалять ссылку или нет», так как это логика Skill, и не форматирует финальный ответ, а отдает только данные в json формате.

После этого остается только прописать наш MCP Tools в OpenWebUI.

MCP Tool — это курьер с прибором учета. Он не решает, куда ехать и что писать в отчёте. Он только измеряет: «Доставлено/не доставлено, время в пути, причина сбоя».
Полный цикл: от вопроса до ответа
Давайте проследим, как работает вся цепочка на реальном сценарии.
Вход: пользователь спрашивает
«Как настроить сетевой интерфейс в панели Invapi?»
Шаг 1: Model + System Prompt
LLM получает вопрос + системный промт;
Находит в базе документ: controlpanel@network_interface@ru.md;
-
Применяет алгоритм:
"убрать @ru.md" → ["controlpanel","network_interface"] > "https://hostkey.ru/documentation/controlpanel/network_interface/" ;
Генерирует черновик:
"Откройте панель Invapi → 'Сеть' → 'Интерфейсы'.
Подробнее: [Настройка сетевого интерфейса]
(https://hostkey.ru/documentation/controlpanel/network_interface/)"
Шаг 2: Skill (url-validator-with-mcp)
Skill получает черновик;
Парсит текст, находит 1 ссылку;
Вызывает MCP Tool:
url_fetch_mcp(url="https://.../network_interface/")
Получает результат:
{ "valid": true, "status_code": 200, "response_time_ms": 342 }
Проверяет формат: Markdown - Да, HTTPS - Да, текст ≠ вопрос - Да ;
Возвращает ответ без изменений (всё валидно).
Шаг 3: А если ссылка не валидна?
MCP Tool вернул:
{ "valid": false, "error": "HTTP 404" }
Skill удаляет конструкцию "[текст](ссылка)" из ответа;
Выдает ответ без ссылки:
"Откройте панель Invapi → 'Сеть' → 'Интерфейсы'.
Точной инструкции не нашёл. Для помощи:
[Техподдержка](https://hostkey.ru/customer-care/)."
Сравнение подходов
Так что-же лучше? System Prompt, Skill или MCP Tools. Свели все основные критерии в такую таблиц, чтобы вы могли выбрать наиболее подходящий вам инструмент.
Критерий |
System Prompt |
Skill |
MCP Tool |
|---|---|---|---|
Когда выполняется |
До генерации ответа |
После генерации |
По вызову из Skill |
Язык реализации |
Естественный язык |
Markdown + логика |
Python/JS код |
Доступ к сети |
Нет |
Только через Tools |
Да |
Контекст диалога |
Полный |
Ответ модели |
Только параметры |
Основная задача |
Поведение, правила |
Пост-обработка, координация |
Конкретное действие |
Гибкость |
Высокая (текст) |
Средняя (шаблон) |
Низкая (код) |
Сложность поддержки |
Низкая |
Средняя |
Высокая |
Заключение
Разработка надёжного ИИ-ассистента — это не только про «умную модель». Это про архитектуру, где составляющие ее компоненты делают своё дело:
System Prompt — отвечает за поведение ассистента, правила, бизнес-логику, «Знай, как надо»;
Skill — на нем пост-обработка и координация, «Проверь, что сделано»;
MCP Tool — выполняет конкретные действия «в мире», «Сделай и доложи».
Когда каждый компонент знает свою зону ответственности, система становится предсказуемой, тестируемой и легко расширяемой. А пользователь получает то, что ему нужно: в нашем случае это точный ответ с рабочими ссылками.
Полезные ссылки
|
AI-платформа Готовые серверы с LLM и инструментами для ИИ и машинного обучения. |
Комментарии (7)

KoIIIeY
29.04.2026 15:23Зачем собирать ссыль с помощью ллм, когда это можно сделать через мцп просто по имени файла?

akdengi Автор
29.04.2026 15:23Доки это md файлы в БЗ openwebui, тут в любом случае иначе дергать ее через API и как то преобразовывать. Плюс там же md-шка из исходников собираемых доков, я же в БЗ не сайт лью, а подгтовленные и очищенные MD от Materail for MkDocs, поэтому реальный URL формируется моделькой, делаеть еще одну тулзу смысла нет. Ну и дергать tools будет llm, а не я сам, у меня бот - это кастомная модель в OpenWebUI с системным промтом, подцепленными MCP (сейчас это invapi где дергаем реальный список VPS, BM и сетевых планов, в будущем будет больше инфы) + вот этот Skill который дергает MCP чекера URL.
И этот чекер у нас подцеплен еще и на другие проекты - он валидирует переводы (сейчас делаю автопереводчик на доп. языки) доков клиентских на внешние ссылки + статьи которые переводим для ресурсов также прогоняем, чтобы не было битых ссылок.
aborouhin
Я так понимаю, в OpenWebUI пока что поддержка скиллов в весьма зачаточном состоянии. Полная спецификация Agent Skills не поддерживается. Включать в скиллы шаблоны, скрипты и даже бинарники, как предполагает эта спецификация, нельзя. Только Markdown. И это вот очень печально :(
akdengi Автор
Полностью не сделали, но там же есть tools, их можно связать со скилами и они сами про это пишут "Pair a skill with Open Terminal or any tool server.". Все равно это шаг вперед и позволил сделать те же self-check переводчики, повысив качество или как тут url-черкер. Особенно важно это, если openwebui во внутреннем контуре и его в инет не пускают, так можно связки делать с внешним миром через mcp-инструменты.
Сейчас еще automation завезли.
aborouhin
Это понятно, но хотелось бы любой скилл, который работает в Клоде (за который я плачý для себя, но для меня проблематично приобрести подписку на всех сотрудников), без изменений использовать и в OpenWebUI (который как раз очень хочется развернуть в локальной сети для всех).
akdengi Автор
Это к разрабам OpenWebUI, но они скорее всего тоже самое скажут. Клод это клод, а тут свое у них и будут ли они дублировать функционал в скиллах из тулз и функций неизвестно.
aborouhin
Ну, стандарт Agent Skills открытый и используется не только Клодом, а довольно внушительным списком инструментов. Увы, OpenWebUI в нём пока нет.