Главная / Статьи

Управление потоками Docling через HTTP: от настройки проекта до индексированных фрагментов

Пошаговое изучение REST API Docling Pipelines: запуск сервера, обнаружение операторов, проверка и выполнение DAG для ввода данных, а также просмотр телеметрии его выполнения.

Издатель: REACTAPP.TOP15 мин чтения3355 слов

Источники: **A Hands-On Guide to the Docling Pipelines REST API**

Преобразование папки с файлами PDF и Word в поисковые векторные части обычно требует специального кода для извлечения данных, разбиения их на части, встраивания информации и создания индексов. Docling Pipelines объединяет эти этапы в операторы, работающие через REST-сервер на базе FastAPI, что позволяет любому клиенту — будь то сервис на Node.js или интерфейс администратора — создавать модульные конвейеры в формате DAG, проверять их корректность и запускать асинхронные операции посредством HTTP. В этом руководстве сервер запускается с использованием Ollama и OpenSearch, после чего демонстрируется весь цикл обработки запроса, начиная с создания проекта и заканчивая отображением статистики выполнения операций на каждом узле.

Подготовка вспомогательных сервисов

Сервер координирует выполнение задач, но процессы встраивания данных и хранения происходят в других местах: здесь используется Ollama для работы с моделями, а OpenSearch служит хранилищем векторов. Сначала необходимо запустить именно эти сервисы.

Самый быстрый способ — это файл QUICKSTART.md проекта, который за несколько минут настраивает Ollama, векторную базу данных и необходимые контейнеры. Агенты для кодирования на основе ИИ, такие как Cursor, Claude Code или Copilot, тоже могут выполнять настройку и проверку; в любом случае необходимо убедиться, что каждый сервис отвечает.

Выбор хранилища векторов

Это архитектурное решение. OpenSearch подходит, когда требуется гибридный поиск, сочетающий поиск по ключевым словам с использованием плотных векторов. Milvus предназначен для специализированной, высокоскоростной инфраструктуры для очень больших коллекций векторов. В этом руководстве используется OpenSearch.

Запуск сервера API

Активируйте виртуальную среду, в которой установлен пакет, и запустите прилагаемую команду:

source .venv/bin/activate
docling-pipelines-api

Для горячей замены кода во время разработки запустите приложение непосредственно с помощью uvicorn, привязав его ко всем интерфейсам на порту 8080:

uvicorn docpipe.api.main:app --reload --host 0.0.0.0 --port 8080

Затем становятся доступны четыре URL:

  • http://localhost:8080/health для проверки работоспособности;
  • http://localhost:8080/api/v1/docs для интерактивного интерфейса Swagger UI;
  • http://localhost:8080/api/v1/redoc для документации ReDoc;
  • http://localhost:8080/api/v1/openapi.json для сырой схемы OpenAPI.

В TypeScript можно передавать документ OpenAPI в генератор клиента вместо ручного определения типов запросов.

Переменные окружения, влияющие на сервер

Установите их перед запуском для настройки поведения:

  • DS_LOG_LEVEL определяет уровень детализации логов. По умолчанию он равен INFO, но может принимать значения DEBUG, INFO, WARNING или ERROR.
  • DOCPIPE_STORAGE_BACKEND указывает место хранения метаданных: duckdb (по умолчанию, локально) или postgres.
  • CORS_ORIGINS содержит список источников, разрешенных к вызову API из браузерного фронт-энда. По умолчанию это http://localhost:3000.
  • OLLAMA_HOST направляет операции с встраиваемыми моделями и большими языковыми моделями к Ollama. По умолчанию это http://localhost:11434; измените его, если Ollama работает на другом порту или на другой машине.

Исследование концовок в Swagger UI

Откройте в браузере http://localhost:8080/api/v1/docs; локально вход не требуется. Раскройте нужный эндпоинт, нажмите Попробовать и отправьте приведённые ниже данные без необходимости писать клиентское приложение.

Краткий обзор жизненного цикла запроса

Полный цикл включает семь вызовов, начиная с создания проекта и заканчивая сводкой результатов его выполнения:

1. Create Project ──▶ 2. Discover Operators ──▶ 3. Validate Flow ──▶ 4. Create Flow ──▶ 5. Execute Run ──▶ 6. Fetch Logs ──▶ 7. Summary

Шаг 1: создание проекта

Проекты группируют потоки задач и отслеживают суммарную статистику их выполнения. Создайте проект с помощью команды POST /api/v1/projects:

{
  "name": "Sample Docling Pipeline Project",
  "description": "Flows for processing invoice documents",
  "tags": ["finance", "pdf", "q3"]
}

Поля:

  • name — обязательное строковое поле длиной от 1 до 255 символов, которое уникально идентифицирует проект.
  • description — необязательное краткое описание цели проекта.
  • tags — необязательный массив строк для классификации проекта.

При успешном выполнении возвращается код 201 Created вместе с сгенерированным project_id, значением flow_count, равным нулю, временными метками и ссылкой href на ресурс:

