Strona główna / Artykuły

Uruchamianie rurociągów Docling przez HTTP: od konfiguracji projektu do indeksowanych fragmentów

Przejdź krok po kroku przez REST API Docling Pipelines: uruchom serwer, odkryj operatorów, zweryfikuj i uruchom DAG do pobierania danych oraz przeczytaj informacje telemetryczne dotyczące jego wykonywania.

Wydawca: REACTAPP.TOP15 min czytania3355 słów

Źródła: **A Hands-On Guide to the Docling Pipelines REST API**

Zmiana foldera plików PDF i Word na wyszukiwalne wektorowe fragmenty zazwyczaj wymaga kodu pomocniczego do wydobywania danych, dzielenia ich na fragmenty, wgrzewania oraz tworzenia indeksów. Docling Pipelines pakuje te etapy jako operatory działające za pomocą serwera REST opartego na FastAPI, dzięki czemu każdy klient – takie jak usługa Node.js czy interfejs administracyjny – może tworzyć modułowe łańcuchy zadań typu DAG, weryfikować je oraz uruchamiać ich asynchroniczne wykonywanie przez HTTP. Ten przewodnik pokazuje, jak uruchomić serwer przy użyciu Ollamy i OpenSearch, oraz omawia cały cykl życia żądania, od utworzenia projektu po odczytanie statystyk wykonywania zadań na poszczególnych węzłach.

Prygotowanie usług tła

Server koordynuje prace, ale procesy wgrzewania danych i ich przechowywanie odbywają się gdzie indziej: tutaj mowa o Ollamie do obsługi modeli oraz OpenSearch jako magazynie wektorów. Najpierw należy je uruchomić.

Najprostszym sposobem jest plik QUICKSTART.md projektu, który w ciągu kilku minut skryptowo konfiguruje Ollamę, bazę wektorową oraz niezbędne kontenery. Agent do programowania oparty na AI, taki jak Cursor, Claude Code lub Copilot, również może przeprowadzić konfigurację i weryfikację; w obu przypadkach należy upewnić się, że każda z usług odpowiada.

Wybór magazynu wektorów

To decyzja architektoniczna. OpenSearch nadaje się, gdy chcesz użyć hybrydowego wyszukiwania łączącego dopasowywanie słów kluczowych z wektorami gęstymi. Milvus jest odpowiedni dla dedykowanej, wysoce skalowalnej infrastruktury przeznaczonej do przechowywania bardzo dużych zbiorów wektorów. W tym przewodniku użyto OpenSearch.

Rozpoczęcie działania serwera API

Aktywuj środowisko wirtualne, w którym zainstalowany jest pakiet, i uruchom dostarczony polecenie:

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

Aby umożliwić szybkie ładowanie podczas rozwoju, uruchom aplikację bezpośrednio za pomocą uvicorn, skonfigurowanego do obsługi wszystkich interfejsów na porcie 8080:

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

Dostępne są następujące cztery adresy URL:

  • http://localhost:8080/health do sprawdzania stanu aplikacji;
  • http://localhost:8080/api/v1/docs do interaktywnego interfejsu Swagger UI;
  • http://localhost:8080/api/v1/redoc do dokumentacji ReDoc;
  • http://localhost:8080/api/v1/openapi.json do surowego schematu OpenAPI.

W TypeScript można przekazać dokument OpenAPI do generatora klienta zamiast ręcznie definiować typy żądań.

Zmienne środowiskowe wpływające na serwer

Ustaw je przed uruchomieniem, aby dostosować zachowanie aplikacji:

  • DS_LOG_LEVEL określa poziom szczegółowości logów. Jego domyślną wartością jest INFO, a może przyjmować wartości DEBUG, INFO, WARNING lub ERROR.
  • DOCPIPE_STORAGE_BACKEND decyduje o miejscu przechowywania metadanych: duckdb (domyślnie, lokalnie) lub postgres.
  • CORS_ORIGINS wymienia adresy, z których frontend w przeglądarce może wywoływać API. Jego domyślną wartością jest http://localhost:3000.
  • OLLAMA_HOST skierowuje operatory embedding i LLM do Ollamy. Domyślną wartością jest http://localhost:11434; należy ją zmienić, jeśli Ollama działa na innej porcie lub maszynie.

