Deploy a Production Langflow Agent

Deploy a Langflow AI agent with Docker Compose, PostgreSQL, and a custom tool.

Introduction

30 Second Summary

AI agents are impressive in a demo. Getting them into production with persistent storage, custom tools, and a callable API is where the real engineering happens.

In this project, you will deploy a production-grade Langflow AI agent using Docker Compose, build a custom Python tool, and call the agent through a REST API.

What You'll Build

A production-ready IT support agent deployed via Docker Compose with PostgreSQL persistence, a custom Python component for ticket classification, and a Python client that calls the agent over REST.

By the end of this project, you'll have:

  • A Docker Compose stack running Langflow and PostgreSQL with persistent volumes and environment-based configuration.
  • A custom Python component (TicketClassifier) mounted into the Docker container via LANGFLOW_COMPONENTS_PATH.
  • A Python client script that calls the agent's REST API with session management for multi-turn conversations.
  • Secret Mission: Package your flow and components into a custom Docker image that runs as a headless API server.

Are there any prerequisites?

You need Docker installed and running, a Google Gemini API key (free tier from https://aistudio.google.com/apikey), and Python 3.10+ installed.

Basic familiarity with Docker and Python is helpful. Langflow itself runs entirely inside Docker, so you do not need to install it locally.

Set Up the Docker Compose Stack

Deploying AI tools like Langflow in production requires a reproducible, persistent infrastructure. Docker Compose lets you define your entire stack as code, so you can spin it up on any machine with a single command.

In this step, you will create a docker-compose.yml file that runs Langflow alongside PostgreSQL for persistent storage. Then you will launch the stack and verify everything is connected.

In this step, get ready to:
  • Write a Docker Compose configuration with Langflow and PostgreSQL services.
  • Create the components directory for mounting custom code later.
  • Launch the stack and verify Langflow is accessible in your browser.
Create your project directory and Docker Compose file

Every Docker Compose project lives in its own directory. The docker-compose.yml file inside that directory defines all the services, networks, and volumes your stack needs.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal and press Enter to open it.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir langflow-docker
cd langflow-docker
  • Confirm the folder exists by running:
ls

You should see the langflow-docker folder listed in the output (if you ran ls before cd) or an empty listing (since you just created and moved into it).

  • Create a new file called docker-compose.yml in your langflow-docker folder and paste the following content:
services:
  langflow:
    image: langflowai/langflow:latest
    pull_policy: always
    ports:
      - "7860:7860"
    depends_on:
      - postgres
    environment:
      - LANGFLOW_DATABASE_URL=postgresql://langflow:langflow@postgres:5432/langflow
      - LANGFLOW_CONFIG_DIR=/app/langflow
      - LANGFLOW_COMPONENTS_PATH=/app/components
    volumes:
      - langflow-data:/app/langflow
      - ./components:/app/components

  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: langflow
      POSTGRES_PASSWORD: langflow
      POSTGRES_DB: langflow
    ports:
      - "5432:5432"
    volumes:
      - langflow-postgres:/var/lib/postgresql/data

volumes:
  langflow-postgres:
  langflow-data:

What does this file do?

This file defines two services that run as Docker containers on the same internal network:

  • langflow pulls the official Langflow image (langflowai/langflow:latest), exposes port 7860 for the web UI, and connects to PostgreSQL via LANGFLOW_DATABASE_URL.
  • postgres runs postgres:16 with credentials that match the connection string above.
  • LANGFLOW_CONFIG_DIR tells Langflow where to store logs, secrets, and monitor data.
  • LANGFLOW_COMPONENTS_PATH tells Langflow where to find custom components you will build in the next step.
  • The named volumes (langflow-data and langflow-postgres) persist data across container restarts.

Why Docker Compose instead of running Langflow locally?

Running Langflow directly with pip install works for quick experiments. Docker Compose gives you PostgreSQL persistence, reproducible deployments, and environment-based configuration in a single file.

This is how production teams deploy Langflow. Everything is version-controlled and portable to any machine with Docker installed.

  • Save docker-compose.yml.

Not sure how to create the file?

You can use any text editor. Open your preferred code editor, create a new file, paste the YAML content above, and save it as docker-compose.yml inside the langflow-docker folder on your Desktop.

Make sure the filename is exactly docker-compose.yml with no extra extension like .txt. help me create docker-compose.yml in my project folder

✔️ Awesome, I've got everything!

Great. Double check you have saved the file before continuing.

ⓧ I'd like to double check the full code

Here is the complete docker-compose.yml file:

services:
  langflow:
    image: langflowai/langflow:latest
    pull_policy: always
    ports:
      - "7860:7860"
    depends_on:
      - postgres
    environment:
      - LANGFLOW_DATABASE_URL=postgresql://langflow:langflow@postgres:5432/langflow
      - LANGFLOW_CONFIG_DIR=/app/langflow
      - LANGFLOW_COMPONENTS_PATH=/app/components
    volumes:
      - langflow-data:/app/langflow
      - ./components:/app/components

  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: langflow
      POSTGRES_PASSWORD: langflow
      POSTGRES_DB: langflow
    ports:
      - "5432:5432"
    volumes:
      - langflow-postgres:/var/lib/postgresql/data

volumes:
  langflow-postgres:
  langflow-data:
Create the components directory

The docker-compose.yml mounts ./components into the container. This directory needs to exist before you start the stack, otherwise Docker will create it with root ownership and you will not be able to write to it later.

  • Create the components directory by running this command:
mkdir components
  • Confirm both files and folders exist by running:
ls

You should see components and docker-compose.yml listed in the output.

Launch and verify the stack

With the configuration file and components directory in place, you can now bring the entire stack online. Docker Compose will pull the images, create the volumes, and start both containers.

  • Start the stack in detached mode by running this command:
docker compose up -d

What does this command do?

The -d flag runs containers in the background so you get your terminal back. Docker Compose pulls the Langflow and PostgreSQL images, creates the named volumes, and starts both services.

The first run takes a few minutes because Docker needs to download the images. Subsequent runs are much faster since the images are cached locally.

You should see output indicating both containers were created and started.

  • Wait about 30 seconds for Langflow to finish starting up, then open your browser and navigate to http://localhost:7860.
  • You should see the Langflow login screen or dashboard.

How do I know PostgreSQL is connected?

If Langflow loads at all, the database connection is working. Langflow requires a successful database connection before it serves the UI.

To double-confirm, create a blank flow (click New Flow, pick any template, then close it). If the flow appears in your dashboard after a browser refresh, PostgreSQL is persisting data correctly.

Langflow not loading at localhost:7860?

Check if both containers are running by typing docker compose ps in your terminal. Both langflow and postgres should show a status of "running" or "Up".

If the langflow container is restarting, check its logs with docker compose logs langflow. A common issue is port 7860 or 5432 already being used by another process.

If Docker cannot pull the images, make sure Docker Desktop is running and you have an internet connection. Help me troubleshoot my Docker Compose stack not starting

Your Langflow stack is running with production-grade persistence. Next up, you will build a custom Python component and mount it into the container as a tool your agent can use.

Build Your Custom Python Component

Your Docker Compose stack is running and Langflow is accessible. The real power of Langflow comes from its extensibility.

In this step, you'll write a custom Python component called TicketClassifier that analyzes IT support tickets. It classifies each ticket by priority and category, then recommends an SLA response time. Once loaded into Langflow, your agent can call this component as a tool to triage incoming requests automatically.

In this step, get ready to:
  • Create a custom Python component that classifies IT tickets by priority and category.
  • Restart the Docker container to load the new component.
  • Verify the component appears in Langflow's visual editor.
Create the component directory and files

Langflow loads custom components from category subfolders inside the path defined by LANGFLOW_COMPONENTS_PATH. Your docker-compose.yml already sets this to /app/components and mounts your local ./components directory into the container. You need to create a tools subfolder inside it.

  • Create the tools subdirectory inside your existing components folder by running this command:
mkdir -p components/tools
  • Confirm the directory exists by running:
ls components/

You should see tools listed in the output.

Why a tools subfolder?

Langflow uses the category folder name to determine where the component appears in the visual editor's sidebar. By placing your file inside a tools folder, it shows up under the "tools" category alongside built-in tools like Calculator and Web Search.

  • Create the __init__.py file inside components/tools/ with the following content:
from .ticket_classifier import TicketClassifierComponent

__all__ = ["TicketClassifierComponent"]

What does this file do?

The __init__.py file makes the tools directory a Python package. Langflow requires this file to recognize and load components from the directory. It imports your component class so Langflow can discover it.

Now you'll write the actual component. The TicketClassifier inherits from Langflow's Component base class, defines an input with tool_mode=True so an agent can call it, and outputs a classification result.

  • Create a new file called ticket_classifier.py inside components/tools/. Add the following code to define the component's structure:
from langflow.custom import Component
from langflow.io import MessageTextInput, Output
from langflow.schema import Data


class TicketClassifierComponent(Component):
    display_name = "Ticket Classifier"
    description = "Classifies IT support tickets by priority and category."
    documentation = "http://docs.langflow.org/components/custom"
    icon = "alert-triangle"
    name = "TicketClassifierComponent"

    inputs = [
        MessageTextInput(
            name="ticket_text",
            display_name="Ticket Text",
            info="The IT support ticket text to classify",
            value="",
            tool_mode=True,
        ),
    ]

    outputs = [
        Output(display_name="Classification", name="output", method="analyze_ticket"),
    ]

What does this code do?

  • The class inherits from Component, which is Langflow's base class for custom nodes.
  • The display_name and description control how it appears in the visual editor.
  • The inputs list defines a single text input called ticket_text. Setting tool_mode=True makes this component usable as a tool for Agent components.
  • The outputs list links to the analyze_ticket method, which you'll add next.
  • Below the outputs list, add the analyze_ticket method that contains the classification logic:
    def analyze_ticket(self) -> Data:
        text = self.ticket_text.lower()

        # Priority classification based on urgency keywords
        if any(word in text for word in ["urgent", "emergency", "down", "critical", "asap", "meeting in"]):
            priority = "HIGH"
        elif any(word in text for word in ["slow", "intermittent", "sometimes", "occasional"]):
            priority = "MEDIUM"
        else:
            priority = "LOW"

        # Category classification
        if any(word in text for word in ["vpn", "network", "wifi", "internet", "connection"]):
            category = "NETWORKING"
        elif any(word in text for word in ["password", "login", "access", "locked", "authentication"]):
            category = "ACCESS_MANAGEMENT"
        elif any(word in text for word in ["install", "update", "software", "application", "crash"]):
            category = "SOFTWARE"
        elif any(word in text for word in ["laptop", "monitor", "keyboard", "hardware", "printer"]):
            category = "HARDWARE"
        else:
            category = "GENERAL"

        classification = {
            "ticket_text": self.ticket_text,
            "priority": priority,
            "category": category,
            "recommended_sla": "1 hour" if priority == "HIGH" else "4 hours" if priority == "MEDIUM" else "24 hours",
        }

        data = Data(value=classification)
        self.status = data
        return data

What does this method do?

The analyze_ticket method is where the actual classification happens. It converts the ticket text to lowercase and checks for urgency keywords to assign a priority (HIGH, MEDIUM, or LOW).

It then checks for topic keywords to assign a category like NETWORKING or ACCESS_MANAGEMENT. Finally, it recommends an SLA response time based on priority and returns everything wrapped in a Langflow Data object.

  • Save components/tools/ticket_classifier.py.

Seeing a syntax error?

Make sure the analyze_ticket method is indented inside the class body (one level of indentation from the class definition). Python is whitespace-sensitive, so the method must align with the inputs and outputs lists above it.

help me fix indentation errors in my ticket_classifier.py component

✔️ Awesome, I've got everything!

Great. Double check you've saved both __init__.py and ticket_classifier.py.

ⓧ I'd like to double check the full code

from langflow.custom import Component
from langflow.io import MessageTextInput, Output
from langflow.schema import Data


class TicketClassifierComponent(Component):
    display_name = "Ticket Classifier"
    description = "Classifies IT support tickets by priority and category."
    documentation = "http://docs.langflow.org/components/custom"
    icon = "alert-triangle"
    name = "TicketClassifierComponent"

    inputs = [
        MessageTextInput(
            name="ticket_text",
            display_name="Ticket Text",
            info="The IT support ticket text to classify",
            value="",
            tool_mode=True,
        ),
    ]

    outputs = [
        Output(display_name="Classification", name="output", method="analyze_ticket"),
    ]

    def analyze_ticket(self) -> Data:
        text = self.ticket_text.lower()

        # Priority classification based on urgency keywords
        if any(word in text for word in ["urgent", "emergency", "down", "critical", "asap", "meeting in"]):
            priority = "HIGH"
        elif any(word in text for word in ["slow", "intermittent", "sometimes", "occasional"]):
            priority = "MEDIUM"
        else:
            priority = "LOW"

        # Category classification
        if any(word in text for word in ["vpn", "network", "wifi", "internet", "connection"]):
            category = "NETWORKING"
        elif any(word in text for word in ["password", "login", "access", "locked", "authentication"]):
            category = "ACCESS_MANAGEMENT"
        elif any(word in text for word in ["install", "update", "software", "application", "crash"]):
            category = "SOFTWARE"
        elif any(word in text for word in ["laptop", "monitor", "keyboard", "hardware", "printer"]):
            category = "HARDWARE"
        else:
            category = "GENERAL"

        classification = {
            "ticket_text": self.ticket_text,
            "priority": priority,
            "category": category,
            "recommended_sla": "1 hour" if priority == "HIGH" else "4 hours" if priority == "MEDIUM" else "24 hours",
        }

        data = Data(value=classification)
        self.status = data
        return data
Restart the container to load your component

Your docker-compose.yml already mounts ./components into the container and sets LANGFLOW_COMPONENTS_PATH. Langflow only scans for new components at startup, so you need to restart the container for it to discover your new files.

  • Restart the Langflow container by running:
docker compose up -d --force-recreate langflow
  • Wait about 30 seconds for Langflow to fully restart.
  • Confirm it's running by checking the container logs:
docker compose logs langflow --tail 20

You should see log output ending with a message indicating Langflow is ready and serving on port 7860.

Container failing to start?

Check the logs for Python import errors. If you see an error mentioning ticket_classifier, there may be a typo in your component code. Verify that __init__.py imports TicketClassifierComponent (exact spelling) from .ticket_classifier.

help me debug why my Langflow container is failing to start after adding a custom component

Verify the component in Langflow's editor
  • Open your browser and go to http://localhost:7860.
  • Click **New Flow** in the top right corner.
  • Select **Blank Flow** to start with an empty canvas.
  • In the component search bar on the left sidebar, type Ticket Classifier.

You should see your Ticket Classifier component appear in the search results under the tools category.

Component not appearing in search?

Verify your directory structure matches this layout exactly:

components/
  tools/
    __init__.py
    ticket_classifier.py

Each category directory must include an __init__.py file. Without it, Langflow cannot recognize the directory as a Python package and will skip loading components from it.

Also confirm that your file is not placed directly in components/ without the tools/ subfolder. Langflow requires the category subfolder structure.

help me troubleshoot why my custom component is not showing up in Langflow's visual editor

Your custom component is loaded and visible in the editor. Next up, you'll wire it into an agent flow alongside Google Gemini and a Web Search tool to create a fully functional IT support agent.

Build and Configure the Agent Flow

Your custom TicketClassifier component is loaded into Langflow and ready to use as a tool. Now it's time to wire everything together into an intelligent agent.

In this step, you'll connect Google Gemini as your LLM, build an agent flow with your custom tool and web search, and test it with a real support ticket.

In this step, get ready to:
  • Configure Google Gemini as your model provider.
  • Create an agent flow with your TicketClassifier and Web Search tools.
  • Test the agent in the Playground with a real IT support ticket.
Configure Google Gemini as your model provider

Before building the agent flow, you need to tell Langflow which LLM to use. Langflow's Model Providers settings let you configure API keys globally so every flow in your instance can access them.

  • In Langflow, click your profile icon in the bottom-left corner of the screen.
  • Select Settings.
  • Click Model Providers in the left sidebar.
  • Find Google in the list of providers and click on it.
  • Paste your Gemini API key into the Google API Key field.
  • Enable the gemini-2.5-flash model by toggling it on.
  • Click Save to confirm your configuration.

Why gemini-2.5-flash?

Google's free tier currently provides free input and output tokens for gemini-2.5-flash. Older models like gemini-1.5 and gemini-2.0 may have zeroed free-tier limits.

If you hit rate limit errors later, gemini-2.5-flash has the most stable free-tier access.

Don't see Google in Model Providers?

Make sure you're running the latest Langflow image. If you started your containers before updating, run docker compose down followed by docker compose up -d to pull the latest image.

Help me troubleshoot Model Providers in Langflow

Create the agent flow from the Simple Agent template

Langflow provides starter templates so you don't have to wire up an agent from scratch. You'll start from the Simple Agent template and swap its default tools for your TicketClassifier and a Web Search component.

  • Return to the Langflow home screen by clicking the Langflow logo in the top-left corner.
  • Click New Flow.
  • Select the Simple Agent template from the list.

What's in the Simple Agent template?

The Simple Agent template comes pre-wired with a Chat Input, Chat Output, an Agent component, and two default tools: Calculator and URL. You'll remove those default tools and add your own.

Now remove the default tools and add your custom ones.

  • Select the URL component on the canvas and press Delete (or Backspace) to remove it.
  • Select the Calculator component and delete it the same way.
  • Open the components sidebar by clicking the + button or searching in the search bar at the top of the sidebar.
  • Search for Ticket Classifier and drag it onto the canvas.
  • Search for Web Search and drag it onto the canvas.

What is the Web Search component?

The Web Search component uses DuckDuckGo to search the web. It requires no API key. When Tool Mode is enabled, the agent can decide when to use it to look up information.

Next, enable Tool Mode and connect both tools to the Agent.

  • Click on the Ticket Classifier component to open its settings.
  • Find the Tool Mode toggle and make sure it is enabled. Your TicketClassifier already has tool_mode=True in its code, so this should already be on.
  • Click on the Web Search component and enable Tool Mode if it is not already enabled.
  • Drag a connection from the Toolset output port on the Ticket Classifier to the Tools input port on the Agent component.
  • Drag a connection from the Toolset output port on Web Search to the same Tools input port on the Agent.

Finally, set the Agent's language model to Gemini.

  • Click on the Agent component to open its settings.
  • In the Language Model dropdown, select gemini-2.5-flash from the list of available models.

Why does gemini-2.5-flash appear in the dropdown?

When you configured Google as a model provider in the previous substep, Langflow made all enabled models available globally. The Agent's Language Model dropdown pulls from this global list, so any flow in your instance can use the same model without reconfiguring keys.

Don't see gemini-2.5-flash in the dropdown?

Go back to Settings > Model Providers > Google and confirm that gemini-2.5-flash is toggled on and that your API key is saved correctly.

Help me fix my Gemini model not appearing in the Agent dropdown

Test the agent in the Playground

Your agent flow is fully wired. The real test is whether the agent intelligently chooses the right tool for a given ticket. The Playground lets you chat with your agent directly inside Langflow.

  • Click the Playground button in the top-right area of the flow editor.
  • Type the following message into the chat input:
My laptop won't connect to VPN and I have a client meeting in 30 minutes
  • Press Enter or click the send button to submit.

Watch the agent's response. You should see it call the TicketClassifier tool first (classifying the ticket as HIGH priority, NETWORKING category), then use Web Search to find VPN troubleshooting steps.

What should I see?

The agent's response should include a priority classification (HIGH, because of the "meeting in 30 minutes" urgency), a category (NETWORKING, because of the VPN keyword), and concrete troubleshooting steps found via web search.

You can expand the tool call details in the Playground to see exactly which tools the agent invoked and what data they returned.

Agent not using your tools?

Check that both tools have their Toolset ports connected to the Agent's Tools port. A missing connection means the agent doesn't know the tool exists.

Also verify that Tool Mode is enabled on both the Ticket Classifier and Web Search components. Without Tool Mode, the component appears on the canvas but isn't exposed to the agent as a callable tool.

Help me debug why my agent isn't calling the TicketClassifier tool

  • Once you've confirmed the agent responds correctly, save your flow by pressing Cmd+S (macOS) or Ctrl+S (Windows).

Your flow now has an ID

When you save, Langflow stores your flow in PostgreSQL with a unique flow ID. You'll use this ID in the next step to call the agent via its REST API.

Your agent is working inside Langflow's visual editor. Next up, you'll call this agent from outside Langflow using its REST API, which is how real applications integrate with Langflow agents in production.

Call the Agent via REST API

Your agent classifies tickets, searches the web, and responds intelligently in the Playground. But a Playground chat window isn't how real applications talk to your agent.

Langflow exposes every saved flow as a REST API endpoint. In this step, you'll create API credentials and write a Python script that sends IT tickets to your agent programmatically with multi-turn conversation support.

In this step, get ready to:
  • Create a Langflow API key and retrieve your flow ID.
  • Write a Python script that calls the agent's REST API endpoint.
  • Test multi-turn conversation using session-based context.
Create an API key and get your flow ID

External applications need two pieces of information to call your flow: an API key for authentication and the flow's unique ID to target the correct endpoint.

  • Return to Langflow at http://localhost:7860.
  • Click the user icon in the top-left corner.
  • Select Settings.
  • Click Langflow API Keys.
  • Click Add New.
  • Copy the generated key and store it somewhere safe (a text file or password manager). You will not be able to see it again after closing this dialog.

Keep your API key private

This key grants full access to your Langflow instance. Do not commit it to version control or share it publicly.

  • Close the Settings dialog and return to your agent flow.
  • Click Share in the flow editor toolbar.
  • Click API access.
  • Locate the flow ID in the generated code snippet. It appears in the URL path as a UUID (e.g. a1b2c3d4-e5f6-...). Copy it.

Where is the flow ID?

The flow ID is the UUID that appears after /api/v1/run/ in the auto-generated code snippets. You can also find it in your browser's URL bar when viewing the flow.

Write the Python API client

Now you'll write a Python script that sends HTTP requests to your agent's endpoint. The script uses the requests library to POST ticket data and parse the agent's response.

  • Install the requests library by running this command in your terminal:
pip install requests
  • You should see output ending with Successfully installed requests-... or Requirement already satisfied.

Seeing 'command not found' for pip?

Try pip3 install requests instead. On some systems, the Python 3 package manager is called pip3.

help me fix pip not being found on my system

  • Create a new file called call_agent.py in your project directory (the same folder as docker-compose.yml) and add the configuration block by pasting this code:
import requests

LANGFLOW_SERVER = "http://localhost:7860"
FLOW_ID = "your-flow-id-here"
API_KEY = "your-langflow-api-key-here"

url = f"{LANGFLOW_SERVER}/api/v1/run/{FLOW_ID}"

headers = {
    "Content-Type": "application/json",
    "x-api-key": API_KEY,
}

What does this code do?

LANGFLOW_SERVER points to your local Langflow instance. FLOW_ID targets the specific flow you built. API_KEY authenticates your requests.

The url variable assembles the full endpoint path: /api/v1/run/{FLOW_ID}. The x-api-key header tells Langflow who you are.

  • Replace your-flow-id-here with the flow ID you copied from the API access pane.
  • Replace your-langflow-api-key-here with the API key you generated earlier.
  • Save call_agent.py.
  • Now add the ask_agent function below the headers block in call_agent.py:

def ask_agent(question, session_id="support-session-001"):
    payload = {
        "output_type": "chat",
        "input_type": "chat",
        "input_value": question,
        "session_id": session_id,
    }

    try:
        response = requests.post(url, json=payload, headers=headers)
        response.raise_for_status()
        data = response.json()
        message = data["outputs"][0]["outputs"][0]["outputs"]["message"]["message"]
        return message
    except requests.exceptions.RequestException as e:
        return f"Error: {e}"
    except (KeyError, IndexError) as e:
        return f"Error parsing response: {e}"

What does this function do?

The payload dictionary contains the required fields for Langflow's /run endpoint: input/output types set to chat, the user's question as input_value, and a session_id for conversation continuity.

The response JSON is deeply nested. The extraction path data["outputs"][0]["outputs"][0]["outputs"]["message"]["message"] navigates to the agent's final text response. The try/except blocks catch both network errors and unexpected response formats.

  • Save call_agent.py.

Why session_id?

Langflow agents maintain a rolling conversation context per session. By sending the same session_id across multiple requests, the agent remembers what was discussed earlier. Different session IDs create isolated conversations.

Test multi-turn conversation

The real power of session-based context is multi-turn conversation. You'll add a main block that submits a ticket and then asks a follow-up question in the same session.

  • Add the main execution block at the bottom of call_agent.py:

if __name__ == "__main__":
    # First message - ticket submission
    print("=== Submitting IT Ticket ===")
    ticket = "My laptop won't connect to VPN and I have a client meeting in 30 minutes"
    response = ask_agent(ticket)
    print(f"Agent: {response}\n")

    # Follow-up in same session
    print("=== Follow-up Question ===")
    followup = "What if that doesn't work? Is there an alternative way to join the meeting?"
    response = ask_agent(followup)
    print(f"Agent: {response}")

What does this code do?

The first call sends a VPN ticket that should trigger the TicketClassifier (HIGH priority, NETWORKING category) and a web search for troubleshooting steps.

The second call uses the same default session_id (support-session-001). Because the session is shared, the agent remembers the VPN issue and provides context-aware follow-up suggestions without needing the ticket repeated.

  • Save call_agent.py.
  • Run the script by executing this command:
python call_agent.py
  • You should see output with two sections: the agent's response to the initial ticket (including priority classification and troubleshooting steps) and a context-aware follow-up response.

What should I see?

The first response should mention HIGH priority, NETWORKING category, and VPN troubleshooting steps. The follow-up response should reference the original VPN issue without you restating it. This confirms session-based memory is working.

The exact wording varies because the Gemini model generates responses dynamically. The key signals are classification data from your TicketClassifier tool and contextual awareness in the follow-up.

Seeing a connection error or 401?

If you see Connection refused, confirm your Docker Compose stack is still running with docker compose ps. Both the langflow and postgres containers should show as running.

A 401 error means your API key is incorrect. Double-check that the value in call_agent.py matches the key you generated in Langflow Settings. If you lost it, create a new key via Settings > Langflow API Keys > Add New.

A 404 error means the flow ID is wrong. Copy it again from Share > API access in the flow editor.

help me debug my Langflow API call that is returning an error

✔️ Awesome, I've got everything!

Great. Double check you've saved call_agent.py and that both API responses printed successfully in your terminal.

ⓧ I'd like to double check the full code

import requests

LANGFLOW_SERVER = "http://localhost:7860"
FLOW_ID = "your-flow-id-here"
API_KEY = "your-langflow-api-key-here"

url = f"{LANGFLOW_SERVER}/api/v1/run/{FLOW_ID}"

headers = {
    "Content-Type": "application/json",
    "x-api-key": API_KEY,
}


def ask_agent(question, session_id="support-session-001"):
    payload = {
        "output_type": "chat",
        "input_type": "chat",
        "input_value": question,
        "session_id": session_id,
    }

    try:
        response = requests.post(url, json=payload, headers=headers)
        response.raise_for_status()
        data = response.json()
        message = data["outputs"][0]["outputs"][0]["outputs"]["message"]["message"]
        return message
    except requests.exceptions.RequestException as e:
        return f"Error: {e}"
    except (KeyError, IndexError) as e:
        return f"Error parsing response: {e}"


if __name__ == "__main__":
    # First message - ticket submission
    print("=== Submitting IT Ticket ===")
    ticket = "My laptop won't connect to VPN and I have a client meeting in 30 minutes"
    response = ask_agent(ticket)
    print(f"Agent: {response}\n")

    # Follow-up in same session
    print("=== Follow-up Question ===")
    followup = "What if that doesn't work? Is there an alternative way to join the meeting?"
    response = ask_agent(followup)
    print(f"Agent: {response}")

Your agent is now callable from any application that can make HTTP requests. Next up, you'll package this entire system as a headless Docker image ready for production deployment.

Secret mission

Package as a Headless API Server

Your agent works great in the visual editor. But production teams don't ship a GUI. They ship a self-contained Docker image that starts up, loads the flow, and exposes only the API. In this secret mission, you'll export your flow, write a Dockerfile, and deploy your agent as a headless microservice.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

This project runs entirely locally with Docker. There are no cloud resources or ongoing costs.

Resources you used:

  • Docker Compose containers (Langflow and PostgreSQL).
  • Docker named volumes (langflow-data and langflow-postgres).
  • Custom Docker image (built during the Secret Mission).
  • Local project files (docker-compose.yml, components/, flows/, call_agent.py, Dockerfile).

Keep everything running

No action needed. Choose this if you're still actively building or experimenting with your Langflow agent.

  • Your Docker Compose stack stays accessible at http://localhost:7860.
  • PostgreSQL persists all your flows and settings in the named volumes.
  • Your Secret Mission container continues running in backend-only mode.

Pause - I'll come back to this later

Shut down running containers to free up memory and CPU. Your data stays intact in Docker volumes.

  • Stop the Docker Compose stack by running:
  • Stop any Secret Mission container that is still running. List running containers, find the one based on your custom image, and stop it:
docker compose stop

This stops the Langflow and PostgreSQL containers without deleting their data.

  • When you're ready to resume, start everything back up by running:
docker compose start
  • Stop the Secret Mission container (if still running) by running:
docker ps
docker stop <container-id>

Delete - I don't want to use this again

Remove all project containers, volumes, images, and files. This deletes all saved flows and data permanently.

  • Stop and remove the Docker Compose containers and their volumes by running:
docker compose down -v
  • Stop and remove the Secret Mission container (if running) by running:
docker ps -a
docker stop <container-id>
docker rm <container-id>
  • Remove the custom Docker image you built for the Secret Mission by running:
docker images
docker rmi <image-id>
  • Delete the local project folder. On macOS or Linux, run:
cd ~/Desktop
ls

Find the name of the project folder you created in Step 1, then remove it:

rm -rf <your-project-folder-name>
  • On Windows, run:
cd ~\Desktop
rmdir /s /q <your-project-folder-name>

That's a Wrap!

That's a Wrap!

You just deployed Langflow the way production teams do it.

You've learned how to:

  • Deploy Langflow with Docker Compose and PostgreSQL persistence, matching production architecture patterns.
  • Build a custom Python component and mount it into a Docker container using LANGFLOW_COMPONENTS_PATH.
  • Create an AI agent with Gemini, Web Search, and your custom tool, then call it via REST API with session-based multi-turn conversation.
  • Package the entire system as a headless Docker image ready for microservice deployment.

Ready to quiz yourself?