Приём одного сотрудника на работу — это 40 минут ручного ввода: оператор перепечатывает в 1С паспорт, водительское удостоверение, диплом, трудовую книжку, а у иностранцев документов ещё больше. Ошибиться в серии паспорта или дате — легко, а отвечать за это потом бухгалтерии.
Задачу «сфотографировал документ → получил заполненную карточку в 1С» сегодня закрывает связка Vision-LLM + OCR. Причём — полностью локально, без отправки персональных данных в облако. Ниже разберём, как это устроено: подходы к распознаванию через языковые модели, гибридный pipeline YOLO + LLM, живые примеры кода и честный разговор про точность. В конце — где научиться собирать такой проект с нуля.

Почему не облачный сервис распознавания
Соблазн отправить скан паспорта в облачный OCR-сервис большой — там всё «из коробки». Но паспорт, водительское, диплом — это персональные данные. По 152-ФЗ и требованиям к трансграничной передаче отправлять сканы документов на зарубежные серверы нельзя. Значит, распознавание должно жить внутри контура компании.
Хорошая новость: современные открытые Vision-модели (Qwen3-VL, семейство *-VL, специализированные OCR-модели) запускаются на локальном железе и по качеству близки к облачным. В кейсе, который мы разбираем, крутится Qwen3-VL на 27 млрд параметров в кванте UD-Q8 — она помещается на связку из трёх видеокарт (3090 + две 5070 Ti), а веса и контекст распределяются по картам через tensor split.
Итог: локальная нейросеть даёт «облачное» качество распознавания документов, не нарушая закон о персональных данных.
Три подхода к распознаванию документов
Прежде чем собирать систему, полезно понять, чем распознавание через LLM отличается от классического OCR. Сравните три подхода — переключайте критерии и смотрите, кто выигрывает:
Коротко:
- Классический OCR (Tesseract, шаблоны по координатам). Быстрый и дешёвый (работает на CPU), но ломается на любом новом макете и почти не берёт рукопись. Хорош там, где бланк строго фиксирован.
- Чистая Vision-LLM. Отправляем всю картинку в мультимодальную модель и промптом просим вернуть JSON. Не зависит от расположения полей, читает рукописную кириллицу, легко меняется под новые поля. Минусы — тяжёлая (нужен GPU) и иногда «плывёт» на мелком тексте вроде вертикального номера паспорта.
- Гибрид YOLO + LLM. Сначала детектор находит и вырезает поля, потом LLM читает каждый мелкий кроп отдельно. Именно этот подход даёт максимальную точность на «сложных» полях и лежит в основе разбираемого проекта.
Общий принцип: как LLM «читает» документ
Мультимодальная модель принимает на вход картинку и текстовый промпт, а на выходе отдаёт текст. Весь фокус — заставить её вернуть строго структурированный JSON, а не свободный рассказ. Для этого в промпте:
- Задаём роль и задачу («ты извлекаешь данные из паспорта РФ»).
- Явно перечисляем поля и их формат (даты —
ДД.ММ.ГГГГ, серия/номер — цифрами). - Просим вернуть только JSON по заданной схеме и ничего лишнего.
Вот минимальный пример вызова локальной модели через Ollama (OpenAI-совместимый API):
import base64, json
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
def encode(path: str) -> str:
with open(path, "rb") as f:
return base64.b64encode(f.read()).decode()
PROMPT = """Ты извлекаешь данные из фотографии паспорта РФ.
Верни СТРОГО JSON по схеме, без пояснений:
{"series": "0000", "number": "000000", "surname": "", "name": "",
"patronymic": "", "birth_date": "ДД.ММ.ГГГГ", "issued_by": ""}
Если поле не читается — верни пустую строку."""
resp = client.chat.completions.create(
model="qwen3-vl:27b",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": PROMPT},
{"type": "image_url",
"image_url": {"url": f"data:image/jpeg;base64,{encode('passport.jpg')}"}},
],
}],
temperature=0, # нам нужен детерминизм, не креатив
response_format={"type": "json_object"},
)
data = json.loads(resp.choices[0].message.content)
print(data)
# {'series': '4509', 'number': '123458', 'surname': 'Иванов', ...}
Ключевые детали: temperature=0 (распознавание — это не творчество), response_format=json_object (модель обязана вернуть валидный JSON), и явная схема прямо в промпте. Этого уже достаточно, чтобы распознать водительское удостоверение хорошего качества почти идеально.
Гибридный pipeline: детекция → кроп → LLM
Чистая LLM спотыкается на мелких полях. Решение — сначала найти поля детектором YOLO11 OBB (ориентированные рамки — ловят текст даже под наклоном), вырезать каждое поле в отдельную картинку и только потом отдавать в языковую модель. Пройдите шаги пайплайна для разных документов:
Детекция полей — отдельный микросервис на YOLO. Он обучается на своём датасете, размеченном в CVAT, и возвращает координаты полей:
from ultralytics import YOLO
model = YOLO("weights_dir/passport/best.pt") # дообученная модель полей паспорта
result = model(image)[0]
crops = []
for box in result.obb: # OBB — oriented bounding boxes
cls = model.names[int(box.cls)] # 'series_number', 'surname', 'name'...
xy = box.xyxyxyxy.cpu().numpy().reshape(-1, 2)
crops.append({"field": cls, "image": crop_by_polygon(image, xy)})
Каждый кроп — это уже маленькая понятная картинка (только серия и номер, только фамилия). LLM читает такой фрагмент заметно точнее, чем весь разворот сразу. Это и есть главный лайфхак гибрида.
Живая демонстрация: что возвращает модель
Ниже — симуляция ответа Vision-LLM на реальных примерах. Нажмите «Распознать» и посмотрите, как поля заполняются с оценкой уверенности. Обратите внимание: на плохом скане модель честно ошибается — и такие поля система помечает для оператора, а не выдаёт за истину:
Сам эндпоинт распознавания — обычная ручка FastAPI, которую одинаково легко дёрнуть из 1С, с сайта или из мобильного приложения:
from fastapi import APIRouter, UploadFile
router = APIRouter(prefix="/api/v1/ocr")
@router.post("/passport/yolo/")
async def recognize_passport(file: UploadFile):
image = await read_image(file)
crops = detect_fields(image) # YOLO: нарезка полей
fields = {}
for crop in crops:
fields[crop["field"]] = read_with_llm(crop["image"], crop["field"])
return normalize(fields) # даты, регистр, валидация
Пример ответа для 1С:
{
"doc_type": "passport_rf",
"series": "4509",
"number": "123458",
"surname": "Иванов",
"name": "Пётр",
"needs_review": ["number"]
}
Поле needs_review — ключевое для продакшена. Оператор в 1С видит заполненную форму и сверяет только помеченные поля, а не набирает всё вручную. Приём одного человека сокращается с 40 до примерно 10 минут.
Рукописный текст и «умная» постобработка
Самый сложный кейс — диплом: часть данных вписана от руки, макеты у всех разные. Здесь классический OCR бессилен, а Vision-LLM читает рукописную кириллицу — пусть и с ошибками на редких фамилиях и названиях вузов (например, «Истринский» модель может прочитать как «Решинский»).
Лечится это постобработкой через эмбеддинги: распознанное название ищем по справочнику всех учебных заведений и заменяем на ближайшее реальное.
# Справочник вузов заранее переведён в векторы (эмбеддинги)
query_vec = embed(recognized_name) # "Решинский пед. колледж"
best = max(institutions, key=lambda x: cosine(query_vec, x.vec))
if cosine(query_vec, best.vec) > 0.82:
recognized_name = best.title # → "Истринский пед. колледж"
Тот же приём работает и для кодов специальностей: модель извлекает название квалификации, а мы находим по нему официальный код в справочнике. Так система сама исправляет часть своих же ошибок распознавания.
Про железо: что нужно для локальной модели
Vision-LLM на 27B — это GPU. В разбираемом кейсе модель в кванте UD-Q8 распределена по трём картам (3090 + 5070 Ti + 5070 Ti) через tensor split, с контекстом 65 000 токенов (связка тянет и 260 000). Если хочется дешевле — можно взять модель поменьше (4B–8B) или использовать гибрид, где в LLM уходит не весь лист, а только мелкие кропы: нагрузка на видеопамять заметно падает.
Точность честно: человек в контуре
Ни одна OCR-система не даёт 100% на «боевых» фото. Поэтому правильная архитектура — human-in-the-loop: модель распознаёт и оценивает свою уверенность, спорные поля помечает, а финальное решение принимает оператор. Это не «костыль», а норма для документооборота: даже 90% автозаполнения экономят колоссальное время, если оставшиеся 10% удобно проверить и поправить.
Где научиться собирать такое с нуля
Проект из этой статьи построен на основе учебного проекта, который разбирается пошагово — от локального запуска модели и промптинга до детекции YOLO, разметки в CVAT, дообучения и интеграции с 1С — на курсе «ChatGPT и нейросети для 1С». Там вы соберёте свой pipeline распознавания документов и научитесь встраивать LLM в реальные бизнес-процессы.
Частые вопросы
Можно ли распознавать документы полностью локально, без интернета?
Да. Открытые Vision-модели (Qwen3-VL и аналоги) запускаются на своём сервере с GPU. Персональные данные не покидают контур компании — это соответствует 152-ФЗ.
Насколько точно нейросеть распознаёт паспорт и водительское удостоверение?
На хорошем скане водительское распознаётся практически идеально. У паспорта слабое место — мелкий вертикальный номер; гибрид YOLO + LLM вырезает это поле отдельно и повышает точность. Спорные поля система помечает для проверки оператором.
Читает ли модель рукописный текст?
Да, Vision-LLM распознаёт рукописную кириллицу — например, рукописные поля в дипломе. На редких фамилиях и названиях возможны ошибки, которые лечатся постобработкой через эмбеддинг-поиск по справочнику.
Как результат распознавания попадает в 1С?
Сервис возвращает JSON, который одинаково легко подтянуть в 1С, на сайт или в мобильное приложение. В 1С оператор видит заполненную карточку и сверяет только помеченные поля.
Что лучше — классический OCR или LLM?
Для строго фиксированных бланков быстрее и дешевле классический OCR. Для потока разных документов (паспорта, ВУ, дипломы, рукопись) выигрывает LLM или гибрид YOLO + LLM — он не зависит от макета и читает рукописный текст.
Хотите разобраться в разработке AI-проектов для 1С глубже — приходите на курс «ChatGPT и нейросети для 1С».