We are going to build a lightweight topic modeling pipeline that feeds raw text snippets into an LLM and returns a structured taxonomy of themes. It is a direct replacement for classical LDA when you need results in minutes instead of days, and it works especially well on short, noisy documents like support tickets or app reviews.
What you'll need
- An Oxlo.ai API key from https://portal.oxlo.ai
- Python 3.10 or newer
- The OpenAI SDK:
pip install openai
Step 1: Configure the Oxlo.ai client
I start by instantiating the OpenAI-compatible client pointing at Oxlo.ai. Because Oxlo.ai uses flat per-request pricing, I do not need to worry about token costs creeping up as I pass long documents or verbose prompts. See https://oxlo.ai/pricing for details.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
Step 2: Prepare the document list
I hardcode six raw support tickets so the script is fully runnable without external dependencies. In production I would read from a CSV or database.
raw_docs = [
"The login button is broken on mobile Safari after the latest update.",
"I love the new dark mode, but the contrast is too low in the sidebar.",
"Billing receipt never arrived. Support took 3 days to respond.",
"Feature request: export to CSV from the dashboard.",
"Dark mode makes text unreadable. Please fix the font colors.",
"Login fails with a 500 error when I use SSO.",
]
Step 3: Define the system prompt
The system prompt forces structured JSON output so I can parse topics programmatically. I keep it strict to avoid markdown fences or extra narration.
SYSTEM_PROMPT = """You are a topic modeling assistant. Read the user-provided text and extract the main topics.
Rules:
- Output ONLY a JSON array of strings.
- Each string is a concise topic label, 2 to 4 words long.
- Use lowercase with underscores, e.g., "mobile_login_bug".
- Return 1 to 3 topics per document.
- Do not add markdown, explanations, or code fences."""
Step 4: Extract topics from every document
I loop over the documents and call Oxlo.ai for each one. Because Oxlo.ai charges a flat rate per request, the cost is predictable even if some tickets are long rants.
import json
def extract_topics(document: str) -> list[str]:
user_message = document
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
)
return json.loads(response.choices[0].message.content)
topics_per_doc = [extract_topics(doc) for doc in raw_docs]
print(topics_per_doc)
Step 5: Consolidate into a ranked taxonomy
Now I flatten the lists, count frequencies, and sort to get the final topic model. This gives me a human-readable dashboard of what is actually being discussed.
from collections import Counter
flat_topics = [t for sublist in topics_per_doc for t in sublist]
topic_counts = Counter(flat_topics)
for topic, count in topic_counts.most_common():
print(f"{topic}: {count}")
Run it
Save the full script as topic_model.py, set your API key, and run python topic_model.py. You should see output similar to this:
[
["mobile_login_bug", "safari_compatibility"],
["dark_mode_ui", "contrast_accessibility"],
["billing_receipt_issue", "support_response_time"],
["feature_request_csv", "dashboard_export"],
["dark_mode_ui", "font_readability"],
["sso_login_error", "server_error_500"]
]
dark_mode_ui: 2
mobile_login_bug: 1
safari_compatibility: 1
contrast_accessibility: 1
billing_receipt_issue: 1
support_response_time: 1
feature_request_csv: 1
dashboard_export: 1
font_readability: 1
sso_login_error: 1
server_error_500: 1
Next steps
Replace the hardcoded list with a CSV reader and process hundreds of tickets in a batch loop. If you need higher reasoning fidelity for ambiguous documents, swap the model string to kimi-k2.6 or deepseek-v3.2 without changing any other code.
Top comments (0)