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
- Python 3.10 or newer
- The OpenAI SDK:
pip install openai - An Oxlo.ai API key from https://portal.oxlo.ai
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)