Prompts for Data Extraction from Unstructured Text

Unstructured text is the bane of every data engineer. From complex PDF invoices to messy email threads and extensive medical or financial reports; the world runs on data that does not fit neatly into a relational database. Where we used to resort to fragile regular expressions (regex), Large Language Models (LLMs) now offer a more robust alternative. But simply asking a model to "extract the data from this" inevitably leads to chaos.

The Challenge of Unstructured Data

When we deploy an LLM for data extraction, our role shifts from programmer to instructor. The model understands the semantics of the text, but needs guidance to force the output into a machine-readable format. Without strict guidelines, an LLM will hallucinate (invent data that is not in the text), randomly change key names in JSON, or add extra conversational text such as "Here is the JSON you requested:".

To prevent these issues, we must structure our prompts strategically. In this article, we cover the essential pillars of a good extraction prompt: field definitions, handling missing values, few-shot prompting, and error detection. In addition, we provide ready-to-use templates for common extraction tasks.

1. Defining Fields and Enforcing the Schema

The absolute foundation of a successful extraction is removing ambiguity about the desired output format. You cannot just settle for the instruction "give me a JSON with name and address". You must establish a hard contract between you and the LLM.

You do this by explicitly including the expected schema in the system prompt. Often, a TypeScript interface or a simplified JSON schema works best. It forces the model to think about data types (string, boolean, array, number). Also, check out our comprehensive guide on enforcing output formats for more technical implementations around JSON mode and function calling.

Best Practice: Describe the Semantics, Not Just the Type

Don't just specify the type, but also tell the model *what* the field means, especially if the source data can be ambiguous. Add comments in your schema definition within the prompt.

You are a precise data extraction API. 
Your only task is to extract information from the text below and return it as a valid JSON object.

Use EXACTLY this JSON schema:
{
  "customer_name": "string", // The full company name or name of the person.
  "total_amount": "number", // The final amount including VAT. Use a dot for decimals, not a comma.
  "currency": "string", // The three-letter ISO code (e.g., EUR, USD).
  "invoice_paid": "boolean" // true if the text indicates the invoice has already been paid, otherwise false.
}