Eksplorowanie punktów końcowych w Swagger UI

Otwórz w przeglądarce http://localhost:8080/api/v1/docs; lokalnie nie jest wymagane logowanie. Rozwiń odpowiedni punkt końcowy, kliknij Przetestuj i wyślij poniższe dane bez pisania klienta.

Zarys cyklu życia żądania

Pełny obieg obejmuje siedem wywołań, od utworzenia projektu do podsumowania jego działania:

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

Krok 1: utworzenie projektu

Projekty grupują różne procesy i śledzą ich łączną statystykę działania. Utwórz jeden za pomocą POST /api/v1/projects:

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

Pola:

  • name to obowiązkowy ciąg znaków o długości od 1 do 255 znaków, który unikalnie identyfikuje projekt.
  • description to opcjonalne krótkie wyjaśnienie celu projektu.
  • tags to opcjonalny tabliczka ciągów znaków służąca do kategoryzacji.

W przypadku sukcesu zwracany jest kod 201 Created wraz z wygenerowanym project_id, wartością flow_count równą zero, datami oraz adresem href do zasobu:

{
  "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"
}

Krok 2: odkrywanie operatorów i modeli

Zamiast zgadywać nazwy i opcje operatorów, zapytaj serwer.

Metryki operatora

Zapytanie GET /api/v1/operators/metadata zwraca szczegółowy schemat każdego operatora, obejmujący ponad 20 wbudowanych opcji oraz wszystkie zewnętrzne operatorze, które zarejestrowałeś. Parametr zapytania internal_features ma domyślną wartość false.

W każdym wpisie features opisuje kolumny, które generuje operator, required_features – kolumny, których potrzebuje od źródła, a attributes – jego opcje wraz z typami i wartościami domyślnymi. Operator extract emituje content oraz doc_id_hash; natomiast chunker wymaga 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"
      }
    }
  }
}

Porównanie required_features z wynikami z źródła pozwala przed wysłaniem potwierdzić, że każdy węzeł otrzyma potrzebne mu kolumny.

Modele dostawców

GET /api/v1/providers/{provider}/models wysyła zapytanie do aktywnego dostawcy (ollama lub watsonx), więc brakujący model lub błędnie wpisany tag zostają wykryte przed uruchomieniem:

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

Każdy model jest wymieniony wraz z nazwą, tagiem, datą modyfikacji oraz rozmiarem w bajtach:

{
  "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
    }
  ]
}

Krok 3: walidacja przepływu przed jego zapisaniem

POST /api/v1/validation/validate_flow sprawdza graf DAG pod kątem błędnych parametrów i uszkodzonych zależności, bez konieczności przechowywania go lub jego uruchamiania.

