Гайд · grounder

Веб-поиск для локальной LLM без VPN

VPN это не тот инструмент. Он гоняет весь ваш трафик через чужой сервер, часто нестабилен и первым отваливается при блокировках. Для того чтобы модель искала в вебе, есть путь чище: поиск на стороне сервера.

Почему локальная LLM не знает актуальных ответов

Любая модель заморожена на дате окончания обучения. Локальная 7B не знает о вышедшей на этой неделе версии фреймворка, о новом методе в библиотеке или о цене, которая изменилась в прошлом месяце. И поскольку её учили быть полезной, вместо честного "не знаю" она с полной уверенностью выдаёт правдоподобную выдумку. В небольшом тесте локальная Qwen2.5-7B сама по себе ответила верно на 3 из 8 вопросов про актуальные факты. С живыми веб-данными в промпте - на 7 из 8.

Вывод простой: модель нужно не переучивать, а подкладывать ей свежие данные из веба прямо в контекст. Это и называется веб-поиском для локальной LLM, и состоит он не из одного шага, а из пяти.

Конвейер: поиск, загрузка, очистка, ранжирование, промпт

"Веб-поиск для локальной модели" - это на самом деле пять шагов. Сама модель делает только последний:

  1. Поиск - превратить вопрос в запрос и получить ранжированные URL результатов.
  2. Загрузка - скачать сами страницы (многие результаты закрыты и возвращаются пустыми).
  3. Очистка - убрать навигацию, рекламу и обвязку, оставив только контент (в markdown).
  4. Ранжирование - оставить только пассажи, относящиеся к вопросу. Именно этот шаг делает данные помещающимися в окно.
  5. Промпт - положить эти пассажи в контекстное окно и дать модели ответить.

Пропустите шаг 4 - и упрётесь в стену, на которой ломается большинство первых попыток.

Стена токенов: почему сырые страницы ломают модель

Локальная модель работает с маленьким окном - тем, что помещается в вашу VRAM, обычно от 4k до 32k токенов, а не с теоретическим максимумом модели. Три полные веб-страницы легко дают более 20 000 токенов.

В нашем замере один набор страниц из выдачи составил 91 039 символов, это примерно 22 759 токенов - в 2,8 раза больше окна на 8k. Модель при этом не отвечает хуже: локальный рантайм (llama.cpp под LM Studio или Ollama) не сокращает ввод сам, а обрезает его или падает с ошибкой. Вы получаете пустой ответ или жёсткую ошибку длины контекста.

Выглядит это так - на вопросе про CEO Anthropic страницы дали около 22 759 токенов, и модель вернула:

HTTP 400: n_keep 29992 >= n_ctx 8192

Та же модель с токен-ограниченным пакетом ответила "Dario Amodei" всего из 3 454 символов. Лекарство - не большее окно, а меньше данных, но правильных: несколько релевантных пассажей, обрезанных под известный бюджет токенов. Разбор измеренного сбоя и решение есть в гайде как дать LLM доступ к вебу.

Подход 1 - свой конвейер на SearXNG

SearXNG - это бесплатный self-hosted метапоисковик. Именно он стоит поисковым бэкендом за Perplexica (в README прямо сказано "Web search powered by SearxNG") и похожими self-hosted RAG-сборками, как раз потому что он бесплатный. Но он закрывает только шаг 1. Загрузку, очистку и ранжирование вы пишете сами, а на публичных инстансах ловите лимиты и блокировки.

# Только шаг 1: спрашиваем инстанс SearXNG и получаем URL результатов
import requests

def searxng_search(query, instance="http://localhost:8080"):
    r = requests.get(f"{instance}/search",
                     params={"q": query, "format": "json"}, timeout=15)
    r.raise_for_status()
    return [hit["url"] for hit in r.json().get("results", [])[:5]]

# Загрузку каждого URL, очистку от обвязки, ранжирование пассажей
# и обрезку под окно вы делаете сами - это уже не SearXNG.

Реальные ограничения

Подход 2 - веб-поиск через API Ollama

У Ollama появился хостовый web search API. Он закрывает поиск и возвращает сниппеты - это шаг вперёд по сравнению с сырым SearXNG. Но нужен бесплатный аккаунт Ollama и API-ключ, а возвращает он результаты, а не готовый под контекст пакет, так что шаг с бюджетом токенов всё ещё ваш.

import requests

def ollama_web_search(query, api_key):
    r = requests.post("https://ollama.com/api/web_search",
        headers={"Authorization": f"Bearer {api_key}"},
        json={"query": query}, timeout=30)
    r.raise_for_status()
    # схема: docs.ollama.com/capabilities/web-search
    return r.json()  # результаты со сниппетами - обрезка под окно всё ещё на вас

