DEV Community

shashank ms
shashank ms

Posted on

Environmental Science Applications of LLMs

We are going to build an environmental compliance agent that ingests a daily air quality monitoring log and a local emissions regulation snippet, then flags any exceedances and drafts corrective actions. This helps environmental consultants and site operators automate the first pass of routine compliance checks without writing brittle rule engines. Because Oxlo.ai uses flat per-request pricing (pricing), sending long monitoring logs with dozens of readings stays predictable even as daily volume grows.

What you'll need

  • Python 3.10 or newer
  • The OpenAI SDK: pip install openai
  • An Oxlo.ai API key from https://portal.oxlo.ai (the Free tier includes 60 requests/day)

1. Set up the Oxlo.ai client

I import the OpenAI SDK and point it at Oxlo.ai. Because the platform is fully OpenAI-compatible, this is the only client setup required.

from openai import OpenAI
import os

client = OpenAI(
    base_url="https://api.oxlo.ai/v1",
    api_key=os.environ.get("OXLO_API_KEY")
)

# Quick sanity check
response = client.chat.completions.create(
    model="llama-3.3-70b",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Say 'Oxlo.ai connection OK'"},
    ],
    max_tokens=10,
)
print(response.choices[0].message.content)

2. Prepare the regulation and monitoring log

I hardcode a tiny regulation excerpt and a fictional daily log so the tutorial stays reproducible without external PDF parsers.

REGULATION_SNIPPET = """
Facility ID: STK-09
Parameter limits (24-hr average):
- PM2.5: must not exceed 35 micrograms/m3
- SO2: must not exceed 75 ppb
- NOx: must not exceed 100 ppb
Exceedances require a corrective action report within 48 hours.
"""

MONITORING_LOG = """
Date: 2024-06-12
Station: STK-09-A
- PM2.5: 28 micrograms/m3 (06:00), 52 micrograms/m3 (14:00), 31 micrograms/m3 (22:00)
- SO2: 60 ppb (06:00), 68 ppb (14:00), 71 ppb (22:00)
- NOx: 110 ppb (06:00), 95 ppb (14:00), 88 ppb (22:00)
Notes: West wind, maintenance on scrubber unit B.
"""

3. Write the system prompt

I keep the prompt in a module-level constant so I can version it in git. It instructs the model to compute averages, compare limits, and return strict JSON.

SYSTEM_PROMPT = """
You are an environmental compliance analyst.
Given a regulation snippet and a daily monitoring log, do the following:
1. Compute the 24-hour average for each parameter using the readings provided.
2. Compare each average against the regulatory limit.
3. For each exceedance, output: parameter, computed_average, limit, severity (Low/Medium/High), and a one-sentence corrective action.
4. If no exceedance, state compliance_status as "Compliant".
5. Return valid JSON with keys: facility_id, date, compliance_status, exceedances (list), and summary.
Do not include markdown formatting in the JSON.
"""

4. Build the JSON analysis function

I wrap the call in a function so I can batch-process logs. I enable JSON mode to avoid parsing brittle markdown.

import json

def analyze_compliance(regulation, log):
    user_message = f"REGULATION:\n{regulation}\n\nMONITORING LOG:\n{log}"
    
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_message},
        ],
        response_format={"type": "json_object"},
    )
    
    raw = response.choices[0].message.content
    return json.loads(raw)

report = analyze_compliance(REGULATION_SNIPPET, MONITORING_LOG)
print(json.dumps(report, indent=2))

5. Draft a human-readable summary

Site managers do not read raw JSON. I run a second pass to turn the structured report into a short paragraph. This two-step pattern keeps the math auditable and the narrative optional.

def summarize_report(report_json):
    user_message = f"Summarize this compliance report in two sentences for a site manager. Be specific about any exceedances.\n\n{json.dumps(report_json)}"
    
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": "You are a concise environmental officer."},
            {"role": "user", "content": user_message},
        ],
    )
    return response.choices[0].message.content

