Symbolic Index for LLM Knowledge — нейро-символическая поисковая архитектура.
Поиск на 64-битных целых числах. Не нужны векторная БД, ANN-граф или модель эмбеддингов.
Ключевой тезис
Поиск никогда не был сложной задачей — он был симптомом неструктурированных данных. Если присвоить 64-битную структуру в момент записи, симптом исчезает.
results = index[(index & mask) == pattern]
Поиск по 100 миллионам сущностей Wikidata в 1,3 ГБ памяти менее чем за секунду. Один Python (NumPy) побеждает оптимизированные векторные БД на C++/Rust — победа архитектуры.
Почему не векторные эмбеддинги?
Векторные эмбеддинги уничтожают структуру.
"Суворов — русский полководец эпохи Екатерины II"
→ Человек сразу понимает: русский, военный, XVIII век
Модель эмбеддингов:
[0.234, -0.891, 0.445, ..., 0.112] (384 измерения float)
→ Структура уничтожена. Невозможно прочитать, person это или location.
Чтобы восстановить уничтоженную структуру:
ANN-граф (HNSW, IVF-PQ), cross-encoder, reranker, фильтр метаданных...
SILK сохраняет структуру.
SIDX: [Human / military / europe / early_modern]
→ Структура жива в битах. Её можно прочитать.
→ Восстанавливать не нужно. Потому что она не была уничтожена.
Суть — в инверсии порядка.
Традиционно: сначала записать → потом структурировать (индексация)
SILK: структурировать при записи → поиск бесплатен
Битовая раскладка SIDX
SIDX следует спецификации Entity Node грамматики GEUL.
[prefix 7 | mode 3 | entity_type 6 | attrs 48]
MSB(63) LSB(0)
| Поле | Биты | Размер | Описание |
|---|---|---|---|
| prefix | 63-57 | 7 | Заголовок протокола GEUL (фиксированный 0001001, игнорируется при поиске) |
| mode | 56-54 | 3 | Режим квантизации/числа (зарегистрированная сущность=0, определённый, универсальный, экзистенциальный и т.д. — 8 режимов) |
| entity_type | 53-48 | 6 | 64 верхнеуровневых типа (Human=0 ~ Election=62, неклассифицированный=63) |
| attrs | 47-0 | 48 | Кодирование атрибутов по типу (определяется кодбуком) |
Диапазон поиска: entity_type 6 бит + attrs 48 бит = 54 бита. QID не входит в SIDX и хранится в отдельном массиве.
attrs 48 бит — схема по типам
Human(0): subclass 5 | occupation 6 | country 8 | era 4 | decade 4 | gender 2 | notability 3 | ...
Star(12): constellation 7 | spectral_type 4 | luminosity 3 | magnitude 4 | ra_zone 4 | dec_zone 4 | ...
Settlement(28): country 8 | admin_level 4 | admin_code 8 | lat_zone 4 | lon_zone 4 | population 4 | ...
Organization(44): country 8 | org_type 4 | legal_form 6 | industry 8 | era 4 | size 4 | ...
Film(51): country 8 | year 7 | genre 6 | language 8 | color 2 | duration 4 | ...
Сейчас определена битовая раскладка атрибутов для 5 типов. Остальные кодируются только по entity_type.
Архитектура
В SILK два конвейера: кодирование (при записи) и поиск (при запросе). Оба работают по одному принципу: символическое удерживает структуру, LLM обрабатывает смысл.
Конвейер кодирования (при записи)
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Тегирование │──►│ Проверка │──►│ Кодирование │
│ LLM │ │ VALID │ │ по кодбуку │
│ документ → │ │ допустимые │ │ JSON → SIDX │
│ JSON │ │ значения │ │ (сборка бит) │
│ (семант. │ │ проверка │ │ │
│ классиф.) │ │ согласован. │ │ │
│ │ │ галлюц.→откл.│ │ │
└──────────────┘ └──────────────┘ └──────────────┘
LLM тегирует код проверяет кодирование по кодбуку
Конвейер поиска (при запросе)
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Интерпретация│──►│ Фильтр │──►│ Решение LLM │
│ запроса │ │ bit AND │ │ только │
│ поиск по │ │ NumPy SIMD │ │ кандидаты │
│ кодбуку │ │100M → десятки│ │ десятки → │
│ + помощь LLM│ │ │ │ ответ │
└──────────────┘ └──────────────┘ └──────────────┘
извлечение широкая точное
смысла фильтрация решение
Ключевая стратегия: широкая фильтрация через bit AND по SIDX без потерь, затем LLM решает только по немногим кандидатам.
Каждый делает то, что умеет лучше всего:
Человек: проектирование структуры (кодбук)
LLM: семантическая классификация (тегирование) + семантическое решение (поиск)
Код: проверка правил (VALID) + сборка битов
CPU: массовое сравнение (NumPy SIMD)
Кодирование: тегирование LLM → проверка VALID
LLM читает документ и тегирует в JSON. VALID выполняет механическую проверку по кодбуку. Значения, отсутствующие в кодбуке, нарушения согласованности между типами и нарушения ограничений физически не могут попасть в индекс.
Тегирование LLM: {"type": "Human", "occupation": "military", "country": "russia"}
VALID: occupation="military" ∈ кодбук? ✓ country="russia" ∈ кодбук? ✓
Human с полем constellation? ✗ отклонён
Кодирование: "military"→6 бит, "russia"→8 бит из кодбука → сборка битов → SIDX uint64
LLM может галлюцинировать. Но VALID играет роль привратника, поэтому вероятность загрязнения индекса равна нулю. Только JSON, прошедший VALID, кодируется в 64-битное целое SIDX по кодбуку.
Поиск: широкая фильтрация, LLM сужает
1. Извлечение смысла запроса поиск по кодбуку 80% / помощь LLM 20%
2. Сборка битовой маски детерминированно — алгоритм
3. bit AND через NumPy детерминированно — полный скан 100M за 20мс
4. Финальное решение LLM только немногие кандидаты — точное семантическое решение
80% поиска — структурные запросы
"Суворов" → Q484523 exact match. bit AND. Готово.
"новости Samsung" → org/company + doc_meta/news. bit AND. Готово.
"саммит Байден-Си" → Q6279 ∩ Q15031 ∩ meeting. Пересечение. Готово.
Структурные запросы (80%): bit AND в SILK достаточно. LLM не нужен.
Семантические запросы (15%): SILK сужает кандидатов → LLM решает по 5-10 случаям.
Генеративные запросы (5%): SILK определяет документ → LLM генерирует.
Multi-SIDX
Один документ/событие несёт несколько SIDX.
Новостная статья "Встреча глав Samsung, NVIDIA и Hyundai":
SIDX[0]: [Human / business / east_asia ] Ли Джэён
SIDX[1]: [Org / company / east_asia ] Samsung
SIDX[2]: [Human / business / n_america] Дженсен Хуанг
SIDX[3]: [Org / company / n_america] NVIDIA
SIDX[4]: [Human / business / east_asia ] Чон Ый Сон
SIDX[5]: [Org / company / east_asia ] Hyundai
SIDX[6]: [Event / meeting / east_asia ] встреча
Всё — одинаковые 64-битные SIDX. Один индекс. Один bit AND для поиска. Поле entity_type различает сущности (Human, Org) и события (Event).
Структура индекса
sidx_array = np.array([...], dtype=np.uint64) # 108.8M × 8B = 870MB
qid_array = np.array([...], dtype=np.uint32) # 108.8M × 4B = 435MB
# Всего ~1.3GB памяти
Построение индекса:
Elasticsearch: токенизация → анализ → обратный индекс → слияние сегментов
Pinecone: эмбеддинг → HNSW-граф → кластеризация
SILK: sort
Никаких структур данных. Один отсортированный массив.
Сравнение с векторными БД
| SILK | Векторная БД | |
|---|---|---|
| Размер индекса (1 трлн записей) | 12TB | 1,5PB (в 125 раз) |
| Построение индекса | sort | HNSW — дни |
| Холодный старт | открыл файл — готово | построение графа — часы |
| Разделённый скан | возможен (результат идентичен) | невозможен (граф разрывается) |
| Сложные условия | пересечение (точно) | сжатие в один вектор (приблизительно) |
| Результаты | точные (множественные операции) | приблизительные (ранжирование по сходству) |
| Ревизия | JSON (белый ящик) | невозможна (чёрный ящик) |
bit AND не зависит от порядка, без состояния, допускает агрегацию.
Векторная БД: весь граф должен быть в памяти. Разделение = разрушение.
SILK: режь где угодно — работает. Разделение = только медленнее. Результат идентичен.
Конвейер ревизии
Векторные эмбеддинги — чёрный ящик, ревизия невозможна. SIDX — это JSON, ревизия возможна.
Этап 1 — тегирование малой LLM Llama 8B / GPT-4o-mini. Точность 85-90%.
Этап 2 — механическая проверка VALID допустимые значения, согласованность, ограничения. Стоимость $0.
Этап 3 — ревизия большой LLM только confidence=low. Точность 99%+.
VALID — привратник: галлюцинации за пределами допустимых значений кодбука физически не могут попасть в индекс.
Лицензия
MIT — Репозиторий GitHub