Перейти к содержимому

5.5 · Проект 2: README и GitHub

Неделя 5 · RAG · ⏱ ~4 ч · Проект

Это второй проект в портфолио. Нанимающий менеджер тратит на репозиторий 2–5 минут: читает README, смотрит схему и цифры, иногда пробует запустить. Проект без README и без цифр качества выглядит как учебный ноутбук; проект с таблицей «вектор / гибрид / гибрид + реранкер», честным разделом об ограничениях и запуском в три команды выглядит как работа инженера. На собеседовании этот README — готовый конспект для рассказа о проекте.

⚠️ Публичный репозиторий проекта 2 содержит код и выдуманную базу знаний kb/ — и больше ничего из магазина. Индекс index.db, кэш raw/, .env, настоящие сообщения клиентов, снимки базы магазина из проекта 1 не должны попасть в него ни файлом, ни скриншотом, ни строчкой README — ни сейчас, ни в истории git.

Читатель Время Что ищет
Рекрутер / HR 30 секунд Что это, стек, есть ли демо (скриншот/GIF)
Нанимающий инженер 3–5 минут Архитектура, решения и их обоснование, цифры качества, код-стиль, тесты
Мы на собеседовании — Готовая история: проблема → решения → измерения → выводы
Мы через полгода 10 минут Как это запустить

Отсюда порядок: сначала что и зачем (с картинкой), потом как работает и насколько хорошо, потом как запустить, в конце детали.

# Shop Support KB RAG
Одна фраза: что это. Скриншот или GIF.
## Problem — зачем: база знаний магазина на 30–50 страниц, вопросы покупателей на русском, нужна ссылка на точный раздел
## Demo — 3–4 примера вопрос → ответ со ссылками (+ один отказ)
## Architecture — схема (Mermaid), две фазы: ingestion и query
## How retrieval works — chunking, гибрид, фильтры, реранкер: по абзацу, с «почему»
## Results — golden set, таблицы метрик, что дало каждое улучшение
## Manual vs LlamaIndex — из AIL-24
## Quickstart — от git clone до первого ответа
## Project structure — дерево с одной строкой на файл
## Evaluation — как перепрогнать eval
## Limitations & next steps— честно
## License / Disclaimer

Пример строки для Demo: «можно ли вернуть вскрытый чай?» → короткий ответ и источник kb/returns.md#opened-tea-returns; отказ — на вопрос, которого в базе нет.

README можно писать по-английски (так шире аудитория) или по-русски — главное, чтобы он был последовательным. В русском README имена файлов, команды и метрики всё равно остаются как есть.

Цифры отличают инженерный проект от демо. Показываем:

  • Как устроен golden set: 20 вопросов, какие типы (разговорные формулировки, без явной категории, точные идентификаторы вроде артикула MUG-NORTH, сравнительные «Свет» против «Свет мини», вопросы на два раздела), как определяется «правильный кусок».

  • Таблицу поиска (из гл. 4.3, гл. 5.1, гл. 5.2) — путь улучшений, а не только финальную цифру. В гл. 4.3 стратегии сравнивались на 10 вопросах и только по hit@3, поэтому ранние строки перепрогоняем на тех же 20 вопросах тем же eval/run_retrieval_eval.py — иначе строки несравнимы:

    Конфигурация hit@3 hit@5 MRR p50, мс
    baseline: вектор, fixed chunks
    + chunking по заголовкам
    + rewrite + фильтр категории
    + BM25 (RRF)
    + реранкер
  • Таблицу ответов (гл. 5.3): корректные / с цитатами / уместные отказы.

  • Честную оговорку: 20 вопросов — маленькая выборка, разница в 1–2 вопроса — шум.

Цифры не подгоняем и «неудобные» строки не убираем: «реранкер не помог на вопросах с артикулами, потому что…» — сильнее, чем идеальная таблица. Именно об этом спросят.

