« у меня есть предложение. У меня есть параллельный доступ к Gemini 3 по подписке advanced. Я сейчас передам туда все эти функции и мы почитаем ответ? » Приветствую! Как архитектор архитектору — ситуация понятная. Проблема «фризов» в Open WebUI при использовании кастомных функций чаще всего кроется в том, что Python-код функций выполняется в контексте веб-сервера, и любой синхронный блокирующий вызов (особенно тяжелый POST с JSON) буквально «ставит на паузу» обработку текущего запроса.
Я проанализировал ваш стек (n8n + Vertex AI + Open WebUI) и предоставленный код. Ниже — разбор «узких мест» и план оптимизации. ## 🛠 Анализ проблем производительности
### 1. Блокирующие вызовы (Synchronous Requests) — Критическая проблема
Функции `raumai_anonymizer_pipe` и `raumai_text_anonymizer` вызывают `requests.post` напрямую в основном потоке. Почему это плохо: Когда пользователь отправляет сообщение, Open WebUI ждет завершения функции `pipe`. Если n8n обрабатывает запрос 60–120 секунд (как указано в ваших таймаутах), поток сервера занят ожиданием. Интерфейс в это время не получает даже подтверждения получения сообщения, что вызывает визуальный «фриз». Отягчающий фактор: В `RaumAI_v4` и `RaumAI4Pro` вы используете `requests.get` для загрузки изображений по URL до создания отдельного потока. Если ссылка медленная — чат замирает. ### 2. Избыточная передача данных (Payload Overload)
В `raumai_anonymizer_pipe` вы отправляете: `payload = {«chat_data»: body}`. Объект `body` в Open WebUI содержит всю историю переписки и все метаданные. Если в чате 20+ сообщений или прикреплено несколько фото в Base64, вы каждый раз пересылаете десятки мегабайт в n8n. Это нагружает сеть и заставляет n8n тратить время на парсинг гигантских JSON. ### 3. Неэффективное «ожидание» в потоках
В функциях с генераторами (например, `RaumAIv4Vertex`) используется цикл `while t.is_alive(): time.sleep(0.2)`. Хотя это лучше, чем прямая блокировка, использование `threading` в асинхронной среде (которой является FastAPI под капотом Open WebUI) — это «костыль». Эффективнее использовать нативный `asyncio`. ## 🚀 Оптимизированные решения
Я подготовил обновленные шаблоны для ваших функций. Основной упор сделан на aiohttp для асинхронности и минимизацию payload .
### Оптимизация №1: Анонимайзер (Переход на Async + Минимальный Payload)
Этот код заменяет блокирующий запрос на асинхронный и отправляет в n8n только необходимое.
Python
«`
import aiohttp
import json
import logging
class Pipe: class Valves: N8N_WEBHOOK_URL: str = «http://10.0.0.20:5678/webhook/raumai-anonymizer-v4» def __init__(self): self.id = «raumai_anonymizer_pipe_v4_optimized» self.name = «🛡️ RaumAI Anonymizer (Async)» self.valves = self.Valves() async def pipe(self, body: dict) -> str: messages = body.get(«messages», []) if not messages: return «Ошибка: пустой запрос.» # ОПТИМИЗАЦИЯ: Отправляем только последнее сообщение и ID чата вместо всего body last_message = messages[-1] payload = { «text»: last_message.get(«content», «»), «chat_id»: body.get(«chat_id», «web-user»), «user»: body.get(«user», {}).get(«email», «anonymous») } try: # Используем асинхронный сеанс aiohttp async with aiohttp.ClientSession() as session: async with session.post( self.valves.N8N_WEBHOOK_URL, json=payload, timeout=120 ) as response: if response.status != 200: return f»❌ Ошибка n8n: Статус {response.status}» data = await response.json() return data.get(«result», str(data)) except Exception as e: return f»⚠️ Ошибка связи (Async): {str(e)}»
«`
### Оптимизация №2: RaumAI Pro Pipe (Streaming + Async)
Для функций, где важна индикация времени, лучше использовать асинхронный генератор. Это уберет «фриз» при загрузке изображений.
Python
«`
import aiohttp
import asyncio
import time
from typing import Union, Generator, Iterator
class Pipe: def __init__(self): self.id = «raumai_v4_pro_async» self.name = «RaumAI 4 Pro (Optimized)» async def pipe(self, body: dict, __user__: dict = None) -> Union[str, Generator, Iterator]: url = «http://10.0.0.20:5678/webhook/raumai04pro» # Извлекаем только текст последнего сообщения messages = body.get(«messages», []) user_message = «» if messages: content = messages[-1].get(«content», «») user_message = content if isinstance(content, str) else «Image/Multimodal» payload = { «text»: user_message, «chatId»: body.get(«chat_id», «web-user»), «user_email»: (__user__ or {}).get(«email», «anonymous»), } async def _gen(): yield «⏳ Соединение…» start_time = asyncio.get_event_loop().time() try: async with aiohttp.ClientSession() as session: # Асинхронный запрос не блокирует UI async with session.post(url, json=payload, timeout=300) as response: while response.status == 200: # Если n8n возвращает ответ долго, мы можем «тикать» в чат elapsed = int(asyncio.get_event_loop().time() — start_time) if elapsed > 2: yield f» -{elapsed}s-» data = await response.json() result_text = data.get(«output», {}).get(«text») or data.get(«result») or str(data) yield f»\n\n{result_text}» break except Exception as e: yield f»\n\n❌ Ошибка: {str(e)}» return _gen()
«` ## 📋 Итоговые рекомендации по архитектуре
1. Чистка Inlet-фильтров: В фильтре `RaumAI v5` вы перебираете сообщения для вставки системной даты. Чтобы ускорить процесс, не ищите `system_msg` каждый раз по всему списку. Просто проверяйте `messages[0]`. Если это не системная роль — вставляйте новую. 2. Обработка изображений: Если вы планируете передавать JSON с Base64 изображениями, убедитесь, что в n8n включены лимиты на размер тела запроса (по умолчанию в Node.js они могут быть малы). 3. Таймауты: Таймаут 120с — это очень много для HTTP-запроса в ожидании UI. Если n8n работает так долго, пользователю лучше выдавать промежуточный ответ «Задача принята, обрабатываю…», а результат присылать через уведомление или обновлять чат асинхронно через базу данных. 4. Удаление лишнего из Payload: Никогда не отправляйте полный `body` в n8n. Используйте конструкцию `payload = {«text»: messages[-1][‘content’], «metadata»: …}`. Это сэкономит время на сериализацию/десериализацию JSON. Что сделать сейчас: В первую очередь обновите `Anonymizer Pipe`, так как он — главный виновник фризов из-за полностью блокирующего кода. Перепишите его на `async def pipe` с использованием `aiohttp`, как показано в примере выше.
Нужна ли помощь с переписыванием логики обработки изображений в асинхронный вид для `RaumAIv4Vertex`?