Simple AI Chatbot in Python

Ever get that feeling where you want to understand the "magic" behind an AI assistant, but every tutorial suggests connecting a dozen libraries and getting a ready-made box? LangChain, LlamaIndex, AutoGen — powerful tools, but they hide the most interesting part: how the conversation between your code and the language model actually works.

And the principle is surprisingly simple: you sent a list of messages — you got a response. That's it. Seriously. Let's build a chatbot in a minute and figure out what happens behind the scenes of every AI assistant.

What We'll End Up With

A console chatbot that:

  • Connects to OpenRouter API (free models available without a card)
  • Supports dialog context — the bot remembers what you talked about earlier
  • Works in real time in the terminal
  • Written in pure Python with minimal dependencies

Two Steps Before Starting

Before writing code, you need two things:

1. API Key from OpenRouter

Go to https://openrouter.ai, create an account and generate an API key. A free model will work for starters — no credit card needed.

2. Create a .env file in the project root

OPENROUTER_API_KEY=your_key_here

Important: this file should never end up in a git repository. Add it to .gitignore right away — this is basic security practice.

Python code

Imports and Setup

import os, requests, json
import dotenv

dotenv.load_dotenv()

url = "https://openrouter.ai/api/v1/chat/completions"
api_key = os.getenv("OPENROUTER_API_KEY")
model_name = "openrouter/free"

Here we load environment variables from the .env file. dotenv is the only external dependency in our project, and it's needed only for convenience: to avoid storing secrets directly in code.

The `url` points to the standard OpenAI-compatible API. Almost all modern LLM providers use the same request format — which means our bot is easily adaptable for OpenAI, Anthropic, or any other models.

Managing Dialog Context

class Context:
    
    def __init__(self):
        self.prompt = []
    
    def add(self, role, message):
        self.prompt.append({"role": role, "content": message})

This is the most important part of the code. Language models don't "remember" previous messages — they receive the entire list of messages at once with each request. That's why we store the entire conversation history in the `self.prompt` list.

Each message consists of two parts: role (`system`, `user`, `assistant`) and text. The system message sets the bot's behavior, user messages are your questions, assistant responses are its reactions.

Sending a Request to the Model

def send_to_llm(context):
    
    data = {
        "model": model_name,
        "messages": context.prompt
    }
    headers = {
        "Authorization": "Bearer " + api_key
    }
    
    response = requests.post(url,
        headers=headers, data=json.dumps(data)
    )
    response.raise_for_status()
    result = response.json()
    
    return result["choices"][0]["message"]["content"]

requests.post sends a POST request with JSON data to the API endpoint. In response, the model returns JSON, from which we only need the text — `result["choices"][0]["message"]["content"]`.

Note `response.raise_for_status()` — this is an error check. If the API returns an error (wrong key, request limit), the code will stop with a clear message instead of silently glitching.

Main Communication Loop

context = Context()
context.add("system", "You are a helper for programmers")

print("Enter your query")

while True:
    
    print("> ", end="")
    query = input().strip()
    
    if query == "":
        continue
    
    if query.lower() in ["q", "quit"]:
        break
    
    context.add("user", query)

    answer = send_to_llm(context)
    print(answer)

    context.add("assistant", answer)

A simple infinite loop: read input → added to context → sent to model → printed response → added response to context. Notice that we add both the question and the answer — so the model "sees" the entire conversation on the next request.

A small nuance: the model has no memory of its own. Each time, the entire conversation history is sent to it again. On long conversations this can become expensive — but for getting started, this is the perfect approach.

Why This Works Better Than You'd Think

Many people think that creating an AI bot requires complex infrastructure. But in reality, the dialog format is standard — the same for GPT, Claude, Llama, and dozens of other models. Mastering this pattern, you understand the principle behind any AI assistant: it's simply a function that takes a list of messages and returns a new one.

Here's what you can already do with this code:

  • Change `model_name` to any available OpenRouter model — from free to the most powerful ones
  • Modify the system message to make the bot a lawyer, copywriter, or coach
  • Save conversation history to a file and continue the conversation later

Resume

A simple AI chatbot in Python — it's not an evening training project, but the foundation for understanding how modern AI assistants work. Just a few lines of code, one external library, and a free API key — and you get a working bot with dialog context.

The key takeaway: behind every AI is an ordinary HTTP request. The model doesn't "think" and doesn't "understand" — it predicts the next word based on a list of messages you sent it. Understanding this principle is the key to creating any AI tools, from simple bots to complex agentic systems.

Try running this code, experiment with system messages and models. Then ask yourself: what if I gave the bot access to a database? Questions are more important than ready-made answers.

Publish date: 16 Jul 2026