LLM for Sentiment Analysis in Product Review Analysis
Product review sentiment analysis at scale means more than positive or negative labels. We will build a structured analyzer that classifies sentiment, extracts specific aspects, and returns machine-readable JSON, all thr
Product review sentiment analysis at scale means more than positive or negative labels. We will build a structured analyzer that classifies sentiment, extracts specific aspects, and returns machine-readable JSON, all through a single Oxlo.ai API request per review. This helps product teams and marketplace vendors turn thousands of raw reviews into actionable data without managing infrastructure.
What you'll need
- Python 3.10 or higher.
- An Oxlo.ai API key from https://portal.oxlo.ai. New accounts get a 7-day full-access trial.
- The OpenAI SDK. Install it with
pip install openai. - A few sample product reviews. We will use inline examples.
Step 1: Configure the Oxlo.ai client
Import the OpenAI SDK and point it at the Oxlo.ai base URL with your API key. Oxlo.ai is fully OpenAI SDK compatible, so the only change is the base_url.
from openai import OpenAI
import json
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key="YOUR_OXLO_API_KEY"
)
Step 2: Write the system prompt
The system prompt constrains the model to return only valid JSON with sentiment labels, aspect scores, and a brief rationale. Keeping this in a constant makes it easy to iterate.
SYSTEM_PROMPT = """You are a product review sentiment analyzer. Analyze the user review and return a single JSON object with exactly these keys:
- sentiment: one of "positive", "neutral", or "negative"
- confidence: a float between 0.0 and 1.0
- aspects: an object with keys "price", "quality", "shipping". Each value is one of "positive", "neutral", or "negative".
- summary: a one-sentence summary of the review
Return only the JSON object, with no markdown formatting."""
Step 3: Analyze a single review
This function sends one review to Llama 3.3 70B on Oxlo.ai and parses the JSON response. We use the model's general-purpose reasoning to handle informal language and mixed sentiments.
def analyze_review(review_text: str):
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": review_text},
],
)
content = response.choices[0].message.content.strip()
# Remove markdown code fences if the model emits them
if content.startswith("
```"):
content = content.split("\n", 1)[1].rsplit("```
", 1)[0].strip()
return json.loads(content)
Step 4: Process a batch of reviews
Real datasets have dozens or thousands of entries. This loop processes a list of reviews, collects results, and catches parsing errors so one bad response does not kill the job.
def analyze_batch(reviews: list[str]):
results = []
for idx, review in enumerate(reviews):
try:
result = analyze_review(review)
result["review_index"] = idx
results.append(result)
except Exception as e:
results.append({
"review_index": idx,
"error": str(e),
"raw_review": review[:200]
})
return results
sample_reviews = [
"Amazing quality but shipping took forever. Worth the price though.",
"Terrible. Broke after one day and customer service ignored me.",
"It's okay. Does what it says. Nothing special.",
]
batch_results = analyze_batch(sample_reviews)
print(json.dumps(batch_results, indent=2))
Step 5: Aggregate into a summary report
Once we have structured JSON for every review, we can compute aggregate statistics without calling the LLM again. This keeps costs flat and predictable on Oxlo.ai request-based pricing, where a long review costs the same per request as a short one.
def summarize_results(results: list[dict]):
total = len(results)
successful = [r for r in results if "error" not in r]
sentiment_counts = {"positive": 0, "neutral": 0, "negative": 0}
for r in successful:
sentiment_counts[r["sentiment"]] += 1
aspect_totals = {
"price": {"positive": 0, "neutral": 0, "negative": 0},
"quality": {"positive": 0, "neutral": 0, "negative": 0},
"shipping": {"positive": 0, "neutral": 0, "negative": 0},
}
for r in successful:
for aspect in aspect_totals:
val = r["aspects"].get(aspect, "neutral")
aspect_totals[aspect][val] += 1
return {
"total_reviews": total,
"successful_analyses": len(successful),
"sentiment_distribution": sentiment_counts,
"aspect_breakdown": aspect_totals,
}
summary = summarize_results(batch_results)
print(json.dumps(summary, indent=2))
Run it
Putting it all together. Here is the complete script and the output it produces when I ran it against Oxlo.ai.
if __name__ == "__main__":
batch_results = analyze_batch(sample_reviews)
print("=== Individual Results ===")
print(json.dumps(batch_results, indent=2))
print("\n=== Summary Report ===")
print(json.dumps(summarize_results(batch_results), indent=2))
Example output:
[
{
"review_index": 0,
"sentiment": "positive",
"confidence": 0.85,
"aspects": {
"price": "positive",
"quality": "positive",
"shipping": "negative"
},
"summary": "User praises quality and price but complains about slow shipping."
},
{
"review_index": 1,
"sentiment": "negative",
"confidence": 0.95,
"aspects": {
"price": "neutral",
"quality": "negative",
"shipping": "neutral"
},
"summary": "User is frustrated that the product broke quickly and support was unresponsive."
},
{
"review_index": 2,
"sentiment": "neutral",
"confidence": 0.75,
"aspects": {
"price": "neutral",
"quality": "neutral",
"shipping": "neutral"
},
"summary": "User finds the product adequate but unremarkable."
}
]
{
"total_reviews": 3,
"successful_analyses": 3,
"sentiment_distribution": {
"positive": 1,
"neutral": 1,
"negative": 1
},
"aspect_breakdown": {
"price": {
"positive": 1,
"neutral": 2,
"negative": 0
},
"quality": {
"positive": 1,
"neutral": 1,
"negative": 1
},
"shipping": {
"positive": 0,
"neutral": 2,
"negative": 1
}
}
}
Next steps
Pipe the JSON output directly into a pandas DataFrame or BI tool for live dashboarding. If you are processing multilingual reviews, swap the model to qwen-3-32b on Oxlo.ai without changing any client code.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.