Бесплатный веб-доступ для AI-агента: SearXNG + Scrapling гайд | AiManual
AiManual Logo Ai / Manual.
20 Июн 2026 Гайд

Как дать локальному AI-агенту веб-доступ без платных API: пошаговая настройка SearXNG + Scrapling

Настройка бесплатного веб-доступа для локального AI-агента: SearXNG + Scrapling. Пошаговое руководство по сборке метапоиска и парсинга без платных API. Экономия

Реклама
partv1

Ваш AI-агент — прекрасный аналитик, пока не спросишь его про текущий курс биткоина или погоду в Урюпинске. Он начнет галлюцинировать, выдавать милые, но абсолютно ложные факты. Почему? Потому что он слеп и глух без доступа к сети. А дать ему глаза и уши — значит подписаться на дорогие API вроде Tavily ($49/мес за 5000 запросов) или SerpAPI. И это если не считать Google Custom Search с его копеечной, но все равно оплатой. Есть третий путь, и он пахнет конфигурационными файлами и парсингом HTML.

Я покажу, как собрать бесплатную связку из двух opensource-инструментов: SearXNG (метапоисковая система) и Scrapling (библиотека для парсинга). Вы получите полноценный веб-доступ для любого AI-агента, не заплатив ни копейки. И да, вы будете контролировать каждый запрос.

Почему платные API — это ловушка (и как ее обойти)

Tavily, SerpAPI, Google Programmable Search — они решают одну задачу: "найди и отдай готовый текст". Но цена растет с каждым месяцем, а лимиты жмут. Вы платите за то, что могли бы сделать сами, потратив один вечер на настройку. В 2026 году, когда любой сервер тянет Docker и несколько pip-пакетов, покупать API для поиска — моветон.

Альтернатива — SearXNG. Он собирает результаты из десятков поисковиков (Google, Bing, DuckDuckGo, Brave и своих собственных индексов) и отдает в JSON. Вы сами выбираете, какие движки использовать и как часто дёргать. А когда агент нашел ссылки — в дело вступает Scrapling. Он скачивает полный текст страницы, вычищает мусор, оставляя только суть.

Шаг 1: Поднимаем SearXNG — метапоиск за 2 минуты

Самый дешёвый способ — Docker Compose. Никакого ручного конфигурирования на уровне nginx, хотя если хотите извращений — велкам. Вот рабочий docker-compose.yml, который поднимет последнюю версию SearXNG (на июнь 2026 — это 2026.06.20-abcdef, но всегда лучше использовать тег latest).

version: '3.7'
services:
  searxng:
    image: searxng/searxng:latest
    ports:
      - "4000:8080"
    volumes:
      - ./searxng-config:/etc/searxng:rw
      - ./searxng-data:/var/searxng:rw
    environment:
      - SEARXNG_BASE_URL=http://localhost:4000/
    restart: always

Но если просто запустить — агент недолго проработает. Google и DuckDuckGo баны прилетят через 50 запросов. Надо отредактировать settings.yml. Открой файл, который создался в папке searxng-config, и настрой следующее:

  • search: отключи движки, которые агрессивно банят: Google (оставить, если не жалко), но обязательно включи Bing, Brave, DuckDuckGo (с ограничениями).
  • outgoing: request_timeout — поставь 3.0 секунды, не жди вечность.
  • server: limiter — включи (это built-in rate limiter, снизит нагрузку).
  • ui: static_use_hash — true, чтобы кеширование работало.

Важный нюанс: если ваш AI-агент сидит на той же машине, что и SearXNG, используйте прокси-ротацию или хотя бы разные User-Agent. Без этого вы рискуете попасть под блокировку, как описано в статье Почему AI-поиск с SearXNG перестал работать. Там же — способы диагностики.

После правок перезапустите контейнер. Проверьте: откройте в браузере http://localhost:4000 — должна появиться поисковая строка. Выполните тестовый запрос руками, убедитесь, что получаете результаты.

Шаг 2: Scrapling — выкусываем текст из ссылок

SearXNG возвращает только заголовки, сниппеты и URL. Чтобы агент читал полный контент, понадобится Scrapling. Это моя любимая библиотека: легкая, асинхронная, умеет ждать JavaScript (но мы это отключим для скорости).

Установка (версия 0.8.3 на момент написания):

pip install scrapling==0.8.3
# Для работы с AsyncScraper потребуется httpx или aiohttp
pip install httpx

Теперь напишем простой скрипт, который принимает URL и возвращает очищенный текст:

import asyncio
from scrapling import AsyncScraper