Цифрам нужен контекст: читатель должен видеть, через какие этапы проходит вопрос. GitHub рендерит Mermaid прямо в Markdown — схема хранится текстом, её легко менять, она видна в диффах:

```mermaid
flowchart LR
subgraph Ingestion
A[sources.yaml] --> B[load: kb/*.md + front matter] --> C[metadata: category, archived]
C --> D[chunk by headings + breadcrumbs] --> E[embed]
E --> F[(SQLite: chunks + sqlite-vec)]
D --> G[(BM25)]
end
subgraph Query
Q[question] --> R[rewrite + category filter, no archived]
R --> S1[vector search] & S2[BM25]
S1 & S2 --> T[RRF] --> U[cross-encoder rerank] --> V[LLM: answer with citations]
end
F --> S1
G --> S2
```

Архивные страницы попадают в индекс с пометкой archived и отсекаются фильтром при поиске (include_archived=False) — поэтому на схеме фильтр стоит в фазе запроса, а не загрузки: схема должна совпадать с кодом. За основу берём схему из своих notes/rag.md (гл. 4.2) и обновляем её по факту — что-то наверняка изменилось.

Воспроизводимость: «можно запустить с нуля»

Заголовок раздела «Воспроизводимость: «можно запустить с нуля»»

Схема и цифры убеждают только тогда, когда проект можно проверить самому. Критерий готовности задачи — проект запускается по README в чистом окружении. Что для этого нужно:

  1. Зафиксированные зависимости. requirements.txt с точными версиями (pip freeze после проверки) или pyproject.toml + lock-файл, если работаем через uv/poetry. Указанная версия Python.
  2. .env.example — список переменных без значений:
    ANTHROPIC_API_KEY=
    OPENAI_API_KEY= # only if you use OpenAI embeddings
    EMBED_MODEL=
    REWRITE_MODEL=
    ANSWER_MODEL=
    RERANK_MODEL=
    Пользователь копирует его в .env и заполняет. Реальный .env — в .gitignore.
  3. База знаний в репозитории, индекс — нет. kb/ коммитим: это исходники, тексты выдуманы и принадлежат нам. Индекс index.db и кэш raw/ не коммитятся: они пересобираются скриптом и раздувают репозиторий, а скачанная в raw/web/ публичная страница (необязательный пункт гл. 4.4) принадлежит её владельцу. В репозитории — код, который строит индекс (ingest.py), и sources.yaml.
  4. Одна точка входа на действие: make ingest, make ask Q="...", make eval, make app, make test — или эквивалентные короткие команды в README. Makefile по сути — то же, что scripts в package.json или задачи в Gradle: имя действия → команда.
  5. Ожидаемое время и стоимость: «индексация kb/ (30–50 страниц): ~N минут, ~$X за эмбеддинги при API-модели (или бесплатно с локальной)».
  6. Офлайн-тесты запускаются без ключей и сети: pytest проходит на чистом клоне.
  7. Проверка «с нуля»: клонируем репозиторий в новую папку, создаём новый venv, идём строго по README. Каждая заминка — правка README.

