Локальный AI-агент на Neo4j RAG с автономными циклами для Apple Silicon | AiManual
AiManual Logo Ai / Manual.
26 Янв 2026 Гайд

Суверенный агент на Neo4j RAG с автономными циклами: полный гайд для Apple Silicon

Пошаговая архитектура автономного агента с Neo4j RAG, ChromaDB и гибридной памятью для Mac M1/M2/M3. Полный стек на 26.01.2026.

Почему суверенный агент - это не просто "еще один 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()}
💡
На практике гибридный подход дает +40% к точности ответов на сложные вопросы типа "Как проект A связан с технологией B через сотрудника C?". Векторный поиск находит похожие документы, графовый - показывает связи между сущностями.

2 Слой планирования: автономные циклы

Вот где начинается магия. Автономный цикл - это не просто cron-задача. Это состояние агента, когда он:

  1. Анализирует накопленные знания
  2. Определяет пробелы в информации
  3. Планирует действия для заполнения пробелов
  4. Выполняет план
  5. Обновляет память

Ключевой компонент - планировщик на основе 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 минут агент сохраняет:

  1. Текущий контекст диалога (если активен)
  2. Состояние планировщика (текущие цели, выполненные шаги)
  3. Историю принятых решений с метаданными (когда, почему, результат)
  4. Статистику использования инструментов

Формат - комбинация 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 это уже не теория. Вот как это работает:

  1. Агент анализирует свои логи ошибок
  2. Находит паттерны: "часто ошибаюсь в вопросах про финансы", "медленно отвечаю на запросы с датами"
  3. Формирует план обучения: "скачать финансовые отчеты", "пройти fine-tuning на временных последовательностях"
  4. Выполняет план (в безопасной среде)
  5. Тестирует улучшения, сравнивает метрики
  6. Если улучшения значительные - применяет к основной системе

Ключевое ограничение: агент не может изменять свой основной код. Только данные, конфигурации, веса моделей (через контролируемый fine-tuning). Безопасность прежде всего.

Если вам интересна тема рекурсивного самосовершенствования, рекомендую мой гайд "Полное руководство по Agentic RAG", где я разбираю безопасные методы автономного обучения.

💡
На 26.01.2026 самый перспективный путь - не создание одного супер-агента, а координация нескольких специализированных агентов. Один работает с документами, другой анализирует код, третий общается с API. Архитектура мульти-агентных систем - тема для отдельного глубокого гайда.

Собирайте. Тестируйте. Экспериментируйте. Но помните: суверенный агент - это не просто техническая игрушка. Это инструмент, который меняет то, как мы работаем с информацией. И главное его преимущество не в автономности, а в том, что он работает на вашем железе, с вашими данными, по вашим правилам. В мире, где каждый чих отправляется в облако, это редкая и ценная вещь.