global_config ustala opcje stosowane w całym procesie (kolumna dokumentu, lokalne wykonywanie, przechowywanie w pamięci, wymuszone ponowne przetworzenie). flow przedstawia węzły, z których każdy ma type, unikalne name, config oraz tablicę depends_on określającą zależności wsteczne. Proces przetwarzania wygląda następująco:

  • przeczytuje pliki PDF, TXT i DOCX z katalogu ./sample_documents na lokalnym systemie plików;
  • wyodrębnia tekst z tych plików za pomocą biblioteki Docling, pomijając jednocześnie wyodrębnianie elementów semantycznych;
  • dzieli treść na fragmenty po 512 jednostek, przy zachowaniu nakładania się o długość 50 jednostek;
  • wbudowuje każdy fragment za pośrednictwem LiteLLM, kierując się do zgodnego z OpenAI endpointu Ollamy wykorzystującego model nomic-embed-text.
  • Zapisuje wektory do indeksu OpenSearch przy użyciu silnika FAISS, algorytmów HNSW i odległości L2 o wymiarze 768, mapując cechy z pipeline’u na pola indeksu za pomocą 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"]
        }
      ]
    }
    

    Niektóre szczegóły są ważne. vector_dimension (768) musi odpowiadać rozmiarowi wyjściowemu modelu embeddingowego. Dane uwierzytelniające występują jako miejsca zastąpień w stylu ${OPENSEARCH_USERNAME} i <ollama-api-key>; prawdziwe hasła należy przechowywać w zmiennych środowiskowych lub specjalnych magazynach haseł, a nie w zapisanych procesach. Wyłączenie use_ssl i verify_certs jest w porządku lokalnie, ale nie na klastrze współdzielonym. Ponadto narzędzie do dzielenia na fragmenty używa chunk_type: „simple”, podczas gdy dokumentacja metadanych określa chunker_type jako fixed, semantic lub hybrid; sprawdź, co zwraca twój serwer.

    Prawidłowa definicja zwraca 200 OK, is_valid: true, puste pola errors i warnings, a także liczbę węzłów:

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

    Krok 4: rejestracja przepływu

    Zapisz zweryfikowaną definicję za pomocą żądania POST /api/v1/flows (is_elyra ma domyślną wartość false). Ciało żądania zawiera tę samą strukturę DAG wraz z opisem i tagami:

    {
      "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"]
        }
      ]
    }
    

    Tutaj istotne są dwie ustawienia globalne:

    • execute_type: „local” uruchamia pipeline wewnątrz procesu serwera.
    • force_ingest: true przetwarza ponownie wcześniej zaimportowane dokumenty: przydatne podczas iteracji, marnotrawne w stanie stabilnym.

    Odpowiedź to 201 Created z nowym flow_id oraz zapisanymi ustawieniami:

    {
      "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"
    }
    

    Krok 5: rozpoczęcie wykonywania

    Wykonania są asynchroniczne. POST /api/v1/job_runs przyjmuje ID i nazwę przepływu:

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

    Od razu zwraca 201 Created wraz z job_run_id i stanem Starting, podczas gdy praca trwa w tle:

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

    Krok 6: sprawdzanie stanu, metryk i logów

    Sprawdzaj GET /api/v1/job_runs/{job_run_id}, dodając parametr include_logs=true w celu uzyskania logów poszczególnych węzłów, aż stan osiągnie wartość końcową (np. Completed), najlepiej z coraz większymi opóźnieniami.

    Długa odpowiedź 200 OK zawiera node_sequence (kolejność wykonywania), job_stats (liczniki dla całego procesu, wraz z node_stats dla poszczególnych węzłów i batch_node_stats dla poszczególnych partii) oraz tablicę node_metadata na najwyższym poziomie. W tym przypadku dwa pliki tekstowe przeszły przez wszystkie pięć węzłów, co dało cztery fragmenty, wszystkie oznaczone indeksami:

    {
      "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"
          }
        }
      ]
    }
    

    Ten przykład nie jest w pełni spójny: job_run_id w job_stats różni się od ID z kroku 5, a flow_id ma wartość unknown. Powiąż procesy za pomocą ID otrzymanego na początku.

    Co czytać najpierw w odpowiedzi

    • job_stats dostarcza liczniki na wysokim poziomie, takie jak total_docs, processed_docs, completed_docs oraz duration.
  • node_stats pokazuje czas trwania i stan każdego kroku; sprawdź go najpierw, gdy proces jest wolny lub zawodzi.
  • Przy użyciu include_logs=true formatowane logi są dodawane u szczytu odpowiedzi, oznaczone nazwą każdego węzła (od węzła pobierania po magazyn OpenSearch).
  • Interpretacja metadanych węzła

    Każdy operator raportuje dane telemetryczne w sekcji node_metadata: przepustowość, błędy oraz liczba przekształceń na każdym etapie. Główne atrybuty to:

    • documents_in_scope: całkowita liczba surowych dokumentów znalezionych i załadowanych przez operatora pobierania; stanowi podstawę do porównań.
    • processed_docs: całkowita liczba dokumentów, które ten operator zdołał przetworzyć pomyślnie podczas procesu; jest równa documents_in_scope, gdy nie wystąpiły żadne błędy.
    • failed_docs_count: całkowita liczba dokumentów, które napotkały tu błędy.
  • failed_docs: tablica, której elementy zawierają id, oryginalną nazwę pliku name, reason błędu oraz document_url (pusty, gdy pliki pochodzą z dysku lokalnego).
  • skipped_docs_count: całkowita liczba dokumentów celowo pominiętych, na przykład w wyniku usuwania duplikatów, logiki związanej z gałęziami lub wykonywania procesów w trybie inkrementalnym.
  • skipped_docs: tablica o strukturze podobnej do failed_docs, gdzie reason wyjaśnia powód pominięcia, na przykład "Filtered by branch condition".
  • page_type_stats: podawana wyłącznie przez operatora ekstrakcji, to słownik mapujący rozszerzenia plików na liczbę przetworzonych stron, na przykład {"pdf": 45, "docx": 12, "txt": 3}.
  • Także w trybie wyłącznie wydobywania, total_pages_converted to całkowita liczba stron we wszystkich formatach, obliczana na podstawie naturalnych liczb dla plików cyfrowych oraz liczb uzyskanych przez OCR dla skanów.
  • Szybka weryfikacja stanu: porównaj documents_in_scope przy pobieraniu z processed_docs w ostatnim węźle; failed_docs i skipped_docs wyjaśniają ewentualne różnice.

    Krok 7: przegląd procesów i historii wykonywań projektu

    GET /api/v1/projects/{project_id}/flows wyświetla procesy projektu wraz z podsumowaniem wykonań (łączna liczba wykonań, ID ostatniego wykonywania, status i czas rozpoczęcia, liczby według statusu), paginowane za pomocą total_count, offset i 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
    }
    

    W tym przykładzie last_run_start_time wygląda na wartości w milisekundach, podczas gdy wartości start_time dla wykonywania zadań są w sekundach, więc należy je znormalizować przed porównaniem.

    Główne wnioski

    API oferuje bezinterfejsową architekturę do przetwarzania dokumentów od początku do końca. Kilka nawyków zapewnia jego niezawodność:

    • Używaj /operators/metadata oraz /providers/{provider}/models, aby programowo odkrywać pola operatorów i dostępne tagi modeli, zamiast wpisywać je ręcznie.
    • Przeprowadzaj każdą definicję DAG przez /validation/validate_flow, aby przed uruchomieniem wykryć błędne typy parametrów oraz uszkodzone połączenia węzłów.
    • Traktuj procesy jako zadania asynchroniczne: przechowuj job_run_id, sprawdzaj stan zadania za pomocą include_logs=true, a także czytaj node_stats i node_metadata, aby zlokalizować błędy w poszczególnych etapach.
    • Unikaj przechowywania danych uwierzytelniających w definicjach przepływów i upewnij się, że vector_dimension jest zgodny z twoim modelem embeddingów.

    Aby to wypróbować, uruchom Ollamę i OpenSearch (lub Milvus), zainstaluj docling-pipelines-api, otwórz Swagger UI i przejdź przez następującą sekwencję: projekt, przepływ, walidacja, uruchomienie, logi.

    Literatura pokrewna

  • Odkrywanie zasobów, a nie ich przenoszenie: wcześniejsze ładowanie aplikacji React przez HTTP/3 — Dowiedz się, dlaczego HTTP/3 sprawia, że późne odkrywanie zasobów staje się prawdziwym wąskim gardłem w aplikacjach React, oraz jak funkcje preloadModule, preinit, Early Hints i chunking to rozwiązują.