Inicio / Artículos

Ejecutar pipelines de Docling a través de HTTP: desde la configuración del proyecto hasta los fragmentos indexados

Recorra paso a paso la API REST de Docling Pipelines: inicie el servidor, descubra los operadores, valide y ejecute un DAG de ingestión, y lea su telemetría de ejecución.

Editor: REACTAPP.TOP15 min de lectura3355 palabras

Fuentes: **A Hands-On Guide to the Docling Pipelines REST API**

Conviertir una carpeta de archivos PDF y Word en fragmentos vectoriales buscables generalmente requiere código especial para la extracción, división en fragmentos, incrustación e indexación. Docling Pipelines agrupa estas etapas como operadores detrás de un servidor REST basado en FastAPI, de modo que cualquier cliente, como un servicio Node.js o una interfaz de administración, pueda crear pipelines modulares en formato DAG, validarlos y activar ejecuciones asíncronas a través de HTTP. Esta guía inicia el servidor con Ollama y OpenSearch y sigue todo el ciclo de vida de una solicitud, desde la creación de un proyecto hasta la lectura de las estadísticas de ejecución por nodo.

Preparación de los servicios de soporte

El servidor orquesta el trabajo, pero las incrustaciones y el almacenamiento se realizan en otro lugar: aquí, Ollama para los modelos y OpenSearch como almacén vectorial. Enciéndalos primero.

La ruta más rápida es el QUICKSTART.md del proyecto, que mediante scripts configura Ollama, una base de datos vectorial y los contenedores necesarios en pocos minutos. Un agente de programación basado en IA como Cursor, Claude Code o Copilot también puede encargarse de la configuración y verificación; en cualquier caso, hay que confirmar que cada servicio responde.

Elegir un almacén de vectores

Se trata de una decisión arquitectónica. OpenSearch es adecuado cuando se desea una búsqueda híbrida, que combine la coincidencia de palabras clave con vectores densos. Milvus es ideal para infraestructuras dedicadas y altamente escalables destinadas a colecciones de vectores muy grandes. Esta guía utiliza OpenSearch.

Iniciar el servidor API

Active el entorno virtual donde está instalado el paquete y ejecute la orden incluida:

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

Para el recargue en tiempo real durante el desarrollo, ejecute la aplicación directamente a través de uvicorn, vinculada a todas las interfaces en el puerto 8080:

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

Luego hay cuatro URLs disponibles:

  • http://localhost:8080/health para las verificaciones de estado;
  • http://localhost:8080/api/v1/docs para la interfaz interactiva de Swagger UI;
  • http://localhost:8080/api/v1/redoc para la documentación de ReDoc;
  • http://localhost:8080/api/v1/openapi.json para el esquema OpenAPI en formato raw.

Desde TypeScript, puede pasar el documento OpenAPI a un generador de clientes en lugar de escribir manualmente los tipos de solicitud.

Variables de entorno que configuran el servidor

Establezca estas antes de iniciar la aplicación para ajustar su comportamiento:

  • DS_LOG_LEVEL determina el nivel de detalle de los registros. Su valor por defecto es INFO y acepta los valores DEBUG, INFO, WARNING o ERROR.
  • DOCPIPE_STORAGE_BACKEND selecciona dónde se almacenarán los metadatos: duckdb (el valor por defecto, local) o postgres.
  • CORS_ORIGINS enumera los orígenes a los que se permite hacer llamadas a la API desde una interfaz frontal basada en navegador. Su valor por defecto es http://localhost:3000.
  • OLLAMA_HOST dirige a los operadores de incrustación y LLM hacia Ollama. El valor por defecto es http://localhost:11434; cámbielo si Ollama se ejecuta en otro puerto o máquina.

Explorando los puntos de extremo en Swagger UI

Abra http://localhost:8080/api/v1/docs en un navegador; localmente no se necesita iniciar sesión. Expanda un endpoint, haga clic en Pruébalo y envíe los datos a continuación sin escribir un cliente.

El ciclo de vida de la solicitud de un vistazo

Un viaje completo consta de siete llamadas, desde la creación del proyecto hasta el resumen de ejecución:

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

Paso 1: crear un proyecto

Los proyectos agrupan flujos y registran sus estadísticas de ejecución acumuladas. Cree uno con POST /api/v1/projects:

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

