A production-ready FastAPI template demonstrating LangGraph's multi-agent orchestration capabilities with hotel and flight booking agents.
- Serial Graph Processing: Orchestrates hotel and flight booking agents in sequence, automatically transitioning between agents
- Standalone Agent Routes: Individual endpoints for hotel and flight booking agents
- State Management: Conversation history persistence using LangGraph checkpointers
- Command Pattern: Tools update graph state using LangGraph's Command API
- Subgraph Architecture: Agents implemented as subgraphs with shared state synchronization
- FastAPI Server: Production-ready API with automatic OpenAPI documentation
- Type Safety: Full Pydantic validation and type hints
This template showcases a real-world travel booking system with:
- Hotel Booking Agent: Handles room availability, pricing, and reservations
- Flight Booking Agent: Manages flight search, pricing, and bookings
- Serial Orchestrator: Coordinates both agents in sequence (hotel → flight) with automatic state transitions
- Subgraphs as Nodes: Reusable agent graphs embedded in parent orchestrator
- Shared State Keys: Automatic state synchronization between parent and child graphs
- Command for State Updates: Tools using
Commandto update state fields - Conditional Routing: State-based routing with LLM-powered intent detection
- Conversation Persistence: Thread-based conversation history with checkpointers
langgraph-multiagent-template/
├── src/
│ ├── graph/
│ │ ├── agents/
│ │ │ ├── hotel_agent.py # Hotel booking subgraph
│ │ │ └── flight_agent.py # Flight booking subgraph
│ │ └── serial_graph.py # Parent orchestrator graph
│ └── server/
│ ├── main.py # FastAPI application entry point
│ └── routes/
│ ├── __init__.py
│ ├── base_handler.py # Shared handler for all routes
│ ├── hotel.py # Standalone hotel endpoint
│ ├── flight.py # Standalone flight endpoint
│ └── serial.py # Serial orchestrator endpoint
├── tests/
│ ├── test_hotel.py # Hotel agent test script
│ ├── test_flight.py # Flight agent test script
│ ├── test_serial.py # Serial orchestrator test script
│ └── run_all_tests.py # Run all tests
├── pyproject.toml # Project dependencies
├── .env # API keys (create this)
├── .gitignore # Git ignore rules
└── README.md
- Python 3.13+
- uv package manager
- OpenAI API key
- Install dependencies:
uv sync- Create a
.envfile in thesrc/directory:
# src/.env
OPENAI_API_KEY=your_openai_api_key_hereStart the server:
cd src/server
uv run python main.pyThe server will start on http://0.0.0.0:8000 by default.
# Run on a different port
uv run python main.py --port 8080
# Enable auto-reload for development
uv run python main.py --reload
# Change log level
uv run python main.py --log-level debug
# Log to file
uv run python main.py --log-to-file
# Custom host
uv run python main.py --host 127.0.0.1- GET
/- API information - GET
/health- Health status
- POST
/graphs/hotel- Book hotel reservations
- POST
/graphs/flight- Book flight reservations
- POST
/graphs/serial- Complete travel booking (hotel → flight)
- GET
/docs- Swagger UI - GET
/redoc- ReDoc
We provide Python test scripts that work on Mac, Windows, and Linux:
- Install test dependencies:
uv sync --extra dev- Make sure the server is running in another terminal:
cd src/server
uv run python main.py- Run all tests:
# Run all three test scenarios
uv run python tests/run_all_tests.py
# Or run individual tests
uv run python tests/test_hotel.py # Test hotel booking agent
uv run python tests/test_flight.py # Test flight booking agent
uv run python tests/test_serial.py # Test serial orchestratorThe test scripts will:
- ✓ Check if the server is running
- ✓ Execute complete conversation flows
- ✓ Display each step with responses
- ✓ Extract and verify confirmation numbers
- ✓ Show a final summary
If you prefer manual testing with curl:
Click to expand curl commands
# Start conversation
curl -X POST "http://localhost:8000/graphs/hotel" \
-H "Content-Type: application/json" \
-d '{"message":"I need a room","thread_id":"hotel-test-1"}'
# Provide details
curl -X POST "http://localhost:8000/graphs/hotel" \
-H "Content-Type: application/json" \
-d '{"message":"John Doe, check-in 2026-03-10, check-out 2026-03-15, 2 guests, deluxe","thread_id":"hotel-test-1"}'
# Confirm booking
curl -X POST "http://localhost:8000/graphs/hotel" \
-H "Content-Type: application/json" \
-d '{"message":"Yes, confirm the booking","thread_id":"hotel-test-1"}'Expected output: Confirmation message with CONF{timestamp} ID
# Start conversation
curl -X POST "http://localhost:8000/graphs/flight" \
-H "Content-Type: application/json" \
-d '{"message":"I need to book a flight","thread_id":"flight-test-1"}'
# Provide details
curl -X POST "http://localhost:8000/graphs/flight" \
-H "Content-Type: application/json" \
-d '{"message":"Jane Smith, New York to London, 2026-04-01, 1 passenger, economy","thread_id":"flight-test-1"}'
# Confirm booking
curl -X POST "http://localhost:8000/graphs/flight" \
-H "Content-Type: application/json" \
-d '{"message":"Book flight AA123","thread_id":"flight-test-1"}'Expected output: Confirmation message with FLT{timestamp} ID
# 1. Welcome
curl -X POST "http://localhost:8000/graphs/serial" \
-H "Content-Type: application/json" \
-d '{"message":"Hello","thread_id":"serial-test-1"}'
# 2. Confirm to proceed
curl -X POST "http://localhost:8000/graphs/serial" \
-H "Content-Type: application/json" \
-d '{"message":"Yes","thread_id":"serial-test-1"}'
# 3. Provide hotel details
curl -X POST "http://localhost:8000/graphs/serial" \
-H "Content-Type: application/json" \
-d '{"message":"Alice Brown, 2026-05-10, 2026-05-15, 2 guests, suite","thread_id":"serial-test-1"}'
# 4. Confirm hotel booking
curl -X POST "http://localhost:8000/graphs/serial" \
-H "Content-Type: application/json" \
-d '{"message":"Yes confirm","thread_id":"serial-test-1"}'
# 5. System automatically transitions to flight booking - provide flight details
curl -X POST "http://localhost:8000/graphs/serial" \
-H "Content-Type: application/json" \
-d '{"message":"Paris to Tokyo, 2026-05-10, 1 passenger, business","thread_id":"serial-test-1"}'
# 6. Confirm flight booking
curl -X POST "http://localhost:8000/graphs/serial" \
-H "Content-Type: application/json" \
-d '{"message":"Book flight DL789","thread_id":"serial-test-1"}'Expected output: Final summary with both hotel (CONF{timestamp}) and flight (FLT{timestamp}) confirmation IDs
- Welcome Phase: User greeted, asked to confirm
- Hotel Phase:
prepare_hotel_phase→ sets phase, clears confirmation_idhotel_bookingsubgraph → collects details, generates confirmationextract_hotel_confirmation→ saves hotel confirmation ID
- Automatic Transition: Routing detects hotel completion, moves to flight
- Flight Phase:
prepare_flight_phase→ sets phase, clears confirmation_idflight_bookingsubgraph → collects details, generates confirmationextract_flight_confirmation→ saves flight confirmation ID
- Confirmation: Shows summary with both IDs
-
Parent State (
TravelBookingState):messages: Full conversation historycurrent_phase: Tracks workflow phaseconfirmation_id: Shared field for subgraphshotel_confirmation_id: Extracted hotel confirmationflight_confirmation_id: Extracted flight confirmation
-
Subgraph States (
HotelBookingState,FlightBookingState):messages: Agent-specific conversationconfirmation_id: Generated by booking tools
Tools use Command to update state:
from langgraph.types import Command
@tool
def confirm_booking(..., runtime: ToolRuntime) -> Command:
confirmation_number = f"CONF{datetime.now().strftime('%Y%m%d%H%M')}"
return Command(
update={
"messages": [ToolMessage(content=message, tool_call_id=runtime.tool_call_id)],
"confirmation_id": confirmation_number
}
)- Create agent in
src/graph/agents/your_agent.py:
from langgraph.types import Command
from langchain.agents import create_agent
class YourAgentState(TypedDict):
messages: Annotated[list, operator.add]
confirmation_id: str
def build_graph(checkpointer=None):
return create_agent(
model=init_chat_model("gpt-4o-mini", model_provider="openai"),
tools=[your_tools],
state_schema=YourAgentState,
checkpointer=checkpointer,
system_prompt="Your agent prompt"
)- Add route in
src/server/routes/your_agent.py:
from .base_handler import BaseAgentHandler
from src.graph.agents.your_agent import build_graph
handler = BaseAgentHandler(graph_type="your_agent")
handler.initialize_graph(build_graph)
@router.post("")
async def handle_message(message_request: MessageRequest):
return await handler.handle_message(message_request)- Register in
src/server/routes/__init__.py
- langgraph>=1.0.0: Graph-based agent orchestration
- langchain>=1.0.0: LLM application framework
- langchain-core>=0.3.0: Core LangChain abstractions
- langchain-openai>=1.0.0: OpenAI integration
- langgraph-cli[inmem]>=0.4.0: InMemorySaver for checkpointing
- fastapi>=0.104.0: Web framework
- uvicorn>=0.24.0: ASGI server
- pydantic>=2.0.0: Data validation
- python-dotenv>=1.0.0: Environment variables
MIT