{
  "project_id": "84c718b2-5f36-4d2c-9a4f-561234bcde01",
  "name": "Sample Docling Pipeline Project",
  "description": "Flows for processing invoice documents",
  "tags": ["finance", "pdf", "q3"],
  "flow_count": 0,
  "created_on": "2026-09-07T10:00:00Z",
  "modified_on": "2026-09-07T10:00:00Z",
  "href": "/api/v1/projects/84c718b2-5f36-4d2c-9a4f-561234bcde01"
}

Шаг 2: поиск операторов и моделей

Вместо того чтобы угадывать названия операторов и их параметры, запросите информацию у сервера.

Метаданные операторов

Запрос GET /api/v1/operators/metadata возвращает подробную схему каждого оператора, включая более 20 встроенных операторов и любые внешние операторы, которые вы зарегистрировали. Параметр запроса internal_features по умолчанию равен false.

В каждой записи features описывает столбцы, которые генерирует оператор, required_features — столбцы, необходимые ему от источника данных, а attributes — его параметры с типами и значениями по умолчанию. Оператор extract выдает content и doc_id_hash; оператор chunker требует content:

{
  "extract_operator": {
    "label": "Extract Operator",
    "category": "Extract",
    "description": "Extracts structured content from documents using Docling",
    "features": {
      "content": {
        "name": "Document Content",
        "description": "The markdown content extracted from the document",
        "type": "string",
        "required": true
      },
      "doc_id_hash": {
        "name": "Hash ID",
        "description": "Hash ID of the document row",
        "type": "string",
        "required": true
      }
    },
    "required_features": [],
    "attributes": {
      "text_extraction_mode": {
        "type": "string",
        "description": "Text extraction strategy (docling_library or docling_serve)",
        "required": true,
        "default": "docling_library"
      },
      "entity_extraction_mode": {
        "type": "string",
        "description": "Entity extraction strategy (litellm, watsonx, docling, or none)",
        "required": false,
        "default": "none"
      }
    }
  },
  "chunker": {
    "label": "Chunker Operator",
    "category": "Functional",
    "description": "Splits documents into smaller semantic or fixed-size passages",
    "features": {
      "content": { "name": "Chunk Content", "type": "string", "required": true }
    },
    "required_features": ["content"],
    "attributes": {
      "chunker_type": {
        "type": "string",
        "description": "Chunking strategy (fixed, semantic, or hybrid)",
        "required": false,
        "default": "hybrid"
      }
    }
  }
}

Сравнение required_features с результатами работы источника данных позволяет до отправки подтвердить, что каждый узел получит необходимые ему столбцы.

Модели поставщиков

GET /api/v1/providers/{provider}/models запрашивает информацию у действующего поставщика (ollama или watsonx), поэтому отсутствие модели или неправильно указанный тег становятся очевидными ещё до начала работы:

curl -s http://localhost:8080/api/v1/providers/ollama/models | jq

Каждая модель указывается с именем, тегом, датой изменения и размером в байтах:

{
  "provider": "ollama",
  "models": [
    {
      "name": "nomic-embed-text:latest",
      "model": "nomic-embed-text:latest",
      "modified_at": "2026-08-15T12:00:00Z",
      "size": 274302450
    },
    {
      "name": "llama3:8b",
      "model": "llama3:8b",
      "modified_at": "2026-08-20T14:30:00Z",
      "size": 4661224676
    }
  ]
}

Шаг 3: проверка работы потока перед его сохранением

POST /api/v1/validation/validate_flow проверяет структуру DAG на наличие некорректных параметров и нарушенных зависимостей без её сохранения или выполнения.