Los campos:

  • name es una cadena obligatoria de 1 a 255 caracteres que identifica de forma única al proyecto.
  • description es una explicación breve opcional sobre el propósito del proyecto.
  • tags es un array opcional de cadenas para su categorización.

El éxito devuelve 201 Created junto con un project_id generado, un flow_count de cero, marcas de tiempo y un href hacia el recurso:

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

Paso 2: descubrir operadores y modelos

En lugar de adivinar los nombres y opciones de los operadores, pregúntele al servidor.

Metadatos de los operadores

GET /api/v1/operators/metadata devuelve un esquema detallado para cada operador, que incluye los más de 20 integrados además de cualquier operador externo que haya registrado. El parámetro de consulta internal_features tiene como valor por defecto false.

En cada entrada, features describe las columnas que genera un operador, required_features las columnas que necesita de fuentes externas, y attributes sus opciones con tipos y valores predeterminados. El operador extracto emite content y doc_id_hash; el chunker, por su parte, requiere 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"
      }
    }
  }
}

Al comparar required_features con los resultados de las fuentes externas, se puede confirmar, antes de enviar el flujo, que cada nodo recibirá las columnas que necesita.

Modelos de proveedores

GET /api/v1/providers/{provider}/models consulta a un proveedor en tiempo real (ollama o watsonx), por lo que cualquier modelo faltante o etiqueta mal escrita se detectará antes de que se ejecute el proceso:

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

Cada modelo se lista con su nombre, etiqueta, fecha de modificación y tamaño en bytes:

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

Paso 3: validar el flujo antes de guardarlo

POST /api/v1/validation/validate_flow verifica si un DAG contiene parámetros incorrectos o dependencias dañadas sin almacenarlo ni ejecutarlo.