Return ONLY the JSON. No introduction, no explanation, no markdown blocks like ```json.

2. Handling Missing Values

One of the biggest risks when using LLMs for extraction is their tendency to want to be helpful. If a certain piece of information (for example, an end date) is not in the text, an untrained model will often try to extrapolate or invent it based on general knowledge. In data engineering, this is called a hallucination, and it corrupts your database.

You must build explicit escape hatches into your prompt for missing data. Instruct the model crystal clear on what should happen if information is missing.

RULES FOR MISSING DATA:
- NEVER invent information. 
- Do not infer data that is not explicitly or implicitly in the source text.
- If a field is missing, explicitly assign the value `null` (do not use "unknown", "N/A", or empty strings "").
- If an array should be empty, return `[]`.

By forcing the model to use null, you can easily validate in your backend which data was successfully retrieved and which was not, without having to parse all kinds of textual variations ("not found", "N/A", "unknown").

3. The Power of Choosing Few-Shot Examples

Although modern models like GPT-4 or Claude 3.5 Sonnet are excellent at zero-shot extraction (extraction without examples), reliability increases dramatically when you apply few-shot prompting. In complex domains, such as legal texts, this is even mandatory.

The secret to a good few-shot prompt for extraction lies in choosing the *right* examples. Do not just provide the 'happy flow' (where all data is neatly present). Make sure your examples include cases where the text is extremely messy, or where many fields are missing, so the model sees in practice how to apply null.

A typical structure looks like this:

  • System Instruction: The rules and the schema.
  • Example 1 (User): A standard piece of text.
  • Example 1 (Assistant): The perfectly extracted JSON.
  • Example 2 (User): A text with missing fields and confusing typos.
  • Example 2 (Assistant): The correct JSON with null values where necessary.
  • Current input (User): The text you want to process now.

4. Systematically Detecting Extraction Errors

No matter how good your prompt is, LLMs are stochastic; the output can vary. To make data extraction robust for production, you should never assume the output is 100% correct. A validation layer is needed between the LLM and your database.

If you want to validate complex extractions over multiple steps, consider using prompt chaining, where a second model is tasked purely with verifying the output of the first model. For structural validation (is the JSON correct, are the data types correct?), we strongly recommend using libraries like Pydantic (for Python) or Zod (for TypeScript) in your application code. This topic is covered in depth on our learning platform on advanced data pipelines.

When a parsing error occurs in your code (for example, the LLM sent a string instead of a number), you can feed the error message directly back to the LLM with the instruction: "The previous output contained an error: [error message]. Correct the JSON."

5. Reusable Prompt Templates

Below you will find three extensively tested system prompts for common extraction scenarios. You can copy and use these directly in your own applications.

Template 1: Invoices and Receipts (Financial)

Invoices are notorious because they are visually structured (tables) but are read as unstructured text via OCR or PDF extraction.

You are an expert in financial administration. Extract the data from the OCR text of an invoice below.

RULES:
1. Strictly follow the JSON schema below.
2. Amounts must be numbers, without currency symbols (e.g., 1250.50).
3. Dates must be formatted in YYYY-MM-DD. If the day or month is unknown, return null.
4. Do not invent data. If a value cannot be found, return `null`.
5. Ignore terms and conditions or promotional texts in the document.

EXPECTED JSON SCHEMA:
{
  "invoice_number": "string | null",
  "invoice_date": "string (YYYY-MM-DD) | null",
  "supplier": {
    "name": "string | null",
    "coc_number": "string | null",
    "vat_number": "string | null"
  },
  "total_excluding_vat": "number | null",
  "total_including_vat": "number | null",
  "vat_amount": "number | null",
  "line_items": [
    {
      "description": "string",
      "quantity": "number | null",
      "unit_price": "number | null",
      "total": "number | null"
    }
  ]
}

Return only the raw JSON. No other text.

Template 2: Emails and Action Items (Communication)

When extracting data from emails, you often want to look past the formal greetings and get straight to the point: who needs to do what, and by when?

Analyze the email (thread) below and extract the key information and action items.

RULES:
- Focus only on business agreements and actions. Ignore small talk.
- Identify the main sender (the person initiating the email).
- An 'action item' is a specific task assigned to a specific person.
- If a deadline is vague (e.g., "next week"), try to convert this to a concrete date in ISO format (YYYY-MM-DD) IF the context mentions the current date. Otherwise, use `null`.

EXPECTED JSON SCHEMA:
{
  "subject_summary": "string (max 10 words)",
  "urgency": "Low" | "Normal" | "High",
  "main_sender": "string (Name or email address) | null",
  "action_items": [
    {
      "task_description": "string",
      "assigned_to": "string (Person name) | null",
      "deadline": "string (YYYY-MM-DD) | null"
    }
  ]
}

Provide only the JSON as output.

Template 3: Complex Medical or Technical Reports

Reports often require extraction of nested data and isolating specific findings from pages full of jargon.

You are a data assistant for technical analyses. Read the inspection report below and extract only the identified defects and the corresponding recommendations.

CRITICAL INSTRUCTIONS:
- Look for sections like "Findings", "Defects", "Risks", or similar synonyms.
- Do not include solutions as defects, and vice versa.
- Classify the severity of each defect based on the text. If the severity is not explicitly mentioned, do not estimate it, but return `null`.
- Refer (if possible) to the page name or paragraph number as the source.

EXPECTED JSON SCHEMA:
{
  "report_title": "string | null",
  "inspection_date": "string (YYYY-MM-DD) | null",
  "identified_defects": [
    {
      "short_description": "string",
      "severity": "Critical" | "Moderate" | "Low" | null,
      "proposed_solution": "string | null",
      "source_paragraph_or_page": "string | null"
    }
  ]
}

Return only valid JSON.

Conclusion

Data extraction from unstructured text is one of the most valuable applications of Large Language Models in modern business. By strictly constraining the model with clear schemas, strict rules for missing values, and well-chosen few-shot examples, you transform a text generator into a reliable data parser. Always remember: feed in the right prompt, and systematically check the JSON output in your program code before saving it to your database.