Lesson · 40 min · Free
Conversational AI: Building Real Chatbots
Conversational AI: Building Real Chatbots In this lesson of Build & Ship Generative AI Applications , we move from single-shot prompting to the systems students actually encounter in industry: chatbots. A chatbot is
Conversational AI: Building Real Chatbots
In this lesson of Build & Ship Generative AI Applications , we move from single-shot prompting to the systems students actually encounter in industry: chatbots. A chatbot is not just a model with a friendly interface — it is an application that manages conversation state , enforces domain boundaries , and connects the model to trusted data sources . For pharmacy and biotech teams, this pattern powers medication information assistants, adverse-event intake tools, internal formulary Q&A bots, and patient-facing triage front ends. The most important conceptual shift is that large language model APIs are stateless . The model has no memory of previous exchanges between requests. Every turn, your application sends the entire conversation — a system prompt defining the bot's role and rules, followed by the alternating user and assistant messages — back to the API. Your code, not the model, is responsible for storing, trimming, and formatting this history. When the conversation approaches the model's context window limit, developers typically truncate the oldest turns or summarize them, keeping the system prompt intact so the bot never "forgets" its instructions. The system prompt is where healthcare-specific guardrails live. A well-designed medical chatbot declares its scope (e.g., "answer questions about approved medications only"), refuses to give patient-specific dosing advice, and includes an escalation path — directing users to a pharmacist, prescriber, or poison control center when a question exceeds its competence. This is not optional polish; in a regulated domain, an ungrounded chatbot that confidently invents a drug interaction is a patient-safety hazard. The two techniques that most reduce this risk are retrieval-augmented generation (feeding the model verified text from a drug database before it answers) and tool calling (letting the model request structured facts from a real function instead of generating them from memory). The first example below shows the minimal skeleton every conversational AI application shares: a persistent message history, a system prompt, and a loop that appends each turn. Notice how the temperature is set low — for factual drug information, we want consistency, not creativity. from openai import OpenAI client = OpenAI() SYSTEM_PROMPT = ( "You are DrugInfo, a medication information assistant for pharmacy staff. " "Answer only questions about approved medications using general drug knowledge. " "Never recommend doses for a specific patient; refer those questions to the " "prescriber or a clinical pharmacist. If you are unsure, say so." ) # The application owns the conversation state — the API does not. history = [{"role": "system", "content": SYSTEM_PROMPT}] def chat(user_input: str) -> str: history.append({"role": "user", "content": user_input}) response = client.chat.completions.create( model="gpt-4o-mini", messages=history, temperature=0.2, # low temperature for factual consistency ) reply = response.choices[0].message.content history.append({"role": "assistant", "content": reply}) return reply while True: user_input = input("You: ") if user_input.strip().lower() in {"quit", "exit"}: break print("DrugInfo:", chat(user_input)) This skeleton works, but it answers purely from the model's training data — exactly what we want to avoid for interaction checking. The second example adds tool calling : we register a check_interaction function, the model decides when it needs it, our code executes the lookup against a real (here, toy) database, and the result is fed back so the model can compose a grounded answer. This same loop, with the dictionary swapped for a production drug-interaction API, is how commercial clinical chatbots stay anchored to authoritative data. import json INTERACTIONS = { frozenset({"warfarin", "amiodarone"}): "Major: amiodarone inhibits warfarin metabolism, raising bleeding risk. " "Monitor INR closely and consider dose reduction.", frozenset({"simvastatin", "clarithromycin"}): "Contraindicated: clarithromycin markedly raises simvastatin levels, " "increasing risk of rhabdomyolysis.", } TOOLS = [{ "type": "function", "function": { "name": "check_interaction", "description": "Check for a clinically significant interaction between two drugs.", "parameters": { "type": "object", "properties": { "drug_a": {"type": "string"}, "drug_b": {"type": "string"}, }, "required": ["drug_a", "drug_b"], }, }, }] def check_interaction(drug_a: str, drug_b: str) -> str: key = frozenset({drug_a.lower(), drug_b.lower()}) return INTERACTIONS.get(key, "No known major interaction in local database.") def run_turn(user_input: str) -> str: history.append({"role": "user", "content": user_input}) response = client.chat.completions.create( model="gpt-4o-mini", messages=history, tools=TOOLS, ) message = response.choices[0].message if message.tool_calls: # model requested a tool call = message.tool_calls[0
Watch the full lesson — free
This topic is part of Build & Ship Generative AI Applications, a complete AI-narrated video course. Press play once and watch the entire lecture like a movie.
Start the course free →