DEV Community

shashank ms
shashank ms

Posted on

LLM Models for Sentiment Analysis and Opinion Mining

Introduction

We're building a sentiment analyzer that reads raw customer feedback and returns structured opinion data: overall sentiment, per-aspect polarity, and dominant emotions. Product teams and support leads can pipe this directly into dashboards without writing custom NLP pipelines. Because Oxlo.ai charges a flat rate per request, processing long reviews or large batches does not inflate costs the way token-based billing would.

What you'll need

  • Python 3.10 or newer
  • The OpenAI SDK: pip install openai
  • An Oxlo.ai API key from https://portal.oxlo.ai
  • A few sample text snippets to analyze

Step 1: Configure the Oxlo.ai client

I start by importing the SDK and pointing it at Oxlo.ai. The base URL and client pattern are fully OpenAI-compatible, so the only difference is the endpoint and key.

import json
import os
from openai import OpenAI

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

Step 2: Define the analysis prompt

The system prompt locks the model into a strict JSON schema. I keep temperature low and instructions explicit so the output is predictable and easy to parse.

SYSTEM_PROMPT = """You are a sentiment analysis and opinion mining engine. Analyze the user text and return only a JSON object with this exact structure:
{
  "sentiment": "positive" | "negative" | "neutral" | "mixed",
  "confidence": 0.0 to 1.0,
  "emotions": ["emotion1", "emotion2"],
  "key_opinions": [
    {"aspect": "topic discussed", "polarity": "positive" | "negative" | "neutral"}
  ],
  "summary": "one sentence capturing the overall stance"
}
Do not wrap the JSON in markdown blocks. Do not add commentary outside the JSON."""

Step 3: Build the core analyzer

This function sends a single text block to Llama 3.3 70B and parses the response. I use Llama 3.3 70B because it follows structured instructions reliably and runs without cold starts on Oxlo.ai. If you need deeper reasoning for sarcastic or ambiguous text, swap the model string to kimi-k2.6 or deepseek-v3.2 with no other changes.

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},
        ],
        temperature=0.1,
        max_tokens=512,
    )
    
    raw = response.choices[0].message.content.strip()
    
    # Defensive strip of accidental markdown fences
    if raw.startswith("

```json"):
        raw = raw.split("```

json", 1)[1]
    if raw.endswith("

```"):
        raw = raw.rsplit("```

", 1)[0]
    
    return json.loads(raw.strip())

Step 4: Add batch processing

Real feedback comes in lists, not singles. This wrapper loops over a collection, attaches a preview of the source text to each result, and catches parsing errors so one bad response does not kill the whole job.

REVIEWS = [
    "The battery life on this laptop is incredible, but the fan noise is unbearable during video calls.",
    "Shipping was fast and the packaging was perfect. Will order again.",
    "I expected better customer service. Waited 40 minutes and got no resolution.",
]

def analyze_batch(texts: list[str]) -> list[dict]:
    results = []
    for text in texts:
        try:
            result = analyze_text(text)
            result["source_text_preview"] = text[:160]
            results.append(result)
        except Exception as e:
            results.append({
                "error": str(e),
                "source_text_preview": text[:160],
            })
    return results

Step 5: Format the report

Finally, I add a small formatter that prints the mined opinions in a readable table. This is the layer you would replace with a database write or a Slack webhook in production.

def print_report(results: list[dict]):
    for r in results:
        if "error" in r:
            print(f"ERROR: {r['error']}\n")
            continue
        
        print(f"Review: {r['source_text_preview']}")
        print(f"Sentiment: {r['sentiment']} (confidence: {r['confidence']:.2f})")
        print(f"Emotions: {', '.join(r['emotions'])}")
        print("Opinions:")
        for op in r["key_opinions"]:
            print(f"  - {op['aspect']}: {op['polarity']}")
        print(f"Summary: {r['summary']}\n")

if __name__ == "__main__":
    parsed = analyze_batch(REVIEWS)
    print_report(parsed)

Run it

Save the full script as sentiment_miner.py, export your key, and run it.

export OXLO_API_KEY="your-key-from-portal.oxlo.ai"
python sentiment_miner.py

Expected output:

Review: The battery life on this laptop is incredible, but the fan noise is unbearable during video calls.
Sentiment: mixed (confidence: 0.91)
Emotions: satisfaction, frustration
Opinions:
  - battery life: positive
  - fan noise: negative
Summary: The user praises battery life but is frustrated by loud fan noise during calls.

Review: Shipping was fast and the packaging was perfect. Will order again.
Sentiment: positive (confidence: 0.96)
Emotions: gratitude, excitement
Opinions:
  - shipping speed: positive
  - packaging: positive
Summary: The customer is highly satisfied with fast shipping and perfect packaging.

Review: I expected better customer service. Waited 40 minutes and got no resolution.
Sentiment: negative (confidence: 0.88)
Emotions: disappointment, anger
Opinions:
  - customer service: negative
  - wait time: negative
Summary: The user is disappointed with long wait times and lack of resolution.

Next steps

Wire the analyzer to a real data source, such as a daily export from your support inbox or a Zapier hook that fires on new app-store reviews. If volume grows, keep an eye on Oxlo.ai's request-based pricing at https://oxlo.ai/pricing. It stays flat regardless of how verbose your customers get, which makes it easy to forecast costs for long-form feedback.

Top comments (0)