global_config establece opciones aplicables a toda la ejecución (columna de documento, ejecución local, almacenamiento en memoria, reingestión forzada). flow enumera los nodos, cada uno con un type, un name único, una config y un array depends_on que indica sus dependencias anteriores. El pipeline es el siguiente:

  • Ingresa archivos PDF, TXT y DOCX desde ./sample_documents en el sistema de archivos local;
  • Extrae su texto con la biblioteca Docling y omite la extracción de entidades;
  • Divide el contenido en fragmentos de 512 unidades con una superposición de 50 unidades;
  • Incrusta cada fragmento mediante LiteLLM, apuntando al endpoint compatible con OpenAI de Ollama con el modelo nomic-embed-text.
  • Escribe los vectores en un índice de OpenSearch utilizando el motor FAISS, la distancia HNSW y L2 con 768 dimensiones, mapeando las características del pipeline a los campos del índice a través de 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"]
        }
      ]
    }
    

    Algunos detalles son importantes. vector_dimension (768) debe coincidir con el tamaño de salida del modelo de embedding. Las credenciales aparecen como marcadores de posición del estilo ${OPENSEARCH_USERNAME} y <ollama-api-key>; guarde los secretos reales en variables de entorno o en un almacén de secretos, no en los flujos almacenados. Desactivar use_ssl y verify_certs está bien en entornos locales, pero no en un clúster compartido. Además, el chunker utiliza chunk_type: "simple", mientras que los documentos de metadatos indican chunker_type como fixed, semantic o hybrid; verifique qué devuelve su servidor.

    Una definición válida devuelve 200 OK, is_valid: true, errors y warnings vacíos, además del recuento de nodos:

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

    Paso 4: registrar el flujo

    Almacene la definición validada mediante POST /api/v1/flows (is_elyra tiene como valor por defecto false). El cuerpo contiene la misma estructura DAG más una descripción y etiquetas:

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

    Hay dos configuraciones globales importantes aquí:

    • execute_type: "local" ejecuta la pipeline dentro del proceso del servidor.
    • force_ingest: true vuelve a procesar los documentos ya ingeridos: útil durante la iteración, pero ineficiente en estado estable.

    La respuesta es 201 Created con un nuevo flow_id y las configuraciones almacenadas:

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

    Paso 5: iniciar una ejecución

    Las ejecuciones son asíncronas. POST /api/v1/job_runs recibe el ID y el nombre del flujo:

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

    Devuelve inmediatamente 201 Created junto con un job_run_id y el estado Starting, mientras el trabajo continúa en segundo plano:

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

    Paso 6: consultar el estado, las métricas y los registros

    Consultar GET /api/v1/job_runs/{job_run_id}, añadiendo include_logs=true para obtener los registros de cada nodo, hasta que el estado sea final (como Completed), idealmente con retrasos cada vez mayores.

    La larga respuesta 200 OK contiene node_sequence (orden de ejecución), job_stats (contadores globales del proceso, con node_stats por nodo y batch_node_stats por lote) además de un array de nivel superior node_metadata. En este caso, dos archivos de texto pasaron por los cinco nodos, generando cuatro fragmentos, todos indexados:

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

    Esta muestra no es completamente coherente: el job_run_id en job_stats difiere del ID del paso 5 y flow_id muestra el valor unknown. Correlacione las ejecuciones con el ID que recibió al inicio.

    Qué leer primero en la respuesta

    • job_stats proporciona contadores de alto nivel como total_docs, processed_docs, completed_docs y duration.
  • node_stats muestra la duración y el estado de cada paso; revíselo primero cuando una ejecución es lenta o falla.
  • Con include_logs=true, los registros formateados se añaden en la raíz de la respuesta, identificados por el nombre de cada nodo (desde el nodo de ingestión hasta el almacén OpenSearch).
  • Interpretación de los metadatos del nodo

    Cada operador informa sobre la telemetría a través de node_metadata: rendimiento, errores y conversiones por etapa. Los atributos principales son:

    • documents_in_scope: un conteo entero de documentos en bruto que encontró y cargó el operador de ingestión; su punto de referencia.
    • processed_docs: un conteo entero de documentos que este operador procesó con éxito en la ejecución; es igual a documents_in_scope cuando no hay fallos.
    • failed_docs_count: un conteo entero de documentos que presentaron errores aquí.
    • failed_docs: un array cuyas entradas contienen un id, el name del archivo original, una reason de error y un document_url (vacío cuando los archivos provienen del disco local).
    • skipped_docs_count: un conteo entero de documentos que se omitieron intencionalmente, por ejemplo mediante eliminación de duplicados, lógica de ramas o ejecuciones incrementales.
    • skipped_docs: un array con la misma estructura que failed_docs, donde reason explica el motivo de la omisión, como por ejemplo "Filtered by branch condition".
    • page_type_stats: informado únicamente por el operador de extracción, un diccionario que relaciona la extensión del archivo con el conteo de páginas analizadas, por ejemplo {"pdf": 45, "docx": 12, "txt": 3}.
    • También de tipo extract-only, total_pages_converted representa la suma entera de páginas en todos los formatos, utilizando los conteos nativos para los archivos digitales y los conteos OCR para las escaneos.

    Una rápida verificación del estado: compare documents_in_scope al momento de la ingesta con processed_docs en el último nodo; failed_docs y skipped_docs explican cualquier diferencia.

    Paso 7: revisar los flujos y el historial de ejecuciones de un proyecto

    GET /api/v1/projects/{project_id}/flows muestra los flujos de un proyecto junto con un resumen de ejecuciones (total de ejecuciones, ID de la última ejecución, estado y hora de inicio, conteos por estado), paginado mediante total_count, offset y 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
    }
    

    En este ejemplo, last_run_start_time parece estar en milisegundos mientras que los valores de start_time de las ejecuciones son en segundos, por lo que es necesario normalizarlos antes de comparar.

    Puntos clave

    La API ofrece una interfaz sin cabeza para el procesamiento de documentos de extremo a extremo. Algunos hábitos la hacen fiable:

    • Use /operators/metadata y /providers/{provider}/models para descubrir de forma programática los campos de los operadores y las etiquetas de modelo disponibles, en lugar de codificarlos manualmente.
    • Ejecute cada definición de DAG a través de /validation/validate_flow para detectar tipos de parámetros incorrectos y conexiones de nodos dañadas antes de la ejecución.
    • Trate las ejecuciones como tareas asíncronas: guarde el job_run_id, realice consultas con include_logs=true y lea node_stats y node_metadata para localizar los fallos por etapa.
    • No incluya credenciales en las definiciones de flujos almacenadas y verifique que vector_dimension coincida con su modelo de embedding.

    Para probarlo, inicie Ollama y OpenSearch (o Milvus), ejecute docling-pipelines-api, abra la interfaz Swagger UI y siga la secuencia: proyecto, flujo, validación, ejecución y registros.

    Lecturas relacionadas