DEV Community

shashank ms
shashank ms

Posted on

LLM Models for Sentiment Analysis and Opinion Mining in Social Media

We are building a real-time social media opinion miner that classifies sentiment, extracts topics, and flags urgent complaints. It helps community managers and product teams cut through noise without reading every thread.

What you'll need

Step 1: Connect to Oxlo.ai and test the endpoint

Before we process any social data, we need to verify the client can reach Oxlo.ai and that our key works. We will make a simple call to Llama 3.3 70B. If you later need to handle multilingual streams, Oxlo.ai lets you swap the model string to qwen-3-32b with no other changes.

from openai import OpenAI

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

response = client.chat.completions.create(
    model="llama-3.3-70b",
    messages=[
        {"role": "user", "content": "Reply with OK if you are online."},
    ],
)

print(response.choices[0].message.content)

Step 2: Craft the system prompt for structured sentiment analysis

Social media text is noisy. A strict system prompt forces the model to return uniform JSON with sentiment labels, confidence scores, mentioned topics, and urgency flags.

SYSTEM_PROMPT = """You are a social media opinion mining engine. Analyze the provided post and return a single JSON object with these exact keys:

- sentiment: one of "positive", "neutral", "negative"
- confidence: integer 1-10
- topics: array of up to 3 strings mentioned in the post
- urgent: boolean, true only if the post signals a bug, outage, safety issue, or requires immediate response
- summary: one sentence summarizing the user's core opinion

Rules:
- Output ONLY valid JSON. No markdown fences, no explanations.
- If the post is sarcastic, classify by the intended meaning.
- Topics should be lowercase with no spaces, use underscores if needed."""

Step 3: Build the analysis function with JSON mode

Now we wire the prompt into a reusable function. We use Oxlo.ai's JSON mode to guarantee parseable output, which lets us skip regex cleanup.

import json
from openai import OpenAI

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

SYSTEM_PROMPT = """You are a social media opinion mining engine. Analyze the provided post and return a single JSON object with these exact keys:

- sentiment: one of "positive", "neutral", "negative"
- confidence: integer 1-10
- topics: array of up to 3 strings mentioned in the post
- urgent: boolean, true only if the post signals a bug, outage, safety issue, or requires immediate response
- summary: one sentence summarizing the user's core opinion

Rules:
- Output ONLY valid JSON. No markdown fences, no explanations.
- If the post is sarcastic, classify by the intended meaning.
- Topics should be lowercase with no spaces, use underscores if needed."""

def analyze_post(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,
    )
    return json.loads(response.choices[0].message.content)

sample = "Love the new update, but the login page keeps crashing every time I try 2FA."
print(analyze_post(sample))

Step 4: Batch process a feed

In production, posts arrive in bursts. We process a list of raw messages sequentially and collect the results into a list of dictionaries for downstream reporting.

import json
from openai import OpenAI

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

SYSTEM_PROMPT = """You are a social media opinion mining engine. Analyze the provided post and return a single JSON object with these exact keys:

- sentiment: one of "positive", "neutral", "negative"
- confidence: integer 1-10
- topics: array of up to 3 strings mentioned in the post
- urgent: boolean, true only if the post signals a bug, outage, safety issue, or requires immediate response
- summary: one sentence summarizing the user's core opinion

Rules:
- Output ONLY valid JSON. No markdown fences, no explanations.
- If the post is sarcastic, classify by the intended meaning.
- Topics should be lowercase with no spaces, use underscores if needed."""

def analyze_post(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,
    )
    return json.loads(response.choices[0].message.content)

raw_posts = [
    "Just switched to your pro plan. The export feature is a lifesaver!",
    "ugh... the mobile app is so laggy after the latest patch. fix it already.",
    "Does anyone know if webhook retries are configurable? docs are unclear.",
    "Shoutout to support team for fixing my billing issue in under 5 mins.",
    "CRITICAL: payment webhook fired but invoice shows unpaid. losing revenue here."
]

