We are building a lightweight text intelligence pipeline that runs sentiment analysis, topic classification, and named entity recognition in a single LLM call. It is useful for support teams, analysts, and developers who need structured insights from unstructured text without maintaining three separate models. Because Oxlo.ai uses flat request-based pricing regardless of input length, you can feed in long support threads or documents without watching costs scale with tokens.
What you'll need
- Python 3.10 or newer
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK:
pip install openai
Step 1: Configure the Oxlo.ai client
First, import the SDK and point it at the Oxlo.ai endpoint. I use Llama 3.3 70B here because it follows mixed instructions and JSON mode reliably, but you can swap in Qwen 3 32B, Kimi K2.6, or DeepSeek V3.2 without changing anything else.
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": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello world"},
],
)
print(response.choices[0].message.content)
Step 2: Write the system prompt
I keep the prompt in a constant so I can tweak labels or categories without touching the Python logic. This instruction forces the model to return a single JSON object containing sentiment, category, and named entities.
SYSTEM_PROMPT = """You are a text analysis engine. Given a user message, output a single JSON object with exactly these keys:
- sentiment: one of "positive", "negative", "neutral", or "mixed"
- category: one of "support", "billing", "sales", or "general"
- entities: an array of objects, each with "text" and "label" (PERSON, ORG, PRODUCT, LOCATION)
Rules:
- Do not include markdown formatting.
- Output only valid JSON.
- If no entities are found, return an empty array."""
Step 3: Implement the analysis function
Now wrap the API call in a function. I enable JSON mode to keep the output structured. On Oxlo.ai, request-based pricing means the cost is the same whether you send a short sentence or a long support transcript, so this pattern stays predictable for bulk jobs.
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 text analysis engine. Given a user message, output a single JSON object with exactly these keys:
- sentiment: one of "positive", "negative", "neutral", or "mixed"
- category: one of "support", "billing", "sales", or "general"
- entities: an array of objects, each with "text" and "label" (PERSON, ORG, PRODUCT, LOCATION)
Rules:
- Do not include markdown formatting.
- Output only valid JSON.
- If no entities are found, return an empty array."""
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"},
)
raw_output = response.choices[0].message.content
return json.loads(raw_output)
Run it
Test the pipeline with a couple of real-world snippets. The first sample mixes praise and criticism, while the second is a plain negative support ticket.
if __name__ == "__main__":
samples = [
"Sarah from TechFlow said the new analytics suite is a game changer, though she finds the invoice portal confusing.",
"I am furious that my shipment from Berlin was delayed again with no explanation.",
]
for text in samples:
result = analyze_text(text)
print(json.dumps(result, indent=2))
Expected output looks like this:
{
"sentiment": "mixed",
"category": "billing",
"entities": [
{"text": "Sarah", "label": "PERSON"},
{"text": "TechFlow", "label": "ORG"},
{"text": "analytics suite", "label": "PRODUCT"},
{"text": "invoice portal", "label": "PRODUCT"}
]
}
{
"sentiment": "negative",
"category": "support",
"entities": [
{"text": "Berlin", "label": "LOCATION"}
]
}
Wrap-up
You can drop this function into a FastAPI endpoint or a Celery worker to process tickets as they arrive. If you need deeper reasoning for ambiguous text, switch the model to kimi-k2.6 or deepseek-v3.2 on Oxlo.ai and leave the rest of the code unchanged.
Top comments (0)