Connecting My LangGraph AI Agent to Postgres

In the initial three installments of this series, I developed a stateful LangGraph agent designed to streamline a 15-minute booking process. This endeavor culminated in a user-friendly Streamlit UI and a robust backend powered by a Postgres database. The inception of this AI agent was inspired by an interaction with a customer service representative from a cleaning service, leading to the creation of an agent that mimics real customer service interactions. This LangGraph-based agent efficiently orchestrates several operations:

  • p]:mt-0″>Responds to customer queries and comprehends their needs.
  • p]:mt-0″>Calculates service pricing and informs the customer.
  • p]:mt-0″>Manages customer acceptance or rejection of the service.
  • p]:mt-0″>Suggests optimized time slots for service.
  • p]:mt-0″>Confirms and records 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. I encourage you to clone the repository and explore it yourself.

Agent Structure

The structure of our AI agent is illustrated in the following diagram:

The conversation progress is maintained within LangGraph’s AgentState, which is saved as checkpoints. When the agent proposes time slots, it references existing bookings from the database to avoid suggesting already occupied times. Upon customer confirmation of a booking, the agent records the relevant details, including technician, time range, address, and price.

Testing the Database

As previously mentioned, the agent operates in two persistence modes: in-memory and Postgres. The in-memory mode is intended for rapid testing and demonstration purposes. If the DATABASE_URL is not set, the application defaults to using InMemoryBookingRepository and MemorySaver, resulting in no tables being created. To conduct a local test with the Streamlit UI, we first install the necessary dependencies using poetry install and create a .env file by duplicating the .env.example file:

poetry installcp .env.example .env 

Next, we need to set the OPENAI_API_KEY in the .env file, leaving the DATABASE_URL empty for this test. We can then launch the Streamlit UI at localhost:8501 with the following command:

poetry run streamlit run customer_service_agent/streamlit_app.py

Upon executing this command, the Streamlit UI appears:

In a brief interaction with the agent, it demonstrated its capability to understand whether all necessary information was provided, allowing it to proceed directly to calculating and presenting a quote. If essential details like size or address were omitted in the initial message, the agent would prompt for these before proceeding with the pricing calculation. While bookings can be completed in the UI, the conversation state resets upon app restart, necessitating the use of database options for persistent data storage.

Testing with Docker

Why Testing with Docker

Testing the project with Docker provides a convenient alternative, allowing us to simulate a Postgres environment without requiring a full installation on our local machines. This method replicates the production database setup locally, enabling the application to interact with an actual PostgreSQL server. Docker enhances reproducibility, as the connection details are specified in the docker-compose.yml and .env.example files, ensuring that all users can replicate the same environment. Moreover, Docker isolates the application from the host system, allowing the database to run within a container. This isolation means we can stop (docker compose down) or wipe it (docker compose down -v) without affecting other applications.

How to Test with Docker

This project includes a docker-compose.yml file that initiates a container running PostgreSQL 16:

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 retrieves the official Postgres image and operates a database server within the container. The Streamlit application continues to run locally, connecting to the container via localhost:5432. Data files are stored in a Docker volume (booking_pgdata), ensuring persistence beyond the container’s memory. To initiate the Docker container, ensure Docker Desktop is installed and running on your machine. Once confirmed, execute the following command:

docker compose up -d

Next, update the .env file with the DATABASE_URL:

DATABASE_URL=postgresql://booking:booking@localhost:5432/booking_agent

Finally, run the application with the command:

poetry run streamlit run customer_service_agent/streamlit_app.py

After executing this command, the Streamlit UI launches. I successfully completed a test booking:

To confirm the system’s functionality, I opened a second browser window at localhost:8501 and requested the same service. The agent did not offer the time slot I had previously booked:

Docker Desktop displays the running processes, illustrating that Streamlit and Docker’s Postgres operate as distinct entities. Restarting the Streamlit application only affects the Python app, while the Postgres container remains active unless explicitly stopped. The data persists in the Docker volume, ensuring that previous bookings remain accessible. The database is only lost if the container is stopped or removed, along with the volume (docker compose down -v).

docker compose down -v[+] down 3/3 ✔ Container customer-service-agent-postgres-1  Removed                                                   0.2s ✔ Volume customer-service-agent_booking_pgdata Removed                                                   0.1s ✔ Network customer-service-agent_default       Removed 

Testing with a Hosted Postgres

Utilizing Docker is not a necessity if a Postgres instance is already available in the cloud (such as Supabase or RDS). In this case, one can easily create a Postgres database through the provider’s dashboard and copy the connection string, which typically resembles:

postgresql://USER:PASSWORD@HOST:PORT/DATABASE

This string should be placed in the .env file as DATABASE_URL. The application can then be started in the same manner as before:

poetry run streamlit run customer_service_agent/streamlit_app.py

The application’s behavior remains consistent, with the only distinction being the location of the Postgres instance, now hosted on a remote server. We are progressively transforming this customer service agent into a viable product capable of delivering genuine business value. There remains work to be done, including the integration of additional channels such as WhatsApp and implementing safety measures to mitigate prompt injection risks. Enhancements to the booking workflow and chat experience are also on the horizon, which will be addressed in future articles.

Tech Optimizer