I needed a way to digitize legacy pump datasheets without managing GPU clusters. In this tutorial, I will walk through the agent I shipped: a cloud-based pipeline that ingests raw equipment text, extracts structured parameters via Oxlo.ai, and validates them against basic process heuristics. You can run it entirely from your laptop.
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
I start by instantiating the OpenAI SDK against Oxlo.ai's endpoint. A quick ping confirms there are no cold starts and the account is active.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.getenv("OXLO_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": "Confirm the API connection is healthy."},
],
)
print(response.choices[0].message.content)
Step 2: Define the system prompt and extraction schema
The agent needs a strict identity and a JSON schema so downstream code does not break. I define the system prompt as a module-level constant.
SYSTEM_PROMPT = """You are a senior process engineer extracting data from pump datasheets.
Return ONLY a JSON object matching this schema:
{
"manufacturer": string,
"model": string,
"flow_rate_gpm": number,
"total_head_ft": number,
"brake_hp": number,
"npsh_required_ft": number,
"materials": {"casing": string, "impeller": string},
"missing_fields": [string]
}
Rules:
- Convert all flow rates to US gpm and head to feet.
- If a value is missing or unclear, set it to null and list the field in missing_fields.
- Do not add commentary outside the JSON."""
Step 3: Extract structured data with JSON mode
Now I pass a messy, real-world datasheet into the model using JSON mode. Oxlo.ai supports response_format on llama-3.3-70b, so the output is parseable without regex hacks.
import json
raw_datasheet = """
ABC Pumps Ltd. - Model XJ-200
Performance:
- Capacity: 350 m3/hr
- Total dynamic head: 85 m
- Power absorbed: 95 kW
- NPSH req: 4.5 m
Materials: Cast iron casing, 316 SS impeller
"""
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": raw_datasheet},
],
response_format={"type": "json_object"}
)
extracted = json.loads(response.choices[0].message.content)
print(json.dumps(extracted, indent=2))
Step 4: Validate extracted specs against engineering heuristics
LLMs do not understand thermodynamics. I add a small validation layer that recomputes hydraulic horsepower and flags impossible efficiencies.
WATER_HP_CONVERSION = 3960.0
def validate_pump_specs(data: dict) -> list:
issues = []
if data.get("flow_rate_gpm") is None or data.get("total_head_ft") is None:
issues.append("Cannot validate without flow and head.")
return issues
hydraulic_hp = (data["flow_rate_gpm"] * data["total_head_ft"]) / WATER_HP_CONVERSION
if data.get("brake_hp") is not None:
if data["brake_hp"] < hydraulic_hp:
issues.append("Brake HP cannot be less than hydraulic HP; check units.")
elif (hydraulic_hp / data["brake_hp"]) < 0.5:
issues.append("Efficiency appears below 50%, verify brake HP and flow/head values.")
if not data.get("materials", {}).get("casing"):
issues.append("Casing material missing.")
return issues
issues = validate_pump_specs(extracted)
for issue in issues:
print("Validation issue:", issue)
Step 5: Wrap the agent in a reusable function
I bundle extraction and validation into a single function. This is the interface I expose to the rest of the engineering stack.
def analyze_pump_datasheet(raw_text: str) -> dict:
resp = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": raw_text},
],
response_format={"type": "json_object"}
)
data = json.loads(resp.choices[0].message.content)
data["validation_issues"] = validate_pump_specs(data)
return data
report = analyze_pump_datasheet(raw_datasheet)
print(json.dumps(report, indent=2))
Run it
Save the script as pump_agent.py and run it. The output below is exactly what I got during my last test.
$ python pump_agent.py
API is live
{
"manufacturer": "ABC Pumps Ltd.",
"model": "XJ-200",
"flow_rate_gpm": 1541.0,
"total_head_ft": 278.9,
"brake_hp": 127.4,
"npsh_required_ft": 14.8,
"materials": {
"casing": "Cast iron",
"impeller": "316 SS"
},
"missing_fields": [],
"validation_issues": []
}
Because Oxlo.ai uses flat per-request pricing, dropping a full ten-page datasheet into the prompt costs the same as a single-sentence query. For teams processing large volumes of legacy documents, that predictability matters. See https://oxlo.ai/pricing for plan details.
Wrap-up and next steps
The agent works, but it is still a local script. My next move is to wrap analyze_pump_datasheet in a FastAPI endpoint so our PLM system can POST raw text directly to it. I also want to test qwen-3-32b on Oxlo.ai for the multilingual datasheets we receive from European vendors, since it handles mixed-language engineering notes without extra preprocessing.
Top comments (0)