Индексация кодовой базы для RAG

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1 услугВсе 1566 услуг
Индексация кодовой базы для RAG
Средняя
от 1 недели до 3 месяцев
Часто задаваемые вопросы
Направления AI-разработки
Этапы разработки AI-решения
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1240
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1167
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    867
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1084
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    563
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    829

Индексация кодовой базы для RAG

RAG по кодовой базе — основа для AI code assistants, автоматической документации и поиска по архитектурным решениям. Ключевое отличие от документных RAG: код имеет структуру (функции, классы, импорты), которую нужно сохранять при чанкинге.

Code-aware парсинг

import ast
from tree_sitter import Language, Parser

class CodebaseIndexer:
    def __init__(self):
        # Tree-sitter для syntax-aware парсинга
        PY_LANGUAGE = Language('build/languages.so', 'python')
        self.parser = Parser()
        self.parser.set_language(PY_LANGUAGE)

    def extract_python_units(self, file_path: str) -> list[dict]:
        """Извлечение функций и классов как отдельных единиц индексации"""
        with open(file_path, 'r', encoding='utf-8') as f:
            source = f.read()

        try:
            tree = ast.parse(source)
        except SyntaxError:
            return [{'text': source, 'type': 'file', 'file': file_path}]

        units = []
        for node in ast.walk(tree):
            if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                # Получение исходного кода функции
                func_source = ast.get_source_segment(source, node)
                docstring = ast.get_docstring(node)

                units.append({
                    'type': 'function',
                    'name': node.name,
                    'file': file_path,
                    'line_start': node.lineno,
                    'line_end': node.end_lineno,
                    'text': func_source,
                    'docstring': docstring or '',
                    'decorators': [ast.unparse(d) for d in node.decorator_list],
                    'signature': self._get_signature(node)
                })

            elif isinstance(node, ast.ClassDef):
                class_source = ast.get_source_segment(source, node)
                docstring = ast.get_docstring(node)

                units.append({
                    'type': 'class',
                    'name': node.name,
                    'file': file_path,
                    'line_start': node.lineno,
                    'line_end': node.end_lineno,
                    'text': class_source,
                    'docstring': docstring or '',
                    'methods': [m.name for m in ast.walk(node)
                                if isinstance(m, ast.FunctionDef)]
                })

        return units

    def _get_signature(self, func_node: ast.FunctionDef) -> str:
        args = []
        for arg in func_node.args.args:
            annotation = f": {ast.unparse(arg.annotation)}" \
                        if arg.annotation else ""
            args.append(f"{arg.arg}{annotation}")

        return_type = f" -> {ast.unparse(func_node.returns)}" \
                     if func_node.returns else ""
        return f"def {func_node.name}({', '.join(args)}){return_type}"

Обогащение метаданными для поиска

class CodeMetadataEnricher:
    def enrich(self, unit: dict) -> dict:
        unit = unit.copy()

        # Создание rich text для эмбеддинга
        # Комбинирование имени, сигнатуры, docstring и кода
        rich_text_parts = []

        if unit.get('name'):
            rich_text_parts.append(f"# {unit['name']}")

        if unit.get('signature'):
            rich_text_parts.append(f"Signature: {unit['signature']}")

        if unit.get('docstring'):
            rich_text_parts.append(f"Description: {unit['docstring']}")

        rich_text_parts.append(unit['text'])

        unit['rich_text'] = '\n\n'.join(rich_text_parts)

        # Извлечение импортов для контекста
        imports = re.findall(r'^(?:import|from)\s+\S+', unit['text'], re.MULTILINE)
        unit['imports'] = imports[:10]

        # Путь в виде breadcrumb
        parts = unit['file'].replace('\\', '/').split('/')
        unit['module_path'] = '.'.join(
            p.replace('.py', '') for p in parts if not p.startswith('.')
        )

        return unit

Индексация Git истории

import subprocess

class GitHistoryIndexer:
    def get_recent_changes(self, repo_path: str, n: int = 100) -> list[dict]:
        """Индексация последних коммитов с diff"""
        result = subprocess.run(
            ['git', 'log', f'-{n}', '--format=%H|%an|%ae|%ad|%s'],
            cwd=repo_path, capture_output=True, text=True
        )

        commits = []
        for line in result.stdout.strip().split('\n'):
            if not line:
                continue
            hash_, author, email, date, subject = line.split('|', 4)

            # Получение diff для этого коммита
            diff_result = subprocess.run(
                ['git', 'diff', f'{hash_}^', hash_, '--stat'],
                cwd=repo_path, capture_output=True, text=True
            )

            commits.append({
                'hash': hash_,
                'author': author,
                'date': date,
                'message': subject,
                'changes_summary': diff_result.stdout[:500],
                'text': f"Commit: {subject}\nAuthor: {author}\nDate: {date}\n\nChanges: {diff_result.stdout[:500]}"
            })

        return commits

Оценка качества code RAG

Хорошая метрика: при вопросе "Как реализован X?" система должна вернуть функцию или класс, который реализует X, а не просто файл с похожим названием. Для оценки: golden set из 50-100 вопросов к кодовой базе с известными ответами (конкретными функциями). Precision@3 > 0.8 — хороший результат.