In the preceding articles of this series, a stateful LangGraph agent was developed to streamline a 15-minute booking process. This agent, which mimics the functionality of a customer service representative, has been enhanced with a user-friendly Streamlit UI and a robust backend powered by a Postgres database. The inception of this AI agent was inspired by a conversation with a customer service representative from a cleaning service, leading to the creation of an agent capable of managing the entire booking process seamlessly.
The LangGraph-based agent orchestrates various operations, including:
- p]:mt-0″ readability=”-1″>Responding to customer queries and understanding their needs.
- p]:mt-0″ readability=”-1″>Calculating the price for services and informing the customer.
- p]:mt-0″ readability=”-1″>Managing customer acceptance or rejection of the proposed service.
- p]:mt-0″ readability=”-1″>Proposing optimized time slots for appointments.
- p]:mt-0″ readability=”-1″>Confirming and recording the appointment details.
This article will delve into testing the Postgres backend using both Docker and a hosted Postgres solution. The complete source code for this project is accessible on GitHub at customer-service-agent. Readers are encouraged to clone the repository and conduct their own tests.
Agent Structure
The structure of the AI agent is represented in a graph workflow, where the conversation progress is maintained in the LangGraph AgentState and saved as checkpoints. When proposing time slots, the agent checks existing bookings in the database to avoid conflicts. Upon customer confirmation, the agent records the booking details, including technician, time range, address, and price.
Testing the Database
The agent operates in two persistence modes: in-memory and Postgres. The in-memory mode is intended for quick testing and demonstration purposes, utilizing InMemoryBookingRepository and MemorySaver when DATABASE_URL is unset, resulting in no tables being created.
To conduct a local test with the Streamlit UI, dependencies must first be installed using poetry install. Subsequently, a .env file can be created by copying the .env.example file:
poetry installcp .env.example .env
Next, the OPENAI_API_KEY must be set in the .env file, while the DATABASE_URL can remain empty for this test. The Streamlit UI can then be initiated at localhost:8501 using the command:
poetry run streamlit run customer_service_agent/streamlit_app.py
Upon launching, the Streamlit UI presents an interactive interface. The AI agent is capable of discerning whether all necessary information is provided to calculate and present a quote. If essential details such as size or address are omitted, the agent prompts the user for this information before proceeding with the price calculation. While bookings can be completed within the UI, it is important to note that the conversation state is lost upon app restart, necessitating the use of database options for data persistence.
Testing with Docker
Why Testing with Docker
Docker provides an alternative for testing the project, allowing for interactions with Postgres without the need to install a database locally. This approach simulates a production-like environment, enabling the application to communicate with an actual PostgreSQL server. Moreover, Docker enhances reproducibility, as the URL and credentials are defined in the docker-compose.yml and .env.example files, providing a consistent setup for all users.
With Docker, the application operates in isolation, and the database runs within a container. This setup allows for easy management; the database can be stopped (docker compose down) or wiped (docker compose down -v) without affecting other applications. Consequently, Docker facilitates rapid testing of the durable backend on a real Postgres instance, maintaining the same connection string each time without requiring Postgres installation on the local machine.
How to Test with Docker
This project includes a docker-compose.yml file that initiates a PostgreSQL 16 container:
services: postgres: image: postgres:16-alpine environment: POSTGRES_USER: booking POSTGRES_PASSWORD: booking POSTGRES_DB: booking_agent ports: - "5432:5432" volumes: - booking_pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U booking -d booking_agent"] interval: 5s timeout: 5s retries: 10volumes: booking_pgdata:
Docker pulls the official Postgres image and runs a database server within the container, while the Streamlit app continues to operate locally, connecting to the container via localhost:5432. The database files are stored in a Docker volume (booking_pgdata), ensuring data persistence beyond the container’s lifecycle. To initiate testing with Docker, ensure Docker Desktop is installed and running, then start the container with:
docker compose up -d
Next, update the .env file to include the DATABASE_URL:
DATABASE_URL=postgresql://booking:booking@localhost:5432/booking_agent
Finally, run the application with:
poetry run streamlit run customer_service_agent/streamlit_app.py
Upon execution, the Streamlit UI launches, allowing for the completion of a test booking. To verify the functionality, a second browser window can be opened at localhost:8501 to request the same service. The agent will not offer the previously booked time slot, demonstrating the system’s ability to manage bookings effectively.
Docker Desktop provides a clear view of the running processes, showcasing the separation between Streamlit and the Docker Postgres container. Restarting Streamlit only affects the Python application, while the Postgres container remains operational, preserving the data stored in the Docker volume. The database is only lost if the container is stopped and the volume deleted using docker compose down -v.
Testing with a Hosted Postgres
For those utilizing a cloud-based Postgres solution, such as Supabase or RDS, Docker is not a necessity. A Postgres database can be created directly through the provider’s dashboard, and the connection string can be copied. This string typically follows the format:
postgresql://USER:PASSWORD@HOST:PORT/DATABASE
This connection string should be placed in the .env file as DATABASE_URL. The application can then be initiated in the same manner as before:
poetry run streamlit run customer_service_agent/streamlit_app.py
The app’s behavior remains consistent regardless of whether Postgres is running locally via Docker or on a remote host. This flexibility allows for the gradual evolution of the customer service agent into a product with tangible business value. Future enhancements may include the integration of additional communication channels, such as WhatsApp, and the implementation of safety measures to mitigate prompt injection risks. Improvements to the booking workflow and chat experience are also on the horizon, with further developments to be detailed in subsequent articles.