DEV Community

shashank ms
shashank ms

Posted on

Applying LLM in Chemistry

We are going to build a command-line chemistry assistant that turns a plain-text reaction description into a structured synthesis plan with safety warnings. This is useful for chemists and lab technicians who need to prototype procedures quickly without manually cross-referencing safety datasheets.

What you'll need

Before starting, make sure you have Python 3.10 or newer installed. You will also need an Oxlo.ai API key from https://portal.oxlo.ai and the OpenAI SDK. Install it with pip.

pip install openai

Step 1: Configure the Oxlo.ai client

I always start by verifying the connection with a simple call. This script initializes the OpenAI-compatible client pointing at Oxlo.ai and asks the model to identify a common reagent.

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": "What is the SMILES notation for benzene?"},
    ],
)

print(response.choices[0].message.content)

If you see the correct SMILES string, your environment is ready.

Step 2: Define the chemistry system prompt

The system prompt is the most important part of this project. It constrains the model to act as a synthesis planner and safety officer. I keep it in a dedicated constant so I can iterate on it without touching the rest of the code.

SYSTEM_PROMPT = """You are a chemistry lab assistant specialized in organic synthesis and safety.
When given a reaction description or target compound, do the following:
1. Identify the reaction type (e.g., substitution, reduction, coupling).
2. List the reagents and solvents required.
3. Identify safety hazards: toxicity, flammability, corrosivity, gas evolution, or exothermic risk.
4. Recommend specific PPE and engineering controls.
5. Suggest waste disposal categories for byproducts.
Return your analysis as a single JSON object with these exact keys:
reaction_type, reagents, hazards, ppe, disposal, notes.
If the input is ambiguous, set reaction_type to "unknown" and ask clarifying questions in the notes field."""

Step 3: Build the synthesis planner

Now I wrap the API call into a function that sends the system prompt plus the user's reaction description. I use JSON mode so the output is machine readable and easy to validate downstream.

import json

def plan_synthesis(description: str) -> dict:
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": description},
        ],
        response_format={"type": "json_object"},
        temperature=0.2,
    )
    raw = response.choices[0].message.content
    return json.loads(raw)

# quick test
result = plan_synthesis("Grignard reaction of phenylmagnesium bromide with acetone")
print(json.dumps(result, indent=2))

Step 4: Add safety validation

LLMs can hallucinate safety details, so I add a second pass that treats the proposed plan as a draft and explicitly challenges it. This does not replace a real MSDS review, but it catches obvious gaps before a human verifies the plan.

SAFETY_PROMPT = """You are a safety auditor. Review the following synthesis plan and flag any missing hazards, incorrect PPE, or unsafe disposal advice.
Return a JSON object with keys: approved (boolean), flags (list of strings), corrected_ppe (list), corrected_disposal (string)."""

def validate_safety(plan: dict, original_description: str) -> dict:
    audit_input = f"Original request: {original_description}\nProposed plan: {json.dumps(plan)}"
    response = client.chat.completions.create(
        model="qwen-3-32b",
        messages=[
            {"role": "system", "content": SAFETY_PROMPT},
            {"role": "user", "content": audit_input},
        ],
        response_format={"type": "json_object"},
        temperature=0.1,
    )
    return json.loads(response.choices[0].message.content)

audit = validate_safety(result, "Grignard reaction of phenylmagnesium bromide with acetone")
print(json.dumps(audit, indent=2))

I use qwen-3-32b here because its agentic reasoning works well for structured critique tasks, and Oxlo.ai lets me switch models for free since I only pay per request.

Step 5: Assemble the CLI runner

Finally, I tie the planner and validator together into a small script that accepts a reaction string from the command line and prints a unified report.

import sys

def main():
    if len(sys.argv) < 2:
        print("Usage: python chem_agent.py 'synthesize ethyl acetate from ethanol and acetic acid'")
        sys.exit(1)

    description = sys.argv[1]
    print(f"Analyzing: {description}\n")

    plan = plan_synthesis(description)
    print("=== Synthesis Plan ===")
    print(json.dumps(plan, indent=2))

    print("\n=== Safety Audit ===")
    audit = validate_safety(plan, description)
    print(json.dumps(audit, indent=2))

    if not audit.get("approved", False):
        print("\nWARNING: Safety flags detected. Review before proceeding.")
    else:
        print("\nNo automated safety flags. Human review still required.")

if __name__ == "__main__":
    main()

Run it

Save the complete script as chem_agent.py and run it with a real reaction. Here is the output I got for a Friedel-Crafts acylation.

$ python chem_agent.py "Friedel-Crafts acylation of anisole with acetyl chloride"

Analyzing: Friedel-Crafts acylation of anisole with acetyl chloride

=== Synthesis Plan ===
{
  "reaction_type": "electrophilic aromatic substitution (Friedel-Crafts acylation)",
  "reagents": [
    "anisole",
    "acetyl chloride",
    "aluminum chloride (AlCl3)",
    "dichloromethane (DCM)"
  ],
  "hazards": [
    "acetyl chloride is corrosive and lachrymatory",
    "AlCl3 is hygroscopic and causes severe skin burns",
    "HCl gas evolution during reaction",
    "DCM is a suspected carcinogen"
  ],
  "ppe": [
    "nitrile gloves",
    "splash goggles",
    "lab coat",
    "fume hood (mandatory)"
  ],
  "disposal": "Quench AlCl3 carefully with ice, then dispose as halogenated organic waste and aqueous metal waste per local regulations.",
  "notes": "Reaction is exothermic. Add acetyl chloride dropwise at 0 degrees C."
}

=== Safety Audit ===
{
  "approved": false,
  "flags": [
    "Missing mention of anhydrous conditions for AlCl3",
    "No fire extinguisher class specified for DCM flammability"
  ],
  "corrected_ppe": [
    "nitrile gloves",
    "splash goggles",
    "lab coat",
    "fume hood",
    "face shield for quenching step"
  ],
  "corrected_disposal": "Quench AlCl3 with crushed ice under stirring, neutralize slowly, then separate halogenated solvent waste from aqueous aluminum waste."
}

WARNING: Safety flags detected. Review before proceeding.

The agent caught the need for anhydrous handling and a face shield during quenching, which I might have skipped in a first draft.

Next steps

Two concrete ways to extend this agent. First, integrate a molecular validation layer using RDKit to check that the SMILES or compound names in the reagents list are chemically valid before the plan is shown to a user. Second, store every generated plan and audit in a local SQLite database so your lab builds a searchable history of reviewed procedures.

Since Oxlo.ai charges a flat rate per request, adding that second safety audit pass costs the same whether the prompt is two sentences or two pages of reaction context. For labs running long experimental protocols, that pricing model keeps the overhead predictable. You can see the details at https://oxlo.ai/pricing.

Top comments (0)