Автодокументация кода нейросетью: docstring, README и CI
Скрипт на Python находит функции без docstring, просит модель описать их и аккуратно вставляет текст в код. Плюс запуск в pre-commit и CI.
4 мин · · разработка · автоматизация
Автодокументация кода нейросетью решает скучную, но важную задачу: у функций без docstring появляется описание параметров и возвращаемого значения в едином стиле. Ниже — скрипт, который находит такие функции через модуль ast, генерирует текст через API и вставляет его так, чтобы файл остался валидным Python. Затем подключим его к pre-commit и GitHub Actions.
Где автоматизация помогает, а где нет
Хорошо получается:
- описать параметры, типы и возвращаемое значение по сигнатуре и телу функции;
- привести docstring к одному стилю (Google, NumPy или Sphinx);
- закрыть «долг» в старом модуле, где документации нет вообще.
Хуже получается то, чего нет в коде: зачем функция существует, какие у неё бизнес-ограничения, почему выбран именно такой алгоритм. Это знает автор, а не модель. Поэтому сгенерированный текст — черновик, который проходит обычное код-ревью.
Схема работы
- Разобрать файл через
astи найти публичные функции и методы без docstring. - Для каждой отправить в модель исходный код функции и требования к стилю.
- Вставить ответ первой строкой тела функции с правильным отступом.
- Проверить, что файл по-прежнему парсится, и только тогда сохранить.
Скрипт auto_doc.py
Нужны Python 3.9+, pip install openai и ключ fetch_... в переменной OPENAI_API_KEY. Скрипт принимает список файлов и правит их на месте.
#!/usr/bin/env python3
import ast
import os
import sys
import textwrap
from openai import OpenAI
client = OpenAI(
base_url="https://api.zukko.pro/v1",
api_key=os.environ["OPENAI_API_KEY"],
)
MODEL = os.environ.get("DOC_MODEL", "glm-5.3-flash")
На что обратить внимание:
- Вставка снизу вверх. Функции сортируются по номеру строки в обратном порядке, поэтому вставка в конце файла не сдвигает номера строк для функций выше.
- Отступ берётся у первой инструкции тела (
col_offset), а не жёстко «4 пробела»: так работают методы классов и вложенные функции. Декораторы и многострочные сигнатуры тоже не мешают — точка вставки считается от тела, а не от строкиdef. - Проверка
ast.parseперед записью. Если что-то пошло не так, скрипт упадёт с ошибкой, а исходный файл останется нетронутым. - Модель задаётся через
DOC_MODEL. Начните сglm-5.3-flashкак самой дешёвой и сравните на 20–30 функциях сclaude-sonnet-5илиgpt-5.6-sol: какой вариант меньше правят на ревью, тот и оставьте. Актуальный список моделей —GET https://api.zukko.pro/v1/modelsили страница «Модели» в кабинете.
Запуск: python3 auto_doc.py src/module.py src/utils.py, затем git diff — и ревью как у обычного изменения. Подробнее о клиенте — в руководстве по Python SDK.
README для модуля
Тот же подход годится для краткого описания модуля. Чтобы не платить за весь код, отправляйте только сигнатуры и существующие docstring:
def module_outline(path: str) -> str:
with open(path, encoding="utf-8") as f:
tree = ast.parse(f.read())
parts = [ast.get_docstring(tree) or ""]
for node in tree.body:
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
header = ast.unparse(node).splitlines()[0]
parts.append(f"{header}\n {ast.get_docstring(node) or ''}")
return "\n\n".join(parts)
def generate_readme_section(path: str) -> str:
prompt = (
"Write a short README section in Markdown for this Python module: "Запуск через pre-commit
Вызов API на каждом коммите замедляет работу, поэтому удобнее держать хук в ручной стадии manual — по документации pre-commit она не срабатывает автоматически. Файл .pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: auto-docstring
name: Generate missing docstrings
entry: python scripts/auto_doc.py
language: python
additional_dependencies: [openai]
types: [python]
stages: [manual]Запуск по требованию: pre-commit run auto-docstring --hook-stage manual --all-files.
Запуск в GitHub Actions
Workflow для pull request: генерирует docstring только в изменённых файлах и коммитит результат в ветку PR. Ключ хранится в секрете репозитория ZUKKO_API_KEY.
name: auto-docstrings
on:
pull_request:
paths:
- "**.py"
permissions:
contents: write
jobs:
docstrings:
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latestДетали: ref: github.head_ref нужен, чтобы пушить в ветку PR, а не в служебный merge-коммит; условие if пропускает PR из форков, где секреты недоступны; коммит создаётся, только если файлы изменились. Для CI заведите отдельный ключ с дневным лимитом и списком разрешённых моделей — см. лимиты ключей.
Сколько это стоит
Допущения: промпт с требованиями к стилю — около 100 токенов, функция — около 300, итого 400 токенов входа; docstring — около 150 токенов выхода. На 1000 функций: 400 000 входа и 150 000 выхода.
| Модель | Формула на 1000 функций | Итого | На 5000 функций |
|---|---|---|---|
glm-5.3-flash ($0.04 / $0.04) | (400 000 × 0.04 + 150 000 × 0.04) / 1 000 000 | $0.022 | $0.11 |
claude-sonnet-5 ($0.3 / $0.3) | (400 000 × 0.3 + 150 000 × 0.3) / 1 000 000 | $0.165 | $0.825 |
Длинные функции стоят пропорционально дороже: функция на 2000 токенов съест в пять раз больше входа. Чтобы попробовать на своём проекте, создайте ключ в кабинете — порядок подключения описан в быстром старте.
Частые вопросы
Может ли модель описать функцию неверно?
Да, особенно если поведение зависит от внешнего состояния или неочевидных побочных эффектов. Поэтому сгенерированные docstring идут в обычный PR и проходят ревью. В промпте есть требование «не выдумывать параметры и поведение» — оно снижает риск, но не убирает его.
Как обновлять docstring, когда меняется код?
Скрипт трогает только функции без docstring, поэтому существующие тексты он не перезапишет. Для обновления храните хеш тела функции рядом с кодом (например, в JSON-файле) и перегенерируйте docstring, когда хеш изменился:
import hashlib
def body_hash(node: ast.AST) -> str:
body = node.body[1:] if ast.get_docstring(node) is not None else node.body
text = "\n".join(ast.unparse(stmt) for stmt in body)
return hashlib.sha256(text.encode()).hexdigest()Может ли вставка сломать код?
Скрипт проверяет результат через ast.parse перед записью, поэтому синтаксически сломанный файл он не сохранит. Сам docstring на выполнение функции не влияет — это строка, которая попадает в атрибут __doc__. Функции, у которых тело записано на одной строке с def, скрипт пропускает.
Учитываются ли type hints?
Да: модель получает исходный код функции целиком, вместе с аннотациями типов, и может описать параметры с их типами. Если аннотаций нет, типы в docstring будут предположением — проверяйте их на ревью.
zukko
Платите в 25×
меньше
за Claude Code & ChatGPT
Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.
Похожие статьи
- Перевод субтитров нейросетью: SRT без сбитых таймкодовГлавный риск при переводе SRT нейросетью — сломанная структура файла. Показываем схему, где таймкоды не попадают к модели, и скрипт на Python с проверкой.
- Редактирование текстов нейросетью: корректура, стиль и сокращениеРазные виды редактуры требуют разных промптов. Показываем формулировки для корректуры, стиля и сокращения, как получать правки с объяснением и не терять авторский голос.
- Нейросеть для написания скриптов: Bash, PowerShell и PythonМодель быстро пишет скрипты для рутины, но запускать их вслепую нельзя. Показываем, как ставить задачу, требовать безопасный режим dry-run и проверять результат до запуска.
- Dify в Docker: установка, первичная настройка и подключение моделейСтавим Dify через Docker Compose, создаём администратора и подключаем модели Claude, GPT и DeepSeek по одному ключу. Отдельно — что делать, если контейнеры не стартуют.