summary = summarize_report(report)
print(summary)

Run it

Here is the complete script. When I ran it against Oxlo.ai, the agent correctly calculated the PM2.5 average as 37.0 micrograms/m3 and flagged the exceedance.

from openai import OpenAI
import os
import json

client = OpenAI(
    base_url="https://api.oxlo.ai/v1",
    api_key=os.environ.get("OXLO_API_KEY")
)

REGULATION_SNIPPET = """
Facility ID: STK-09
Parameter limits (24-hr average):
- PM2.5: must not exceed 35 micrograms/m3
- SO2: must not exceed 75 ppb
- NOx: must not exceed 100 ppb
Exceedances require a corrective action report within 48 hours.
"""

MONITORING_LOG = """
Date: 2024-06-12
Station: STK-09-A
- PM2.5: 28 micrograms/m3 (06:00), 52 micrograms/m3 (14:00), 31 micrograms/m3 (22:00)
- SO2: 60 ppb (06:00), 68 ppb (14:00), 71 ppb (22:00)
- NOx: 110 ppb (06:00), 95 ppb (14:00), 88 ppb (22:00)
Notes: West wind, maintenance on scrubber unit B.
"""

SYSTEM_PROMPT = """
You are an environmental compliance analyst.
Given a regulation snippet and a daily monitoring log, do the following:
1. Compute the 24-hour average for each parameter using the readings provided.
2. Compare each average against the regulatory limit.
3. For each exceedance, output: parameter, computed_average, limit, severity (Low/Medium/High), and a one-sentence corrective action.
4. If no exceedance, state compliance_status as "Compliant".
5. Return valid JSON with keys: facility_id, date, compliance_status, exceedances (list), and summary.
Do not include markdown formatting in the JSON.
"""

def analyze_compliance(regulation, log):
    user_message = f"REGULATION:\n{regulation}\n\nMONITORING LOG:\n{log}"
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_message},
        ],
        response_format={"type": "json_object"},
    )
    raw = response.choices[0].message.content
    return json.loads(raw)

def summarize_report(report_json):
    user_message = f"Summarize this compliance report in two sentences for a site manager. Be specific about any exceedances.\n\n{json.dumps(report_json)}"
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": "You are a concise environmental officer."},
            {"role": "user", "content": user_message},
        ],
    )
    return response.choices[0].message.content

if __name__ == "__main__":
    report = analyze_compliance(REGULATION_SNIPPET, MONITORING_LOG)
    print("--- JSON REPORT ---")
    print(json.dumps(report, indent=2))
    print("\n--- SUMMARY ---")
    print(summarize_report(report))

Example JSON output:

{
  "facility_id": "STK-09",
  "date": "2024-06-12",
  "compliance_status": "Non-Compliant",
  "exceedances": [
    {
      "parameter": "PM2.5",
      "computed_average": "37.0 micrograms/m3",
      "limit": "35 micrograms/m3",
      "severity": "Medium",
      "corrective_action": "Inspect scrubber unit B and increase stack monitoring frequency until readings stabilize below the limit."
    }
  ],
  "summary": "One parameter exceeded the 24-hour average limit."
}

Example summary paragraph:

The STK-09 site exceeded the PM2.5 24-hour average on June 12 with a reading of 37.0 micrograms/m3, likely due to the scrubber unit B maintenance. Inspect the unit and resample within 24 hours to confirm the correction.

Wrap-up and next steps

That is a working compliance agent. The flat per-request model on Oxlo.ai means you can feed it multi-page logs and lengthy regulation excerpts without watching token costs scale. Two concrete ways to extend this:

  • Replace the hardcoded regulation with a retrieval step. Use Oxlo.ai's embeddings endpoint (BGE-Large) to pull relevant clauses from a full PDF regulation library and inject them into the prompt.
  • Schedule the script as a daily cron job that reads emailed monitoring logs, runs the analysis, and posts the JSON report to your internal dashboard.

Top comments (0)