Разработка AI-генерации документации к коду

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1Все 1566 услуг
Разработка AI-генерации документации к коду
Средний
~5 дней
Часто задаваемые вопросы

Направления AI-разработки

Этапы разработки AI-решения

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1285
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1197
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    902
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1119
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    586
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    853

AI-генерация документации для кода

Документация устаревает на следующий день после написания — это константа разработки. AI-генерация документации решает проблему иначе: не "писать раз и поддерживать", а "регенерировать при каждом изменении". Docstrings, API-документация, README-файлы, архитектурные описания — всё это можно автоматизировать с качеством, которое превышает среднее по команде.

Docstring генератор

from anthropic import Anthropic
import ast
from pathlib import Path

client = Anthropic()

DOCSTRING_SYSTEM = """Ты — технический писатель, специализирующийся на Python документации.
Пиши docstrings в формате Google Style:

```python
def function(param: type) -> type:
    \"\"\"Краткое описание в одну строку (повелительное наклонение).

    Детальное описание при необходимости. Объясняй ЧТО делает функция,
    не КАК. Упоминай нетривиальные алгоритмы или важные side effects.

    Args:
        param: Описание параметра. Не указывай тип — он есть в аннотации.

    Returns:
        Что возвращает. Не указывай тип.

    Raises:
        ValueError: Когда и почему возникает.

    Example:
        >>> result = function(value)
        >>> assert result == expected
    \"\"\"

Правила:

  • Краткая строка: повелительное наклонение (Calculates/Returns/Creates, не Calculate/Return)
  • Не повторяй имя функции и типы из аннотаций
  • Добавляй Example только для нетривиальных функций
  • Для простых getter/setter — минимальная документация"""

def generate_docstring(function_source: str) -> str: """Генерирует docstring для функции"""

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    system=DOCSTRING_SYSTEM,
    messages=[{
        "role": "user",
        "content": f"Напиши docstring для этой функции. Верни только docstring (тройные кавычки), без кода функции:\n\n```python\n{function_source}\n```"
    }]
)

return response.content[0].text.strip()

def add_docstrings_to_file(file_path: str, overwrite: bool = False) -> str: """Добавляет docstrings ко всем функциям в файле""" source = Path(file_path).read_text() tree = ast.parse(source) lines = source.splitlines()

# Собираем функции без docstrings
functions_to_document = []

for node in ast.walk(tree):
    if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
        has_docstring = (
            node.body and
            isinstance(node.body[0], ast.Expr) and
            isinstance(node.body[0].value, ast.Constant) and
            isinstance(node.body[0].value.value, str)
        )

        if not has_docstring or overwrite:
            func_source = ast.unparse(node)
            functions_to_document.append({
                "name": node.name,
                "line": node.lineno,
                "source": func_source,
                "indent": len(lines[node.lineno - 1]) - len(lines[node.lineno - 1].lstrip()),
            })

# Генерируем docstrings
insertions = {}  # line_number -> docstring_text
for func_info in functions_to_document:
    docstring = generate_docstring(func_info["source"])

    # Индентируем docstring
    indent = " " * (func_info["indent"] + 4)
    docstring_lines = docstring.strip().splitlines()
    indented = "\n".join(indent + line if line.strip() else line for line in docstring_lines)

    # Строка после def ... :
    insertions[func_info["line"]] = indented

# Вставляем docstrings в исходный код
result_lines = []
for i, line in enumerate(lines, 1):
    result_lines.append(line)
    if i in insertions:
        result_lines.append(insertions[i])

return "\n".join(result_lines)

### API документация (OpenAPI/Swagger)

```python
from pydantic import BaseModel
import json

def generate_api_docs(router_source: str, existing_models: str = "") -> str:
    """Генерирует OpenAPI-совместимую документацию для FastAPI роутера"""

    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=4096,
        messages=[{
            "role": "user",
            "content": f"""Проанализируй FastAPI роутер и добавь полную OpenAPI документацию:
- summary и description для каждого endpoint
- description для всех параметров (Path, Query, Body)
- response_model с примерами
- HTTPException с кодами и описаниями

FastAPI роутер:
```python
{router_source}

{f"Существующие модели:{chr(10)}python{chr(10)}{existing_models}{chr(10)}" if existing_models else ""}

Верни улучшенный роутер с полной документацией.""" }] )

return response.content[0].text

def generate_readme_section(module_path: str) -> str: """Генерирует README секцию для Python модуля""" source = Path(module_path).read_text()

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=2048,
    messages=[{
        "role": "user",
        "content": f"""Напиши README секцию для этого Python модуля.

Структура:

Название модуля

Краткое описание (1-2 предложения).

Использование

# Пример кода

