Files
llm/docs/runbooks/1c-rag.md
T

10 KiB
Raw Blame History

1C RAG

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

Principles

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

Add Sources

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

plugins/1c/rag/sources

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

  • .md
  • .txt
  • .bsl
  • .os

Prepare Corpus

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

Результат:

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

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

Test With Example

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

python scripts/build_1c_rag_index.py

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

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

python scripts/build_1c_rag_vector_index.py

Результат:

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:

$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:

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:

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

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

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

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

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

plugins/1c/rag/profiles.yaml

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

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

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

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

  • metadata
  • bsl
  • query
  • safety
  • docs

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

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:

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

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

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

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

Ask With Context

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

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

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

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

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

python scripts/check_1c_rag_prompt.py

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

python scripts/check_1c_rag_quality.py --print

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

python scripts/check_1c_rag_profiles.py --print

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

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

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

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

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

Metadata Snapshot Flow

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

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:

python scripts/check_1c_rag_freshness.py --print

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

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

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:

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

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

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

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

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

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

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

С OpenAI-compatible embedding endpoint:

$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 из результата.