AI Agent · RAG · LangGraph

Cymbal Air Assistant

Refactored Chat + RAG

A refactored version of the Cymbal Air Toolbox Demo originally built by Google Cloud Platform. This implementation replaces the original dependencies with a modern, maintainable stack focused on readability, testability, and local-first development.

PythonLangGraphFastMCPPostgreSQLpgvectorGROQ
View project on GitHub

Overview

From monolithic logic to a modular agent

The main goal of the refactor is to make the conversational agent easier to understand, test, and maintain. Instead of putting the complete workflow into a single graph, each responsibility is represented by a dedicated node.

The resulting architecture combines LangGraph for orchestration, FastMCP for tool execution, PostgreSQL with pgvector for persistent data and retrieval, and modern LLM providers through GROQ.

Modular architecture

Every graph node has a focused responsibility, making the workflow easier to reason about.

Local-first RAG

PostgreSQL and pgvector provide a local database and vector search environment.

Testable flow

Individual graph behaviors can be tested through dedicated end-to-end notebooks.

Human confirmation

Booking operations pause for user confirmation before mutating ticket data.

Architecture

What changed from the original

Original

Google Toolbox
AlloyDB
Gemini
Monolithic graph logic

This project

Refactored
FastMCP
PostgreSQL + pgvector
OpenAI / GPT OSS via GROQ
Modular LangGraph flow

LangGraph

Agent workflow

The graph separates normal conversations, tool execution, authentication, and flight booking validation into explicit stages. The booking flow can pause before the final mutation and continue after user confirmation.

create_graph()
Cymbal Air Assistant LangGraph agent flow
checkpointer = MemorySaver()
langgraph_app = await create_graph(checkpointer, model)

Graph nodes

Single-responsibility nodes

agent_node

Calls the LLM with the current message history and determines the next action.

tool_node

Executes tool calls returned by the agent through the MCP server.

booking_validation_node

Validates flight information and asks the user to confirm before a ticket is inserted.

insert_ticket_node

Performs the actual ticket insertion after the user confirms the booking.

request_login_node

Returns a login-required response when an authenticated tool is called without a valid token.

Chat flow

Readable decision flow

01

User Input

02

agent_node

LLM processes the current message history.

03

agent_should_continue()

Determines whether tools or direct completion are required.

04

tool_node

Executes regular MCP tools.

05

booking_validation_node

Validates and requests confirmation for ticket insertion.

06

insert_ticket_node

Creates the ticket after confirmation.

Human-in-the-loop

Booking confirmation

Before a ticket is inserted, the graph validates the flight information and pauses the workflow. The user can confirm or cancel the operation before the final database mutation.

booking_validation_node
│
├── confirmation required
│
├── user confirms
└── insert_ticket_node
│
└── user cancels
└── END

Project structure

Organized around clear responsibilities

docker-compose.yml

PostgreSQL + pgvector container

pyproject.toml

Project dependencies managed with uv

mcp/

FastMCP server and authentication middleware

helper/

LLM prompt template

agent.py

LangGraph nodes, edges, and state

Getting started

Local-first development

The project is designed to run locally using uv for Python dependency management and Docker for PostgreSQL with pgvector.

Environment setup
uv venv --python 3.12
source .venv/bin/activate

uv sync

cp .env.example .env

docker compose up -d
Start MCP server
cd mcp
uv run python server.py

# Server
http://127.0.0.1:8150

# SSE
http://127.0.0.1:8150/sse
Required environment variables
# LLM API Keys
GEMINI_API_KEY=your_gemini_api_key
GROQ_API_KEY=your_groq_api_key

# Auth
SECRET_KEY=your_jwt_secret
ALGORITHM=HS256

# PostgreSQL / pgvector
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=cymbal
POSTGRES_PORT=5432

Development workflow

Jupyter notebooks

The agent and chat interface are currently developed and executed through notebooks in a deliberate sequence.

1

1_preparation.ipynb

Database setup, table creation, and data loading.

2

2_agent.ipynb

Initializes the LangGraph agent and checkpointer.

3

3_chat-interface.ipynb

Interactive chat session and main entry point.

Run 1_preparation.ipynb once to seed the database, then use the agent and chat notebooks for day-to-day development.

Testing

End-to-end agent scenarios

Agent behavior is validated through dedicated notebooks. Each scenario creates its own thread ID and authentication token so tests remain isolated from each other.

Book Flight Accept
Book Flight Cancel
Book Flight Another Airline
Search flights for a month
Check logged in user ticket
Test underlying LLM connection

Technology

Modern AI agent stack

PythonuvLangGraphFastMCPPostgreSQLpgvectorDockerJupyterGROQOpenAIGPT OSSJWT Authentication

Readable agent architecture, built for iteration.

Cymbal Air Assistant demonstrates how a complex conversational workflow can be decomposed into explicit, testable LangGraph nodes while keeping infrastructure local and maintainable.

View source