# Building and Testing a Persistent Database Backend for an AI Booking Agent
## Introduction
AI-powered customer service agents are transforming how businesses handle routine interactions like appointment scheduling. One practical application is a booking agent for a cleaning service that guides customers through a complete reservation process — from initial inquiry to confirmed appointment — in roughly fifteen minutes.
This kind of agent goes beyond simple chatbot responses. It orchestrates multiple steps in a structured workflow: understanding what the customer needs, calculating pricing, handling acceptance or rejection of the quote, suggesting available time slots, and finally recording the booking in a database. The entire process is powered by LangGraph, a framework designed for building stateful, multi-step AI applications.
A critical component of this system is the persistence layer. Without a reliable database, every conversation disappears once the application restarts. In this piece, we’ll walk through setting up and testing a PostgreSQL backend for the booking agent using two approaches: Docker for local development and a hosted Postgres instance for cloud-based testing.
## How the AI Booking Agent Works
The agent operates as a graph-based workflow, meaning it follows a defined sequence of steps while maintaining conversation state throughout the interaction.
At the core of the system is the agent’s ability to understand customer intent. When someone initiates a conversation, the agent determines what information is already available and what it still needs. For example, if the customer provides their address and the size of the space right away, the agent can move directly to pricing. If key details are missing, it asks clarifying questions before proceeding.
Once the agent has enough information, it calculates a price based on the service type and any relevant factors. The customer can then accept or decline the offer. If they accept, the agent checks the database for existing bookings to propose time slots that are actually available. Finally, when the customer confirms a time, the agent writes the booking details — including technician assignment, time range, service address, and final price — into the database.
Conversation progress is tracked using LangGraph’s state management system, which saves checkpoints at each step. This allows the agent to resume or reference earlier parts of the conversation if needed.
## Why Database Persistence Matters
The agent supports two persistence modes. The first is an in-memory mode, which is convenient for quick experimentation and demonstrations. When no database connection is configured, the application uses a simple in-memory repository that requires no setup — no tables, no configuration, nothing. The tradeoff, however, is that all data vanishes the moment the application stops running.
The second mode connects to a real PostgreSQL database. This is essential for any scenario where you want bookings to survive application restarts, support multiple concurrent sessions, or test the full end-to-end workflow. With a database backend, the agent can check for scheduling conflicts across all previous bookings, ensuring that it never double-books a technician.
Testing the database layer properly is important because it validates that the entire booking pipeline — from reading existing records to writing new ones — works reliably.
## Testing the Backend with Docker
### Why Use Docker for Testing
Docker provides a lightweight, isolated environment for running PostgreSQL without requiring a full database installation on your local machine. Instead of installing Postgres, configuring it, and managing system-level processes, you can spin up a completely self-contained database server inside a container.
This approach offers several practical advantages:
– **Realistic testing**: The application communicates with an actual PostgreSQL instance rather than a simulated one, so you’re testing against the same database engine you’d use in production.
– **Reproducibility**: The database configuration — including credentials and connection details — lives in a `docker-compose.yml` file and an environment template, so anyone on the team can replicate the exact same setup.
– **Isolation**: The database runs in its own container, separate from other applications on your computer. You can stop or completely remove it without affecting anything else.
– **Speed**: Setting up a full Postgres instance with Docker takes just a few minutes.
### Setting Up Docker for the Booking Agent
The project includes a `docker-compose.yml` file that defines a PostgreSQL 16 container using the official Alpine image. The container is configured with a specific username, password, and database name, and it exposes the default PostgreSQL port so that the application can connect to it.
To get started, make sure Docker Desktop is installed and running on your machine. Once the Docker engine is active, you can launch the database container with a single command.
Next, you need to configure the application to connect to this database. This involves adding a connection string to your environment file that points to `localhost` on the standard PostgreSQL port, using the credentials defined in the Docker configuration.
The connection string follows the standard PostgreSQL format, specifying the username, password, host, port, and database name.
With the database container running and the connection string in place, you can start the Streamlit user interface using the same command you’d use for the in-memory mode. The application will now talk to the real Postgres instance running inside the Docker container.
### Verifying That Database Persistence Works
After launching the interface, you can complete a test booking by going through the full conversation flow with the agent. Once the booking is confirmed, the details are written into the Postgres database inside the Docker container.
The real test of persistence comes when you open a new browser session or restart the Streamlit application. If the backend is working correctly, the agent will still be able to see the previously recorded booking. When you ask for the same service again, the agent should skip the time slots that are already taken, because it queries the database for existing appointments before proposing new ones.
You can verify the Docker container is still running independently of the Streamlit app. Since the Streamlit interface and the Postgres container are separate processes, stopping and restarting the user interface does not affect the database. The booking data persists in a Docker-managed volume, which is essentially a persistent disk storage area that survives container restarts.
The data only gets deleted if you explicitly stop the container and remove the associated volume. This makes Docker a safe and controlled environment for experimentation — you can reset the database state whenever you need to by removing the volume, or simply let it persist to accumulate test data over multiple sessions.
## Testing with a Hosted Postgres Instance
If you prefer not to use Docker or need a database that’s accessible from multiple machines, you can connect the agent to a cloud-hosted PostgreSQL instance instead.
Providers like Supabase and Amazon RDS offer managed PostgreSQL databases that you can set up directly from their dashboards. Once you create a database, the provider gives you a connection string in the standard PostgreSQL format, including the host address, port, username, password, and database name.
To use this with the booking agent, simply paste the connection string into the `DATABASE_URL` field in your environment file and start the application as usual. The behavior is identical to the Docker setup — the only difference is that the database now runs on a remote server rather than in a local container.
This approach is useful when you want to test the agent from different devices or share a database across a team. It also brings you closer to a production-like architecture where the application and database are hosted separately.
At this stage, the booking agent has a fully functional, persistent backend capable of storing and retrieving appointment data reliably. There are still additional improvements to explore, such as adding support for communication channels like WhatsApp, implementing safeguards against prompt injection attacks, and refining the overall chat experience. These enhancements can build on the foundation described here.
## Frequently Asked Questions
### What is LangGraph and why is it used for this agent?
LangGraph is a framework for building stateful, multi-step AI applications. It allows developers to define a graph-based workflow where each node represents a specific action — such as understanding a customer’s request, calculating a price, or writing to a database — and the edges define the flow between these actions. It’s particularly well-suited for booking systems because it can maintain conversation state and orchestrate complex, sequential operations.
### What happens if DATABASE_URL is not set?
When the `DATABASE_URL` environment variable is not configured, the application falls back to in-memory persistence. This means it uses `InMemoryBookingRepository` and `MemorySaver`, which store all data in RAM. No database tables are created, and all booking data is lost when the application stops. This mode is ideal for quick demos and testing basic conversational flows without any database setup.
### Can I use a different PostgreSQL version with Docker?
Yes, the `docker-compose.yml` file specifies PostgreSQL 16 Alpine, but you can change the image tag to any supported version. Just make sure the version you choose is compatible with the database drivers used by the application.
### How does the agent avoid double-booking time slots?
Before proposing available time slots, the agent queries the database to retrieve all existing bookings. It then filters out any time ranges that overlap with confirmed appointments, ensuring that it only suggests slots that are actually open.
### Is the Docker volume necessary?
The Docker volume (`booking_pgdata`) is what makes the database persistent. Without it, the data inside the Postgres container would be lost when the container is stopped or removed. The volume maps the container’s internal storage to a persistent location managed by Docker, so the data survives container restarts and rebuilds.
### Can I switch between in-memory and database modes?
Yes. Simply set or unset the `DATABASE_URL` in your `.env` file. When it’s set, the agent uses the Postgres backend. When it’s empty, it reverts to in-memory mode. No code changes are needed — this is handled automatically by the application configuration.
### What security considerations should I keep in mind for a hosted Postgres?
When using a cloud-hosted database, make sure to use strong passwords, restrict access by IP address if possible, enable SSL connections, and never commit connection strings or credentials to version control. Use environment files and secrets management tools to keep sensitive information secure.
## Conclusion
Setting up a persistent database backend is a crucial step in turning an AI booking agent from a demo into a usable product. By testing with Docker locally and with a hosted Postgres instance in the cloud, you can validate that the system handles data storage, retrieval, and conflict detection correctly across multiple sessions.
The combination of LangGraph for workflow orchestration and PostgreSQL for durable data storage creates a solid foundation for a customer service agent that can reliably manage the full booking lifecycle — from understanding customer needs all the way through to recording confirmed appointments.
As you continue to develop this kind of system, consider expanding it with additional communication channels, stronger security measures, and a more refined conversational experience to make it truly production-ready.
Thank you for reading



