DEV Community

shashank ms
shashank ms

Posted on

Building LLM Models from Scratch: A Step-by-Step Tutorial

We are going to build an LLM training assistant that generates PyTorch implementations of transformer components and explains the math behind them. If you are learning how LLMs work under the hood, this gives you a reusable coding partner that runs on Oxlo.ai's flat-rate API. The finished script is under 80 lines and uses only the OpenAI SDK.

What you'll need

Step 1: Make a first call to a coding model

Before adding abstractions, verify that Oxlo.ai returns valid PyTorch with a single user message. I use deepseek-v3.2 because it handles reasoning and code generation well.

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="deepseek-v3.2",
    messages=[
        {"role": "user", "content": "Write a minimal PyTorch implementation of multi-head self-attention with einsum. No main function."},
    ],
)
print(response.choices[0].message.content)

Step 2: Lock in a system prompt

A generic chat model drifts between teaching styles. We pin it to a senior ML engineer persona who returns only code and a short explanation. Edit the prompt below to change the agent's personality or constraints.

from openai import OpenAI

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

SYSTEM_PROMPT = """You are a senior ML engineer helping a colleague build an LLM from scratch in PyTorch.
Rules:
- Provide only valid, runnable PyTorch code.
- After the code, give a 2-sentence explanation of the key mathematical idea.
- Do not use external libraries beyond torch and torch.nn.
- If the user asks for a variant, rewrite the full function so it remains self-contained."""

response = client.chat.completions.create(
    model="deepseek-v3.2",
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": "Implement scaled dot-product attention with causal masking."},
    ],
)
print(response.choices[0].message.content)

Step 3: Enforce structured output with JSON mode

Parsing free-form markdown is brittle. Oxlo.ai supports JSON mode, so we request a strict schema with 'code' and 'explanation' fields that our script can consume directly.

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 senior ML engineer helping a colleague build an LLM from scratch in PyTorch.
Rules:
- Provide only valid, runnable PyTorch code.
- After the code, give a 2-sentence explanation of the key mathematical idea.
- Do not use external libraries beyond torch and torch.nn.
- If the user asks for a variant, rewrite the full function so it remains self-contained."""

response = client.chat.completions.create(
    model="deepseek-v3.2",
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": "Implement scaled dot-product attention with causal masking. Return JSON with keys 'code' and 'explanation'."},
    ],
    response_format={"type": "json_object"},
)
result = json.loads(response.choices[0].message.content)
print("=== CODE ===")
print(result["code"])
print("=== EXPLANATION ===")
print(result["explanation"])

Step 4: Iterate with multi-turn context

Real architectures are built in layers. We keep conversation history so the model can revise the previous block, such as adding Rotary Position Embedding to the attention mechanism it just wrote.

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 senior ML engineer helping a colleague build an LLM from scratch in PyTorch.
Rules:
- Provide only valid, runnable PyTorch code.
- After the code, give a 2-sentence explanation of the key mathematical idea.
- Do not use external libraries beyond torch and torch.nn.
- If the user asks for a variant, rewrite the full function so it remains self-contained."""

messages = [
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": "Implement scaled dot-product attention with causal masking. Return JSON with keys 'code' and 'explanation'."},
]

response = client.chat.completions.create(
    model="deepseek-v3.2",
    messages=messages,
    response_format={"type": "json_object"},
)
result = json.loads(response.choices[0].message.content)
messages.append({"role": "assistant", "content": json.dumps(result)})

messages.append({"role": "user", "content": "Now rewrite that same function to use Rotary Position Embedding (RoPE) instead of absolute positional encodings. Return JSON."})

response = client.chat.completions.create(
    model="deepseek-v3.2",
    messages=messages,
    response_format={"type": "json_object"},
)
result = json.loads(response.choices[0].message.content)
print(result["code"])

Step 5: Wrap everything in a reusable helper

Copy-pasting client setup for every component gets old. This helper accepts a conversation history and returns the parsed result, so you can assemble an entire transformer block by 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 senior ML engineer helping a colleague build an LLM from scratch in PyTorch.
Rules:
- Provide only valid, runnable PyTorch code.
- After the code, give a 2-sentence explanation of the key mathematical idea.
- Do not use external libraries beyond torch and torch.nn.
- If the user asks for a variant, rewrite the full function so it remains self-contained."""

def generate_component(messages, model="deepseek-v3.2"):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        response_format={"type": "json_object"},
    )
    return json.loads(response.choices[0].message.content)

history = [
    {"role": "system", "content": SYSTEM_PROMPT},
]

prompts = [
    "Write a PyTorch RMSNorm layer. Return JSON with keys 'code' and 'explanation'.",
    "Write a PyTorch SwiGLU feed-forward layer compatible with Llama-style dims. Return JSON.",
    "Write a PyTorch DecoderBlock that composes RMSNorm, causal self-attention, and SwiGLU. Return JSON.",
]

for p in prompts:
    history.append({"role": "user", "content": p})
    out = generate_component(history)
    history.append({"role": "assistant", "content": json.dumps(out)})
    print(f"--- {p[:40]}... ---")
    print(out["code"][:300] + "...")

Run it

Here is a complete end-to-end script that generates a minimal GPT-style architecture. I run it against Oxlo.ai and show the first few lines of the generated model.

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 senior ML engineer helping a colleague build an LLM from scratch in PyTorch.
Rules:
- Provide only valid, runnable PyTorch code.
- After the code, give a 2-sentence explanation of the key mathematical idea.
- Do not use external libraries beyond torch and torch.nn.
- If the user asks for a variant, rewrite the full function so it remains self-contained."""

def generate_component(messages, model="qwen-3-32b"):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        response_format={"type": "json_object"},
    )
    return json.loads(response.choices[0].message.content)

history = [
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": "Generate a complete, minimal GPT-style LM in PyTorch with an Embedding layer, 2 DecoderBlocks, and a head that outputs logits over a vocab of 1000. Return JSON with keys 'code' and 'explanation'."},
]

result = generate_component(history)
print(result["code"])

Example output (truncated):

class GPT(nn.Module):
    def __init__(self, vocab_size=1000, dim=512, heads=8, layers=2):
        super().__init__()
        self.tok_emb = nn.Embedding(vocab_size, dim)
        self.blocks = nn.ModuleList([DecoderBlock(dim, heads) for _ in range(layers)])
        self.norm = RMSNorm(dim)
        self.head = nn.Linear(dim, vocab_size, bias=False)
    def forward(self, x):
        ...
Explanation: This stacks two decoder blocks with causal self-attention and SwiGLU, followed by a linear projection to vocabulary logits, matching the minimal GPT architecture.

Wrap-up

You now have a scriptable assistant that generates transformer components on demand. Two concrete next steps: add a local torch.jit.script or exec sanity check so the agent's code is validated before you save it to disk, and swap in llama-3.3-70b or kimi-k2.6 when you move from block generation to full training-loop design. Because Oxlo.ai charges a flat rate per request, you can send long system prompts and multi-turn histories without watching token meters climb.

Top comments (0)