DEV Community

shashank ms
shashank ms

Posted on

Real-World Challenges of LLM Applications

Most LLM tutorials stop at a single prompt. In production you are paying for long context, wrestling with structured output, and wiring in tools. We will build a support ticket resolution agent that reads a long email thread, queries an internal runbook, and returns structured JSON with citations, running on Oxlo.ai flat per-request pricing so thread length never spikes the bill.

What you'll need

Step 1: Verify the connection to Oxlo.ai

I always start with a smoke test. This snippet initializes the OpenAI SDK against Oxlo.ai and pings Llama 3.3 70B to confirm the endpoint and key are live.

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": "user", "content": "Say ok"}],
    max_tokens=10
)
print(response.choices[0].message.content)

Step 2: Define the output schema and system prompt

Real agents need structured output, not prose. I define a Pydantic schema and a strict system prompt that forces JSON mode and requires citations from the runbook.

SYSTEM_PROMPT = """You are a senior support engineer. Analyze the customer ticket thread and produce a structured resolution draft.

Rules:
1. State the root cause in one sentence.
2. Call the search_runbook tool to find the relevant internal guide.
3. Draft a calm, specific customer reply.
4. Cite the runbook section you used.
5. Output strict JSON matching the requested schema. No markdown fences."""

from pydantic import BaseModel, Field

class TicketResolution(BaseModel):
    root_cause: str = Field(description="One-sentence root cause")
    runbook_section: str = Field(description="Exact section title used")
    draft_reply: str = Field(description="Customer-facing reply with citation")
    confidence: str = Field(description="high, medium, or low")

Step 3: Build the runbook lookup tool

Next I stub the internal runbook as a Python dict and expose it to the model via the standard OpenAI tools schema.

import json

RUNBOOK_DB = {
    "smtp": "Section 4.2: If customers report email delivery delays after DNS changes, verify SPF record propagation and flush local DNS cache.",
    "billing": "Section 7.1: Prorated refunds require the account to be active for less than 14 days and no usage over 10 GB.",
    "oauth": "Section 9.3: Redirect URI mismatches after deployments usually mean the env variable OAUTH_CALLBACK was not promoted."
}

def search_runbook(query: str) -> str:
    for keyword, text in RUNBOOK_DB.items():
        if keyword in query.lower():
            return text
    return "No exact match found. Escalate to L3."

tools = [
    {
        "type": "function",
        "function": {
            "name": "search_runbook",
            "description": "Search the internal support runbook by keyword.",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "Keyword or phrase to look up, e.g. smtp, billing, oauth."
                    }
                },
                "required": ["query"]
            }
        }
    }
]

Step 4: Implement the agent loop

This is the core loop. I pass the full thread, let the model call the runbook tool if it wants, then feed the result back and demand final JSON. Because Oxlo.ai charges per request rather than per token, a fifty-message thread costs the same as a short one.

def resolve_ticket(thread_text: str) -> TicketResolution:
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": f"Ticket thread:\n{thread_text}\n\nProduce the JSON output now."}
    ]

    response = client.chat.completions.create(
        model="qwen-3-32b",
        messages=messages,
        tools=tools,
        tool_choice="auto",
        response_format={"type": "json_object"},
        temperature=0.2
    )

    msg = response.choices[0].message

    if msg.tool_calls:
        tool_call = msg.tool_calls[0]
        args = json.loads(tool_call.function.arguments)
        runbook_result = search_runbook(args["query"])

        messages.append({
            "role": "assistant",
            "content": msg.content or "",
            "tool_calls": [
                {
                    "id": tool_call.id,
                    "type": "function",
                    "function": {
                        "name": tool_call.function.name,
                        "arguments": tool_call.function.arguments
                    }
                }
            ]
        })
        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": runbook_result
        })

        final = client.chat.completions.create(
            model="qwen-3-32b",
            messages=messages,
            response_format={"type": "json_object"},
            temperature=0.2
        )
        raw = final.choices[0].message.content
    else:
        raw = msg.content

    data = json.loads(raw)
    return TicketResolution(**data)

Run it

I run the agent against a long, realistic thread. The flat pricing means I do not have to truncate history to save money.

if __name__ == "__main__":
    long_thread = """
From: alice@client.com
Subject: Emails bouncing after weekend migration

Hi team,

We cut over our DNS to your platform on Saturday. Since Monday morning, roughly 30% of outbound messages are delayed by 4+ hours. No changes were made on our end after go-live.

From: support@ours.com
Can you share the exact error code?

From: alice@client.com
It says 4.4.7 timeout. Our SPF record looks correct in the dashboard.

From: support@ours.com
Thanks. We will investigate and update shortly.

[additional back-and-forth omitted for brevity]
"""

    result = resolve_ticket(long_thread)
    print(result.model_dump_json(indent=2))

Example output:

{
  "root_cause": "SPF record propagation delay after DNS cutover caused intermittent SMTP timeouts.",
  "runbook_section": "Section 4.2: If customers report email delivery delays after DNS changes, verify SPF record propagation and flush local DNS cache.",
  "draft_reply": "Thanks for your patience, Alice. We traced the delay to SPF record propagation following your Saturday DNS cutover. Please verify your SPF record is fully propagated and flush your local DNS cache per Section 4.2. Let us know if delays persist after 24 hours.",
  "confidence": "high"
}

Wrap-up

Swap the stub runbook for a real vector store like pgvector or Milvus and you have a production support pipeline. If you need deeper reasoning on ambiguous threads, route them through DeepSeek R1 671B on Oxlo.ai. For details on flat per-request pricing that stays predictable at scale, see https://oxlo.ai/pricing.

Top comments (0)