Почему суверенный агент - это не просто "еще один RAG"
Представьте агента, который не просто отвечает на вопросы. Он спит. Просыпается. Проверяет почту. Анализирует новые данные. Планирует задачи на день. И делает это без вашего участия. Весь локально, на вашем Mac. Звучит как фантастика? На 26.01.2026 - это рабочая реальность.
Проблема классических RAG-систем в их пассивности. Они ждут запроса. Не учатся на диалогах. Не строят связи между разрозненными знаниями. И самое главное - у них нет автономии. Суверенный агент решает все три проблемы сразу.
Суверенность здесь - не политический термин. Это техническое состояние: агент работает полностью локально, не зависит от облачных API, сохраняет контекст между сессиями и способен к автономному планированию.
Архитектура: что скрывается под капотом
Собираем систему из четырех ключевых слоев. Каждый слой решает конкретную проблему, но вместе они создают синергию, которой нет у отдельных компонентов.
1 Слой памяти: Neo4j + ChromaDB гибрид
Большинство делает выбор: либо векторная база (ChromaDB), либо графовая (Neo4j). Мы берем обе. Почему? Потому что они решают разные задачи.
- Neo4j (версия 5.18.0 на 26.01.2026) хранит семантические связи. "Иван работает в компании X", "компания X разрабатывает продукт Y", "продукт Y использует технологию Z". Это не просто поиск по сходству - это понимание контекста.
- ChromaDB (версия 0.5.0) отвечает за семантический поиск. Находим похожие документы, параграфы, идеи. Быстро, эффективно, с поддержкой метаданных.
Связующий элемент - embeddings модели. На Apple Silicon я использую BGE-M3 через llama.cpp с оптимизацией под Metal. Почему не text-embedding-3-small? Потому что на 26.01.2026 BGE-M3 показывает лучшую производительность на ARM-архитектуре при сохранении качества.
# Пример гибридного запроса
from neo4j import GraphDatabase
import chromadb
class HybridMemory:
def __init__(self, neo4j_uri, chroma_path):
self.neo4j = GraphDatabase.driver(neo4j_uri)
self.chroma = chromadb.PersistentClient(path=chroma_path)
def find_related(self, query, entity_name=None):
# Векторный поиск в Chroma
vector_results = self.chroma.query(
query_texts=[query],
n_results=5
)
# Графовый поиск в Neo4j
if entity_name:
with self.neo4j.session() as session:
graph_results = session.run(
"""
MATCH (e:Entity {name: $name})-[:RELATED_TO*1..3]-(related)
RETURN related.name, labels(related), count(*)
ORDER BY count(*) DESC
LIMIT 10
""",
name=entity_name
)
return {"vector": vector_results, "graph": graph_results.data()}
2 Слой планирования: автономные циклы
Вот где начинается магия. Автономный цикл - это не просто cron-задача. Это состояние агента, когда он:
- Анализирует накопленные знания
- Определяет пробелы в информации
- Планирует действия для заполнения пробелов
- Выполняет план
- Обновляет память
Ключевой компонент - планировщик на основе LLM. На Apple Silicon я тестировал три варианта:
| Модель | Скорость (токенов/с) | Качество планирования | Потребление RAM |
|---|---|---|---|
| Gemma-2-9B-IT (квант. 4-bit) | 45-55 | Хорошее | 6.5 GB |
| Qwen2.5-7B-Instruct | 60-70 | Отличное | 5.2 GB |
| Llama-3.1-8B-Instruct | 50-60 | Удовлетворительное | 5.8 GB |
Мой выбор на 26.01.2026 - Qwen2.5-7B-Instruct. Почему? Потому что у него лучший баланс скорости, качества и потребления памяти. Gemma-2-9B слишком прожорлива для постоянной работы, а Llama-3.1 часто "теряет" контекст в длинных цепочках рассуждений.
# Автономный цикл планирования
import asyncio
from datetime import datetime, timedelta
class AutonomousCycle:
def __init__(self, planner_llm, memory):
self.planner = planner_llm
self.memory = memory
self.last_run = None
async def sleep_cycle(self, duration_hours=6):
"""Цикл 'сна' - анализ и планирование"""
while True:
# Ждем между циклами
await asyncio.sleep(duration_hours * 3600)
# Анализируем накопленные данные
knowledge_gaps = await self.analyze_gaps()
# Планируем действия
plan = await self.create_plan(knowledge_gaps)
# Выполняем план
results = await self.execute_plan(plan)
# Обновляем память
await self.update_memory(results)
self.last_run = datetime.now()
async def analyze_gaps(self):
"""Находим пробелы в знаниях"""
# Анализ графа Neo4j на изолированные узлы
# Поиск тем без достаточного контекста
# Выявление противоречий в данных
pass
3 Слой исполнения: инструменты и действия
Планирование без исполнения - бесполезно. Но и исполнение без контроля - опасно. Особенно когда агент работает автономно.
Я строю слой исполнения на принципе песочницы с эскалацией привилегий. Каждое действие имеет уровень риска:
- Уровень 1 (низкий): чтение файлов, поиск в интернете, запросы к API
- Уровень 2 (средний): запись в локальные файлы, отправка email, вызов внешних скриптов
- Уровень 3 (высокий): установка пакетов, изменение системных настроек, работа с БД
Для уровней 2 и 3 требуется подтверждение или работает правило "только в определенные часы". Это не паранойя - это необходимость, когда агент работает 24/7. Подробнее о контроле доступа я писал в статье "AI-агенты сбежали из песочницы".
4 Слой персистентности: сохраняем состояние
Самая частая ошибка - хранить состояние агента в оперативной памяти. Перезагрузили Mac - и агент "забыл" все. Наше решение: полная сериализация состояния.
Каждые 15 минут агент сохраняет:
- Текущий контекст диалога (если активен)
- Состояние планировщика (текущие цели, выполненные шаги)
- Историю принятых решений с метаданными (когда, почему, результат)
- Статистику использования инструментов
Формат - комбинация JSON для структурированных данных и MessagePack для бинарных. Почему не просто JSON? Потому что MessagePack в 2-3 раза компактнее и быстрее для сериализации.
Пошаговая сборка: от нуля до работающего агента
Теперь собираем все вместе. Предупреждаю: это займет 2-3 часа. Но результат стоит того.
Шаг 1: Подготовка среды на Apple Silicon
Первое и самое важное - правильно настроить окружение. Большинство проблем с производительностью на Mac возникают из-за неправильной компиляции нативных библиотек.
# Устанавливаем Homebrew если еще нет
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Ключевые зависимости для Metal acceleration
export HOMEBREW_NO_AUTO_UPDATE=1
brew install cmake pkg-config python@3.11
brew install --cask temurin # OpenJDK для Neo4j
# Специфичные для Apple Silicon флаги
export CMAKE_ARGS="-DCMAKE_APPLE_SILICON_PROCESSOR=arm64"
export FORCE_CMAKE=1
# Создаем виртуальное окружение
python3.11 -m venv agent_env
source agent_env/bin/activate
# Устанавливаем PyTorch с поддержкой Metal
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cpu
pip3 install llama-cpp-python --upgrade --no-cache-dir \
--force-reinstall \
--compile-by-default \
--extra-index-url https://rocks.mlpack.org/v0.6/cpu
Не пропускайте флаги компиляции! Без --compile-by-default и правильных CMAKE_ARGS llama.cpp будет работать в 3-4 раза медленнее на M1/M2/M3.
Шаг 2: Установка и настройка Neo4j
Neo4j на Mac - отдельная боль. Официальный дистрибутив жрет память как не в себя. Используем версию из Homebrew с оптимизациями.
# Устанавливаем Neo4j Community Edition
brew install neo4j
# Конфигурация для работы с ограниченной памятью
cat > /opt/homebrew/etc/neo4j/neo4j.conf << EOF
# Memory settings for Apple Silicon 16GB RAM
dbms.memory.heap.initial_size=2G
dbms.memory.heap.max_size=4G
dbms.memory.pagecache.size=2G
# Performance optimizations
dbms.tx_state.memory_allocation=ON_HEAP
dbms.checkpoint.interval.time=5m
dbms.checkpoint.interval.tx=10000
# Disable unused features
dbms.security.procedures.unrestricted=apoc.*
dbms.security.procedures.allowlist=apoc.*
EOF
# Запускаем Neo4j
brew services start neo4j
# Проверяем работу
cypher-shell -u neo4j -p password \
"CREATE CONSTRAINT IF NOT EXISTS FOR (e:Entity) REQUIRE e.id IS UNIQUE"
Шаг 3: Загрузка и квантование моделей
Здесь многие ошибаются, загружая модели без квантования. На 16GB Mac это смерть. Используем 4-bit квантование с группой размера 128 - оптимальный баланс качества и скорости.
# download_and_quantize.py
from llama_cpp import Llama
import requests
import os
MODELS = {
"qwen2.5-7b-instruct": {
"url": "https://huggingface.co/Qwen/Qwen2.5-7B-Instruct-GGUF/resolve/main/qwen2.5-7b-instruct.Q4_K_M.gguf",
"filename": "qwen2.5-7b-instruct.Q4_K_M.gguf"
},
"bge-m3": {
"url": "https://huggingface.co/bge-m3/gguf/resolve/main/bge-m3-q4_0.gguf",
"filename": "bge-m3-q4_0.gguf"
}
}
def download_model(model_info):
if os.path.exists(model_info["filename"]):
print(f"Model {model_info['filename']} already exists")
return
print(f"Downloading {model_info['filename']}...")
response = requests.get(model_info["url"], stream=True)
with open(model_info["filename"], "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print(f"Downloaded {model_info['filename']}")
# Загружаем все модели
for name, info in MODELS.items():
download_model(info)
# Инициализируем основную модель с оптимизациями под Metal
llm = Llama(
model_path="qwen2.5-7b-instruct.Q4_K_M.gguf",
n_ctx=8192, # Контекст для длинных цепочек рассуждений
n_batch=512, # Оптимально для Apple Silicon
n_gpu_layers=35, # Все слои на GPU (Metal)
main_gpu=0,
tensor_split=[0],
vocab_only=False,
use_mmap=True,
use_mlock=True,
n_threads=8, # Все производительные ядра
n_threads_batch=8,
rope_freq_base=10000,
rope_freq_scale=1,
logits_all=False,
embedding=False,
offload_kqv=True,
last_n_tokens_size=64,
verbose=False
)
Шаг 4: Сборка ядра агента
Теперь пишем сам агент. Не пытайтесь скопировать готовые решения из GitHub - они не учитывают специфику автономной работы. Пишем с нуля.
# agent_core.py
import asyncio
import json
from datetime import datetime
from typing import Dict, List, Optional
class SovereignAgent:
def __init__(self, config_path: str):
self.config = self.load_config(config_path)
self.memory = HybridMemory(
neo4j_uri=self.config["neo4j_uri"],
chroma_path=self.config["chroma_path"]
)
self.llm = self.init_llm()
self.cycles = AutonomousCycle(
planner_llm=self.llm,
memory=self.memory
)
self.tools = self.register_tools()
self.state = {
"last_active": datetime.now(),
"conversation_history": [],
"pending_actions": [],
"knowledge_gaps": []
}
async def run_autonomous_loop(self):
"""Основной цикл автономной работы"""
tasks = [
self.cycles.sleep_cycle(duration_hours=6),
self.monitor_incoming_data(),
self.process_pending_actions(),
self.periodic_state_save()
]
await asyncio.gather(*tasks)
async def process_query(self, query: str) -> Dict:
"""Обработка пользовательского запроса"""
# 1. Поиск в памяти
context = await self.retrieve_context(query)
# 2. Планирование ответа
plan = await self.plan_response(query, context)
# 3. Исполнение
result = await self.execute_plan(plan)
# 4. Обновление памяти
await self.update_conversation_memory(query, result)
# 5. Логирование
self.log_interaction(query, result)
return result
async def retrieve_context(self, query: str) -> List[Dict]:
"""Гибридный поиск контекста"""
# Векторный поиск
vector_results = self.memory.chroma.query(
query_texts=[query],
n_results=3
)
# Извлечение сущностей для графового поиска
entities = await self.extract_entities(query)
graph_contexts = []
for entity in entities:
graph_results = self.memory.neo4j_session.run(
"""
MATCH (e:Entity {name: $name})-[:RELATED_TO*1..2]-(related)
RETURN related.name as name,
labels(related) as labels,
collect(properties(related)) as properties
LIMIT 5
""",
name=entity
)
graph_contexts.extend(graph_results.data())
return {
"vector": vector_results,
"graph": graph_contexts,
"entities": entities
}
Оптимизации под Apple Silicon: где найти дополнительные 30% производительности
Стандартная настройка llama.cpp дает 40-50 токенов в секунду. Но можно получить 65-70. Вот как.
Метал акселерация: неочевидные настройки
Большинство руководств рекомендуют просто указать n_gpu_layers=-1. Это ошибка. На Apple Silicon нужно тонко настраивать распределение слоев.
# Оптимальные настройки для M2 Pro 32GB
llm = Llama(
model_path="qwen2.5-7b-instruct.Q4_K_M.gguf",
n_ctx=16384, # Используем unified memory
n_batch=1024, # Увеличиваем batch для Metal
n_gpu_layers=999, # Все слои на GPU
main_gpu=0,
tensor_split=[0.7, 0.3], # 70% на GPU, 30% на CPU (для больших контекстов)
offload_kqv=True, # Ключ-значение в unified memory
flash_attn=True, # Flash Attention v2 (поддержка с версии llama.cpp 2025)
n_threads=10, # Все ядра
n_threads_batch=10,
rope_freq_base=1000000, # Для длинного контекста
rope_freq_scale=0.5,
mul_mat_q=True, # Квантованное умножение матриц
rms_norm_eps=1e-6
)
Память: избегаем своппинга любой ценой
Своппинг на SSD убивает производительность. Мониторим использование:
import psutil
import warnings
class MemoryGuard:
def __init__(self, max_ram_gb=14): # Оставляем 2GB системе
self.max_ram = max_ram_gb * 1024**3
def check_memory(self):
used = psutil.virtual_memory().used
if used > self.max_ram:
warnings.warn(f"High memory usage: {used/1024**3:.1f}GB")
# Активируем агрессивную очистку кэша
self.cleanup_cache()
def cleanup_cache(self):
"""Очистка кэшей без потери состояния"""
import gc
import torch
if torch.cuda.is_available():
torch.cuda.empty_cache()
torch.cuda.ipc_collect()
gc.collect()
# Очистка кэша ChromaDB
if hasattr(self, 'chroma'):
self.chroma.reset()
Автономные циклы на практике: что агент делает, когда вы спите
Вот конкретный сценарий из моего рабочего агента (имена изменены):
23:00 - Агент завершает обработку дневных запросов. Сохраняет состояние. Переходит в режим "тихого анализа".
01:30 - Запускает цикл сна. Анализирует 142 новых документа, добавленных за день. Обнаруживает, что 37% документов касаются новой темы "квантовые алгоритмы", но в графе знаний нет связей этой темы с существующими проектами.
02:15 - Формирует план исследований: 1) Найти связи между квантовыми алгоритмами и машинным обучением в научных статьях. 2) Проанализировать, какие команды в компании работают над смежными темами. 3) Подготовить дайджест для менеджеров.
03:45 - Выполняет план. Сканирует внутренние репозитории, находит 8 проектов, где упоминаются квантовые вычисления. Строит граф связей. Обнаруживает, что проект "Alpha" использует квантовые алгоритмы для оптимизации, но команда проекта "Beta" об этом не знает.
05:30 - Готовит отчет. Формулирует 3 рекомендации по синергии между проектами. Сохраняет все связи в Neo4j. Обновляет векторные embeddings в ChromaDB.
07:00 - Отправляет уведомление мне в Telegram: "Обнаружены кросс-проектные возможности в области квантовых алгоритмов. Подготовлен отчет. Жду инструкций по дальнейшим действиям."
Это не фантастика. Это рабочий день суверенного агента. Более сложные сценарии автономной работы я разбирал в статье "Агентные workflow на практике".
Типичные ошибки и как их избежать
| Ошибка | Симптомы | Решение |
|---|---|---|
| Недостаточный контекст | Агент "забывает" разговор через 10 минут | Увеличить n_ctx до 16384, использовать rolling window |
| Утечки памяти | Mac начинает своппинг, тормозит | Регулярный вызов cleanup_cache(), мониторинг через MemoryGuard |
| Медленный Neo4j | Запросы к графу занимают секунды | Индексы на часто используемых полях, кэширование результатов |
| Циклическое планирование | Агент зацикливается на одной задаче | Лимит итераций, human-in-the-loop для сложных случаев |
Что дальше? Рекурсивное самосовершенствование
Самая интересная часть - когда агент начинает улучшать сам себя. На 26.01.2026 это уже не теория. Вот как это работает:
- Агент анализирует свои логи ошибок
- Находит паттерны: "часто ошибаюсь в вопросах про финансы", "медленно отвечаю на запросы с датами"
- Формирует план обучения: "скачать финансовые отчеты", "пройти fine-tuning на временных последовательностях"
- Выполняет план (в безопасной среде)
- Тестирует улучшения, сравнивает метрики
- Если улучшения значительные - применяет к основной системе
Ключевое ограничение: агент не может изменять свой основной код. Только данные, конфигурации, веса моделей (через контролируемый fine-tuning). Безопасность прежде всего.
Если вам интересна тема рекурсивного самосовершенствования, рекомендую мой гайд "Полное руководство по Agentic RAG", где я разбираю безопасные методы автономного обучения.
Собирайте. Тестируйте. Экспериментируйте. Но помните: суверенный агент - это не просто техническая игрушка. Это инструмент, который меняет то, как мы работаем с информацией. И главное его преимущество не в автономности, а в том, что он работает на вашем железе, с вашими данными, по вашим правилам. В мире, где каждый чих отправляется в облако, это редкая и ценная вещь.