docetl

repository·main·Indexed 26 days ago

https://github.com/ucbepic/docetl

A declarative and agentic framework for processing large collections of structured and unstructured data using LLMs. Version 0.3.0 provides operators such as map, reduce, and filter, and includes a reasoning optimizer that automatically optimizes pipelines for cost and accuracy using directives. It supports a Python API, YAML-based low-code configurations, and the DocWrangler visual playground for interactive prompt development.

Tokens
118.6K
Snippets
257
Records
468
Agent score
86%

What's inside docetl

  1. Understand Plan Rewrites in DocETL

    main

    DocETL automatically applies equivalence-preserving rewrites to your pipeline's logical plan before execution. These rewrites reorder operations to reduce LLM costs without changing the final output. Unlike MOAR, which optimizes for accuracy/cost trade-offs offline, plan rewrites are applied automatically at the start of every run.

    Key rewrite types include:

    • Selection pushdown: Moves a keep (filter) operation below an LLM summarize (map) operation if the filter does not depend on fields written by the map. This ensures the LLM only processes rows that will ultimately be kept.
    • Limit pushdown: Moves a positional head (like sample with method: first) below one-to-one operations so upstream LLM calls only run on the rows that survive the head.
  2. Automatic Plan Rewrites

    main

    DocETL automatically performs plan rewrites at the start of every run. This mechanism reorders your pipeline when DocETL can prove the output remains unchanged. For example, it may move a filter before an expensive map so the map only processes rows that pass the filter.

    Key Characteristics:

    • Setup: Requires no configuration; it is enabled by default.
    • Benefit: Improves efficiency without manual intervention.
  3. Unnest Operation Overview

    main

    The unnest operation expands an array field or a dictionary in the input data into multiple items, allowing individual elements to be processed separately in subsequent steps.

    Behavioral differences:

    • List-type unnesting: Replaces the original key with each individual element from the list.
    • Dictionary-type unnesting: Adds new keys to the parent dictionary based on the expand_fields parameter. The original nested dictionary is also preserved.

    Note that unnest does not have an output schema; it modifies the data structure in place.

  4. Use the TopK operation for retrieval and ranking

    main

    The topk operation retrieves the most relevant items from a dataset. It is used for tasks like finding documents for a query, filtering datasets, RAG pipelines, or recommendations.

    Supported retrieval methods:

    • embedding: Semantic similarity (meaning-based).
    • fts: Keyword-based retrieval using BM25.
    • llm_compare: LLM-based ranking for complex reasoning or multi-factor comparison.

    Note: In the Python API, topk does not have a dedicated Frame method. Instead, you define it within the config["operations"] dictionary and reference it in a pipeline step using DSLRunner.

  5. Use Retrievers to inject context into prompts

    main

    A retriever indexes a dataset once and, for each item an operation processes, searches the index and injects the top matches into the prompt as {{ retrieval_context }}. This is useful for providing relevant information from a large knowledge base without exceeding context windows.

    Key characteristics:

    • Uses LanceDB for local indexing (no external server required).
    • Supports full-text search (FTS), vector search, or hybrid search.
    • Can index any pipeline dataset or the output of a previous pipeline step.
    • If your prompt does not explicitly use {{ retrieval_context }}, DocETL appends the retrieved matches to the prompt automatically.
  6. Run DocWrangler using Docker

    main

    The fastest way to start the DocWrangler playground is using Docker. This method sets up a persistent Docker volume, builds the DocETL image, and runs both the UI (at http://localhost:3000) and the API (at http://localhost:8000).

    1. Configure Environment Files

    Create a .env file in the root directory for the FastAPI backend:

    # Required: API key for your preferred LLM provider (OpenAI, Anthropic, etc)
    OPENAI_API_KEY=your_api_key_here 
    BACKEND_ALLOW_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
    BACKEND_HOST=localhost
    BACKEND_PORT=8000
    BACKEND_RELOAD=True
    FRONTEND_HOST=localhost
    FRONTEND_PORT=3000

    Create a .env.local file in the website directory for the frontend:

    # Optional: Required for AI assistant chatbot and prompt engineering tools (OpenAI only)
    OPENAI_API_KEY=sk-xxx
    OPENAI_API_BASE=https://api.openai.com/v1
    MODEL_NAME=gpt-4o-mini
    
    NEXT_PUBLIC_BACKEND_HOST=localhost
    NEXT_PUBLIC_BACKEND_PORT=8000

    2. Launch and Cleanup

    To start the playground:

    make docker

    To remove all Docker resources (including the persistent data volume):

    make docker-clean
  7. Use the Pandas `.semantic` accessor

    main

    The .semantic accessor allows you to run DocETL operations directly on pandas DataFrames. It serves as a convenience layer for quick, single-operation tasks. For complex, multi-step pipelines where optimization is required, use the DocETL Python API or YAML instead.

    To use it, install docetl via pip and ensure you have pandas installed.

    pip install docetl
  8. Use DocWrangler UI

    main

    DocWrangler is a visual playground for interactive prompt development. It allows you to edit prompts and see results in real time.

    • Online Playground: Visit docetl.org/playground.
    • Local Execution: You can run DocWrangler locally or via Docker (refer to the DocWrangler Setup documentation for details).
  9. Use Value Sampling for very large groups

    main

    When groups are too large for full processing, use value_sampling to select a representative subset of the data.

    Available Methods:

    • random: Randomly select a subset of values.
    • first_n: Select the first N values.
    • cluster: Use K-means clustering to select representative samples.
    • sem_sim: Select samples based on semantic similarity to a query.

    For sem_sim (semantic similarity), you must provide embedding_model, embedding_keys (the fields to embed), and query_text.

    frame = frame.reduce(
        name="sampled_reduce_sem_sim",
        reduce_key="product_id",
        prompt="""Summarize the reviews for product {{ inputs[0].product_id }}, focusing on comments about battery life and performance:
    {% for item in inputs %}
    Review {{ loop.index }}: {{ item.review }}
    {% endfor %}",""",
        value_sampling={
            "enabled": True,
            "method": "sem_sim",
            "sample_size": 30,
            "embedding_model": "text-embedding-3-small",
            "embedding_keys": ["review"],
            "query_text": "Battery life and performance",
        },
        output={"schema": {"summary": "string"}},
    )
  10. Use the DocETL Python API

    main

    The Python API is recommended for production code, notebooks, and scripting. You can define pipelines using operators like map and reduce.

    Key features include:

    • Setting a default_model.
    • Configuring rate_limits for llm_call and llm_tokens.
    • Using read_json to load data.
    • Using .map() for per-item operations with a prompt and output schema.
    • Using .reduce() to aggregate data based on a reduce_key.
    • Running the pipeline with .show() (for a sample) or .collect() (for a full run).
    • Accessing pipeline.total_cost to see the execution cost.
    import docetl
    
    docetl.default_model = "gpt-4o-mini"
    docetl.rate_limits = {
        "llm_call": [{"count": 500, "per": 1, "unit": "minute"}],
        "llm_tokens": [{"count": 200_000, "per": 1, "unit": "minute"}],
    }
    
    # Classify support tickets, then summarize each category
    pipeline = docetl.read_json("tickets.json")
    
    pipeline = pipeline.map(
        prompt="Classify this support ticket: {{ input.text }}",
        output={"schema": {"category": "str", "priority": "str"}},
    )
    
    pipeline = pipeline.reduce(
        reduce_key="category",
        prompt="Summarize these tickets: {% for t in inputs %}{{ t.text }}{% endfor %}",
        output={"schema": {"summary": "str"}},
    )
    
    pipeline.schema()  # {'category': 'str', 'summary': 'str'}
    pipeline.show()  # run on 5 docs and print results
    rows = pipeline.collect()  # full run
    print(f"Cost: ${pipeline.total_cost:.4f}")
  11. Write evaluation functions for MOAR optimization

    main

    To optimize your DocETL pipeline using MOAR, you must provide an evaluation function that reads pipeline output and returns numeric metrics. MOAR uses a specific metric from your returned dictionary (defined by metric_key) as the accuracy metric for optimization.

    Function Signatures

    1. Single-argument signature: Use this if you only need the pipeline output. The function receives results_path (the path to the JSON file containing pipeline output). Note: Pipeline output often includes original input data (e.g., a src attribute), so you may not need the original dataset file separately.

    2. Two-argument signature: Use this if you need to compare output against the original dataset. The function receives dataset_path and results_path automatically.

    Return Value

    Your function must return a dictionary of numeric metrics. The key provided to metric_key during optimization must exist in this dictionary.