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

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

Напрямки AI-розробки

Етапи розробки AI-рішення

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1284
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1196
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    901
  • 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 = """You are a technical writer specializing in Python documentation.
Write docstrings in Google Style format:

```python
def function(param: type) -> type:
    \"\"\"Brief description in one line (imperative mood).

    Detailed description if needed. Explain WHAT the function does,
    not HOW. Mention non-trivial algorithms or important side effects.

    Args:
        param: Parameter description. Don't specify type — it's in annotation.

    Returns:
        What it returns. Don't specify type.

    Raises:
        ValueError: When and why it arises.

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

Rules:

  • Brief line: imperative mood (Calculates/Returns/Creates, not Calculate/Return)
  • Don't repeat function name and types from annotations
  • Add Example only for non-trivial functions
  • For simple getter/setter — minimal documentation"""

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"Write docstring for this function. Return only docstring (triple quotes), no function code:\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"""Analyze FastAPI router and add complete OpenAPI documentation:
- summary and description for each endpoint
- description for all parameters (Path, Query, Body)
- response_model with examples
- HTTPException with codes and descriptions

FastAPI router:
```python
{router_source}

{f"Existing models:{chr(10)}python{chr(10)}{existing_models}{chr(10)}" if existing_models else ""}

Return improved router with complete documentation.""" }] )

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"""Write README section for this Python module.

Structure:

Module Name

Brief description (1-2 sentences).

Usage

# Example code

API

Table of public functions/classes with descriptions.

Requirements

Dependencies if any.

Module code:

{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"""Write architecture documentation for project.

Project structure:

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


Key files:
{key_content}

Include:
1. Architecture overview (2-3 paragraphs)
2. Main components and their roles
3. Interaction diagram (text)
4. Key patterns used in code
5. Entry points and startup

Format: Markdown for 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 тижні