DEV Community

shashank ms
shashank ms

Posted on

Using LLM for Named Entity Recognition and Sentiment Analysis

We are going to build a small command line tool that scans customer feedback, extracts named entities, and scores overall sentiment. It returns structured JSON that you can pipe into a dashboard or database. I run this on Oxlo.ai because the flat per-request pricing keeps costs predictable whether the input is a one-line tweet or a full page review.

What you'll need

Oxlo.ai is fully OpenAI SDK compatible, so the only difference is the base URL.

Step 1: Initialize the Oxlo.ai client

I import the JSON library for parsing and set up the client pointing at Oxlo.ai. If you want to experiment with multilingual feedback later, you can swap in qwen-3-32b without changing any other code.

import json
from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

Step 2: Lock down the system prompt

The prompt forces JSON mode output with strict keys so we do not have to guess the schema at runtime.

SYSTEM_PROMPT = """You are a structured data extraction engine.
Analyze the user provided text and return only a JSON object with no markdown formatting.
The object must contain exactly two keys:
1. "sentiment": an object with "label" (positive, negative, neutral, or mixed) and "score" (a float from -1.0 to 1.0).
2. "entities": a list of objects, each with "text" (the exact mention) and "type" (one of: Person, Organization, Product, Location).
If no entities are found, use an empty list. Do not include commentary."""

Step 3: Build the extraction function

This function sends the text to Oxlo.ai and parses the response. I use response_format={"type": "json_object"} to guarantee valid JSON.

def analyze_text(text: str) -> dict:
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": text},
        ],
        response_format={"type": "json_object"},
        temperature=0.1,
    )

    raw = response.choices[0].message.content
    return json.loads(raw)

For deeper reasoning on ambiguous reviews, you can switch the model to kimi-k2.6 or deepseek-v3.2 without touching the rest of the code.

Step 4: Batch process a list of feedback items

In production I usually read from a queue or CSV. Here is a small loop over a Python list that collects results.

def analyze_batch(texts: list[str]) -> list[dict]:
    results = []
    for idx, text in enumerate(texts, start=1):
        print(f"Processing item {idx} / {len(texts)} ...")
        result = analyze_text(text)
        results.append({
            "input": text,
            "sentiment": result.get("sentiment", {}),
            "entities": result.get("entities", []),
        })
    return results

Run it

Here are three real-ish samples I used to sanity check the pipeline.

if __name__ == "__main__":
    samples = [
        "Alice from Acme Corp said the new WidgetPro is a disaster, but she loves the packaging.",
        "Berlin headquarters shipped my order early. Absolutely thrilled with the service.",
        "I contacted support because the Model X battery drains overnight. Very frustrating experience.",
    ]

    output = analyze_batch(samples)
    print(json.dumps(output, indent=2))

When I ran this against llama-3.3-70b, I got the following structured output.

[
  {
    "input": "Alice from Acme Corp said the new WidgetPro is a disaster, but she loves the packaging.",
    "sentiment": {
      "label": "mixed",
      "score": -0.2
    },
    "entities": [
      {"text": "Alice", "type": "Person"},
      {"text": "Acme Corp", "type": "Organization"},
      {"text": "WidgetPro", "type": "Product"}
    ]
  },
  {
    "input": "Berlin headquarters shipped my order early. Absolutely thrilled with the service.",
    "sentiment": {
      "label": "positive",
      "score": 0.85
    },
    "entities": [
      {"text": "Berlin", "type": "Location"}
    ]
  },
  {
    "input": "I contacted support because the Model X battery drains overnight. Very frustrating experience.",
    "sentiment": {
      "label": "negative",
      "score": -0.75
    },
    "entities": [
      {"text": "Model X", "type": "Product"}
    ]
  }
]

Wrap up and next steps

The whole script is under fifty lines and needs no training data. Because Oxlo.ai charges per request instead of per token, you can throw long-form reviews at it without watching the meter run on input tokens.

Two concrete ways to extend this. First, add a Pydantic model to validate the JSON schema before you write to your database. Second, wrap the analyzer in a FastAPI endpoint and stream results back with Oxlo.ai's streaming responses so the client sees entities populate as they are generated.

Top comments (0)