В последних версиях OpenWebUI появились Skills, и я решил сразу же их «пристроить» в дело. Одной из задач их применения виделась валидация ссылок, которые чат-бот техподдержки отдает в своем ответе: модель должна отвечать на вопросы по документации, строить корректные ссылки на статьи и не выдумывать несуществующие эндпоинты и URL.
Готовые серверы с LLM и инструментами для ИИ и машинного обучения.
Казалось бы, простая задача — всего лишь нужно написать в системном промте: «Проверяй ссылки перед отправкой». Но модель не умеет делать HTTP-запросы. Она — текстогенератор, а не браузер. Поэтому встал вопрос:
В текущей экосистеме 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, так как сам он не может пройти по ссылке и проверить ответ от сервера.
Уровень 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.
Полный цикл: от вопроса до ответа
Давайте проследим, как работает вся цепочка на реальном сценарии.
Вход: пользователь спрашивает
«Как настроить сетевой интерфейс в панели 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 — выполняет конкретные действия «в мире», «Сделай и доложи».
Когда каждый компонент знает свою зону ответственности, система становится предсказуемой, тестируемой и легко расширяемой. А пользователь получает то, что ему нужно: в нашем случае это точный ответ с рабочими ссылками.
Полезные ссылки
- Официальная документация OpenWebUI Skills
- Model Context Protocol Specification
- Документация по MCP и mcpo в OpenWebUI
Готовые серверы с LLM и инструментами для ИИ и машинного обучения.