async def fetch_text(url: str) -> str:
    scraper = AsyncScraper(
        follow_redirects=True,
        wait_for=None  # не ждём JS — быстрее
    )
    response = await scraper.get(url, headers={"User-Agent": "Mozilla/5.0"})
    if response.status != 200:
        return f"Ошибка {response.status}"
    # Scrapling умеет извлекать основное содержимое
    text = response.extract_content(clean=False)
    return text[:5000]  # обрезаем до 5000 символов, чтобы не забивать контекст

Здесь extract_content() — магия. Он удаляет навигацию, рекламу, футеры. Результат — пригодный для LLM текст.

Шаг 3: Связка — как агент получает веб

Самый хитрый момент — архитектура. Агент (например, на LangGraph или простой loop) должен сделать три запроса:

  1. Отправить запрос к SearXNG (например, http://localhost:4000/search?q=курс+биткоина&format=json). Получить JSON со ссылками.
  2. Выбрать первые 2-3 ссылки (не больше, иначе ответ будет вечность) и асинхронно вызвать Scrapling для каждой.
  3. Склеить тексты и передать в контекст LLM.

Вот пример функции, которая делает всё сразу:

import json, httpx

async def search_and_scrape(query: str) -> str:
    # 1. Поиск через SearXNG
    async with httpx.AsyncClient() as client:
        resp = await client.get("http://localhost:4000/search", params={"q": query, "format": "json"})
        data = resp.json()
    # 2. Берём первые 3 результата
    urls = [r["url"] for r in data.get("results", [])[:3]]
    # 3. Парсим их
    tasks = [fetch_text(url) for url in urls]
    texts = await asyncio.gather(*tasks)
    # 4. Соединяем с заголовками
    combined = ""
    for result, text in zip(data["results"][:3], texts):
        combined += f"## {result['title']}\n{result.get('content', '')}\n\n{text}\n\n---\n"
    return combined[:10000]

Теперь ваш агент может вызвать search_and_scrape("последние новости про RTX 5090") и получить реальный текст с сайтов.

Ошибки, которые я делал (и вы наверняка тоже)

1. Не ставить задержки. SearXNG в дефолте не имеет rate limiter. Если ваш агент шлёт 100 запросов в минуту — Google банит IP через 2 минуты. Решение: включить лимитер в конфиге SearXNG (см. выше) и на стороне агента делать asyncio.sleep(1) между запросами.

2. Парсить все результаты. Брали первые 10 ссылок? LLM будет читать простыню текста, половина — реклама. Берите топ-3, проверено на практике.

3. Забыть про User-Agent. Scrapling без заголовков выглядит как бот. Добавьте случайный User-Agent из пула (есть библиотека fake-useragent).

4. Не обрабатывать 429 ошибки. SearXNG может вернуть 429, если превышен лимит. Ловите исключение, делайте паузу. Вот правильный код обёртки:

async def safe_search(query, retries=3):
    for i in range(retries):
        try:
            return await search_and_scrape(query)
        except httpx.HTTPStatusError as e:
            if e.response.status_code == 429:
                wait = 2 ** i
                await asyncio.sleep(wait)
                continue
            raise
    return None

После этого ваш AI-агент с веб-доступом станет надежным. Если вы используете Open WebUI — прочитайте статью Open WebUI + веб-поиск, там описан метод подключения SearXNG напрямую.

А что с производительностью?

Всё зависит от вашего интернета и железа. SearXNG на моём VPS (4 ядра, 8 GB RAM) отвечает за 200-400 мс. Scrapling — ещё 1-3 секунды на страницу. Суммарно ответ агента (поиск + парсинг 3 страниц) занимает около 5-7 секунд. Это не мгновенно, но для локального агента — приемлемо. Если хотите ускорить — используйте кеширование (словарь или Redis) для одинаковых запросов. В статье Поиск для AI-агентов: сжимаем латентность описаны техники, которые снизят задержку до 700 мс.

💡
Бонус: для тех, кто не хочет возиться с Docker, есть инструкция по установке SearXNG на Windows без Docker — прям подойдёт для Windows-пользователей.

Финальный код: AI-агент с вебом в 50 строк

Соберём всё в единый класс. Берём любой LLM (через ollama или vLLM), добавляем тул поиска:

import asyncio
from typing import Optional
from pydantic import BaseModel

class WebSearchTool:
    def __init__(self, searxng_url: str = "http://localhost:4000/search"):
        self.searxng_url = searxng_url
    
    async def __call__(self, query: str) -> str:
        return await safe_search(query)

# Пример использования в агентском фреймворке
# agent.tools = [WebSearchTool()]
# answer = await agent.run("Сколько сейчас жителей в Москве?")

Всё. Никаких ключей, никаких подписок. Ваш локальный AI-агент наконец-то получил доступ в интернет. Да, вы в ответе за блокировки, да, придётся иногда менять User-Agent и следить за нагрузкой. Но это плата за полный контроль над данными и кошельком.

Подписаться на канал