# 1C RAG

Цель: подготовить локальный корпус знаний по 1С для поиска и ответов без дообучения модели.

## Principles

- Сначала RAG и инструменты, потом fine-tuning.
- Не загружать в репозиторий базы, выгрузки, приватные документы, персональные данные и секреты.
- Не выдумывать метаданные конкретной базы 1С. Если нужны реквизиты или объекты, использовать tool boundary.

## Add Sources

Кладем материалы в:

```text
plugins/1c/rag/sources
```

Поддерживаемые форматы:

- `.md`
- `.txt`
- `.bsl`
- `.os`

## Prepare Corpus

```powershell
python scripts/validate_1c_rag_sources.py --print
python scripts/prepare_1c_rag_corpus.py
```

Результат:

```text
plugins/1c/datasets/prepared/rag_corpus.jsonl
plugins/1c/datasets/prepared/rag_manifest.json
```

Этот файл не коммитится.

## Test With Example

```powershell
python scripts/prepare_1c_rag_corpus.py --source-dir plugins/1c/rag/examples --output plugins/1c/datasets/prepared/rag_corpus.example.jsonl
```

## Build Search Index

```powershell
python scripts/build_1c_rag_index.py
```

Для синтетического примера:

```powershell
python scripts/build_1c_rag_index.py --corpus plugins/1c/datasets/prepared/rag_corpus.example.jsonl --output plugins/1c/datasets/prepared/rag_index.example.json
```

## Build Vector Index

```powershell
python scripts/build_1c_rag_vector_index.py
```

Результат:

```text
plugins/1c/datasets/prepared/rag_vector_index.sqlite
```

Индекс хранит `corpus_hash`, `embedding_model`, размерность, дату сборки и метаданные чанков.
Текущий provider `local-hashing-v1` - это локальный deterministic vector baseline, а не нейросемантическая embedding-модель. Он нужен, чтобы отладить формат, freshness и hybrid retrieval без скачивания модели. Нейросемантический provider подключается следующим слоем без смены SQLite-схемы.

OpenAI-compatible embedding endpoint:

```powershell
$env:EMBEDDING_API_KEY = "<token-if-needed>"
python scripts/build_1c_rag_vector_index.py `
  --embedding-provider openai-compatible `
  --embedding-model "<embedding-model-name>" `
  --embedding-base-url "http://docker-gpu.cin.su:8000" `
  --embedding-api-key-env EMBEDDING_API_KEY
```

Для поиска по такому индексу query embedding должен считаться тем же provider/model:

```powershell
python scripts/search_1c_rag_hybrid.py "реквизиты справочника номенклатура" `
  --profile metadata `
  --embedding-base-url "http://docker-gpu.cin.su:8000" `
  --embedding-api-key-env EMBEDDING_API_KEY
```

Ключ не пишется в индекс и читается только из переменной окружения.

Полная сборка knowledge base теперь строит corpus, lexical index и vector index:

```powershell
python scripts/build_1c_knowledge_base.py --include-example-bsl
```

Если vector index временно не нужен:

```powershell
python scripts/build_1c_knowledge_base.py --skip-vector-index
```

## Search

```powershell
python scripts/search_1c_rag.py "метаданные справочника" --index plugins/1c/datasets/prepared/rag_index.example.json
```

Поиск использует локальный lexical BM25-подобный индекс с нормализацией 1С-терминов и алиасами вроде `1с/bsl`, `справочник/catalog`, `метаданные/metadata`.

Профили RAG лежат в:

```text
plugins/1c/rag/profiles.yaml
```

Основные профили:

- `auto`
- `general`
- `metadata`
- `bsl`
- `query`
- `safe-change`

`auto` выбирает профиль по тексту вопроса. Например, вопросы про реквизиты уходят в `metadata`, вопросы про процедуры и ошибки BSL - в `bsl`, вопросы с `ВЫБРАТЬ` - в `query`.

При подготовке корпуса источники получают смысловой `source_type`:

- `metadata`
- `bsl`
- `query`
- `safety`
- `docs`

Полезные параметры:

```powershell
python scripts/search_1c_rag.py "реквизиты справочника номенклатура" `
  --index plugins/1c/datasets/prepared/rag_index.example.json `
  --profile metadata `
  --limit 5 `
  --candidate-limit 30 `
  --dedupe-by-document
```

Vector search:

```powershell
python scripts/search_1c_rag_vector.py "реквизиты справочника номенклатура" --profile metadata --limit 5
```

Hybrid search объединяет lexical и vector результаты через reciprocal-rank fusion:

