Introduction
Conversational AI is no longer a niche hobby; enterprises use it for customer support, lead qualification, and internal tooling. Rasa Open Source gives you a production‑ready stack that runs on‑premise, lets you keep data private, and offers full Python extensibility. In this article we’ll walk through a complete Rasa project from scratch, covering environment setup, NLU training, story‑driven dialogue management, custom actions, testing, and deployment. By the end you’ll have a working chatbot that can greet users, answer FAQs, and fetch dynamic data from an external API.
Actionable Insight – Start every Rasa project in its own virtual environment. It isolates dependencies and makes CI/CD pipelines deterministic.
Prerequisites
| Requirement | Version |
|---|---|
| Python | 3.9‑3.11 |
| Rasa | 3.6+ |
| pip | latest |
| git | any |
You’ll also need a basic familiarity with YAML and Python. If you haven’t installed Rasa yet, run:
python -m venv rasa-env
source rasa-env/bin/activate # Windows: .\rasa-env\Scripts\activate
pip install --upgrade pip
pip install rasa
Verify the installation:
rasa --version
# Expected output: Rasa Open Source 3.x.x
Project Skeleton
Create a fresh directory and initialise a Rasa project:
mkdir travel-bot && cd travel-bot
rasa init --no-prompt
The command scaffolds the following structure:
travel-bot/
├─ actions/
│ └─ actions.py
├─ data/
│ ├─ nlu.yml
│ └─ stories.yml
├─ config.yml
├─ domain.yml
└─ credentials.yml
We’ll replace the auto‑generated files with our own definitions.
Defining the Domain
domain.yml is the single source of truth for intents, entities, slots, actions, and responses. For a travel‑assistant bot we need:
version: "3.0"
intents:
- greet
- goodbye
- ask_flight_status
- inform
entities:
- flight_number
slots:
flight_number:
type: text
influence_conversation: false
responses:
utter_greet:
- text: "Hey there! I’m your travel assistant. How can I help you today?"
utter_goodbye:
- text: "Safe travels! 👋"
utter_ask_flight:
- text: "Sure, could you share the flight number?"
utter_flight_status:
- text: "Fetching status for flight {flight_number}..."
utter_flight_not_found:
- text: "I couldn’t locate that flight. Please double‑check the number."
actions:
- action_flight_status
Why this matters – Slots are lightweight containers for user‑provided data (e.g., a flight number). By declaring influence_conversation: false we tell the dialogue policy not to treat the slot as a decision factor, which keeps the conversation flow deterministic.
Training the NLU Model
Rasa’s NLU component learns from annotated examples. Replace data/nlu.yml with:
version: "3.0"
nlu:
- intent: greet
examples: |
- hi
- hello
- hey there
- good morning
- intent: goodbye
examples: |
- bye
- see you later
- goodbye
- catch you later
- intent: ask_flight_status
examples: |
- what's the status of flight AA123?
- can you check flight BA456?
- flight status for DL789
- I need the status of flight LH321
entities:
- flight_number: AA123
- flight_number: BA456
- flight_number: DL789
- flight_number: LH321
- intent: inform
examples: |
- it's AA123
- flight number is BA456
- the flight is DL789
Actionable Insight – Use the entities: block inside each example to seed the entity extractor. This speeds up convergence and reduces the need for a large dataset.
Train the model:
rasa train
# Output: Model trained successfully. Model path: models/...
Story‑Based Dialogue Management
Stories encode the expected path of a conversation. Create data/stories.yml:
version: "3.0"
stories:
- story: greet and ask flight status
steps:
- intent: greet
- action: utter_greet
- intent: ask_flight_status
- action: utter_ask_flight
- intent: inform
entities:
flight_number: "AA123"
- slot_was_set:
- flight_number: "AA123"
- action: action_flight_status
- action: utter_flight_status
- story: goodbye
steps:
- intent: goodbye
- action: utter_goodbye
Rasa’s default policy (TED Policy) will learn a finite‑state machine from these stories. For more complex flows you can add rules.yml or use form actions, but the above suffices for a minimal demo.
Implementing a Custom Action
The action_flight_status action contacts a mock API to retrieve real‑time flight data. Replace actions/actions.py with:
# actions/actions.py
import requests
from typing import Any, Text, Dict, List
from rasa_sdk import Action, Tracker
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.events import SlotSet
API_ENDPOINT = "https://api.mockflight.com/status"
class ActionFlightStatus(Action):
"""Calls an external flight‑status API and returns a friendly message."""
def name(self) -> Text:
return "action_flight_status"
def run(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict[Text, Any],
) -> List[Dict[Text, Any]]:
flight_number = tracker.get_slot("flight_number")
if not flight_number:
dispatcher.utter_message(text="I need a flight number first.")
return []
try:
response = requests.get(f"{API_ENDPOINT}/{flight_number}", timeout=5)
response.raise_for_status()
data = response.json()
status = data.get("status", "unknown")
message = f"✈️ Flight {flight_number} is currently *{status}*."
except requests.RequestException:
message = "❗️ I couldn’t reach the flight service. Please try again later."
except KeyError:
message = "❓ I didn’t understand the response from the service."
dispatcher.utter_message(text=message)
# Clear the slot so the bot can handle a new query cleanly
return [SlotSet("flight_number", None)]
Tip – If you don’t have a real API, spin up a simple Flask mock:
# mock_flight_api.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/status/<flight>")
def status(flight):
return jsonify({"status": "on time" if flight.endswith("1") else "delayed"})
if __name__ == "__main__":
app.run(port=5001)
Run it in the background and change API_ENDPOINT to http://localhost:5001/status.
Running the Action Server
rasa run actions
# Output: Action endpoint listening on 5055
Leave this terminal open; Rasa Core will call the endpoint whenever action_flight_status is triggered.
Interactive Testing
Launch the chatbot in the shell:
rasa shell
Sample interaction:
User: hi
Bot: Hey there! I’m your travel assistant. How can I help you today?
User: what's the status of flight AA123?
Bot: Sure, could you share the flight number?
User: it's AA123
Bot: Fetching status for flight AA123...
✈️ Flight AA123 is currently *on time*.
User: bye
Bot: Safe travels! 👋
**
Top comments (0)