Почему обычный ИИ-агент сдохнет в ЧС?
Представь: наводнение, землетрясение или техногенная авария. Вышки сотовой связи рухнули, спутниковый интернет перегружен, 4G нет. Твой любимый ИИ-агент, который вчера писал письма и генерировал картинки, превращается в тыкву. Он просто не может никуда сходить — API не отвечают, база знаний в облаке недоступна. Знакомо? Команда Сбера столкнулась с этим в 2025 году на учениях МЧС. Тогда мы поняли: агент для ЧС должен работать так же надежно, как топор или радиостанция — без единого запроса во внешний мир.
Мы не первые, кто обжегся на безопасности агентов. Недавно я писал о 40 000 голых агентов с root-доступом — яркий пример, что доверять агенту без офлайн-режима опасно в принципе. А если ЧС, то ставки еще выше.
Как мы в Сбере строили офлайн-агента: архитектура
Внутреннее название проекта — "Просоюз" (Палочка-выручалочка). Задача: агент на ноутбуке или Raspberry Pi 5, который дает инструкции по эвакуации, подсказывает аптечки, находит ближайшее укрытие, рассчитывает маршрут по скачанным картам — и все это без интернета. Стек: локальная LLM + векторная база + RAG + кастомные инструменты. Никаких облаков.
Архитектура максимально простая — мы не стали городить сложную оркестрацию с роутингом запросов. Подробнее про этот подход я рассказывал в статье "Архитектура автономных ИИ-агентов без роутинга". Там же объясняю, почему лишние слои только замедляют отклик — а в ЧС каждая секунда на вес золота.
Ключевое отличие от обычных агентов: все данные зашиты в образ. Никаких внешних вызовов, никаких "подумаю и отвечу позже".
Шаг 1: Выбор модели и квантизация под CPU
Первая дилемма: модель должна влезать в 8-16 ГБ ОЗУ и работать на CPU (GPU в бункере не предусмотрен). На конец 2025 года лучшим кандидатом была Llama 4 Scout от Meta — 17B параметров, отличное понимание инструкций. Но после тестов мы остановились на Qwen3-8B (апрель 2025) — она быстрее на CPU и точнее в русском языке. Обе доступны в квантизации GGUF через llama.cpp.
Как это выглядит на практике:
pip install llama-cpp-python
wget https://huggingface.co/Qwen/Qwen3-8B-GGUF/resolve/main/qwen3-8b-q4_k_m.gguf
python3 -c "
from llama_cpp import Llama
llm = Llama(model_path='qwen3-8b-q4_k_m.gguf', n_ctx=4096, n_threads=4)
print(llm('Что делать при пожаре?', max_tokens=100)['choices'][0]['text'])
"Обрати внимание на n_threads=4 — это количество ядер CPU. На Raspberry Pi 5 с 4 ядрами генерация одного ответа занимает ~15 секунд. Долго, но для ЧС приемлемо. Если не терпится попробовать офлайн-агента на своем ноутбуке, а опыта не хватает — советую пройти курс Нейросети с нуля от Skillbox, там разбирают основы инференса LLM и работы с локальными моделями.
Шаг 2: Векторная БД на коленке — FAISS без интернета
Чтобы агент отвечал на вопросы по инструкциям МЧС, картам и справочникам, нужна векторная база. Требование — она не должна уходить в сеть. FAISS от Facebook — идеальный вариант. Индексы храним локально, в файле.
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np
# Модель эмбеддингов (тоже локальная)
model = SentenceTransformer('intfloat/multilingual-e5-large')
docs = [
"При пожаре немедленно покиньте здание, не пользуйтесь лифтом",
"При землетрясении встаньте в дверной проем или под стол",
"Аптечка первой помощи: бинт, жгут, антисептик, обезболивающее"
]
embeddings = model.encode(docs)
index = faiss.IndexFlatL2(embeddings.shape[1])
index.add(np.array(embeddings))
faiss.write_index(index, 'mchs_instructions.index')На практике мы добавили ~2000 документов — актуальные на 2025 год памятки МЧС, карты районов, списки убежищ. Индекс весит ~200 МБ. Загружается за секунду.
Шаг 3: RAG-пайплайн, который не врет (почти)
Сборка пайплайна на LangChain — самый прямолинейный вариант. Агент получает запрос, ищет топ-3 похожих документа из FAISS, подставляет в промпт.
from langchain.chains import RetrievalQA
from langchain.llms import LlamaCpp
from langchain.vectorstores import FAISS
from langchain.embeddings import HuggingFaceEmbeddings
llm = LlamaCpp(model_path='qwen3-8b-q4_k_m.gguf', n_ctx=4096, n_threads=4)
embedding = HuggingFaceEmbeddings(model_name='intfloat/multilingual-e5-large')
vectorstore = FAISS.load_local('mchs_instructions.index', embedding)
qa = RetrievalQA.from_chain_type(llm=llm, chain_type='stuff', retriever=vectorstore.as_retriever())
response = qa.run('Что взять с собой при эвакуации?')
print(response)Но есть нюанс: модель может выдумывать то, чего нет в документах. Мы решили жестко ограничить промпт: "Отвечай только на основе предоставленного контекста. Если ответа нет, скажи "нет информации"". Это снизило галлюцинации с 30% до 2%. Подробнее про борьбу с "тупостью" агента читай в статье Agent Skills.
Шаг 4: Инструменты агенту — GPS, компас, калькулятор
Обычный RAG отвечает текстом. Агент же должен уметь выполнять действия. Например, рассчитать расстояние до укрытия по координатам. Для этого мы добавили инструменты (tool use) — функции, которые агент может вызывать по запросу.
from langchain.tools import tool
@tool
def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> str:
"""Возвращает расстояние между двумя координатами в метрах"""
import math
R = 6371000
phi1, phi2 = math.radians(lat1), math.radians(lat2)
delta_phi = math.radians(lat2 - lat1)
delta_lambda = math.radians(lon2 - lon1)
a = math.sin(delta_phi/2)**2 + math.cos(phi1)*math.cos(phi2)*math.sin(delta_lambda/2)**2
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
return f'{R * c:.0f} метров'
# Подключение к агенту (упрощенно)
tools = [calculate_distance]
agent = create_agent(llm, tools, retriever)Таким образом, на вопрос "Где ближайшее убежище?" агент сначала находит координаты укрытий в векторной БД, потом вызывает calculate_distance для каждого и возвращает ближайшее.
Шаг 5: Упаковка в Docker и запуск на Raspberry Pi 5
Финальный штрих — Docker-образ со всем внутри: модель, индексы, код. Никаких монтирований внешних сетей. Мы сделали образ на основе python:3.12-slim с предустановленными зависимостями. Размер — ~12 ГБ (модель 4.5 ГБ, эмбеддер 2 ГБ, документы и индексы 200 МБ).
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# Отключаем любые сетевые вызовы
ENV TRANSFORMERS_OFFLINE=1
ENV HF_HUB_OFFLINE=1
CMD ["python", "agent_server.py"]Запускаем на Raspberry Pi 5 (8 ГБ):
docker run --cpus=4 --memory=7g -p 8080:8080 prosouz-agent:latestAPI-сервер на FastAPI принимает запросы, агент обрабатывает их локально. Время ответа — 20-30 секунд для сложного вопроса.
Грабли, на которые мы наступили
Грабли 1: Модель галлюцинировала карты. На запрос "Покажи план эвакуации из здания" агент мог нарисовать текстовое описание, не совпадающее с реальными планами. Решение — добавить строгий чек: если вопрос про план, агент должен выдать только ссылку на документ, а не генерировать текст.
Грабли 2: Векторная БД разбухала. Мы добавили слишком много мелких документов (чеки, списки инвентаря). Индекс вырос до 1.5 ГБ, что замедлило поиск. Чистка и объединение похожих документов решило проблему.
Грабли 3: Latency на слабом железе. На Raspberry Pi 5 генерация ответа занимала до 40 секунд. Пришлось уменьшить контекст до 2048 токенов и включить кэширование KV-кэша. Это ускорило повторяющиеся запросы в 2 раза.
Отдельная тема — безопасность. Если агент работает в публичном доступе (например, в мобильном приложении МЧС), он должен быть надежно изолирован. Мой коллега писал об угрозах агентного ИИ в 2026 году — ситуация серьезная. Поэтому мы вообще не даем агенту доступ к файловой системе или сети вне Docker-контейнера.
Часто задаваемые вопросы
Какую модель лучше использовать на русском языке?
Из проверенных — Qwen3-8B (отличная поддержка русского), Llama 4 Scout (чуть хуже, но универсальнее). Если нужно что-то совсем легкое — Mistral Small 3.1 (7B), но качество ниже.
Можно ли запустить на телефоне?
Теоретически да, через llama.cpp с Android NDK. Но мы не тестировали — телефон в ЧС может быстро разрядиться. Ноутбук или микрокомпьютер надежнее.
Где брать официальные инструкции МЧС?
Все открытые памятки есть на сайте МЧС России. Мы скачали их в PDF и перевели в markdown. Также добавили локальные карты с OSM (OpenStreetMap) — их можно выгрузить через утилиту osmium.
Как часто обновлять данные?
Рекомендуем обновлять образ каждые полгода — менять модели на новые версии и синхронизировать документы. Однако сам агент не должен требовать обновления во время ЧС.
Что дальше?
Мы выложили базовый код в open source. Если хотите собрать своего офлайн-агента, рекомендую изучить наш гайд по внедрению нейросетей в IT-компанию — там разобраны правильные паттерны интеграции. А для повышения отказоустойчивости стоит глянуть DevOps для ИИ — как заставить нейросеть мониторить и чинить инфраструктуру.
Офлайн-агенты станут стандартом для экстренных служб. Вопрос не в том, нужно ли это, а в том, кто первый сделает надежное решение. Команда Сбера свой движок уже запустила в пилот. А ты?