How to use Rasa for devops

devops入门25 分钟阅读2026/6/30

Last month, our team's Slack channel became a running joke. Every time a service crashed at 2 AM, the on-call engineer would fumble through half-remembered kubectl commands, trying to remember the exact syntax to scale a deployment or tail logs from a specific pod. I watched a senior engineer spend 20 minutes trying to figure out why a container was OOM-killed, typing the wrong kubectl logs flags over and over.

That's when it hit me: why are we memorizing CLI syntax for infrastructure we interact with sporadically? I wanted to build a conversational interface for our DevOps pipeline—something where you could just say "scale the API to 5 replicas" or "show me the errors from the payment service," and it would actually execute the commands safely. After evaluating a few frameworks, I landed on Rasa.

Here's how I built a conversational DevOps agent using Rasa, and what I learned along the way.

Why Rasa for DevOps?

Most people associate Rasa with customer support chatbots. But Rasa's real strength is its ability to handle multi-turn, task-oriented conversations with strict business logic—exactly what you need when you're modifying infrastructure. You don't want an LLM freestypping a kubectl delete namespace command. You want it to gather the required parameters, confirm with the user, and then execute a well-defined action.

Rasa's CALM architecture (Conversational AI with Language Models) is designed precisely for this: it uses LLMs for understanding natural language, but keeps the actual execution logic deterministic and controlled. That's a non-negotiable when your agent can affect production systems.

Getting Started: The Setup

First, you'll need a Rasa Developer Edition license (it's free). Then, set up a clean virtual environment:

python3 -m venv rasa-devops
source rasa-devops/bin/activate
pip install rasa

Initialize a new project:

rasa init --no-prompt devops-agent
cd devops-agent

This scaffolds the basic Rasa project structure. You'll see files like domain.yml, data/nlu.yml, data/stories.yml, and actions/actions.py. These are the core files you'll be editing.

Defining What the Agent Can Do

The domain.yml file is where you declare your intents, entities, slots, and responses. For a DevOps agent, I started with three core operations: scaling services, checking logs, and listing deployments.

# domain.yml
intents:
  - scale_service
  - check_logs
  - list_deployments
  - confirm
  - deny

entities:
  - service_name
  - replica_count
  - namespace

slots:
  service_name:
    type: text
    mappings:
      - type: from_entity
        entity: service_name
  replica_count:
    type: float
    mappings:
      - type: from_entity
        entity: replica_count
  namespace:
    type: text
    mappings:
      - type: from_entity
        entity: namespace

actions:
  - action_scale_service
  - action_check_logs
  - action_list_deployments

A mistake I made early on: I initially used type: any for the replica_count slot. Don't do that. Using type: float ensures Rasa validates the input before passing it to your action. You really don't want a string like "five" being passed to a kubectl scale --replicas= command.

Training the NLU

Next, define your training examples in data/nlu.yml:

# data/nlu.yml
nlu:
  - intent: scale_service
    examples: |
      - scale [api-service](service_name) to [3](replica_count) replicas
      - I need [payment](service_name) scaled up to [5](replica_count)
      - increase [auth](service_name) to [2](replica_count)
      - can you scale [frontend](service_name) to [10](replica_count)?
      - [worker](service_name) needs [4](replica_count) replicas

  - intent: check_logs
    examples: |
      - show me the logs for [api-service](service_name)
      - tail [payment](service_name) logs
      - what's happening in [auth](service_name)?
      - check [frontend](service_name) error logs

  - intent: list_deployments
    examples: |
      - what's running in [production](namespace)?
      - list deployments in [staging](namespace)
      - show me all services in [default](namespace)

I was surprised by how few examples Rasa needs to get reasonable accuracy. I started with about 8-10 examples per intent, and it was already picking up variations like "bump up the API to 5" correctly. But you should absolutely add more edge cases—especially ambiguous phrasings.

Writing the Custom Actions

This is where the DevOps magic happens. Custom actions in Rasa are Python classes that run your business logic. They live in actions/actions.py.

Here's the action for scaling a service:

# actions/actions.py
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
import subprocess

