DEV Community

shashank ms
shashank ms

Posted on

Using LLM for Sentiment Analysis and Opinion Mining in Social Media

Social media generates endless unstructured opinion data. We will build a lightweight Python pipeline that ingests raw posts and extracts structured sentiment, emotion, and key opinion targets using an LLM. The finished tool fits into any brand analytics stack and runs on Oxlo.ai's flat per-request pricing, so analyzing long threads or large batches stays predictable.

What you'll need

Step 1: Set up the Oxlo.ai client

Initialize the OpenAI-compatible client pointing at Oxlo.ai. I keep my key in an environment variable, but a hardcoded string works for local testing.

import os
from openai import OpenAI

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

Step 2: Define the system prompt

The system prompt forces the model to return strict JSON and tells it what to extract: sentiment label, confidence score, primary emotion, targeted entities, and a brief reasoning string.

SYSTEM_PROMPT = """You are a social media sentiment analysis engine.
Analyze the user-provided post and return a single JSON object with these exact keys:
- sentiment: one of ["positive", "neutral", "negative"]
- confidence: float between 0.0 and 1.0
- emotion: one of ["joy", "anger", "sadness", "fear", "surprise", "disgust", "neutral"]
- targets: list of strings, the brands, products, or people mentioned
- reasoning: one sentence explaining why

Rules:
- Output ONLY valid JSON.
- Do not wrap the JSON in markdown code fences.
- If the post is ambiguous, use "neutral" and set confidence below 0.6.
"""

Step 3: Build the analysis function

This helper takes a raw post string, calls Llama 3.3 70B with JSON mode enabled, parses the response, and returns a Python dict. If your social data is multilingual, swap the model ID to qwen-3-32b.

import json

def analyze_post(post_text: str) -> dict:
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": post_text},
        ],
        response_format={"type": "json_object"},
        temperature=0.1,
        max_tokens=512,
    )
    
    raw = response.choices[0].message.content
    return json.loads(raw)

Step 4: Ingest a batch of posts

Real social listening means processing multiple posts. This loop iterates over a list, calls analyze_post for each, and collects the results. Because Oxlo.ai charges per request rather than per token, running this on verbose posts or long comment threads costs the same as short ones.

raw_posts = [
    "Just spent 4 hours on hold with @AcmeCorp support. Absolutely furious. Never again.",
    "The new Oxlo.ai pricing model is a lifesaver for our agentic workflows. Huge fan.",
    "Meh, the update is okay I guess. Nothing special but it works.",
    "Why does every AI tool claim to be 'revolutionary'? Starting to feel like marketing fluff.",
]

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

Step 5: Aggregate and report

Turn the structured outputs into actionable metrics. This simple aggregation counts sentiment distribution and surfaces the most frequently mentioned targets.

from collections import Counter

sentiment_counts = Counter([r["sentiment"] for r in results])
target_counts = Counter()
for r in results:
    for t in r.get("targets", []):
        target_counts[t.lower()] += 1

print("Sentiment Distribution:", dict(sentiment_counts))
print("Top Mentioned Targets:", target_counts.most_common(5))

for r in results:
    print(f"[{r['sentiment'].upper()}] {r['emotion']} | targets: {r['targets']}")
    print(f"    Reasoning: {r['reasoning']}")
    print()

Run it

Save the script as sentiment_pipeline.py and run it. Here is the complete runnable file followed by the output I see on my machine.

import os
import json
from collections import Counter
from openai import OpenAI

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

SYSTEM_PROMPT = """You are a social media sentiment analysis engine.
Analyze the user-provided post and return a single JSON object with these exact keys:
- sentiment: one of ["positive", "neutral", "negative"]
- confidence: float between 0.0 and 1.0
- emotion: one of ["joy", "anger", "sadness", "fear", "surprise", "disgust", "neutral"]
- targets: list of strings, the brands, products, or people mentioned
- reasoning: one sentence explaining why

Rules:
- Output ONLY valid JSON.
- Do not wrap the JSON in markdown code fences.
- If the post is ambiguous, use "neutral" and set confidence below 0.6.
"""

def analyze_post(post_text: str) -> dict:
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": post_text},
        ],
        response_format={"type": "json_object"},
        temperature=0.1,
        max_tokens=512,
    )
    raw = response.choices[0].message.content
    return json.loads(raw)

if __name__ == "__main__":
    raw_posts = [
        "Just spent 4 hours on hold with @AcmeCorp support. Absolutely furious. Never again.",
        "The new Oxlo.ai pricing model is a lifesaver for our agentic workflows. Huge fan.",
        "Meh, the update is okay I guess. Nothing special but it works.",
        "Why does every AI tool claim to be 'revolutionary'? Starting to feel like marketing fluff.",
    ]

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

    sentiment_counts = Counter([r["sentiment"] for r in results])
    target_counts = Counter()
    for r in results:
        for t in r.get("targets", []):
            target_counts[t.lower()] += 1

    print("Sentiment Distribution:", dict(sentiment_counts))
    print("Top Mentioned Targets:", target_counts.most_common(5))
    print()

    for r in results:
        print(f"[{r['sentiment'].upper()}] {r['emotion']} | targets: {r['targets']}")
        print(f"    Reasoning: {r['reasoning']}")
        print()

Example output:

Sentiment Distribution: {'negative': 2, 'positive': 1, 'neutral': 1}
Top Mentioned Targets: [('acmecorp support', 1), ('oxlo.ai pricing model', 1), ('ai tool', 1)]

[NEGATIVE] anger | targets: ['AcmeCorp support']
    Reasoning: The user expresses extreme frustration after a long wait time and states they will never use the service again.

[POSITIVE] joy | targets: ['Oxlo.ai pricing model']
    Reasoning: The user explicitly praises the pricing model and calls themselves a huge fan.

[NEUTRAL] neutral | targets: ['update']
    Reasoning: The post is lukewarm, acknowledging functionality without enthusiasm or criticism.

[NEGATIVE] disgust | targets: ['AI tool']
    Reasoning: The user criticizes repetitive marketing language and expresses skepticism.

Next steps

Wire the pipeline to a real-time source by replacing the static raw_posts list with a webhook listener that ingests live platform feeds. For deeper insight, add a second pass with kimi-k2.6 or deepseek-v3.2 to generate summary reports from the aggregated JSON instead of reading rows manually.

Top comments (0)