Всё, что нужно для запуска, должно попасть в репозиторий, а всё лишнее — остаться снаружи. Проект живёт в projects/02-support-kb-rag/ учебного репозитория; публикуем ли мы его целиком или выносим проект в отдельный, проверки ниже — для всего, что окажется на GitHub, включая историю.

  • Секреты. Перед первым push проверяем не только текущие файлы, но и всю историю коммитов: ключ, однажды закоммиченный и потом удалённый, остаётся в истории. Инструмент — например, gitleaks (gitleaks detect по репозиторию). Если ключ утёк — отзываем его у провайдера немедленно, переписывание истории не спасает (его уже могли увидеть).
  • .gitignore: .env, .venv/, index*.db* (index.db, index_ctx.db), raw/, cache/, __pycache__/, .DS_Store, persist-папки LlamaIndex.
  • Никаких персональных данных и ничего из баз магазина. Проект работает только с базой знаний; в нём нет и не должно быть снимков shop.db из проекта 1, путей к ../shop-live/, настоящих сообщений клиентов, логов с их вопросами и токена Telegram-бота из проекта 1. Вопросы в golden set и Demo — выдуманные.
  • Disclaimer: учебный проект; магазин «Полка» и его правила выдуманы; ответы модели могут быть неточными, их стоит сверять по ссылкам на разделы.
  • Лицензия на код (MIT — частый выбор для портфолио) и на тексты kb/ — они наши, их можно выпустить под той же или отдельной лицензией. Чужую веб-страницу из raw/web/ не распространяем.
  • Чистая история коммитов: осмысленные сообщения. Не нужно 50 коммитов «fix».
  • Скриншот или GIF интерфейса из гл. 5.3 в начале README.
  • CI: GitHub Actions, запускающий pytest (офлайн-тесты) на каждый push — зелёный бейдж показывает, что тесты реальные.
  • Раздел «Design decisions»: почему sqlite-vec, почему chunking по заголовкам, почему архив исключается фильтром, почему отказ фиксированной фразой — 1–2 предложения на решение.
  • Пример вывода eval в eval/RESULTS.md, ссылка из README.

Честные ограничения показывают зрелость. Примеры для этого проекта: база знаний выдуманная и небольшая (30–50 страниц); индекс обновляется вручную запуском ingest.py; golden set из 20 вопросов написан автором (риск подгонки); оценка ответов ручная; противоречия внутри базы (порог бесплатной доставки в delivery.md и promo.md) не разрешаются; передача человеку — запись в outputs/handoff.jsonl, а не очередь поддержки. И next steps: автоматический eval с LLM-судьёй, переиндексация при изменении kb/, интерфейс для покупателей с передачей оператору, contextual retrieval на всём индексе.

Для README провайдер важен в четырёх местах.

  1. .env.example под наш стек. Вместо переменных из «Теории»:
    ANSWER_PROVIDER=deepseek # deepseek | ollama | anthropic (comparison)
    DEEPSEEK_API_KEY=
    ANSWER_MODEL= # e.g. deepseek-flash; check api-docs.deepseek.com
    REWRITE_MODEL=
    OLLAMA_BASE_URL=http://localhost:11434/v1
    ANSWER_MODEL_LOCAL= # e.g. qwen3:4b, for the offline mode (8 GB RAM)
    EMBED_BACKEND=ollama # ollama | st
    EMBED_MODEL= # e.g. bge-m3
    RERANK_MODEL= # local cross-encoder, e.g. BAAI/bge-reranker-v2-m3
    ANTHROPIC_API_KEY= # optional, comparison runs only
  2. Quickstart с Ollama. Добавляем шаги «установить Ollama по официальной инструкции для своей ОС с ollama.com, ollama pull bge-m3 (и локальную LLM для офлайн-режима)» и честные требования: сколько места на диске и памяти занимают модели (например, «офлайн-режим проверен на машине с 8 GB RAM с моделью класса 4B; модели 7–8B требуют 16 GB») и что без запущенной Ollama ingest не работает. Режим «полностью офлайн» (Ollama для эмбеддингов и ответов) — сильный пункт для README: проект можно запустить без единого ключа.
  3. Стоимость и время. «Индексация: бесплатно, локальные эмбеддинги, ~N минут (процессор, объём RAM). Ответ: ~$X за вопрос на deepseek-flash, посчитано по usage (дата, цены с api-docs.deepseek.com); в пиковые часы DeepSeek вдвое дороже». Дату указываем обязательно — цены и имена моделей меняются.
  4. Куда уходят данные. Один абзац в README: при ANSWER_PROVIDER=deepseek вопросы и найденные куски базы знаний отправляются в API DeepSeek (серверы в Китае); эмбеддинги и реранкинг локальные. База знаний публичная, поэтому облако для неё допустимо. Настоящие сообщения клиентов в облако не уходят никогда: для них есть режим Ollama. Читатель README (и интервьюер) должен это видеть.

