Все чаще разработчикам приходится решать рутинные задачи автоматизации. Одна из наиболее типичных это автоматический разбор документов (счетов, актов, накладных).
Мы создадим простого AI-агента для обработки счетов, в ввиде pdf-файлов или картинок, и научим его превращать их в структурированные данные. Для этого будем использовать python и OpenAI API c structured_output (это позволяет жестко задать структуру ответа без лишних деталей).
Агент будет иметь следующие функции:
принимает PDF и изображения;
преобразует в изображения;
отправляет в OpenAI API;
получает нормализованный JSON;
валидирует данные;
сохраняет в CSV или базу;
работает как Telegram-бот.
Подготовка окружения
Установим зависимости необходимые для работы агента:
pip install openai python-dotenv pydantic pdf2image pillow python-telegram-botТакже потребуется Poppler для конвертации PDF → PNG (Ubuntu):
sudo apt install poppler-utilsСоздаём .env:
OPENAI_API_KEY=ваш_ключКонвертация PDF в изображения
Хотя OpenAI умеет воспринимать PDF, но надежнее будет подавать информацию как изображения. Причина конвертации в том, что PDF может быть битый или иметь другие проблемы с layout, а картинку LLM проще распознать.
from pdf2image import convert_from_path
def pdf_to_images(pdf_path):
pages = convert_from_path(pdf_path, dpi=200) # dpi 200 = оптимум по качеству/скорости
image_paths = []
for i, page in enumerate(pages):
path = f"/tmp/invoice_page_{i}.png"
page.save(path, "PNG")
image_paths.append(path)
return image_pathsИ функция, которая проверяет тип файла, и если нужно приводит к единому формату PNG
from PIL import Image
SUPPORTED_IMAGE_EXT = (".png", ".jpg", ".jpeg")
def prepare_images(path: str) -> list[str]:
path = path.lower()
# PDF → PNG
if path.endswith(".pdf"):
return pdf_to_images(path)
# Картинка → используем напрямую
if path.endswith(SUPPORTED_IMAGE_EXT):
img = Image.open(path).convert("RGB")
out_path = "/tmp/uploaded_image.png"
img.save(out_path, "PNG")
return [out_path]
raise ValueError("Unsupported file format")
Определяем структуру данных (Pydantic)
В Responses API можно заставить модель возвращать только строго заданную структуру данных. Для этого описывается класс (через Pydantic), который определяет, какой именно JSON разрешён.
from pydantic import BaseModel
class Invoice(BaseModel):
invoice_number: str | None
invoice_date: str | None
vendor: str | None
total_amount: float | None
currency: str | None
line_items: list[dict] | None # [{description, qty, price}]Если в модели None, то AI не имеет права возвращать лишнее. Это даёт стабильность.
Парсинг документа через OpenAI Responses API
Используем модель семейства gpt-5.x, они лучше всего работают с документами.
from openai import OpenAI
client = OpenAI()
def parse_invoice(image_path):
with open(image_path, "rb") as f:
binary = f.read()
response = client.responses.create(
model="gpt-5.1",
input=(
"Extract invoice fields and return only valid JSON. "
"If numeric values contain commas or spaces, normalize them. "
"If document is rotated, still parse correctly."
),
image={"data": binary, "mime_type": "image/png"},
structured_output=Invoice,
)
return response.output_as(Invoice)Почему это важно:
мы не используем чат-completion, только строгий responses;
модели серии gpt-5.* обеспечивают предсказуемость структуры;
минимизация галлюцинаций;
Валидатор
AI может ошибаться в мелочах, забыть сумму, подменить валюту, пропустить номер. Поэтому нужно добавить валидацию.
def validate_invoice(inv: Invoice) -> list[str]:
errors = []
if not inv.invoice_number:
errors.append("Missing invoice_number")
if not inv.total_amount:
errors.append("Missing total_amount")
if inv.currency and inv.currency.upper() not in ("USD", "EUR", "GBP", "BYN", "RUB"):
errors.append(f"Unknown currency '{inv.currency}'")
return errorsСохранение в CSV
Реализуем функцию для сохрания результатов в csv-файл, но по желанию, можно сохранять также в базу данных (Mysql/PostgreSQL)
import csv, os
def save_invoice(inv: Invoice, path="invoices.csv"):
is_new = not os.path.exists(path)
with open(path, "a", newline="") as f:
writer = csv.writer(f)
if is_new:
writer.writerow(inv.model_fields.keys())
writer.writerow(inv.model_dump().values())Основная функция обработки документа
Реализуем точку входа, где передаем путь к файлу, а все остальное код сделает сам.
def process_document(path: str):
images = prepare_images(path)
results = []
for img in images:
result = parse_invoice(img)
errors = validate_invoice(result)
if not errors:
save_invoice(result)
results.append((result, errors))
return resultsИнтегрируем агента в Telegram-бот
Для начала нам нужно создать бота в телеграм, для этого нужно выполнить следующие шаги:
Открываем поиск → вводим: @BotFather
Затем выполняем команду:
/newbotBotFather задаст два вопроса:
Название бота — любое имя.
username, заканчивающийся на
_bot
пример:invoice_reader_ai_bot
После создания BotFather выдаст API-ключ вашего бота:
Use this token to access the HTTP API:
1234567890:ABC123....XYZСоздаем минимальный Telegram сервер на python-telegram-bot
import os
from telegram import Update
from telegram.ext import ApplicationBuilder, MessageHandler, filters, ContextTypes
TOKEN = "ваш_bot_token"
async def handle_document(update: Update, context: ContextTypes.DEFAULT_TYPE):
file = update.message.document
# путь для временного хранения
path = f"/tmp/{file.file_name}"
tg_file = await file.get_file()
await tg_file.download_to_drive(path)
await update.message.reply_text("Файл получен, начинаю обработку...")
if file.file_name.lower().endswith(".pdf"):
results = process_pdf(path)
errors = [e for _, e in results if e]
if errors:
await update.message.reply_text(
"Есть ошибки:\n" + "\n".join(str(e) for e in errors)
)
else:
await update.message.reply_text("Готово. Данные извлечены.")
else:
await update.message.reply_text("Поддерживаются только PDF.")
app = ApplicationBuilder().token(TOKEN).build()
app.add_handler(MessageHandler(filters.Document.ALL, handle_document))
app.run_polling()
Почему такая интеграция удобна?
Telegram становится:
интерфейсом загрузки документов;
интерфейсом получения результата;
интерфейсом логгирования;
Реальные сценарии использования
Автоматизация бухгалтерии - можно загрузить пачку PDF, а агент сам нормализует данные.
Сервис SaaS "AI-парсер документов" - на базе этой реализации можно создать небольшой сервис, который будет автоматизировать рутинные задачи.