5.5 · Проект 2: README и GitHub
Неделя 5 · RAG · ⏱ ~4 ч · Проект
Зачем это AI-инженеру
Заголовок раздела «Зачем это AI-инженеру»Это второй проект в портфолио. Нанимающий менеджер тратит на репозиторий 2–5 минут: читает README, смотрит схему и цифры, иногда пробует запустить. Проект без README и без цифр качества выглядит как учебный ноутбук; проект с таблицей «вектор / гибрид / гибрид + реранкер», честным разделом об ограничениях и запуском в три команды выглядит как работа инженера. На собеседовании этот README — готовый конспект для рассказа о проекте.
⚠️ Публичный репозиторий проекта 2 содержит код и выдуманную базу знаний kb/ — и больше ничего из магазина. Индекс index.db, кэш raw/, .env, настоящие сообщения клиентов, снимки базы магазина из проекта 1 не должны попасть в него ни файлом, ни скриншотом, ни строчкой README — ни сейчас, ни в истории git.
Кто читает README и что ищет
Заголовок раздела «Кто читает README и что ищет»| Читатель | Время | Что ищет |
|---|---|---|
| Рекрутер / HR | 30 секунд | Что это, стек, есть ли демо (скриншот/GIF) |
| Нанимающий инженер | 3–5 минут | Архитектура, решения и их обоснование, цифры качества, код-стиль, тесты |
| Мы на собеседовании | — | Готовая история: проблема → решения → измерения → выводы |
| Мы через полгода | 10 минут | Как это запустить |
Отсюда порядок: сначала что и зачем (с картинкой), потом как работает и насколько хорошо, потом как запустить, в конце детали.
Структура README
Заголовок раздела «Структура README»# 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 имена файлов, команды и метрики всё равно остаются как есть.
Раздел Results — сердце README
Заголовок раздела «Раздел Results — сердце 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 — схема хранится текстом, её легко менять, она видна в диффах:
```mermaidflowchart 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 в чистом окружении. Что для этого нужно:
- Зафиксированные зависимости.
requirements.txtс точными версиями (pip freezeпосле проверки) илиpyproject.toml+ lock-файл, если работаем черезuv/poetry. Указанная версия Python. .env.example— список переменных без значений:Пользователь копирует его вANTHROPIC_API_KEY=OPENAI_API_KEY= # only if you use OpenAI embeddingsEMBED_MODEL=REWRITE_MODEL=ANSWER_MODEL=RERANK_MODEL=.envи заполняет. Реальный.env— в.gitignore.- База знаний в репозитории, индекс — нет.
kb/коммитим: это исходники, тексты выдуманы и принадлежат нам. Индексindex.dbи кэшraw/не коммитятся: они пересобираются скриптом и раздувают репозиторий, а скачанная вraw/web/публичная страница (необязательный пункт гл. 4.4) принадлежит её владельцу. В репозитории — код, который строит индекс (ingest.py), иsources.yaml. - Одна точка входа на действие:
make ingest,make ask Q="...",make eval,make app,make test— или эквивалентные короткие команды в README. Makefile по сути — то же, чтоscriptsвpackage.jsonили задачи в Gradle: имя действия → команда. - Ожидаемое время и стоимость: «индексация
kb/(30–50 страниц): ~N минут, ~$X за эмбеддинги при API-модели (или бесплатно с локальной)». - Офлайн-тесты запускаются без ключей и сети:
pytestпроходит на чистом клоне. - Проверка «с нуля»: клонируем репозиторий в новую папку, создаём новый 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.
Раздел Limitations
Заголовок раздела «Раздел Limitations»Честные ограничения показывают зрелость. Примеры для этого проекта: база знаний выдуманная и небольшая (30–50 страниц); индекс обновляется вручную запуском ingest.py; golden set из 20 вопросов написан автором (риск подгонки); оценка ответов ручная; противоречия внутри базы (порог бесплатной доставки в delivery.md и promo.md) не разрешаются; передача человеку — запись в outputs/handoff.jsonl, а не очередь поддержки. И next steps: автоматический eval с LLM-судьёй, переиндексация при изменении kb/, интерфейс для покупателей с передачей оператору, contextual retrieval на всём индексе.
DeepSeek и Ollama: как это сделать у нас
Заголовок раздела «DeepSeek и Ollama: как это сделать у нас»Для README провайдер важен в четырёх местах.
.env.exampleпод наш стек. Вместо переменных из «Теории»:ANSWER_PROVIDER=deepseek # deepseek | ollama | anthropic (comparison)DEEPSEEK_API_KEY=ANSWER_MODEL= # e.g. deepseek-flash; check api-docs.deepseek.comREWRITE_MODEL=OLLAMA_BASE_URL=http://localhost:11434/v1ANSWER_MODEL_LOCAL= # e.g. qwen3:4b, for the offline mode (8 GB RAM)EMBED_BACKEND=ollama # ollama | stEMBED_MODEL= # e.g. bge-m3RERANK_MODEL= # local cross-encoder, e.g. BAAI/bge-reranker-v2-m3ANTHROPIC_API_KEY= # optional, comparison runs only- Quickstart с Ollama. Добавляем шаги «установить Ollama по официальной инструкции для своей ОС с ollama.com,
ollama pull bge-m3(и локальную LLM для офлайн-режима)» и честные требования: сколько места на диске и памяти занимают модели (например, «офлайн-режим проверен на машине с 8 GB RAM с моделью класса 4B; модели 7–8B требуют 16 GB») и что без запущенной Ollamaingestне работает. Режим «полностью офлайн» (Ollama для эмбеддингов и ответов) — сильный пункт для README: проект можно запустить без единого ключа. - Стоимость и время. «Индексация: бесплатно, локальные эмбеддинги, ~N минут (процессор, объём RAM). Ответ: ~$X за вопрос на
deepseek-flash, посчитано поusage(дата, цены с api-docs.deepseek.com); в пиковые часы DeepSeek вдвое дороже». Дату указываем обязательно — цены и имена моделей меняются. - Куда уходят данные. Один абзац в 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, место на диске |
| Стоимость | За токены | За токены | За токены, кэш-попадания дешевле, пиковые часы дороже | Бесплатно, время и память машины |
| Пометка о данных | Облако США | Облако США | Облако в Китае — явно написать | Локально |
Маршрут изучения
Заголовок раздела «Маршрут изучения»- GitHub Docs: About READMEs (≈10 мин). Выносим: где GitHub ищет README, как работают относительные ссылки и якоря заголовков (проверяем, что ссылки на разделы
kb/из Demo открываются). - GitHub Docs: Creating diagrams → раздел про Mermaid (≈15 мин). Набрасываем схему и проверяем её в предпросмотре GitHub или в Mermaid Live Editor.
- README двух-трёх известных RAG/LLM-репозиториев на GitHub (например, rank_bm25, sqlite-vec): как устроены quickstart и примеры (≈20 мин). Берём идеи структуры, не тексты.
- gitleaks → README: установка и
detectпо локальному репозиторию (≈15 мин). Это сторонний инструмент: перед установкой проверяем источник (официальная формула brew, релиз с GitHub с проверкой контрольной суммы или пакет своей ОС). - (Опционально) Документация GitHub Actions: quickstart для Python — workflow, который ставит зависимости и запускает
pytest(≈20 мин). - Сверяем в 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 с ключом закоммитили, потом удалили следующим коммитом и запушили. Достаточно ли этого?
b. Ключ остаётся в истории git и мог быть уже скопирован; единственная надёжная мера — отозвать и перевыпустить ключ (а историю при желании почистить дополнительно).
Вопрос 4
Зачем .env.example, если есть .env?
Показывает, какие переменные нужны, без значений; реальный .env с секретами не коммитится, а пользователю есть что скопировать и заполнить.
Вопрос 5
Что не так в этом фрагменте README?
## ResultsAccuracy: 95%. The system works great.## Quickstartcd /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
Как сделать так, чтобы проект можно было запустить вообще без облачных ключей?
b. Провайдеры переключаются переменными окружения, и в офлайн-режиме все модели работают на Ollama и Sentence Transformers. Коммитить ключ нельзя, а бесплатные облачные тарифы могут обучаться на входных данных.
Что дальше
Заголовок раздела «Что дальше»Проект 2 закрыт. Следующий этап плана опирается на него: в неделях 6–8 тот же ассистент в projects/02-support-kb-rag/ становится проектом 3 — golden set и таблицы метрик, которые мы собрали, станут основой для систематических evals и LLM-судьи, а сам поиск по базе знаний — инструментом для агента в последующих главах. Начинаем с того, как оценивают LLM-системы: 6.1 · Статьи про evals.