{"slug": "building-a-conversational-ai-agent-with-python-and-rasa-a-step-by-step-guide", "title": "Building a Conversational AI Agent with Python and Rasa: A Step‑by‑Step Guide", "summary": "A developer built a conversational AI travel assistant using Python and Rasa Open Source, creating a chatbot that can greet users, answer FAQs, and fetch dynamic flight status data from an external API. The project walks through environment setup, NLU training, story-driven dialogue management, custom actions, and deployment, with the final bot capable of handling intents like flight status inquiries and user information collection. The implementation uses Rasa's domain file as a single source of truth for intents, entities, slots, and actions, while training the NLU model on annotated examples to enable entity extraction and conversation flow management.", "body_md": "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.\n\nActionable Insight– Start every Rasa project in its own virtual environment. It isolates dependencies and makes CI/CD pipelines deterministic.\n\n| Requirement | Version |\n|---|---|\n| Python | 3.9‑3.11 |\n| Rasa | 3.6+ |\n| pip | latest |\n| git | any |\n\nYou’ll also need a basic familiarity with YAML and Python. If you haven’t installed Rasa yet, run:\n\n```\npython -m venv rasa-env\nsource rasa-env/bin/activate   # Windows: .\\rasa-env\\Scripts\\activate\npip install --upgrade pip\npip install rasa\n```\n\nVerify the installation:\n\n```\nrasa --version\n# Expected output: Rasa Open Source 3.x.x\n```\n\nCreate a fresh directory and initialise a Rasa project:\n\n```\nmkdir travel-bot && cd travel-bot\nrasa init --no-prompt\n```\n\nThe command scaffolds the following structure:\n\n```\ntravel-bot/\n├─ actions/\n│  └─ actions.py\n├─ data/\n│  ├─ nlu.yml\n│  └─ stories.yml\n├─ config.yml\n├─ domain.yml\n└─ credentials.yml\n```\n\nWe’ll replace the auto‑generated files with our own definitions.\n\n`domain.yml`\n\nis the single source of truth for intents, entities, slots, actions, and responses. For a travel‑assistant bot we need:\n\n```\nversion: \"3.0\"\n\nintents:\n  - greet\n  - goodbye\n  - ask_flight_status\n  - inform\n\nentities:\n  - flight_number\n\nslots:\n  flight_number:\n    type: text\n    influence_conversation: false\n\nresponses:\n  utter_greet:\n    - text: \"Hey there! I’m your travel assistant. How can I help you today?\"\n  utter_goodbye:\n    - text: \"Safe travels! 👋\"\n  utter_ask_flight:\n    - text: \"Sure, could you share the flight number?\"\n  utter_flight_status:\n    - text: \"Fetching status for flight {flight_number}...\"\n  utter_flight_not_found:\n    - text: \"I couldn’t locate that flight. Please double‑check the number.\"\n\nactions:\n  - action_flight_status\n```\n\n**Why this matters** – Slots are lightweight containers for user‑provided data (e.g., a flight number). By declaring `influence_conversation: false`\n\nwe tell the dialogue policy not to treat the slot as a decision factor, which keeps the conversation flow deterministic.\n\nRasa’s NLU component learns from annotated examples. Replace `data/nlu.yml`\n\nwith:\n\n```\nversion: \"3.0\"\nnlu:\n  - intent: greet\n    examples: |\n      - hi\n      - hello\n      - hey there\n      - good morning\n\n  - intent: goodbye\n    examples: |\n      - bye\n      - see you later\n      - goodbye\n      - catch you later\n\n  - intent: ask_flight_status\n    examples: |\n      - what's the status of flight AA123?\n      - can you check flight BA456?\n      - flight status for DL789\n      - I need the status of flight LH321\n    entities:\n      - flight_number: AA123\n      - flight_number: BA456\n      - flight_number: DL789\n      - flight_number: LH321\n\n  - intent: inform\n    examples: |\n      - it's AA123\n      - flight number is BA456\n      - the flight is DL789\n```\n\n**Actionable Insight** – Use the `entities:`\n\nblock inside each example to seed the entity extractor. This speeds up convergence and reduces the need for a large dataset.\n\nTrain the model:\n\n```\nrasa train\n# Output: Model trained successfully. Model path: models/...\n```\n\nStories encode the expected path of a conversation. Create `data/stories.yml`\n\n:\n\n```\nversion: \"3.0\"\nstories:\n  - story: greet and ask flight status\n    steps:\n      - intent: greet\n      - action: utter_greet\n      - intent: ask_flight_status\n      - action: utter_ask_flight\n      - intent: inform\n        entities:\n          flight_number: \"AA123\"\n      - slot_was_set:\n          - flight_number: \"AA123\"\n      - action: action_flight_status\n      - action: utter_flight_status\n\n  - story: goodbye\n    steps:\n      - intent: goodbye\n      - action: utter_goodbye\n```\n\nRasa’s default policy (TED Policy) will learn a finite‑state machine from these stories. For more complex flows you can add `rules.yml`\n\nor use `form`\n\nactions, but the above suffices for a minimal demo.\n\nThe `action_flight_status`\n\naction contacts a mock API to retrieve real‑time flight data. Replace `actions/actions.py`\n\nwith:\n\n``` python\n# actions/actions.py\nimport requests\nfrom typing import Any, Text, Dict, List\n\nfrom rasa_sdk import Action, Tracker\nfrom rasa_sdk.executor import CollectingDispatcher\nfrom rasa_sdk.events import SlotSet\n\nAPI_ENDPOINT = \"https://api.mockflight.com/status\"\n\nclass ActionFlightStatus(Action):\n    \"\"\"Calls an external flight‑status API and returns a friendly message.\"\"\"\n\n    def name(self) -> Text:\n        return \"action_flight_status\"\n\n    def run(\n        self,\n        dispatcher: CollectingDispatcher,\n        tracker: Tracker,\n        domain: Dict[Text, Any],\n    ) -> List[Dict[Text, Any]]:\n        flight_number = tracker.get_slot(\"flight_number\")\n        if not flight_number:\n            dispatcher.utter_message(text=\"I need a flight number first.\")\n            return []\n\n        try:\n            response = requests.get(f\"{API_ENDPOINT}/{flight_number}\", timeout=5)\n            response.raise_for_status()\n            data = response.json()\n            status = data.get(\"status\", \"unknown\")\n            message = f\"✈️ Flight {flight_number} is currently *{status}*.\"\n        except requests.RequestException:\n            message = \"❗️ I couldn’t reach the flight service. Please try again later.\"\n        except KeyError:\n            message = \"❓ I didn’t understand the response from the service.\"\n\n        dispatcher.utter_message(text=message)\n        # Clear the slot so the bot can handle a new query cleanly\n        return [SlotSet(\"flight_number\", None)]\n```\n\nTip– If you don’t have a real API, spin up a simple Flask mock:\n\n``` python\n# mock_flight_api.py\nfrom flask import Flask, jsonify\napp = Flask(__name__)\n\n@app.route(\"/status/<flight>\")\ndef status(flight):\n    return jsonify({\"status\": \"on time\" if flight.endswith(\"1\") else \"delayed\"})\n\nif __name__ == \"__main__\":\n    app.run(port=5001)\n```\n\nRun it in the background and change `API_ENDPOINT`\n\nto `http://localhost:5001/status`\n\n.\n\n```\nrasa run actions\n# Output: Action endpoint listening on 5055\n```\n\nLeave this terminal open; Rasa Core will call the endpoint whenever `action_flight_status`\n\nis triggered.\n\nLaunch the chatbot in the shell:\n\n```\nrasa shell\n```\n\nSample interaction:\n\n```\nUser: hi\nBot: Hey there! I’m your travel assistant. How can I help you today?\nUser: what's the status of flight AA123?\nBot: Sure, could you share the flight number?\nUser: it's AA123\nBot: Fetching status for flight AA123...\n✈️ Flight AA123 is currently *on time*.\nUser: bye\nBot: Safe travels! 👋\n```\n\n**", "url": "https://wpnews.pro/news/building-a-conversational-ai-agent-with-python-and-rasa-a-step-by-step-guide", "canonical_source": "https://dev.to/fazil_hasanov_8150a43b0ff/building-a-conversational-ai-agent-with-python-and-rasa-a-step-by-step-guide-1fig", "published_at": "2026-06-05 17:04:59+00:00", "updated_at": "2026-06-05 17:43:21.659900+00:00", "lang": "en", "topics": ["natural-language-processing", "artificial-intelligence", "machine-learning", "ai-agents", "ai-tools"], "entities": ["Rasa", "Python", "YAML"], "alternates": {"html": "https://wpnews.pro/news/building-a-conversational-ai-agent-with-python-and-rasa-a-step-by-step-guide", "markdown": "https://wpnews.pro/news/building-a-conversational-ai-agent-with-python-and-rasa-a-step-by-step-guide.md", "text": "https://wpnews.pro/news/building-a-conversational-ai-agent-with-python-and-rasa-a-step-by-step-guide.txt", "jsonld": "https://wpnews.pro/news/building-a-conversational-ai-agent-with-python-and-rasa-a-step-by-step-guide.jsonld"}}