```powershell
python scripts/search_1c_rag_hybrid.py "реквизиты справочника номенклатура" --profile metadata --limit 5
```

Если `vector_freshness.status = stale`, нужно пересобрать vector index после обновления corpus.

## Ask With Context

Без вызова модели, только сборка prompt:

```powershell
python scripts/ask_1c_rag.py "Какие реквизиты есть у справочника Номенклатура?" --index plugins/1c/datasets/prepared/rag_index.example.json --print-prompt
```

С явным профилем:

```powershell
python scripts/ask_1c_rag.py "Проверь BSL-код процедуры ПередЗаписью" --profile bsl --print-prompt
```

Проверка обязательных правил в prompt:

```powershell
python scripts/check_1c_rag_prompt.py
```

Проверка качества поиска по smoke-набору:

```powershell
python scripts/check_1c_rag_quality.py --print
```

Проверка auto-routing профилей:

```powershell
python scripts/check_1c_rag_profiles.py --print
```

С запущенной моделью:

```powershell
python scripts/ask_1c_rag.py "Какие реквизиты есть у справочника Номенклатура?" --index plugins/1c/datasets/prepared/rag_index.example.json --base-url http://docker-gpu.cin.su:8000 --model qwen3-4b-instruct
```

## Tool Contract

Контракт инструментов:

```text
plugins/1c/tools/tool-contract.yaml
```

Главное правило: модель не должна придумывать структуру базы 1С. Если ответ зависит от метаданных, сначала нужен вызов инструмента.

## Metadata Snapshot Flow

Для проверки RAG на структуре 1С можно сгенерировать source из metadata snapshot:

```powershell
python scripts/validate_1c_metadata_snapshot.py plugins/1c/metadata/examples/metadata.example.json
python scripts/convert_1c_metadata_to_rag.py --input plugins/1c/metadata/examples/metadata.example.json --output plugins/1c/rag/sources/metadata.example.generated.md
python scripts/build_1c_knowledge_base.py --include-example-bsl
python scripts/ask_1c_rag.py "Какие реквизиты есть у справочника Номенклатура?" --print-prompt
```

`build_1c_knowledge_base.py` выполняет валидацию snapshot, конвертацию источников, сборку corpus и индекса за один проход.
Перед сборкой corpus он также запускает `validate_1c_rag_sources.py`, чтобы заблокировать неподдерживаемые расширения, пустые файлы и вероятные секреты.

Manifest содержит source path, source type, file type, content hash и число чанков. Его удобно использовать для аудита и будущего инкрементального обновления индекса.

Проверить, не устарел ли manifest относительно `plugins/1c/rag/sources`:

```powershell
python scripts/check_1c_rag_freshness.py --print
```

Статус `stale` означает, что появились новые файлы, изменился hash, был удален источник или изменился `source_type`. После этого нужно пересобрать knowledge base.

Проверить, не устарел ли vector index относительно corpus:

```powershell
python scripts/check_1c_rag_vector_freshness.py --print
```

## Semantic Cache Embedding Worker

Для live 1C-адаптера semantic cache заполняется отдельно от RAG corpus. Адаптер отдает pending документы с `document_id` и `content_sha1`; worker считает embedding и вызывает `semantic.cache.embedding.upsert`. Если документ изменился, адаптер отвергнет embedding по `content_sha1`.

Dry-run:

```powershell
python scripts/embed_1c_semantic_cache.py --base-id upo_test --kind Template --limit 20 --dry-run --json
```

Запись baseline-векторов:

```powershell
python scripts/embed_1c_semantic_cache.py --base-id upo_test --kind Template --limit 20
```

Поиск по semantic cache с автоматически рассчитанным query embedding:

```powershell
python scripts/search_1c_semantic_cache.py "ОбластьШапка" --base-id upo_test --kind Template --limit 5 --validate-candidates
```

Если нужно перед поиском автоматически дозаполнить pending embeddings:

```powershell
python scripts/search_1c_semantic_cache.py "ОбластьШапка" --base-id upo_test --kind Template --embed-pending --validate-candidates
```

С OpenAI-compatible embedding endpoint:

```powershell
$env:EMBEDDING_API_KEY = "<token-if-needed>"
python scripts/embed_1c_semantic_cache.py `
  --base-id upo_test `
  --kind Template `
  --embedding-provider openai-compatible `
  --embedding-model "<embedding-model-name>" `
  --embedding-base-url "http://docker-gpu.cin.su:8000" `
  --embedding-api-key-env EMBEDDING_API_KEY
```

Semantic cache остается candidate-only: перед программными изменениями использовать `validate_candidates=true` или live read-selector из результата.
