Чемпионат Ростелекома «Топ уровень» | Направление: ИИ / Data Engineering
- Что это и зачем
- Архитектура — как всё устроено
- Структура проекта
- Быстрый старт
- Стратегия чункования
- 5 примеров запросов с ответами
- Метрики качества
- Частые проблемы
- FAQ — часто задаваемые вопросы
Проблема: в крупных IT-компаниях десятки репозиториев. Если новый сотрудник спросит «как здесь обрабатывается авторизация?» — обычный поиск (grep, Ctrl+F) не поможет, потому что слова вопроса не совпадают с именами функций в коде.
Решение: CodeLens понимает смысл вопроса, а не буквальные слова. Работает на русском и английском языке.
Тип задачи: RAG (Retrieval-Augmented Generation) — «найди похожее + покажи». LLM-ответы реализованы как дополнительная функция.
┌─────────────────────────────────────────────────────────────────┐
│ ЭТАП 1: ИНДЕКСАЦИЯ │
│ (запускается один раз) │
│ │
│ codebase_python.zip │
│ │ │
│ ▼ │
│ [index.py] читает .py-файлы │
│ │ │
│ ▼ │
│ AST-парсер нарезает код на куски (чанки): │
│ • каждая функция → отдельный чанк │
│ • каждый метод класса → отдельный чанк │
│ • каждый класс целиком → отдельный чанк │
│ │ │
│ ▼ │
│ SentenceTransformer превращает текст чанка в вектор │
│ "def create_access_token..." → [0.12, -0.45, 0.78, ...] │
│ (список из 384 чисел — «координаты» в смысловом пространстве) │
│ │ │
│ ▼ │
│ ChromaDB сохраняет: id + текст кода + вектор + метаданные │
│ Файл на диске: ./chroma_db/ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ ЭТАП 2: ПОИСК │
│ (при каждом вопросе пользователя) │
│ │
│ Вопрос: "как создаётся JWT-токен?" │
│ │ │
│ ▼ │
│ SentenceTransformer → вектор вопроса [0.09, -0.51, 0.82, ...] │
│ │ │
│ ▼ │
│ ChromaDB сравнивает вектор вопроса со всеми векторами чанков │
│ (косинусное расстояние = угол между векторами в 384D-пространстве)│
│ │ │
│ ▼ │
│ Возвращает топ-5 самых похожих чанков (≤ 0.17 сек.) │
│ │ │
│ ▼ │
│ Streamlit показывает результаты с подсветкой кода │
└─────────────────────────────────────────────────────────────────┘
Представьте, что каждый кусок кода — это точка в пространстве из 384 измерений. Тексты, похожие по смыслу (даже если написаны по-разному или на разных языках), оказываются рядом в этом пространстве.
"создать токен доступа" → рядом с → "def create_access_token()"
"проверить пользователя" → рядом с → "def authenticate_user()"
Этим занимается модель paraphrase-multilingual-MiniLM-L12-v2 — она обучена понимать
смысл текста на 50+ языках.
numpy — это библиотека для работы с массивами чисел. Она нужна потому что:
sentence-transformersвозвращает эмбеддинги какnumpy.ndarray- Вызов
.tolist()переводит numpy-массив в обычный список Python - Без numpy эти операции были бы в 10-50 раз медленнее
- numpy — обязательная зависимость sentence-transformers, без неё он не установится
./chroma_db/ ← папка с векторной БД (создаётся index.py)
chroma.sqlite3 ← SQLite-файл: все чанки, векторы, метаданные
index_metadata.json ← статистика индексации (сколько файлов, чанков)
./results.json ← создаётся при нажатии "Запустить оценку"
содержит топ-5 chunk_id для каждого из 15 вопросов
нужен для внешнего scorer'а (score.py)
project/
├── index.py ← Компонент 1: индексирует код, создаёт chroma_db/
├── app.py ← Компонент 3: веб-интерфейс Streamlit
├── score.py ← скрипт оценки Precision@5 (из датасета, не трогать)
├── eval_questions.json ← 15 тестовых вопросов с эталонными ответами
├── sample_queries.txt ← 20 примеров запросов для демо
├── codebase_python.zip ← архив с кодом GymHero (FastAPI-проект)
├── requirements.txt ← зависимости
├── README.md ← этот файл
└── chroma_db/ ← создаётся автоматически после python index.py
pip install -r requirements.txtЕсли возникают ошибки с версиями — попробуйте:
pip install chromadb sentence-transformers streamlit numpypython index.pyЧто происходит внутри:
- Распаковывает
codebase_python.zip→ папкаgymhero/ - Обходит все
.py-файлы рекурсивно - Для каждого файла строит AST-дерево и вырезает функции/классы
- Генерирует эмбеддинги батчами по 32 штуки
- Сохраняет всё в
chroma_db/
Занимает 1–3 минуты. Модель (~120 МБ) скачивается автоматически при первом запуске.
Успешный финиш выглядит так:
============================================================
INDEXING COMPLETE
============================================================
Files processed: 82
Total chunks indexed: 650+
- Functions: 250+
- Methods: 300+
- Classes: 80+
Vector DB stored at: ./chroma_db
============================================================
Если хотите переиндексировать заново (например, после изменений):
python index.py # автоматически пересоздаёт коллекциюstreamlit run app.pyБраузер откроется автоматически на http://localhost:8501
Выбранная стратегия: один чанк = одна функция или один класс.
| Стратегия | Плюсы | Минусы |
|---|---|---|
| Фиксированные N строк | Просто | Разрезает функции посередине, теряет смысл |
| Весь файл = один чанк | Не теряет контекст | Вектор "размывается", плохая точность |
| Функция/класс (выбрано) | Смысловая единица кода, хорошая точность | Очень длинные функции могут быть шумными |
Используется стандартный модуль Python ast (Abstract Syntax Tree — абстрактное синтаксическое дерево).
AST — это структура, которую Python строит при парсинге кода, ещё до исполнения.
# ast.parse() читает код и строит дерево
tree = ast.parse(source)
# ast.walk() обходит дерево и находит все функции и классы
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef): # нашли функцию
# node.lineno — начальная строка
# node.end_lineno — конечная строка
# ast.get_docstring(node) — докстринг, если естьЕсли у функции есть docstring — он добавляется перед кодом:
"Returns JWT access token for authenticated user\ndef create_access_token(data: dict):\n ..."
Это улучшает поиск: docstring написан на человеческом языке и ближе к тому, что спрашивает пользователь.
Ожидаемый топ результат: gymhero/security.py → функция create_access_token
Содержит логику создания JWT с помощью библиотеки jose.
Ожидаемый топ результат: gymhero/security.py → функция verify_token или decode_token
Показывает, как сервис проверяет подпись токена и извлекает payload.
Ожидаемый топ результат: gymhero/config.py → класс Settings
Содержит DATABASE_URL, SECRET_KEY и другие параметры конфигурации.
Ожидаемый топ результат: роутеры или сервисы в gymhero/api/ или gymhero/crud/
Функции для работы с тренировками (workout blocks, exercises).
Ожидаемый топ результат: Pydantic-схемы в gymhero/schemas/
Классы UserCreate или UserRegister с обязательными полями.
Что это: доля правильных чанков среди топ-5 результатов поиска.
Precision@5 = (число правильных чанков в топ-5) / min(5, число эталонных чанков)
Пример:
- Вопрос: «как создаётся токен?»
- Эталонные ответы:
[security.py:create_access_token:12](1 правильный) - Топ-5 нашли:
[security.py:create_access_token:12, config.py:Settings:11, ...] - Precision@5 = 1/1 = 100%
Целевое значение: ≥ 60%
Способ 1 — через интерфейс Streamlit: Вкладка «Метрики (Precision@5)» → кнопка «▶ Запустить оценку»
Способ 2 — через командную строку:
# Сначала запустить оценку в Streamlit (создаст results.json)
# Затем:
python score.py --predictions results.json --questions eval_questions.jsonТребование: ≤ 3 секунды на запрос. Текущий результат: ~0.1–0.2 секунды (видно на скриншоте: 0.17 сек)
Это не ошибка поиска — система находит нужные файлы. Это особенность формулы.
ChromaDB возвращает косинусное расстояние от 0 до 2:
0= векторы идентичны (100% совпадение)1= векторы перпендикулярны (0% связи)2= векторы противоположны
Формула перевода в проценты:
score_pct = (1.0 - distance / 2.0) * 100Если ChromaDB вернул расстояние ≈ 2.0, то score = (1 - 2/2) * 100 = 0%.
Почему расстояние ≈ 2? Это может быть из-за того что ChromaDB по умолчанию использует L2 (евклидово) расстояние вместо косинусного. При L2 значения могут выходить за диапазон [0, 2], и формула даёт неверный результат.
Как исправить — при создании коллекции указать метрику явно:
# В index.py, в методе CodeIndexer.__init__:
self.collection = self.client.create_collection(
name=config.chroma_collection_name,
metadata={
"description": "Code chunks from Python project",
"hnsw:space": "cosine" # ← ДОБАВИТЬ ЭТУ СТРОКУ
}
)После этого пересоздать индекс (python index.py) — проценты станут корректными (30–90%).
→ Сначала запустите python index.py
→ pip install chromadb sentence-transformers streamlit numpy
→ Нормально, это занимает 1–3 минуты. Модель обрабатывает 32 чанка за раз.
→ pip install numpy
→ Откройте вручную: http://localhost:8501
Q: Почему numpy в requirements.txt?
A: numpy нужен sentence-transformers для хранения векторов в памяти. Вызов .tolist() в
коде переводит numpy-массив в стандартный Python-список, который принимает ChromaDB.
Без numpy sentence-transformers вообще не установится.
Q: Что такое AST?
A: Abstract Syntax Tree (абстрактное синтаксическое дерево) — это структура данных, которую
Python строит при разборе кода. Каждый узел дерева — это конструкция языка: функция, класс,
вызов, присваивание. Модуль ast позволяет обходить это дерево и находить функции/классы
без ручного разбора текста.
Q: Почему модель «multilingual»?
A: Модель paraphrase-multilingual-MiniLM-L12-v2 обучена на 50+ языках одновременно.
Вопрос на русском «создание токена» и код на английском create_access_token попадают
в соседние точки в 384-мерном пространстве смыслов.
Q: Можно ли добавить LLM-ответы? A: Да, это дополнительный компонент. Нужно передать найденные чанки + вопрос в языковую модель (Ollama локально или API). Это даст более связный ответ на русском языке.