From Demo to Product: Why the Backend Needs an Upgrade
In the first two articles of this series, I built a stateful LangGraph agent that automates a 15-minute booking process and wrapped it with a Streamlit UI to improve the user experience. The agent acts like a real customer service representative, handling the entire booking journey: responding to queries, calculating service prices, managing acceptances or rejections, proposing optimized time slots, and confirming appointments.
The next phase is to build a proper backend, starting with migrating from in-memory storage to a PostgreSQL database. We'll keep Streamlit as the user interface but replace the in-memory adapters with PostgreSQL. This transition is crucial to support multiple frontends (e.g., WhatsApp, Streamlit) sharing the same backend, turning this into a scalable product ready for real business use.
The full source code is available on GitHub at customer-service-agent. Feel free to clone and test it yourself.
What the Database Looks Like Now: In-Memory Limitations
Calling the current setup a database would be generous—it's just two Python objects living inside the process. The first is a LangGraph checkpointer, a state persistence layer that saves a snapshot of the agent's graph state at each execution step. When the graph is compiled, conversation state is stored in memory:
graph.compile(checkpointer=checkpointer or MemorySaver())
This checkpointer enables the agent to resume across turns; without it, every customer message would start a new conversation. The second object is a Python list protected by a lock, storing confirmed appointments in an in-memory repository:
class InMemoryBookingRepository:
def init(self) -> None:
self._lock = threading.RLock()
self.technicians = {...} # hardcoded cleaners
self._bookings: list[Booking] = []
def list_bookings(self) -> list[Booking]:
with self._lock:
return list(self._bookings)
def create_booking(self, option, details, price) -> Booking:
check overlap in Python, then append to self._bookings
...
The scheduling engine calls listbookings() to avoid double-booking, and confirmation calls createbooking(), which re-checks overlap before appending. This simple structure works for initial testing and demos, allowing us to validate LangGraph routing and logic—but it's far from production-ready.
Why a Proper Database Is Non-Negotiable
The current in-memory 'database' fails as soon as we leave a single demo process. When the process restarts, conversation checkpoints and bookings vanish. Being in-memory means there's no shared availability—Session A cannot see bookings from Session B, so each process has its own calendar. For a booking product, this is critical: the agent might offer a slot based on a stale in-memory view, then 'confirm' a booking that another session already took.
When using the Streamlit UI, it looked like a product, but the storage behaved like a notebook kernel. To be considered a real product, we need a proper database—and we'll use PostgreSQL, a free, open-source relational database system.
Designing the PostgreSQL Schema for a Booking System
We need a relational database with bookings and technician tables. Migrating to PostgreSQL introduces several key benefits: persistent state across restarts, shared availability across all sessions and frontends, and atomic operations to prevent double-booking. The schema should include tables for users, technicians, and bookings, with proper foreign keys and constraints to enforce data integrity.
For the LangGraph checkpointer, we can use PostgresSaver to persist conversation state in the database, allowing seamless recovery and multi-turn conversations even after restarts. This migration also enables horizontal scaling—multiple instances of the agent can operate concurrently, sharing the same data store.
Conclusion: The Path to a Scalable AI Agent
By replacing in-memory storage with PostgreSQL, we've transformed the agent from a demo into a deployable product. This change not only improves reliability and data consistency but also opens the door to multi-channel deployment and real business operations. The next steps could include API endpoints, authentication, and further optimizations.
Have questions or feedback? Let's connect in the comments below.
