# Актуальный векторный поиск по коду 1С

Цель: семантический поиск по локальному SQL-индексу адаптера без потери
актуальности кода. Вектор является только производным кешем: перед выдачей
адаптер сверяет найденные фрагменты с текущим состоянием конфигурации и
отбрасывает либо переиндексирует устаревшие записи.

## Выбранная модель

- `Qwen/Qwen3-Embedding-0.6B-GGUF`, квантование `Q8_0`;
- OpenAI-compatible endpoint на `http://docker-gpu.cin.su:8082`;
- `llama.cpp`, `--embedding --pooling last`;
- CPU-only (`--n-gpu-layers 0`), чтобы не менять работающие GPU-сервисы;
- endpoint возвращает 1024 измерения, клиент использует Matryoshka-срез до
  запрошенных 384 измерений и повторно нормализует его.

Образ `llama.cpp` зафиксирован digest, а модель — официальным repository/quant
селектором. Это исключает незаметную смену runtime при повторном deploy.

Модель и образ публичные, endpoint работает в изолированном тестовом контуре
без токена. Постоянный кеш модели хранится вне git в
`Z:\LLM\models\cache\llama.cpp`.

## Развёртывание

Проверить итоговую конфигурацию:

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/deploy_embeddings.ps1 -ConfigOnly
```

Запустить сервис с загрузкой образа:

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/deploy_embeddings.ps1 -Pull
```

Первый запуск скачивает модель в постоянный кеш и поэтому может занять
несколько минут. Скрипт ждёт `/health`, проверяет имя модели и делает реальный
запрос к `/v1/embeddings`.

## Обновление векторов

Сначала адаптер должен содержать актуальные текстовые chunks. Затем worker
забирает только отсутствующие либо изменившиеся фрагменты:

```powershell
python scripts/embed_1c_code_vectors.py `
  --adapter-url http://docker.cin.su:8011/rpc `
  --base-id upo_test `
  --embedding-provider openai-compatible `
  --embedding-model qwen3-embedding-0.6b `
  --dimensions 384 `
  --embedding-base-url http://docker-gpu.cin.su:8082 `
  --limit 500 `
  --batch-size 8 `
  --chunk-kind routine `
  --max-text-chars 4000 `
  --json
```

Worker по умолчанию индексирует `routine`: процедуры и функции дают наиболее
точный контекст для программирования и заметно быстрее пересчитываются при
частых изменениях. Для диагностического покрытия модульных фрагментов можно
добавить второй `--chunk-kind module`; это более дорогой отдельный проход.
`--max-text-chars` не обрезает код молча: длинные chunks пропускаются в этом
проходе и остаются pending. Их нужно разбивать на окна отдельной задачей либо
индексировать в период низкой нагрузки с большим лимитом.

Размерность входит в имя кеша (`openai-compatible:qwen3-embedding-0.6b@d384`),
поэтому векторы разных размеров никогда не смешиваются.

## Поиск

```powershell
python scripts/search_1c_code_vectors.py `
  "где рассчитывается сумма документа перед проведением" `
  --adapter-url http://docker.cin.su:8011/rpc `
  --base-id upo_test `
  --embedding-provider openai-compatible `
  --embedding-model qwen3-embedding-0.6b `
  --dimensions 384 `
  --embedding-base-url http://docker-gpu.cin.su:8082 `
  --limit 10 `
  --json
```

Поиск вызывается с `strict=true` и `verify=true`. Сохранённые изменения
Конфигуратора перекрывают активную конфигурацию, а удалённые/изменённые chunks
не возвращаются по старому вектору.

Для активного кода расширений хеш `ConfigCAS` разрешается через текущий
manifest расширения в descriptor объекта. Результат содержит обычные
`kind/name`, а также `extension` и `extension_guid`; вызывающему коду не нужно
работать с CAS-хешами как с именами объектов.

Перед первым глобальным поиском после обновления адаптера нужно постранично
заполнить локальную карту владельцев:

```json
{
  "method": "metadata.module_owner_cache.backfill",
  "payload": {
    "base_id": "upo_test",
    "limit": 50,
    "kind_index": 0,
    "offset": 0
  }
}
```

Следующий вызов получает `kind_index` и `offset` из `next_cursor`. Повторять до
`complete=true`. Операция читает актуальные метаданные, но пишет только в
локальный SQLite адаптера. Найденные имена сразу добавляются в существующие
строки лексического и векторного индексов; перестраивать embeddings не нужно.
По умолчанию объекты без строк code index быстро пропускаются. `deep=true`
нужен только для отдельного фонового заполнения владельцев неиндексированных
модулей и не должен использоваться в интерактивном поиске.

Для `Qwen3-Embedding` клиент автоматически добавляет к векторизуемому запросу
англоязычную инструкцию поиска по исходному коду 1С, как рекомендует карточка
модели. В `query` адаптера остаётся исходный русский текст, поэтому лексическая
часть гибридного поиска не загрязняется служебным префиксом.

## Отдельная векторная БД

На текущем этапе не требуется. Векторы хранятся рядом с индексом адаптера в
SQLite и выбираются линейным сканированием. Это проще и гарантирует атомарную
проверку актуальности. Отдельный ANN-движок имеет смысл только после замера
десятков тысяч актуальных chunks и неприемлемой задержки; источником истины всё
равно остаётся 1С/SQL, а ANN должен хранить `chunk_id` и `text_sha1` как
проверяемую производную копию.