Реальные ограничения

Если Ollama - ваш основной рантайм, отдельно разобран сценарий подключения в гайде Ollama и доступ в интернет. Сам Ollama не является MCP-хостом, поэтому для инструментов поверх него ставят MCP-совместимый клиент.

Подход 3 - токен-ограниченный пакет доказательств

Три шага, которые реально делают веб-поиск пригодным для локальной модели - загрузить настоящую страницу, очистить её и отранжировать до того, что помещается, - каждый раз одни и те же. Поисковый API, сделанный под LLM, проходит все пять шагов и возвращает токен-ограниченный пакет доказательств: модель получает только те пассажи, что отвечают на вопрос. Grounder - один из таких инструментов (его делаем мы); важнее сам паттерн, чем поставщик.

import requests

KEY = "your_grounder_key"   # бесплатный тариф: 1500 страниц в месяц, без карты

# Один вызов: поиск -> загрузка -> ранжирование -> пакет под ваше окно
r = requests.post("https://grounder.dev/v1/deep_search",
    headers={"Authorization": f"Bearer {KEY}"},
    json={"query": "последняя стабильная LTS-версия Node.js",
          "max_tokens": 600},           # гарантирует, что влезет в модель на 8k
    timeout=180)
pack = r.json()
# pack["passages"] - ранжированные пассажи со ссылками, обрезанные под окно
evidence = "\n\n".join(p["text"] for p in pack["passages"])

# Передайте evidence локальной модели в промпте - оно останется в пределах окна.

Дальше модель отвечает по данным, которые точно помещаются:

import requests  # любой OpenAI-совместимый локальный сервер (LM Studio, Ollama)

resp = requests.post("http://localhost:1234/v1/chat/completions", json={
    "model": "qwen2.5-7b-instruct",
    "messages": [{"role": "user",
        "content": f"Отвечай только по этим данным.\n\nДАННЫЕ:\n{evidence}\n\nВопрос: последняя стабильная LTS-версия Node.js"}]
}).json()
print(resp["choices"][0]["message"]["content"])

Тот же паттерн лежит в основе более общего разбора - как подключить нейросеть к интернету: инструменты подключаются к MCP-совместимому клиенту, а модель получает уже готовые к контексту данные.

Какой подход подходит

 SearXNG (свой конвейер)Ollama web searchТокен-ограниченный пакет
Какие шаги закрываетПоискПоиск + сниппетыПоиск + загрузка + очистка + ранжирование
Помещается в малое окноПишете самиПишете самиДа (обрезка по токенам)
Закрытые страницыНа васОграниченноОбрабатываются
Нужен аккаунтНет (self-host)ДаДа (есть бесплатный тариф)
Операционная нагрузкаВысокая (хостинг + разработка)СредняяНизкая

Россия и вопрос VPN

У локального сценария в России есть отдельная сложность: даже собрав конвейер, шаг загрузки упирается в заблокированные сайты. SearXNG на вашей машине и локальная модель сидят за российским каналом, поэтому нужный источник может просто не открыться, и тогда включают VPN.

Токен-ограниченный пакет снимает это иначе. Grounder делает поиск и читает страницы со своих серверов за пределами России, а вам возвращает уже очищенные пассажи. Заблокированный источник открывается на стороне сервиса, VPN на вашей машине не нужен. Честная граница: это решает повседневную блокировку отдельных сайтов, а не полное отключение по белым спискам - от тотального шатдауна интернета никакой внешний сервис не спасёт.

Прозрачность: Grounder - наш коммерческий продукт, так что интерес у нас есть. Но конвейер из пяти шагов и ограничение по токенам верны независимо от инструмента: SearXNG и API Ollama - реальные и рабочие варианты, а собрать шаг загрузки и ранжирования самому - тоже честный путь. Числа выше взяты из нашего собственного прогона (Qwen2.5-7B, окно 8k, 8 вопросов) - это направляющий тест, а не статистическое исследование. Конкретные номера версий сверяйте с официальным источником.

Частые вопросы

Можно ли сделать веб-поиск для LLM вообще без VPN?

Да. Если поиск и чтение страниц выполняет сервер за пределами России, вашему компьютеру VPN не нужен - достаточно доступа к этому серверу.

Чем это лучше локального поиска вроде SearXNG?

Локальный поиск качает страницы из вашей сети, поэтому упирается в блокировки. grounder читает их снаружи, включая закрытые в РФ источники.