Using an LLM API as a Data Labeling Tool

Data Science & ML Projects (Intermediate)

Chapter 7 · Using an LLM API as a Data Labeling Tool

Every project so far in this course trained something — an LSTM, a random forest, a CNN, a BiLSTM tagger. This chapter trains nothing. It uses an already-trained LLM, through its API, as one stage inside a larger data pipeline — a genuinely different, genuinely modern data-science technique, distinct from anything a from-scratch model in this course could do alone.

What We're Building

A pipeline that labels a large batch of unstructured customer-feedback text with a custom business category ("Billing Issue," "Bug Report," "Feature Request," "Praise," "Other") — categories with no existing labeled training data anywhere, making dsproj2-1's own from-scratch approach genuinely impractical here. The LLM applies the labeling scheme directly, at scale, the moment it can be described in a prompt — then the results get analyzed statistically with this track's own established tools.

Step 1: The Unlabeled Batch

import pandas as pd

feedback = pd.read_csv("customer_feedback.csv")   # one column: raw text, no labels at all
print(feedback.shape)

Step 2: Designing the Labeling Prompt

CATEGORIES = ["Billing Issue", "Bug Report", "Feature Request", "Praise", "Other"]

PROMPT_TEMPLATE = """Classify the following customer feedback into exactly one of these categories: {categories}.

Feedback: "{text}"

Respond with only valid JSON: {{"category": "...", "confidence": "high"|"medium"|"low"}}"""
This is prompt1's own material, applied
Clarity, context, and an explicit output format — prompt1's own anatomy of a good prompt, put directly to use rather than re-taught here. Asking for structured JSON specifically, rather than a free-form sentence, is what makes Step 4's own parsing step possible at all.

Step 3: Calling the API — claude-adv1's Own Territory, Applied Minimally

import anthropic, json, time

client = anthropic.Anthropic()   # API key read from environment, per claude-adv1

def label_feedback(text):
    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=100,
        messages=[{"role": "user", "content": PROMPT_TEMPLATE.format(categories=CATEGORIES, text=text)}],
    )
    time.sleep(0.5)
    return json.loads(response.content[0].text)

The full API surface — streaming, tool use, system prompts — is claude-adv1's own territory in depth. This chapter uses exactly one call shape, deliberately, since the point here is what the API call accomplishes inside a larger pipeline, not the API itself.

A genuinely different cost model than every earlier API chapter
dsproj1-3's and dsproj1-6's own APIs (Open-Meteo, REST Countries) were free, with no per-call cost. A real LLM API call has a real, per-token price, and rate limits that matter at genuine scale — 10,000 feedback rows means 10,000 real billed calls, not a rounding error. This is a real, practical planning consideration specific to this chapter, not present anywhere else in this track.

Step 4: Running the Batch and Parsing Results

results = []
for text in feedback["text"]:
    try:
        label = label_feedback(text)
        results.append({"text": text, **label})
    except (json.JSONDecodeError, KeyError):
        results.append({"text": text, "category": "parse_error", "confidence": None})

labeled = pd.DataFrame(results)

Same JSON-parsing reflex ds1-3 established, now applied to a live model's own output instead of a file or a conventional API response — and the same defensive try/except instinct this track has used since dsproj1-1's own scraper, since a model occasionally returning malformed JSON is a real, expected possibility, not a bug to be surprised by.

Step 5: Analyzing the Labeled Data — This Track's Own Familiar Tools

import matplotlib.pyplot as plt

category_counts = labeled["category"].value_counts()
category_counts.plot(kind="barh", color="#15803D")
plt.title("Feedback Volume by Category")
plt.savefig("feedback_categories.png")

Nothing new here at all — .value_counts() and a bar chart, the exact ds1-level analysis skill this whole track opened with, applied here to data an LLM helped create rather than data that arrived pre-labeled.

The Honest Trade-Off, Stated Directly

dsproj2-1's own from-scratch modelThis chapter's LLM-as-tool
Training data needed25,000 labeled examplesNone — the category scheme just needs describing
Cost per use once builtFree — a trained model runs at no marginal costReal per-call cost, every single time
Measured accuracyA real number, against a held-out test setNo ground truth to measure against, unless one is built
Adapting to a new category schemeRequires retraining on newly labeled dataEdit the prompt
Honest limitation: no measured accuracy, and real hallucination risk
Unlike every model trained earlier in this course, there's no test-set accuracy number for this chapter's own labels — no pre-existing ground truth exists to compare against. Per llm1-10's own mechanical explanation, an LLM has no built-in fact-checker; it can mislabel ambiguous feedback confidently, the same way it can hallucinate anywhere else. The real, practical mitigation is what production pipelines actually do: pull a random sample of the labeled output and have a human spot-check it, the same "flag for review, don't blindly trust" instinct dsproj1-2's own outlier-flagging and dsproj1-6's own missing-value handling already established in this track.

No training data required

A custom category scheme works the moment it can be described in a prompt.

Structured output via JSON

Requesting a specific format makes the model's own output immediately parseable.

Real per-call cost

Unlike every free API used earlier in this track — a genuine planning factor at scale.

Sample-based spot-checking

The practical substitute for a held-out test-set accuracy number.

Extend This Project

Try these on your own:

  • Pull a random sample of 50 labeled rows, label them by hand, and compute real agreement between your labels and the model's own — a genuine, honest accuracy estimate.
  • Add a retry-with-backoff loop around label_feedback() for real API rate-limit errors, not just malformed JSON.
  • Ask the model to also extract a one-sentence summary alongside the category, and add it as a new column.
  • Compare this chapter's own category-volume chart against Chapter 1's own sentiment scores for the same feedback batch, if both are available — do certain categories skew more negative?

What's Next

Chapter 8: Capstone — An End-to-End ML-Powered Mini App — collection, cleaning, feature engineering, model training/evaluation, and a minimal interface, combined into one real application, with a chapter-attribution table spanning both dsproj1 and dsproj2.

Chapter 7 Quick Reference

  • The only chapter in this course that trains nothing — an already-trained LLM used as one stage in a larger pipeline
  • prompt1's own prompt-anatomy material applied directly, not re-taught; claude-adv1's own API surface used minimally, on purpose
  • Real per-call cost and rate limits — a genuinely different consideration than this track's earlier free APIs
  • No training data needed for a custom category scheme — but also no measured test-set accuracy, unlike every model trained earlier in this course
  • llm1-10's own hallucination material applies directly here — sample-based human spot-checking is the honest, practical mitigation