DEV Community

shashank ms
shashank ms

Posted on

Unlocking the Potential of LLMs in Biology

Introduction

We are building a literature triage agent that reads biology abstracts, extracts genes, proteins, and diseases, and suggests testable hypotheses. It is meant for bioinformaticians and bench scientists who need to scan dozens of papers without reading every word. The whole thing runs against Oxlo.ai's flat per-request endpoint, so a 10,000-token methods section costs the same as a one-sentence query.

What you will need

  • Python 3.10 or newer
  • The OpenAI SDK: pip install openai
  • An Oxlo.ai API key from https://portal.oxlo.ai
  • A handful of PubMed abstracts to test with

1. Configure the OpenAI-compatible client

Oxlo.ai exposes a fully OpenAI-compatible API. We instantiate the client once, point it at https://api.oxlo.ai/v1, and select a model. I use llama-3.3-70b because it handles dense scientific vocabulary reliably.

from openai import OpenAI

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

# Verify connectivity with a lightweight call
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)

2. Write the biology system prompt

The system prompt is the only manual logic we need. It forces the model to emit strict JSON containing extracted entities and confidence scores, so downstream code never has to parse free text.

SYSTEM_PROMPT = """You are a precision biology parser. Read the user-provided abstract and extract the following:

1. Genes and proteins (use official HGNC gene symbols where possible)
2. Diseases or phenotypes
3. Cell lines or organisms
4. Molecular pathways mentioned

Return ONLY a JSON object with this exact structure:
{
  "entities": [
    {"type": "gene", "name": "TP53", "confidence": 0.98},
    {"type": "disease", "name": "glioblastoma", "confidence": 0.91}
  ],
  "summary": "One-sentence summary of the finding"
}

If an entity is ambiguous, mark confidence below 0.8. Do not add markdown fences or commentary outside the JSON."""

3. Extract structured entities from an abstract

We call the chat completions endpoint with JSON mode enabled. Oxlo.ai supports this on all major models, which guarantees valid JSON output without brittle regex.

import json

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

abstract = (
    "CRISPR-Cas9 screening identified BRCA1 as a synthetic lethal target "
    "in pancreatic ductal adenocarcinoma cell lines. Loss of BRCA1 function "
    "increased sensitivity to PARP inhibitors via defective homologous recombination."
)

result = extract_entities(abstract)
print(json.dumps(result, indent=2))

4. Generate hypotheses from extracted entities

With entities in hand, we run a second pass to suggest experiments. I switch to kimi-k2.6 for this step because its reasoning capabilities are strong for open-ended scientific inference, but the client setup is identical.

HYPOTHESIS_PROMPT = """You are a principal investigator. Given the extracted entities from a paper, propose one testable hypothesis and one follow-up experiment. Return ONLY JSON:

{
  "hypothesis": "string",
  "experiment": "string",
  "needed_reagents": ["list"]
}"""

def suggest_hypothesis(entities_json: dict) -> dict:
    response = client.chat.completions.create(
        model="kimi-k2.6",
        messages=[
            {"role": "system", "content": HYPOTHESIS_PROMPT},
            {"role": "user", "content": json.dumps(entities_json)},
        ],
        response_format={"type": "json_object"},
        temperature=0.3,
    )
    return json.loads(response.choices[0].message.content)

hyp = suggest_hypothesis(result)
print(json.dumps(hyp, indent=2))

5. Batch process multiple abstracts

Real pipelines process dozens of papers at once. We wrap the two passes in a single function and iterate over a list. Because Oxlo.ai uses flat per-request pricing, long abstracts do not inflate cost the way token-based providers would. You can see exact plan details at https://oxlo.ai/pricing.

def process_papers(abstracts: list[str]) -> list[dict]:
    outputs = []
    for idx, text in enumerate(abstracts, 1):
        entities = extract_entities(text)
        hypothesis = suggest_hypothesis(entities)
        outputs.append({
            "paper_index": idx,
            "entities": entities,
            "hypothesis": hypothesis,
        })
    return outputs

batch = [
    (
        "Single-cell RNA-seq revealed a rare microglial subtype expressing TREM2 "
        "in the Alzheimer's disease mouse model. This population was enriched near amyloid plaques."
    ),
    (
        "The mTORC1 pathway was hyperactivated in TSC2-deficient patient-derived "
        "iPSC neurons, leading to abnormal axon guidance that was rescued by rapamycin."
    ),
]

findings = process_papers(batch)
print(json.dumps(findings, indent=2))

Run it

Save the script as bio_agent.py, replace YOUR_OXLO_API_KEY with your key from the portal, and execute. Here is a complete end-to-end block.

from openai import OpenAI
import json

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

SYSTEM_PROMPT = """You are a precision biology parser. Read the user-provided abstract and extract the following:

1. Genes and proteins (use official HGNC gene symbols where possible)
2. Diseases or phenotypes
3. Cell lines or organisms
4. Molecular pathways mentioned

Return ONLY a JSON object with this exact structure:
{
  "entities": [
    {"type": "gene", "name": "TP53", "confidence": 0.98},
    {"type": "disease", "name": "glioblastoma", "confidence": 0.91}
  ],
  "summary": "One-sentence summary of the finding"
}

If an entity is ambiguous, mark confidence below 0.8. Do not add markdown fences or commentary outside the JSON."""

HYPOTHESIS_PROMPT = """You are a principal investigator. Given the extracted entities from a paper, propose one testable hypothesis and one follow-up experiment. Return ONLY JSON:

{
  "hypothesis": "string",
  "experiment": "string",
  "needed_reagents": ["list"]
}"""

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

def suggest_hypothesis(entities_json: dict) -> dict:
    response = client.chat.completions.create(
        model="kimi-k2.6",
        messages=[
            {"role": "system", "content": HYPOTHESIS_PROMPT},
            {"role": "user", "content": json.dumps(entities_json)},
        ],
        response_format={"type": "json_object"},
        temperature=0.3,
    )
    return json.loads(response.choices[0].message.content)

if __name__ == "__main__":
    abstract = (
        "CRISPR-Cas9 screening identified BRCA1 as a synthetic lethal target "
        "in pancreatic ductal adenocarcinoma cell lines. Loss of BRCA1 function "
        "increased sensitivity to PARP inhibitors via defective homologous recombination."
    )
    entities = extract_entities(abstract)
    hyp = suggest_hypothesis(entities)
    print("=== ENTITIES ===")
    print(json.dumps(entities, indent=2))
    print("\n=== HYPOTHESIS ===")
    print(json.dumps(hyp, indent=2))

Expected output looks similar to this (your exact JSON may vary):

{
  "entities": [
    {"type": "gene", "name": "BRCA1", "confidence": 0.97},
    {"type": "disease", "name": "pancreatic ductal adenocarcinoma", "confidence": 0.94},
    {"type": "molecule", "name": "PARP inhibitors", "confidence": 0.92},
    {"type": "pathway", "name": "homologous recombination", "confidence": 0.89}
  ],
  "summary": "CRISPR screening links BRCA1 loss to PARP inhibitor sensitivity in pancreatic cancer via homologous recombination defects."
}

Wrap-up

Two concrete next steps. First, wire the JSON output into a vector database like Chroma or pgvector so you can search across hundreds of papers by gene or pathway. Second, add a tool-calling step that uses the NCBI E-utilities API to fetch full text automatically, letting the agent ground its hypotheses in the latest literature without manual copy and paste.

Top comments (0)