Prompt engineering in batch processing isn’t about finding the "best" prompt; it’s about designing prompts that are robust enough to handle variations in input data and produce consistent, predictable outputs across a large volume of items.

Let’s see this in action. Imagine we have a dataset of product descriptions and we want to extract key features for a catalog.

import openai
import pandas as pd

# Assume 'your_api_key' is set as an environment variable
# openai.api_key = os.environ.get("your_api_key")

def extract_features(description):
    prompt = f"""
    Extract the main product features from the following description.
    Return a JSON object with a list of features.
    If no features are found, return an empty list.

    Description:
    "{description}"

    JSON Output:
    """
    try:
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=[
                {"role": "system", "content": "You are a helpful assistant designed to extract structured data."},
                {"role": "user", "content": prompt}
            ],
            temperature=0.0, # Low temperature for deterministic output
            max_tokens=150
        )
        return response.choices[0].message['content']
    except Exception as e:
        print(f"Error processing description: {description[:50]}... Error: {e}")
        return None

# Load your data
df = pd.read_csv("products.csv") # Assume products.csv has a 'description' column

# Process in batches (example with a small subset for demonstration)
results = []
for index, row in df.head(10).iterrows(): # Processing first 10 rows
    features_json_str = extract_features(row['description'])
    if features_json_str:
        try:
            # Basic parsing, real-world might need more robust error handling
            import json
            features_data = json.loads(features_json_str)
            results.append({"product_id": row['product_id'], "features": features_data.get("features", [])})
        except json.JSONDecodeError:
            print(f"Failed to parse JSON for product ID: {row['product_id']}")
            results.append({"product_id": row['product_id'], "features": []})
    else:
        results.append({"product_id": row['product_id'], "features": []})

# Display results
output_df = pd.DataFrame(results)
print(output_df)

This pipeline takes raw text descriptions, feeds them into a carefully crafted prompt for an LLM, and aims to output structured JSON. The core problem it solves is transforming unstructured text into a usable format for further analysis or integration, at scale. The prompt itself acts as the instruction set, defining the task, the expected output format (JSON), and providing context. The temperature=0.0 is crucial for batch processing, ensuring that for the same input, the LLM will always produce the same output, which is vital for reproducibility and consistency.

Internally, the system works by sending individual requests to the LLM API. For batch processing, this means iterating through your dataset, constructing a unique prompt for each item (often by injecting the item’s data into a template), sending it, receiving the response, and then parsing/storing that response. The key levers you control are:

  1. The Prompt: This is your primary interface. It dictates the LLM’s behavior. You define the task, the persona (optional), the desired output format, constraints, and examples (few-shot learning).
  2. Model Choice: Different models (e.g., gpt-3.5-turbo, gpt-4) have varying capabilities, costs, and speeds. Choose one that balances performance and budget for your batch size.
  3. API Parameters: temperature (for determinism), max_tokens (to control output length and cost), top_p, frequency_penalty, and presence_penalty all influence the output. For batch processing, deterministic parameters are usually preferred.
  4. Data Preprocessing: How you clean and format your input data before sending it to the LLM can drastically affect the quality of the output.
  5. Post-processing: Parsing the LLM’s output, handling errors, and structuring it into your final desired format.

When designing prompts for batch processing, consider edge cases: what happens if a description is empty? What if it’s in a different language? What if the product has no discernible features? Your prompt needs to gracefully handle these. For instance, you might add instructions like: "If the description is too short or irrelevant, return an empty list for features."

The most common pitfall is assuming the LLM will always adhere perfectly to the requested output format. Even with temperature=0.0, minor deviations can occur, especially with complex JSON structures or when the LLM "hallucinates" or misunderstands a nuance. You need to build robust parsing and validation into your post-processing step. This means not just json.loads(), but also checking if the expected keys ("features" in our example) are present and if the data types are correct. For example, you might wrap the json.loads in a try-except block and, if it fails, log the problematic input and prompt, and potentially retry with a slightly modified prompt or a more powerful model.

The next challenge will be optimizing the throughput and cost of your batch processing pipeline, likely involving techniques like parallel API calls or using a dedicated batch inference service.

Want structured learning?

Take the full Prompt-engineering course →