results = []
for post in raw_posts:
    try:
        parsed = analyze_post(post)
        parsed["original"] = post
        results.append(parsed)
    except Exception as e:
        print(f"Failed on post: {post[:50]}... Error: {e}")

print(f"Processed {len(results)} posts successfully.")

Step 5: Aggregate and surface insights

Raw classifications are not enough. We need a small aggregator that counts sentiment distribution, lists top topics, and surfaces any urgent posts that need immediate attention.

from collections import Counter
import json

# Assumes `results` is populated from Step 4
def aggregate(results: list[dict]) -> dict:
    sentiments = Counter([r["sentiment"] for r in results])
    topics = Counter()
    urgent = []

    for r in results:
        topics.update(r.get("topics", []))
        if r.get("urgent"):
            urgent.append({
                "summary": r["summary"],
                "original": r["original"],
                "confidence": r["confidence"]
            })

    return {
        "total": len(results),
        "sentiment_counts": dict(sentiments),
        "top_topics": topics.most_common(5),
        "urgent_posts": urgent
    }

report = aggregate(results)
print(json.dumps(report, indent=2))

Run it

Putting it all together, the script ingests five realistic social posts and prints a structured report. Here is the complete script and the output it produces.

import json
from collections import Counter
from openai import OpenAI

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

SYSTEM_PROMPT = """You are a social media opinion mining engine. Analyze the provided post and return a single JSON object with these exact keys:

- sentiment: one of "positive", "neutral", "negative"
- confidence: integer 1-10
- topics: array of up to 3 strings mentioned in the post
- urgent: boolean, true only if the post signals a bug, outage, safety issue, or requires immediate response
- summary: one sentence summarizing the user's core opinion

Rules:
- Output ONLY valid JSON. No markdown fences, no explanations.
- If the post is sarcastic, classify by the intended meaning.
- Topics should be lowercase with no spaces, use underscores if needed."""

def analyze_post(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,
    )
    return json.loads(response.choices[0].message.content)

raw_posts = [
    "Just switched to your pro plan. The export feature is a lifesaver!",
    "ugh... the mobile app is so laggy after the latest patch. fix it already.",
    "Does anyone know if webhook retries are configurable? docs are unclear.",
    "Shoutout to support team for fixing my billing issue in under 5 mins.",
    "CRITICAL: payment webhook fired but invoice shows unpaid. losing revenue here."
]

results = []
for post in raw_posts:
    try:
        parsed = analyze_post(post)
        parsed["original"] = post
        results.append(parsed)
    except Exception as e:
        print(f"Failed: {e}")

def aggregate(results: list[dict]) -> dict:
    sentiments = Counter([r["sentiment"] for r in results])
    topics = Counter()
    urgent = []
    for r in results:
        topics.update(r.get("topics", []))
        if r.get("urgent"):
            urgent.append({
                "summary": r["summary"],
                "original": r["original"],
                "confidence": r["confidence"]
            })
    return {
        "total": len(results),
        "sentiment_counts": dict(sentiments),
        "top_topics": topics.most_common(5),
        "urgent_posts": urgent
    }

print(json.dumps(aggregate(results), indent=2))

Example output:

{
  "total": 5,
  "sentiment_counts": {
    "positive": 2,
    "neutral": 1,
    "negative": 2
  },
  "top_topics": [
    ["webhook", 2],
    ["payment", 1],
    ["mobile_app", 1],
    ["export_feature", 1],
    ["billing", 1]
  ],
  "urgent_posts": [
    {
      "summary": "Payment webhook fired but invoice remains unpaid causing revenue loss.",
      "original": "CRITICAL: payment webhook fired but invoice shows unpaid. losing revenue here.",
      "confidence": 10
    }
  ]
}

Wrap-up and next steps

This pipeline gives you a working opinion miner on Oxlo.ai. Because Oxlo.ai uses flat request-based pricing, feeding it long threads or multi-turn conversations costs the same as short tweets, which makes it a practical choice for high-volume social monitoring.

Concrete next steps:

  • Wire the analyzer to a live firehose and write results to Postgres.
  • Add a severity threshold that pages the on-call engineer when urgent posts appear.

Top comments (0)