class ActionScaleService(Action):
    def name(self) -> Text:
        return "action_scale_service"

    def run(self, dispatcher: CollectingDispatcher,
            tracker: Tracker,
            domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:

        service_name = tracker.get_slot("service_name")
        replica_count = tracker.get_slot("replica_count")
        namespace = tracker.get_slot("namespace") or "default"

        if not service_name or not replica_count:
            dispatcher.utter_message(
                text="I need both a service name and replica count to scale."
            )
            return []

        # Execute the kubectl command
        try:
            result = subprocess.run(
                [
                    "kubectl", "scale", "deployment", service_name,
                    f"--replicas={int(replica_count)}",
                    "-n", namespace
                ],
                capture_output=True,
                text=True,
                timeout=30
            )

            if result.returncode == 0:
                msg = f"Done. {service_name} scaled to {int(replica_count)} replicas in {namespace}."
                dispatcher.utter_message(text=msg)
            else:
                dispatcher.utter_message(
                    text=f"Something went wrong: {result.stderr.strip()}"
                )
        except subprocess.TimeoutExpired:
            dispatcher.utter_message(
                text="The command timed out. Check your cluster connection."
            )
        except Exception as e:
            dispatcher.utter_message(
                text=f"Unexpected error: {str(e)}"
            )

        return [SlotSet("service_name", None),
                SlotSet("replica_count", None),
                SlotSet("namespace", None)]

Critical lesson learned: Always clear your slots at the end of the action. I forgot this initially, and the agent kept trying to scale the same service repeatedly because the slots persisted across conversation turns. The SlotSet events resetting to None fix this.

Also, notice I'm using subprocess.run with a timeout. When you're running kubectl commands against a cluster, things can hang. The timeout prevents the action from blocking indefinitely.

Adding Confirmation Flows

For a DevOps agent, you absolutely need confirmation before executing destructive actions. Rasa handles this through stories and forms. Here's how I set up a confirmation flow in data/stories.yml:

# data/stories.yml
stories:
  - story: scale with confirmation
    steps:
      - intent: scale_service
      - action: utter_confirm_scale
      - intent: confirm
      - action: action_scale_service

  - story: scale denied
    steps:
      - intent: scale_service
      - action: utter_confirm_scale
      - intent: deny
      - action: utter_scale_cancelled

And the corresponding responses in your domain:

responses:
  utter_confirm_scale:
    - text: "Please confirm: you want to scale {service_name} to {replica_count} replicas in {namespace}?"
  utter_scale_cancelled:
    - text: "Scale operation cancelled."

This is where Rasa's structured approach really pays off. An unconstrained LLM might skip the confirmation step or phrase it ambiguously. With Rasa, the confirmation is mandatory and explicit.

Running the Agent

Start the action server first:

cd actions
rasa run actions

Then in a separate terminal, start the Rasa server with the interactive shell:

rasa shell

Here's what an actual conversation looks like:

Your input ->  scale the payment service to 5 replicas in production
Bot: Please confirm: you want to scale payment to 5.0 replicas in production?
Your input ->  yes
Bot: Done. payment scaled to 5 replicas in production.

And when something goes wrong:

Your input ->  show me logs for the api-service
Bot: Running kubectl logs for api-service in default namespace...
Bot: [Last 10 lines of logs appear here]

Connecting to Real Infrastructure

In production, you wouldn't run subprocess calls to kubectl. Instead, you'd use the Kubernetes Python client library, or hit your platform's API directly—similar to how the Scalingo API project (a great reference at github.com/SC-Samir/chatbotsamirrasa) integrates with a PaaS provider via REST calls.

For example, replacing the subprocess call with the official client:

from kubernetes import client, config

class ActionScaleService(Action):
    def run(self, dispatcher, tracker, domain):
        config.load_incluster_config()
        apps_v1 = client.AppsV1Api()

        service_name = tracker.get_slot("service_name")
        replica_count = int(tracker.get_slot("replica_count"))
        namespace = tracker.get_slot("namespace") or "default"

        body = {'spec': {'replicas': replica_count}}
        apps_v1.patch_namespaced_deployment_scale(
            name=service_name,
            namespace=namespace,
            body=body
        )
        # ... handle response

This is cleaner, more reliable, and lets you use service account tokens for auth instead of relying on local kubeconfig files.

Practical Tips and Honest Limitations

Tips:

  • Test with rasa interactive before deploying. It lets you correct the agent's understanding in real-time and saves the corrections as training data.
  • Use Rasa X for conversation-driven development. It gives you a UI to review conversations and annotate where the agent misunderstood.
  • Namespace your slots. I had a bug where the namespace slot was being filled by the NLU when someone said "in production" in a completely unrelated context. Add strict entity extraction conditions.
  • Add rate limiting to your actions. A conversational loop bug could trigger rapid scaling commands. I added a simple in-memory throttle.

Limitations:

  • Rasa's learning curve is real. The YAML-driven configuration gets verbose quickly. Understanding the interplay between stories, rules, and forms took me a solid week of trial and error.
  • The LLM integration requires a license for production. The Developer Edition is free, but you'll need Rasa Pro for production CALM features. If you want to avoid that, you can self-host the fine-tuned model they reference in their docs, but that's more operational overhead.
  • Multi-cluster management is complex. My current setup assumes a single cluster context. Supporting multiple clusters means adding cluster selection to the conversation flow, which adds significant complexity.
  • Real-time log streaming doesn't fit the request-response model well. For check_logs, I had to settle on returning the last N lines rather than streaming output. A proper streaming solution would require WebSocket integration, which Rasa supports but requires custom channel work.

The biggest takeaway for me: Rasa's strength isn't in being the smartest conversationalist—it's in being the most reliable. When your agent can scale production infrastructure, reliability beats cleverness every time. The structured confirmation flows, slot validation, and deterministic action execution give you the guardrails that raw LLM prompts simply don't provide.

相关 Agent

D

Devin

Cognition AI 的自主 AI 软件工程师

了解更多 →