global_config задаёт параметры для всего процесса (столбец документа, локальная обработка, хранение в памяти, принудительная повторная обработка). flow содержит список узлов, каждый из которых имеет type, уникальное name, config и массив depends_on, указывающий на предыдущие узлы в цепочке. Последовательность действий:

  • загружает файлы PDF, TXT и DOCX из папки ./sample_documents на локальной файловой системе;
  • извлекает текст с помощью библиотеки Docling, пропуская извлечение сущностей;
  • делит содержимое на фрагменты по 512 единиц с перекрытием в 50 единиц;
  • встраивает каждый фрагмент с помощью LiteLLM, обращаясь к совместимому с OpenAI конценту Ollama с использованием модели nomic-embed-text.
  • Записывает векторы в индекс OpenSearch с использованием движка FAISS, алгоритмов HNSW и расстояния L2 с 768 измерениями, преобразуя характеристики пайплайна в поля индекса через feature_mappings.
  • {
      "flow_name": "complete-document-pipeline",
      "global_config": {
        "doc_column": "content",
        "disable_validation": true,
        "force_ingest": true,
        "storage": "in-memory",
        "execute_type": "local"
      },
      "flow": [
        {
          "type": "ingest_source",
          "name": "ingest_source_filesystem",
          "config": {
            "provider": "filesystem",
            "connection_params": {
              "paths": ["./sample_documents"]
            },
            "include_filter": "pdf,txt,docx"
          }
        },
        {
          "type": "extract_operator",
          "name": "extract_with_docling",
          "config": {
            "text_extraction": {
              "provider": "docling_library",
              "doc_column": "content"
            },
            "entity_extraction": {
              "provider": "none"
            }
          },
          "depends_on": ["ingest_source_filesystem"]
        },
        {
          "type": "chunker",
          "name": "simple_chunker",
          "config": {
            "chunk_type": "simple",
            "chunk_size": 512,
            "chunk_overlap": 50,
            "retain_original_content": false
          },
          "depends_on": ["extract_with_docling"]
        },
        {
          "type": "embeddings",
          "name": "ollama_embeddings",
          "config": {
            "provider": "litellm",
            "provider_config": {
              "model_id": "openai/nomic-embed-text",
              "api_base": "http://localhost:11434/v1",
              "api_key": "<ollama-api-key>"
            },
            "embeddings_column": "embeddings",
            "overlap_ratio": 0.1
          },
          "depends_on": ["simple_chunker"]
        },
        {
          "type": "vectordb",
          "name": "opensearch_vector_store",
          "config": {
            "provider": "opensearch",
            "doc_id_column": "doc_id_hash",
            "embeddings_column": "embeddings",
            "vector_dimension": 768,
            "create_index": true,
            "provider_config": {
              "index_name": "sample-documents-index",
              "host": "localhost",
              "port": 9200,
              "username": "${OPENSEARCH_USERNAME}",
              "password": "${OPENSEARCH_PASSWORD}",
              "use_ssl": false,
              "verify_certs": false,
              "batch_size": 100,
              "engine": "faiss",
              "algorithm": "hnsw",
              "space_type": "l2"
            },
            "feature_mappings": [
              { "feature_name": "doc_id_hash",  "mapped_column_name": "pk" },
              { "feature_name": "content",      "mapped_column_name": "text" },
              { "feature_name": "name",         "mapped_column_name": "doc_name" },
              { "feature_name": "path",         "mapped_column_name": "file_path" },
              { "feature_name": "embeddings",   "mapped_column_name": "vector_embeddings" },
              { "feature_name": "chunk_id",     "mapped_column_name": "chunk_id" },
              { "feature_name": "chunk_index",  "mapped_column_name": "chunk_index" }
            ]
          },
          "depends_on": ["ollama_embeddings"]
        }
      ]
    }
    

    Некоторые детали имеют значение. Значение vector_dimension (768) должно совпадать с размером выходных данных модели эмбеддингов. Учетные данные представлены в виде заменителей вида ${OPENSEARCH_USERNAME} и <ollama-api-key>; настоящие секреты следует хранить в переменных окружения или специальных хранилищах секретов, а не в сохраненных потоках. Отключение use_ssl и verify_certs допустимо локально, но не на общедоступном кластере. Кроме того, инструмент разбиения данных использует chunk_type: "simple", тогда как в документах метаданных указаны значения chunker_type — fixed, semantic и hybrid; необходимо проверить, что возвращает ваш сервер.

    При успешной обработке возвращается код 200 OK, значение is_valid: true, пустые массивы errors и warnings, а также информация о количестве узлов:

    {
      "is_valid": true,
      "errors": [],
      "warnings": [],
      "node_count": 5
    }
    

    Шаг 4: регистрация потока

    Зарегистрируйте проверенную конфигурацию с помощью запроса POST /api/v1/flows (значение is_elyra по умолчанию равно false). В теле запроса указывается тот же DAG вместе с описанием и метками:

    {
      "flow_name": "complete-document-pipeline",
      "description": "Complete document processing pipeline: Ingest -> Extract -> Chunk -> Embed -> Store in OpenSearch",
      "tags": ["docling", "embeddings", "opensearch"],
    
      "global_config": {
        "doc_column": "content",
        "disable_validation": true,
        "force_ingest": true,
        "storage": "in-memory",
        "execute_type": "local"
      },
      "flow": [
        {
          "type": "ingest_source",
          "name": "ingest_source_filesystem",
          "config": {
            "provider": "filesystem",
            "connection_params": { "paths": ["./sample_documents"] },
            "include_filter": "pdf,txt,docx"
          }
        },
        {
          "type": "extract_operator",
          "name": "extract_with_docling",
          "config": {
            "text_extraction": { "provider": "docling_library", "doc_column": "content" },
            "entity_extraction": { "provider": "none" }
          },
          "depends_on": ["ingest_source_filesystem"]
        },
        {
          "type": "chunker",
          "name": "simple_chunker",
          "config": {
            "chunk_type": "simple",
            "chunk_size": 512,
            "chunk_overlap": 50,
            "retain_original_content": false
          },
          "depends_on": ["extract_with_docling"]
        },
        {
          "type": "embeddings",
          "name": "ollama_embeddings",
          "config": {
            "provider": "litellm",
            "provider_config": {
              "model_id": "openai/nomic-embed-text",
              "api_base": "http://localhost:11434/v1",
              "api_key": "<ollama-api-key>"
            },
            "embeddings_column": "embeddings",
            "overlap_ratio": 0.1
          },
          "depends_on": ["simple_chunker"]
        },
        {
          "type": "vectordb",
          "name": "opensearch_vector_store",
          "config": {
            "provider": "opensearch",
            "doc_id_column": "doc_id_hash",
            "embeddings_column": "embeddings",
            "vector_dimension": 768,
            "create_index": true,
            "provider_config": {
              "index_name": "sample-documents-index",
              "host": "localhost",
              "port": 9200,
              "username": "${OPENSEARCH_USERNAME}",
              "password": "${OPENSEARCH_PASSWORD}",
              "use_ssl": false,
              "verify_certs": false,
              "batch_size": 100,
              "engine": "faiss",
              "algorithm": "hnsw",
              "space_type": "l2"
            },
            "feature_mappings": [
              { "feature_name": "doc_id_hash", "mapped_column_name": "pk" },
              { "feature_name": "content", "mapped_column_name": "text" },
              { "feature_name": "name", "mapped_column_name": "doc_name" },
              { "feature_name": "path", "mapped_column_name": "file_path" },
              { "feature_name": "embeddings", "mapped_column_name": "vector_embeddings" },
              { "feature_name": "chunk_id", "mapped_column_name": "chunk_id" },
              { "feature_name": "chunk_index", "mapped_column_name": "chunk_index" }
            ]
          },
          "depends_on": ["ollama_embeddings"]
        }
      ]
    }
    

    Здесь важны два глобальных параметра:

    • execute_type: "local" — запускает обработку внутри процесса сервера.
    • force_ingest: true — перепроцессирует ранее загруженные документы: удобно во время итераций, но неэффективно при постоянной работе.

    В качестве ответа возвращается код 201 Created с новым идентификатором flow_id и сохраненными настройками:

    {
      "flow_id": "e2c31e42-7719-482a-a53d-2490b830d971",
      "flow_name": "complete-document-pipeline",
      "description": "Complete document processing pipeline: Ingest -> Extract -> Chunk -> Embed -> Store in OpenSearch",
      "tags": ["docling", "embeddings", "opensearch"],
      "global_config": {
        "doc_column": "content",
        "disable_validation": true,
        "force_ingest": true,
        "storage": "in-memory",
        "execute_type": "local"
      },
      "created_on": "2026-09-07T10:05:00Z",
      "modified_on": "2026-09-07T10:05:00Z"
    }
    

    Шаг 5: запуск обработки

    Запуски происходят асинхронно. Метод POST /api/v1/job_runs принимает ID и название потока:

    {
      "flow_id": "e2c31e42-7719-482a-a53d-2490b830d971",
      "flow_name": "complete-document-pipeline"
    }
    

    Он сразу возвращает код 201 Created вместе с job_run_id и статусом Starting, в то время как обработка продолжается на фоне:

    {
      "job_run_id": "99ecd646-bb8e-494a-a0c5-86b40c1d4f90",
      "status": "Starting",
      "message": "Job run created successfully"
    }
    

    Шаг 6: опрос статуса, метрик и логов

    Проводите опросы по адресу GET /api/v1/job_runs/{job_run_id}, указывая параметр include_logs=true для получения логов по отдельным узлам, пока статус не станет окончательным (например, Completed), желательно увеличивая интервалы между опросами.

    Длинный ответ 200 OK содержит node_sequence (порядок выполнения), job_stats (счетчики для всей задачи, включая node_stats для каждого узла и batch_node_stats для каждой группы) а также массив верхнего уровня node_metadata. Здесь два текстовых файла прошли через все пять узлов, в результате чего получились четыре фрагмента, все из которых имеют индексы:

    {
      "node_sequence": [
        "ca564648-1715-45c3-be67-7c654344a99c",
        "49f285b0-3afa-40eb-8111-3ec6a57c4904",
        "447e0511-5125-4ffb-8396-edb972976080",
        "2636f8b6-ef9a-4805-bda0-9fcaa853e16a",
        "0c773955-f1c7-4b68-b157-38966382cb9c"
      ],
      "job_stats": {
        "job_id": "9be63026-c08c-49c1-ba12-600838f3b22f",
        "job_run_id": "13bd4219-7947-4977-bc69-1ae6628567d3",
        "status": "Completed",
        "message": "",
        "start_time": 1788943163,
        "end_time": 1788943166,
        "duration": 3,
        "heartbeat_timestamp": 0,
        "total_docs": 2,
        "processed_docs": 2,
        "completed_docs": 2,
        "failed_docs": 0,
        "skipped_docs": 0,
        "deleted_doc_count": 0,
        "total_pages_processed": 2,
        "page_type_stats": {
          "txt": 2
        },
        "execution_time": 0,
        "orchestrator": "Python",
        "container_kind": null,
        "container_id": "",
        "flow_id": "unknown",
        "user_id": null,
        "account_id": null,
        "user_entitlements": {},
        "report_status": "NOT_AVAILABLE",
        "report_generation_started_at": null,
        "report_generation_completed_at": null,
        "node_stats": {
          "ca564648-1715-45c3-be67-7c654344a99c": {
            "id": "ca564648-1715-45c3-be67-7c654344a99c",
            "name": "extract_with_docling",
            "node_status": "Completed",
            "error": "",
            "start_time": 1788943163,
            "end_time": 1788943163,
            "time_taken": 0,
            "col_names": [
              "content",
              "doc_id_hash",
              "document_format",
              "id",
              "metadata",
              "modified_time",
              "name",
              "pages_processed",
              "path",
              "source_id"
            ],
            "total_docs": 2,
            "failed_docs": 0,
            "skipped_docs": 0,
            "docs_completed": 2,
            "docs_completed_count": 2,
            "node_metadata": {
              "node_metadata": {
                "failed_docs": [],
                "processed_docs": 2,
                "node_status": "Completed",
                "total_pages_converted": 2,
                "page_type_stats": {
                  "txt": 2
                },
                "failed_docs_count": 0,
                "skipped_docs": [],
                "documents_in_scope": 2,
                "skipped_docs_count": 0,
                "completed_docs_count": 2,
                "Progress": "1 of 1 batches (100.0%) | Completed: 1"
              },
              "id": "ca564648-1715-45c3-be67-7c654344a99c",
              "operator": "extract_with_docling"
            },
            "batch_id": null,
            "batch_num": null
          },
          "49f285b0-3afa-40eb-8111-3ec6a57c4904": {
            "id": "49f285b0-3afa-40eb-8111-3ec6a57c4904",
            "name": "ingest_source_filesystem",
            "node_status": "Completed",
            "error": "",
            "start_time": 1788943163,
            "end_time": 1788943163,
            "time_taken": 0,
            "col_names": [
              "id",
              "name",
              "document_format",
              "metadata",
              "source_id",
              "path",
              "modified_time"
            ],
            "total_docs": 2,
            "failed_docs": 0,
            "skipped_docs": 0,
            "docs_completed": 2,
            "docs_completed_count": 2,
            "node_metadata": {
              "id": "49f285b0-3afa-40eb-8111-3ec6a57c4904",
              "operator": "ingest_source_filesystem",
              "node_metadata": {
                "documents_in_scope": 2,
                "processed_docs": 2,
                "failed_docs_count": 0,
                "failed_docs": [],
                "skipped_docs_count": 0,
                "skipped_docs": [],
                "node_status": "Completed"
              }
            },
            "batch_id": null,
            "batch_num": null
          },
          "447e0511-5125-4ffb-8396-edb972976080": {
            "id": "447e0511-5125-4ffb-8396-edb972976080",
            "name": "simple_chunker",
            "node_status": "Completed",
            "error": "",
            "start_time": 1788943163,
            "end_time": 1788943164,
            "time_taken": 1,
            "col_names": [
              "chunked_content",
              "doc_id_hash",
              "document_format",
              "id",
              "metadata",
              "modified_time",
              "name",
              "pages_processed",
              "path",
              "source_id"
            ],
            "total_docs": 2,
            "failed_docs": 0,
            "skipped_docs": 0,
            "docs_completed": 2,
            "docs_completed_count": 2,
            "node_metadata": {
              "node_metadata": {
                "failed_docs": [],
                "total_chunks": 4,
                "processed_docs": 2,
                "node_status": "Completed",
                "failed_docs_count": 0,
                "skipped_docs": [],
                "documents_in_scope": 2,
                "skipped_docs_count": 0,
                "completed_docs_count": 2,
                "Progress": "1 of 1 batches (100.0%) | Completed: 1"
              },
              "id": "447e0511-5125-4ffb-8396-edb972976080",
              "operator": "simple_chunker"
            },
            "batch_id": null,
            "batch_num": null
          },
          "2636f8b6-ef9a-4805-bda0-9fcaa853e16a": {
            "id": "2636f8b6-ef9a-4805-bda0-9fcaa853e16a",
            "name": "ollama_embeddings",
            "node_status": "Completed",
            "error": "",
            "start_time": 1788943164,
            "end_time": 1788943164,
            "time_taken": 0,
            "col_names": [
              "chunked_content",
              "doc_id_hash",
              "document_format",
              "embeddings",
              "id",
              "metadata",
              "modified_time",
              "name",
              "pages_processed",
              "path",
              "source_id"
            ],
            "total_docs": 2,
            "failed_docs": 0,
            "skipped_docs": 0,
            "docs_completed": 2,
            "docs_completed_count": 2,
            "node_metadata": {
              "node_metadata": {
                "failed_docs": [],
                "processed_docs": 2,
                "node_status": "Completed",
                "failed_docs_count": 0,
                "skipped_docs": [],
                "documents_in_scope": 2,
                "skipped_docs_count": 0,
                "completed_docs_count": 2,
                "Progress": "1 of 1 batches (100.0%) | Completed: 1"
              },
              "id": "2636f8b6-ef9a-4805-bda0-9fcaa853e16a",
              "operator": "ollama_embeddings"
            },
            "batch_id": null,
            "batch_num": null
          },
          "0c773955-f1c7-4b68-b157-38966382cb9c": {
            "id": "0c773955-f1c7-4b68-b157-38966382cb9c",
            "name": "opensearch_vector_store",
            "node_status": "Completed",
            "error": "",
            "start_time": 1788943164,
            "end_time": 1788943165,
            "time_taken": 1,
            "col_names": [
              "chunked_content",
              "doc_id_hash",
              "document_format",
              "embeddings",
              "id",
              "metadata",
              "modified_time",
              "name",
              "pages_processed",
              "path",
              "source_id"
            ],
            "total_docs": 2,
            "failed_docs": 0,
            "skipped_docs": 0,
            "docs_completed": 2,
            "docs_completed_count": 2,
            "node_metadata": {
              "node_metadata": {
                "failed_docs": [],
                "chunks_indexed_successfully": 4,
                "processed_docs": 2,
                "total_chunks_to_index": 4,
                "node_status": "Completed",
                "chunks_failed_to_index": 0,
                "number_of_batches": 1,
                "failed_docs_count": 0,
                "skipped_docs": [],
                "documents_in_scope": 2,
                "skipped_docs_count": 0,
                "completed_docs_count": 2,
                "Progress": "1 of 1 batches (100.0%) | Completed: 1"
              },
              "id": "0c773955-f1c7-4b68-b157-38966382cb9c",
              "operator": "opensearch_vector_store"
            },
            "batch_id": null,
            "batch_num": null
          }
        },
        "batch_node_stats": {
          "2636f8b6-ef9a-4805-bda0-9fcaa853e16a": {
            "d9467158-dc5a-422e-92f7-d389776f8e50": {
              "id": "2636f8b6-ef9a-4805-bda0-9fcaa853e16a",
              "name": "ollama_embeddings",
              "node_status": "Completed",
              "error": "",
              "start_time": 1788943164,
              "end_time": 1788943164,
              "time_taken": 0,
              "col_names": [
                "id",
                "name",
                "document_format",
                "metadata",
                "source_id",
                "path",
                "modified_time",
                "pages_processed",
                "chunked_content",
                "embeddings",
                "doc_id_hash"
              ],
              "total_docs": 2,
              "failed_docs": 0,
              "skipped_docs": 0,
              "docs_completed": 2,
              "docs_completed_count": 2,
              "node_metadata": {
                "id": "2636f8b6-ef9a-4805-bda0-9fcaa853e16a",
                "operator": "ollama_embeddings",
                "node_metadata": {
                  "documents_in_scope": 2,
                  "processed_docs": 2,
                  "failed_docs_count": 0,
                  "failed_docs": [],
                  "skipped_docs_count": 0,
                  "skipped_docs": [],
                  "node_status": "Completed"
                }
              },
              "batch_id": "d9467158-dc5a-422e-92f7-d389776f8e50",
              "batch_num": 0
            }
          },
          "0c773955-f1c7-4b68-b157-38966382cb9c": {
            "d9467158-dc5a-422e-92f7-d389776f8e50": {
              "id": "0c773955-f1c7-4b68-b157-38966382cb9c",
              "name": "opensearch_vector_store",
              "node_status": "Completed",
              "error": "",
              "start_time": 1788943164,
              "end_time": 1788943165,
              "time_taken": 1,
              "col_names": [
                "id",
                "name",
                "document_format",
                "metadata",
                "source_id",
                "path",
                "modified_time",
                "pages_processed",
                "chunked_content",
                "embeddings",
                "doc_id_hash"
              ],
              "total_docs": 2,
              "failed_docs": 0,
              "skipped_docs": 0,
              "docs_completed": 2,
              "docs_completed_count": 2,
              "node_metadata": {
                "id": "0c773955-f1c7-4b68-b157-38966382cb9c",
                "operator": "opensearch_vector_store",
                "node_metadata": {
                  "documents_in_scope": 2,
                  "processed_docs": 2,
                  "failed_docs_count": 0,
                  "failed_docs": [],
                  "skipped_docs_count": 0,
                  "skipped_docs": [],
                  "node_status": "Completed",
                  "number_of_batches": 1,
                  "total_chunks_to_index": 4,
                  "chunks_indexed_successfully": 4,
                  "chunks_failed_to_index": 0
                }
              },
              "batch_id": "d9467158-dc5a-422e-92f7-d389776f8e50",
              "batch_num": 0
            }
          },
          "447e0511-5125-4ffb-8396-edb972976080": {
            "d9467158-dc5a-422e-92f7-d389776f8e50": {
              "id": "447e0511-5125-4ffb-8396-edb972976080",
              "name": "simple_chunker",
              "node_status": "Completed",
              "error": "",
              "start_time": 1788943163,
              "end_time": 1788943164,
              "time_taken": 1,
              "col_names": [
                "id",
                "name",
                "document_format",
                "metadata",
                "source_id",
                "path",
                "modified_time",
                "pages_processed",
                "doc_id_hash",
                "chunked_content"
              ],
              "total_docs": 2,
              "failed_docs": 0,
              "skipped_docs": 0,
              "docs_completed": 2,
              "docs_completed_count": 2,
              "node_metadata": {
                "id": "447e0511-5125-4ffb-8396-edb972976080",
                "operator": "simple_chunker",
                "node_metadata": {
                  "documents_in_scope": 2,
                  "processed_docs": 2,
                  "failed_docs_count": 0,
                  "failed_docs": [],
                  "skipped_docs_count": 0,
                  "skipped_docs": [],
                  "node_status": "Completed",
                  "total_chunks": 4
                }
              },
              "batch_id": "d9467158-dc5a-422e-92f7-d389776f8e50",
              "batch_num": 0
            }
          },
          "ca564648-1715-45c3-be67-7c654344a99c": {
            "d9467158-dc5a-422e-92f7-d389776f8e50": {
              "id": "ca564648-1715-45c3-be67-7c654344a99c",
              "name": "extract_with_docling",
              "node_status": "Completed",
              "error": "",
              "start_time": 1788943163,
              "end_time": 1788943163,
              "time_taken": 0,
              "col_names": [
                "id",
                "name",
                "document_format",
                "metadata",
                "source_id",
                "path",
                "modified_time",
                "content",
                "pages_processed",
                "doc_id_hash"
              ],
              "total_docs": 2,
              "failed_docs": 0,
              "skipped_docs": 0,
              "docs_completed": 2,
              "docs_completed_count": 2,
              "node_metadata": {
                "id": "ca564648-1715-45c3-be67-7c654344a99c",
                "operator": "extract_with_docling",
                "node_metadata": {
                  "documents_in_scope": 2,
                  "processed_docs": 2,
                  "failed_docs_count": 0,
                  "failed_docs": [],
                  "skipped_docs_count": 0,
                  "skipped_docs": [],
                  "node_status": "Completed",
                  "page_type_stats": {
                    "txt": 2
                  },
                  "total_pages_converted": 2
                }
              },
              "batch_id": "d9467158-dc5a-422e-92f7-d389776f8e50",
              "batch_num": 0
            }
          }
        }
      },
      "node_metadata": [
        {
          "id": "ca564648-1715-45c3-be67-7c654344a99c",
          "operator": "extract_with_docling",
          "node_metadata": {
            "failed_docs": [],
            "processed_docs": 2,
            "node_status": "Completed",
            "total_pages_converted": 2,
            "page_type_stats": {
              "txt": 2
            },
            "failed_docs_count": 0,
            "skipped_docs": [],
            "documents_in_scope": 2,
            "skipped_docs_count": 0,
            "completed_docs_count": 2,
            "Progress": "1 of 1 batches (100.0%) | Completed: 1"
          }
        },
        {
          "id": "49f285b0-3afa-40eb-8111-3ec6a57c4904",
          "operator": "ingest_source_filesystem",
          "node_metadata": {
            "documents_in_scope": 2,
            "processed_docs": 2,
            "failed_docs_count": 0,
            "failed_docs": [],
            "skipped_docs_count": 0,
            "skipped_docs": [],
            "node_status": "Completed"
          }
        },
        {
          "id": "447e0511-5125-4ffb-8396-edb972976080",
          "operator": "simple_chunker",
          "node_metadata": {
            "failed_docs": [],
            "total_chunks": 4,
            "processed_docs": 2,
            "node_status": "Completed",
            "failed_docs_count": 0,
            "skipped_docs": [],
            "documents_in_scope": 2,
            "skipped_docs_count": 0,
            "completed_docs_count": 2,
            "Progress": "1 of 1 batches (100.0%) | Completed: 1"
          }
        },
        {
          "id": "2636f8b6-ef9a-4805-bda0-9fcaa853e16a",
          "operator": "ollama_embeddings",
          "node_metadata": {
            "failed_docs": [],
            "processed_docs": 2,
            "node_status": "Completed",
            "failed_docs_count": 0,
            "skipped_docs": [],
            "documents_in_scope": 2,
            "skipped_docs_count": 0,
            "completed_docs_count": 2,
            "Progress": "1 of 1 batches (100.0%) | Completed: 1"
          }
        },
        {
          "id": "0c773955-f1c7-4b68-b157-38966382cb9c",
          "operator": "opensearch_vector_store",
          "node_metadata": {
            "failed_docs": [],
            "chunks_indexed_successfully": 4,
            "processed_docs": 2,
            "total_chunks_to_index": 4,
            "node_status": "Completed",
            "chunks_failed_to_index": 0,
            "number_of_batches": 1,
            "failed_docs_count": 0,
            "skipped_docs": [],
            "documents_in_scope": 2,
            "skipped_docs_count": 0,
            "completed_docs_count": 2,
            "Progress": "1 of 1 batches (100.0%) | Completed: 1"
          }
        }
      ]
    }
    

    Этот пример не полностью согласован: job_run_id в job_stats отличается от ID пятого шага, а значение flow_id равно unknown. Сопоставляйте запуски по ID, который вы получили в начале.

    Что читать в первую очередь в ответе

    • job_stats содержит общие счетчики, такие как total_docs, processed_docs, completed_docs и duration.
  • node_stats отображает продолжительность и статус каждого этапа; проверяйте его в первую очередь, когда выполнение замедляется или завершается с ошибкой.
  • При настройке include_logs=true форматированные логи добавляются в корневую часть ответа, привязанные к имени каждого узла (от узла ввода данных до хранилища OpenSearch).
  • Интерпретация метаданных узлов

    Каждый оператор передает телеметрию в разделе node_metadata: пропускную способность, количество ошибок и результатов обработки на каждом этапе. Основные атрибуты:

    • documents_in_scope: целое число, указывающее количество необработанных документов, найденных и загруженных оператором ввода; это базовый показатель.
    • processed_docs: целое число, обозначающее количество документов, которые оператор успешно обработал в ходе выполнения; равно documents_in_scope, если не произошло ошибок.
    • failed_docs_count: целое число, указывающее количество документов, в обработке которых возникли ошибки.
  • failed_docs: массив, элементы которого содержат id, исходное name файла, причину ошибки reason и document_url (пустой, когда файлы берутся с локального диска).
  • skipped_docs_count: целое число документов, которые были намеренно пропущены, например в результате удаления дубликатов, использования логики веток или пошаговой обработки.
  • skipped_docs: массив, структура которого аналогична failed_docs; в поле reason указывается причина пропуска, например "Filtered by branch condition".
  • page_type_stats: указывается только оператором извлечения — словарь, в котором ключи представляют расширения файлов, а значения — количество обработанных страниц, например {"pdf": 45, "docx": 12, "txt": 3}.
  • Также следует отметить, что total_pages_converted представляет собой целочисленную сумму страниц во всех форматах: для цифровых файлов используются встроенные показатели количества страниц, а для сканов — результаты OCR-анализа.
  • Быстрая проверка состояния: сравните значение documents_in_scope на этапе загрузки с значением processed_docs в последнем узле; значения failed_docs и skipped_docs объясняют любые расхождения.

    Шаг 7: просмотр потоков обработки и истории запусков проекта

    GET /api/v1/projects/{project_id}/flows возвращает список потоков обработки проекта с кратким отчетом о запусках (общее количество запусков, ID последнего запуска, статус и время начала, количество записей по каждому статусу); результаты отображаются с учетом параметров total_count, offset и limit:

    {
      "flows": [
        {
          "flow_id": "e2c31e42-7719-482a-a53d-2490b830d971",
          "name": "complete-document-pipeline",
          "tags": ["docling", "embeddings", "opensearch"],
          "created_on": "2026-09-07T10:05:00Z",
          "job_run_summary": {
            "total_runs": 1,
            "last_run_id": "99ecd646-bb8e-494a-a0c5-86b40c1d4f90",
            "last_run_status": "Completed",
            "last_run_start_time": 1788775200000,
            "status_counts": { "Completed": 1 }
          }
        }
      ],
      "total_count": 1,
      "offset": 0,
      "limit": 100
    }
    

    В этом примере значение last_run_start_time указано в миллисекундах, тогда как значения start_time для запусков задач — в секундах, поэтому перед сравнением необходимо их нормализовать.

    Основные выводы

    API предоставляет безголовой интерфейс для полноценной обработки документов. Несколько привычек помогают сделать её надежной:

    • Используйте /operators/metadata и /providers/{provider}/models, чтобы программно находить поля операторов и доступные теги моделей вместо их жесткой прописки.
    • Прогоняйте каждое определение DAG через /validation/validate_flow, чтобы выявить неверные типы параметров и поврежденные связи между узлами до начала выполнения.
    • Рассматривайте запуски как асинхронные задачи: сохраняйте job_run_id, выполняйте опросы с параметром include_logs=true, а также читайте данные node_stats и node_metadata, чтобы выявить сбои на каждом этапе.
    • Не храните учетные данные в определениях потоков обработки и проверяйте, что значение vector_dimension соответствует вашей модели эмбеддингов.

    Чтобы попробовать, запустите Ollama и OpenSearch (или Milvus), выполните команду docling-pipelines-api, откройте интерфейс Swagger UI и пройдитесь по последовательности операций: проект, поток обработки, проверка, запуск, логи.