VOOZH about

URL: https://dev.to/fazil_hasanov_8150a43b0ff/building-a-conversational-ai-agent-with-python-and-rasa-a-step-by-step-guide-1fig

⇱ Building a Conversational AI Agent with Python and Rasa: A Step‑by‑Step Guide - DEV Community


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: "Heythere!I’myourtravelassistant.HowcanIhelpyoutoday?"
 utter_goodbye:
 - text: "Safetravels!👋"
 utter_ask_flight:
 - text: "Sure,couldyousharetheflightnumber?"
 utter_flight_status:
 - text: "Fetchingstatusforflight{flight_number}..."
 utter_flight_not_found:
 - text: "Icouldn’tlocatethatflight.Pleasedouble‑checkthenumber."

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! 👋

**