В разделе Results указываем, какие модели дали цифры (бэкенд и модель эмбеддингов, реранкер, генератор, дата) и, если делали, строки сравнения DeepSeek / Ollama / Claude из глав 5.1 и 5.3. Таблица «одинаковый пайплайн, разные генераторы» — хорошая тема на собеседовании.

Что важно для README Claude OpenAI DeepSeek Ollama
Нужен ключ Да Да Да (DEEPSEEK_API_KEY) Нет
Что указать в Quickstart Ключ Ключ Ключ, имя модели Установка, ollama pull, место на диске
Стоимость За токены За токены За токены, кэш-попадания дешевле, пиковые часы дороже Бесплатно, время и память машины
Пометка о данных Облако США Облако США Облако в Китае — явно написать Локально
  1. GitHub Docs: About READMEs (≈10 мин). Выносим: где GitHub ищет README, как работают относительные ссылки и якоря заголовков (проверяем, что ссылки на разделы kb/ из Demo открываются).
  2. GitHub Docs: Creating diagrams → раздел про Mermaid (≈15 мин). Набрасываем схему и проверяем её в предпросмотре GitHub или в Mermaid Live Editor.
  3. README двух-трёх известных RAG/LLM-репозиториев на GitHub (например, rank_bm25, sqlite-vec): как устроены quickstart и примеры (≈20 мин). Берём идеи структуры, не тексты.
  4. gitleaks → README: установка и detect по локальному репозиторию (≈15 мин). Это сторонний инструмент: перед установкой проверяем источник (официальная формула brew, релиз с GitHub с проверкой контрольной суммы или пакет своей ОС).
  5. (Опционально) Документация GitHub Actions: quickstart для Python — workflow, который ставит зависимости и запускает pytest (≈20 мин).
  6. Сверяем в README имена моделей и цены с DeepSeek: Models & Pricing, а системные требования Ollama — с официальной страницей для своей ОС (например, Ollama: macOS) (≈10 мин). Дату проверки записываем рядом с цифрами.
  • README не совпадает с кодом: команды переименованы, переменные окружения добавлены, а README устарел. Единственная защита — прогон «с нуля» в конце.
  • Ключ в истории git — самая дорогая ошибка; проверяем историю, не только последний коммит.
  • Закоммиченный индекс / raw/ — раздувает репозиторий и распространяет чужой контент. kb/, наоборот, должен быть в git: без него индекс не собрать.
  • Архивная страница в Demo. Если в примере ответа цитируется kb/returns-2025.md, это не украшение, а баг поиска — сначала чиним фильтр.
  • Тесты, требующие сети или ключей, падают у любого, кто склонировал. Разделяем офлайн-тесты и eval, который ходит в API.
  • Цифры без методики («точность 95%») вызывают недоверие. Всегда: на каком наборе, сколько вопросов, как считали.
  • Абсолютные пути (/Users/..., C:\...) в коде или README — у других людей их нет. Только относительные пути и переменные окружения.
  • Захардкоженные имена моделей в коде — выносим в .env, модели меняются.
  • Публикация — осознанное решение. Перед push проходим чек-лист ниже; репозиторий можно сначала создать приватным и сделать публичным, когда всё проверено.
  • Уборка. Удаляем мёртвый код и экспериментальные ноутбуки, которые не нужны (или переносим в experiments/ с пометкой). Имена моделей и пути — из .env/конфига.
  • Зависимости. requirements.txt (или pyproject.toml + lock) с точными версиями; отдельно requirements-llamaindex.txt, если LlamaIndex-версия живёт в том же репозитории. В README указана версия Python.
  • .env.example со всеми переменными (вариант для DeepSeek + Ollama из раздела выше); .gitignore по списку из теории.
  • Ollama в Quickstart: шаги установки, ollama pull нужных моделей, место на диске и RAM, режим «без ключей» (всё на Ollama). Абзац «Куда уходят данные».
  • Точки входа. Makefile (или раздел команд в README): setup, ingest, ask, app, eval, test.
  • README по структуре из теории:
    • одна фраза + скриншот/GIF;
    • Problem;
    • Demo: 3–4 вопроса из golden set с ответами и ссылками kb/<file>.md#<anchor> + 1 отказ;
    • Architecture: Mermaid-схема двух фаз;
    • How retrieval works: chunking, фильтр категории и исключение архива, гибрид + RRF, реранкер — по абзацу с «почему»;
    • Results: методика golden set, таблица пути улучшений поиска, таблица ответов, оговорка про размер выборки;
    • Manual vs LlamaIndex (из гл. 5.4);
    • Quickstart; Project structure; Evaluation; Limitations & next steps; License; Disclaimer.
  • Проверка секретов и данных. git log -p | grep -iE "api_key|sk-|token" как быстрый взгляд и полноценная проверка gitleaks по всей истории. Убеждаемся, что .env, index.db, raw/ и любые *.db не отслеживаются (git ls-files) и не появлялись в истории (git log --all --stat -- '*.db' '.env' 'raw/*'), а kb/ — отслеживается.
  • Прогон с нуля. git clone в новую папку → новый venv → строго по README: setup → заполнить .env → ingest → ask → app → test. Записываем каждую заминку и правим README. Повторяем, пока не пройдёт без единого вопроса.
  • (Опционально) CI. .github/workflows/test.yml: офлайн pytest на push; бейдж в README.
  • Push на GitHub. Создаём репозиторий (можно сначала приватным), push, открываем страницу на GitHub и проверяем: схема Mermaid рендерится, ссылки и картинки работают, таблицы читаются. Затем делаем публичным и включаем secret scanning / push protection.
  • Питч. В конце README или в своих заметках — 5 предложений о проекте для собеседования: проблема, архитектура, самое полезное улучшение (с цифрой), главный урок, что сделали бы дальше.
  • Проект запускается по README с нуля в чистом клоне и новом окружении: от git clone до ответа со ссылками на разделы kb/ без единой неописанной команды.
  • В README есть схема архитектуры, описание поиска, примеры вопросов и ответов, цифры качества с методикой, сравнение с LlamaIndex и ограничения.
  • kb/, sources.yaml, скрипт индексации и инструкция по запуску на месте; index.db, raw/ и .env не в репозитории.
  • В репозитории и его истории нет секретов, персональных данных, настоящих сообщений клиентов и ничего из баз магазина; проект запушен на GitHub.

