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