Как я сделал LLM-сервис, который понимает буровые сводки. Data Engineering.. Data Engineering. fastapi.. Data Engineering. fastapi. llm.. Data Engineering. fastapi. llm. nlp.. Data Engineering. fastapi. llm. nlp. oil.. Data Engineering. fastapi. llm. nlp. oil. openai.. Data Engineering. fastapi. llm. nlp. oil. openai. pydantic.. Data Engineering. fastapi. llm. nlp. oil. openai. pydantic. python.. Data Engineering. fastapi. llm. nlp. oil. openai. pydantic. python. rnd.. Data Engineering. fastapi. llm. nlp. oil. openai. pydantic. python. rnd. Будущее здесь.

Привет! Меня зовут Стас, я занимаюсь R&D в компании ROGII.

Я пришёл в ROGII после нескольких лет работы «в поле» — от тундры Уренгойских месторождений до Сахалина. Там я понял, что буровые данные живут в хаосе: у каждого вендора — свой формат, у каждой скважины — свой стиль отчёта.
Когда я оказался в компании, которая консолидирует буровые данные в облаке, задача встала ребром: нужно научить машину понимать суточные рапорты так же, как это делает инженер.

Мы собрали 507 PDF‑файлов (всего 14 678 страниц) и выделили 23 типа отчётов по признаку компании и структуры.
Но традиционные подходы: ручной ввод, регулярки, rule‑based и классический NLP — оказались или неэффективными, или нежизнеспособными.
Тогда я обратился к LLM.

Как я сделал LLM-сервис, который понимает буровые сводки - 1

В этой статье я расскажу, как построил микросервис, с какими проблемами столкнулся, как их решил и что планирую дальше.


Данные и цели

Для описания данных мы решили не изобретать формат, а использовать существующий стандарт WITSML — отраслевой протокол обмена буровыми данными.
В процессе обсуждения с бизнесом список полей рос, и к извлечению данных добавился AI‑summary операций — выделение ключевых событий, на которые должен обратить внимание инженер по бурению.

Архитектура сервиса

Микросервис построен на Python с использованием FastAPI и httpx. CPU-bound задачи по работе с PDF выполняются через pdfplumber, а взаимодействие с LLM реализовано на OpenAI Python SDK. Файлы хранятся в S3-совместимом хранилище, а результаты возвращаются в формате JSON.

Сервис состоит из двух веток:

  1. Extractor – извлекает структуру данных и параметры операций.

  2. Summarizer – формирует AI-инсайты по результатам экстракции.

Так как сервис должен был интегрироваться с почтой и корпоративным дашбордом, я изначально закладывал асинхронную архитектуру: клиент получает 200 OK, а обработка продолжается в фоне через пул процессов.

Интересно, что отказ от специализированных SDK для S3 (например, boto3) позволил легко перенести загрузочный модуль на Azure Blob Storage без переписывания кода – решение, которое оказалось неожиданно дальновидным.

Extractor: из PDF в структурированные данные

Экстрактор проходит несколько этапов:

  1. Загрузка файла из S3.

  2. Извлечение текстового слоя. Так как большинство рапортов сохраняются с уже встроенным OCR-слоем, мы не выполняем дополнительное распознавание изображений. Для извлечения текста и таблиц используется pdfplumber, который работает напрямую с текстовым слоем PDF. Эксперименты показали, что LLM воспринимает табличные документы точнее в HTML, чем в Markdown.

  3. LLM-процессинг. Реализован базовый класс для запросов к модели с поддержкой function calling и structured output.

Рапорты приходят на разных языках (английский, русский, испанский), с различными форматами дат. Первым шагом выполняется region recognition – определение языка документа. Затем извлекаются даты отчёта и начала строительства скважины, нормализуемые в ISO-формат.

Основная сложность при работе с буровыми суточными сводками – это операции.
Таблицы в рапортах крайне непредсказуемы: в одних указывается начало или конец операции, в других только длительность, часто значения разбросаны между несколькими столбцами.