API

Таблица публичных функций/классов с описанием.

Требования

Зависимости если есть.

Код модуля:

{source[:3000]}
```"""
        }]
    )

    return response.content[0].text

Архитектурная документация

def generate_architecture_doc(project_root: str) -> str:
    """Генерирует архитектурное описание проекта"""
    from pathlib import Path
    import os

    # Собираем структуру проекта
    structure = []
    for root, dirs, files in os.walk(project_root):
        # Пропускаем vendor, cache
        dirs[:] = [d for d in dirs if d not in {
            ".git", "__pycache__", "node_modules", ".venv", "migrations"
        }]
        level = root.replace(project_root, "").count(os.sep)
        indent = " " * 2 * level
        structure.append(f"{indent}{os.path.basename(root)}/")
        for file in files:
            if file.endswith((".py", ".ts", ".tsx")):
                structure.append(f"{indent}  {file}")

    # Читаем ключевые файлы
    key_files_content = {}
    key_patterns = ["models.py", "urls.py", "routes.ts", "app.py", "main.py"]
    for root, _, files in os.walk(project_root):
        for file in files:
            if file in key_patterns:
                file_path = os.path.join(root, file)
                try:
                    key_files_content[file_path] = Path(file_path).read_text()[:1500]
                except Exception:
                    pass
        if len(key_files_content) >= 5:
            break

    key_content = "\n\n".join([
        f"### {path}\n```python\n{content}\n```"
        for path, content in key_files_content.items()
    ])

    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=4096,
        messages=[{
            "role": "user",
            "content": f"""Напиши архитектурную документацию для проекта.

Структура проекта:

{chr(10).join(structure[:100])}


Ключевые файлы:
{key_content}

Включи:
1. Обзор архитектуры (2-3 абзаца)
2. Основные компоненты и их роли
3. Схема взаимодействия (текстовая)
4. Ключевые паттерны, используемые в коде
5. Точки входа и запуск

Формат: Markdown для README."""
        }]
    )

    return response.content[0].text

Автообновление документации в CI/CD

# .github/workflows/docs.yml
name: Update Documentation

on:
  push:
    branches: [main]
    paths:
      - "src/**/*.py"
      - "app/**/*.py"

jobs:
  update-docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Generate docstrings
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          python scripts/generate_docs.py \
            --source src/ \
            --check-missing-only \
            --output-report docs/coverage.json

      - name: Update API docs
        run: |
          python scripts/generate_api_docs.py \
            --routers app/api/ \
            --output docs/api/

      - name: Commit if changed
        run: |
          git config user.email "[email protected]"
          git config user.name "Docs Bot"
          git add docs/
          git diff --staged --quiet || git commit -m "docs: auto-update documentation"
          git push

Практический кейс: микросервис с 0% документации

Задача: Python FastAPI сервис, 4200 строк, 67 endpoints, 0 docstrings. Онбординг нового разработчика занимал 3 недели.

Автоматизация:

  • Batch-генерация docstrings для всех функций (182 функции, 45 мин)
  • Генерация OpenAPI descriptions для всех endpoints
  • Архитектурный README с описанием компонентов

Результаты:

  • Docstring coverage: 0% → 91%
  • Время онбординга нового разработчика: 3 недели → 1 неделя
  • Вопросы в Slack "как работает X": -68%
  • Качество оценка документации командой: 4.1/5.0

Нюанс: для 8% функций с нетривиальной бизнес-логикой AI-генерированная документация была неточной — потребовала ручной правки. Система помечает такие функции (cyclomatic complexity > 10) для ревью.

Docstring coverage как метрика CI

def check_docstring_coverage(source_dir: str, threshold: float = 0.8) -> bool:
    """Проверяет coverage docstrings, возвращает False если ниже порога"""
    total = 0
    documented = 0

    for py_file in Path(source_dir).rglob("*.py"):
        source = py_file.read_text()
        try:
            tree = ast.parse(source)
        except SyntaxError:
            continue

        for node in ast.walk(tree):
            if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                if not node.name.startswith("_"):  # Публичные функции
                    total += 1
                    if (node.body and isinstance(node.body[0], ast.Expr) and
                            isinstance(node.body[0].value, ast.Constant)):
                        documented += 1

    coverage = documented / total if total > 0 else 1.0
    print(f"Docstring coverage: {coverage:.1%} ({documented}/{total})")
    return coverage >= threshold

Сроки

  • Docstring генератор для существующей кодовой базы: 2–3 дня
  • OpenAPI документация для FastAPI/Django REST: 3–5 дней
  • Автоматическое обновление в CI/CD: 1 неделя
  • Архитектурная документация + wiki: 1–2 недели