Вопрос 1

Назвать порядок основных разделов README и объяснить, почему «как запустить» не идёт первым.

Что и зачем (с картинкой) → демо → архитектура → как работает поиск → результаты → сравнение → quickstart → структура → eval → ограничения → лицензия. Первыми идут ценность и доказательства: большинство читателей не будет запускать проект, а решит по первым экранам.

Вопрос 2

Почему индекс (index.db) и скачанную в raw/ веб-страницу не стоит коммитить, а kb/ — стоит? Две причины для первого.

Индекс и raw/ пересобираются скриптом и раздувают репозиторий; скачанная веб-страница принадлежит её владельцу, и распространять её — не наше право. kb/ — исходники: без них индекс не собрать, тексты выдуманы и принадлежат автору.

Вопрос 3

.env с ключом закоммитили, потом удалили следующим коммитом и запушили. Достаточно ли этого?

  • a) да, файла больше нет
  • b) нет: ключ в истории; нужно отозвать ключ у провайдера
  • c) достаточно сделать репозиторий приватным
  • d) достаточно git rm --cached .env

b. Ключ остаётся в истории git и мог быть уже скопирован; единственная надёжная мера — отозвать и перевыпустить ключ (а историю при желании почистить дополнительно).

Вопрос 4

Зачем .env.example, если есть .env?