Наивная генерация через LLM приводит к галлюцинациям: модель «додумывает» поля, которых нет, либо некорректно интерпретирует дублирующиеся значения.

Чтобы этого избежать, я применил подход с использованием structured output. Суть подхода — не позволять модели «думать свободно», а направлять её рассуждение в контекст заранее заданной схемы данных. Фактически, схема (в моём случае — Pydantic‑модель) становится каркасом мышления модели: все ответы она должна укладывать в определённую структуру и типизацию.

class Operation(BaseModel):
    dTimStart: str
    dTimEnd: str
    duration: float 
    depthReached: float | None
    operationCode: str | None
    comments: str | None

Эта схема определяет не только формат вывода, но и логику рассуждения модели. В следующих проектах я активно продолжаю развивать этот подход. Для некоторых груп полей LLM сначала получает контекст таблицы, затем список функций — «инструмент» с аргументами, с заданным описаннием.

Модель не может вернуть произвольный текст — она должна обосновать каждое поле. Такой подход уменьшил количество галлюцинаций в операциях примерно на 40-60%, особенно при сложных таблицах, где встречаются пустые поля, объединённые ячейки и условные обозначения.

Отдельная головная боль – глубина забоя.
Объяснить модели, что глубина инструмента ≠ глубина скважины, оказалось не тривиально. Некоторые операции (например, спуско-подъёмные) не меняют забой вовсе, а при цементировании он даже уменьшается. LLM изначально не понимает этих контекстных зависимостей, поэтому я встроил семантические правила валидации ответа прямо в схему рассуждения.

class ValidateOperation(BaseModel):
	d_tim_start: str = Field()
	d_tim_end: str = Field()
	depth_reached: float = Field()
	comments: str | None = Field(
		description="Return all relevant data on the operation that you can find"
		)
	duration: float
	non_productive_time_identified: bool

class ChainOfThought(BaseModel):
	completeness: bool = Field(
		description="The operations follow each other in sequence without time gaps and cover a period of 24 hours"
		)
	details: bool = Field(
		description="Availability of additional columns in the operations table to be included in the `description` field"
		)

class ValidateOperationsResponse(BaseModel):
	chain_of_thought: ChainOfThought
	operation: list[ValidateOperation]

Это не просто валидация – это управляемое рассуждение по схеме, где логика заложена на уровне данных.

Summarizer: AI-инсайты для инженеров

Summarizer формирует AI-инсайты: короткие выжимки из суточного отчёта, где инженер видит, что произошло важного за сутки – без чтения десятков страниц.

Модель не просто суммирует текст, а связывает инсайты с временными рамками и глубиной скважины. Это превращает статический отчёт в интерактивный слой данных для анализа. Чтобы не допустить ошибок вычислений, агрегирующие функции выполняются через tools, а не “чёрным ящиком” LLM.

Результаты и метрики

Дашборд с operations и summary

Дашборд с operations и summary

Сервис работает в продакшене уже три месяца.
Контроль затрат ведётся через дашборды OpenAI, а ключевая метрика – фидбэк пользователей.
Следующий этап – внедрение LLM-tracing через Langfuse и CI-job в пайплайн для автоматического контроля качества извлечения. Так же в планах развивать пайплайн экстракции, улучшать резолверы дат и глубин, добавить метрики качества.

Полный вид дашборда с виджетом AI Insights

Полный вид дашборда с виджетом AI Insights

За время работы над этим сервисом я понял, что LLM‑интеграция — это не магия, а инженерная дисциплина.

Чтобы модель работала надёжно, ей нужно дать структуру, контекст и границы рассуждения.

В этом смысле схема вывода ответа стала для меня способом не просто парсить отчёты, а формализовать мышление инженера.

Думаю, в ближайшие годы именно такие подходы сделают LLM‑системы по‑настоящему промышленными.

Автор: Devenir-Glorieux

Источник

Rambler's Top100