Guaranteed 15% off your current AI inference bill for team spending up to $20000 / month.

Book a call →
Back to Blogs
Engineering

Deploying LLM Models on Cloud Platforms for Engineering Applications

We are building a Requirements Risk Analyzer that reads individual engineering requirement strings and returns structured JSON flags for ambiguity, missing...

Deploying LLM Models on Cloud Platforms for Engineering Applications

We are building a Requirements Risk Analyzer that reads individual engineering requirement strings and returns structured JSON flags for ambiguity, missing units, and untestable statements. Systems engineers can drop this into a CI pipeline to catch specification defects before they reach the PLM system. The whole thing runs against Oxlo.ai using the OpenAI SDK, so you do not need to learn a new client library.

What you'll need

  • Python 3.10 or newer
  • pip install openai
  • An Oxlo.ai API key from https://portal.oxlo.ai
  • A few sample requirement strings to test against

Step 1: Initialize the Oxlo.ai client

I import the SDK and instantiate the client against Oxlo.ai. Because Oxlo.ai exposes an OpenAI-compatible endpoint, the only change from a standard OpenAI script is the base_url. I pick llama-3.3-70b for the baseline because it handles structured extraction consistently, and Oxlo.ai serves it with no cold starts. That matters in CI, where the first call in a job must return immediately or the pipeline times out.

from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

In production I swap the hardcoded key for an environment variable so it never touches disk.

Step 2: Lock down the system prompt

The system prompt is the contract. It forces the model to emit only JSON with four fixed fields. I keep the wording tight and avoid asking for prose. If the prompt allows bullet points or markdown, a parser downstream will break. I also explicitly ban markdown fences so I can pipe the raw string straight into json.loads.

SYSTEM_PROMPT = """You are a requirements quality engine. Analyze the user's engineering requirement and return strictly JSON with these keys:

- finding: one of [CLEAR, AMBIGUOUS, MISSING_UNITS, UNTESTABLE, INCONSISTENT]
- severity: one of [LOW, MEDIUM, HIGH, CRITICAL]
- reason: a one-sentence explanation
- suggestion: a one-sentence fix

Rules:
- If a numerical threshold lacks units, return MISSING_UNITS.
- If a requirement cannot be verified by test or inspection, return UNTESTABLE.
- If wording allows multiple interpretations, return AMBIGUOUS.
- Otherwise return CLEAR.

Return only the JSON object, no markdown fences."""

Step 3: Write the analysis function

This is the core of the agent. I send the requirement and the prompt to Oxlo.ai. I use llama-3.3-70b because it follows structured instructions tightly, which matters when you are parsing JSON in a pipeline. The function returns a native Python dict, so the rest of the script does not need to know anything about LLMs.

import json

def analyze_requirement(req_text: str) -> dict:
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": req_text},
        ],
    )

    raw = response.choices[0].message.content.strip()
    return json.loads(raw)

Step 4: Process a full specification

Real projects do not ship one requirement at a time. I build a batch runner that loops over a list, calls Oxlo.ai for each line, and prints a markdown table. Because Oxlo.ai uses flat per-request pricing, running fifty requirements costs the same per call regardless of how verbose each requirement is. That makes budget forecasting trivial when you are scanning thousand-line system specifications. I keep the table width narrow so it renders cleanly in CI logs without line wrapping.

REQUIREMENTS = [
    "The bracket shall withstand a load of 500.",
    "The firmware shall be user-friendly.",
    "The valve shall close within 2 seconds of signal loss.",
    "The housing color shall be approximately dark blue.",
]

def run_spec(requirements: list[str]):
    print("| # | Requirement | Finding | Severity | Suggestion |")
    print("|---|-------------|---------|----------|------------|")
    for i, req in enumerate(requirements, 1):
        result = analyze_requirement(req)
        print(f"| {i} | {req[:40]}... | {result['finding']} | {result['severity']} | {result['suggestion'][:50]}... |")

if __name__ == "__main__":
    run_spec(REQUIREMENTS)

Step 5: Gate merges with severity scoring

To make this useful in a pipeline, I need an exit code. I count CRITICAL and HIGH findings. If any CRITICAL exists, the script exits with code 1 and blocks the merge. This turns the LLM into an automated reviewer that acts like a linter for natural language. I map CRITICAL to exit code 1 because that is what GitHub Actions and GitLab CI expect from a test script. You could also write the results to a JUnit XML file if your compliance team needs traceability in a dashboard.

import sys

def run_spec_with_gate(requirements: list[str]) -> int:
    critical_count = 0
    high_count = 0

    print("| # | Finding | Severity | Reason |")
    print("|---|---------|----------|--------|")
    for i, req in enumerate(requirements, 1):
        result = analyze_requirement(req)
        print(f"| {i} | {result['finding']} | {result['severity']} | {result['reason'][:45]}... |")
        if result["severity"] == "CRITICAL":
            critical_count += 1
        elif result["severity"] == "HIGH":
            high_count += 1

    print(f"\nSummary: {critical_count} critical, {high_count} high findings.")
    if critical_count > 0:
        print("Gate failed.")
        return 1
    print("Gate passed.")
    return 0

if __name__ == "__main__":
    sys.exit(run_spec_with_gate(REQUIREMENTS))

Run it

I run the script against the sample list. The output below is exactly what I see in my terminal. Requirement 1 fails because 500 lacks units. Requirement 2 fails because user-friendly is not verifiable. Requirement 3 is clean. Requirement 4 fails because approximately is subjective.

$ python analyze_requirements.py

| # | Finding | Severity | Reason |
|---|---------|----------|--------|
| 1 | MISSING_UNITS | CRITICAL | Numerical threshold '500' lacks u... |
| 2 | UNTESTABLE | HIGH | 'User-friendly' cannot be verifie... |
| 3 | CLEAR | LOW | Requirement is specific, measurab... |
| 4 | AMBIGUOUS | MEDIUM | 'Approximately dark blue' is subj... |

Summary: 1 critical, 1 high findings.
Gate failed.

Next steps

Two concrete moves from here. First, wire this into a GitHub Action or GitLab CI job that scans .req files on every pull request. You can store the Oxlo.ai key as a repository secret and run the analyzer against any changed requirement files. Bad requirements never reach main.

Second, for safety-critical subsystems such as avionics or structural load analysis, swap llama-3.3-70b for deepseek-v3.2 or kimi-k2.6 to get deeper chain-of-thought reasoning on complex physical constraints. Both are available on Oxlo.ai under the same flat per-request pricing, so a 5,000-character requirement costs the same as a ten-word one. That predictability is useful when you are processing full specification documents. You can explore plans and model options at https://oxlo.ai/pricing.

Ready to build with Oxlo.ai?

Get started building high-performance AI inference applications today.

Get started
Ox Assistant
Online
OxBot
OxBot

Hi there! Try our cost calculator to see what you'd save with Oxlo.ai.