Показывает, какие переменные нужны, без значений; реальный .env с секретами не коммитится, а пользователю есть что скопировать и заполнить.

Вопрос 5

Что не так в этом фрагменте README?

## Results
Accuracy: 95%. The system works great.
## Quickstart
cd /Users/me/projects/rag && python ingest.py

Цифра без методики (на чём, сколько вопросов, какая метрика) и оценочное «works great»; абсолютный путь /Users/me/... не сработает у других; не показаны venv, установка зависимостей, .env, запуск Ollama.

Вопрос 6

Почему в Results стоит показать путь улучшений, а не только финальную цифру?

Он показывает инженерный процесс и вклад каждого решения (chunking, фильтр категории, BM25, реранкер) — это тема разговора на собеседовании и доказательство, что автор измерял, а не угадывал.

Вопрос 7

Чем Mermaid-схема в README лучше картинки PNG?

Она хранится текстом рядом с кодом, легко правится, видна в диффах и рендерится GitHub без отдельных файлов.

Вопрос 8

Как проверить, что проект «запускается с нуля»?

Клонировать в новую папку, создать новое окружение и пройти строго по README без отступлений; каждая заминка — исправление README; повторять до чистого прохода.

Вопрос 9

Почему офлайн-тесты не должны требовать ключей и сети?

Иначе они падают у любого, кто склонировал репозиторий, и в CI; офлайн-тесты проверяют логику (токенизация, RRF, фильтр архива, цитаты), а походы в API — это отдельный eval.

Вопрос 10

Что написать в Limitations для этого проекта? Три пункта.

Например: маленькая выдуманная база знаний; маленький golden set, написанный автором; ручная оценка ответов; индекс обновляется вручную; противоречия внутри базы не разрешаются; передача человеку без настоящей очереди поддержки.

Вопрос 11

Почему в Demo, golden set и примерах нельзя использовать настоящие сообщения клиентов и данные из снимка базы магазина?

Репозиторий публичный: имена, телефоны, номера заказов покупателей и цифры магазина — приватные данные, а токены и пути к базам — риск безопасности. Этот проект работает только с базой знаний; вопросы для демо и golden set выдумываются. Данные магазина в курсе тоже выдуманы, но обращаемся с ними как с настоящими.

Вопрос 12

Почему в README проекта на DeepSeek стоит отдельно написать, куда уходят данные, и что там должно быть?

Вопросы пользователя и куски базы знаний уходят в облачный API с серверами в Китае; пользователь должен это знать до того, как что-то спросит. В абзаце: что уходит (вопрос + найденные куски), что остаётся локально (эмбеддинги, реранкер), почему это допустимо (база знаний публичная) и какой режим выбрать для настоящих сообщений клиентов (Ollama).

Вопрос 13

Как сделать так, чтобы проект можно было запустить вообще без облачных ключей?

  • a) никак
  • b) режим, где эмбеддинги, реранкер и генерация — локальные (Ollama + Sentence Transformers), выбирается переменными окружения
  • c) закоммитить свой ключ DeepSeek
  • d) использовать бесплатный тариф любого облака

b. Провайдеры переключаются переменными окружения, и в офлайн-режиме все модели работают на Ollama и Sentence Transformers. Коммитить ключ нельзя, а бесплатные облачные тарифы могут обучаться на входных данных.

Проект 2 закрыт. Следующий этап плана опирается на него: в неделях 6–8 тот же ассистент в projects/02-support-kb-rag/ становится проектом 3 — golden set и таблицы метрик, которые мы собрали, станут основой для систематических evals и LLM-судьи, а сам поиск по базе знаний — инструментом для агента в последующих главах. Начинаем с того, как оценивают LLM-системы: 6.1 · Статьи про evals.