LangChain.js Booking Service Tutorial & Quiz

šŸ¤– What is LangChain? Why is it "Smart"?

LangChain is not an LLM—it's a framework or orchestration layer that wraps around LLMs to help you use them more effectively, especially in complex applications like chatbots, agents, and workflows.

So why is LangChain considered "smart"?

It's not that LangChain itself is intelligent—it's that it enables smart behavior by:

  1. Leveraging the intelligence of LLMs (like OpenAI's GPT models), and
  2. Structuring how those LLMs interact with functions, memory, tools, and user input.

Here's what makes LangChain powerful or "smart" in practice:

graph LR
    User["User Input
(Natural Language)"] subgraph LangChain Framework LLM["LLM (e.g., GPT-4)"] Tools["Tools
(Functions/APIs)"] Memory["Memory
(Conversation History)"] Chain["Chains & Workflows"] end User --> LLM LLM -- "Intent & Reasoning" --> Tools LLM -- "Uses" --> Memory LLM -- "Drives" --> Chain Tools -- "Results" --> LLM Memory -- "Context" --> LLM Chain -- "Orchestrates" --> LLM LLM --> User

šŸ’” Summary

But again, LangChain isn't the brain—the LLM is. LangChain is the brainstem: it routes, structures, and organizes the flow of thought.

LangChain Booking Tutorial

šŸŽÆ What We'll Build

In this tutorial, you'll build a smart booking assistant that can check availability, create bookings, reschedule, and cancel appointments—all powered by LangChain and LLMs. You'll see how to move from manual parsing to a flexible, AI-driven workflow that understands user intent and manages tasks using tools, memory, and chains.

Below is an architecture diagram showing how OpenAI, LangChain, tools, and APIs interact in this app:

flowchart TD
    User["User"]
    UI["Web UI / Chatbot"]
    LangChain["LangChain Framework"]
    OpenAI["OpenAI LLM (GPT-4)"]
    Tools["LangChain Tools"]
    API["Booking API (Availability, Create, Reschedule, Cancel)"]
    Memory["Conversation Memory"]

    User -- "User input" --> UI
    UI -- "Request (text)" --> LangChain
    LangChain -- "Natural language understanding" --> OpenAI
    LangChain -- "Function/tool call" --> Tools
    LangChain -- "Context/memory" --> Memory
    Tools -- "API request" --> API
    OpenAI -- "LLM response" --> LangChain
    Memory -- "History/context" --> LangChain
    API -- "API response" --> Tools
    LangChain -- "Response (text/result)" --> UI
    UI -- "Display result" --> User
        

The user interacts with the UI, which sends requests to LangChain. LangChain orchestrates the flow: it uses OpenAI for language understanding, tools for function calls, memory for context, and APIs for real-world actions. The results are returned to the user via the UI.

šŸ“‹ Step 1: Without LangChain - Basic Function Calling

// Basic API functions
const bookingAPI = {
  getAvailability: async (date) => {
    // Check if date is available
    return { available: true, slots: ['09:00', '14:00', '16:00'] };
  },
  
  createBooking: async (userEmail, date) => {
    // Create booking
    return { id: '12345', userEmail, date, status: 'confirmed' };
  },
  
  rescheduleBooking: async (bookingId, newDate) => {
    // Reschedule existing booking
    return { id: bookingId, newDate, status: 'rescheduled' };
  },
  
  cancelBooking: async (bookingId) => {
    // Cancel booking
    return { id: bookingId, status: 'cancelled' };
  }
};

// Without LangChain - Manual parsing
async function handleUserRequest(userInput) {
  const input = userInput.toLowerCase();
  
  if (input.includes('available') || input.includes('free')) {
    // Extract date manually
    const dateMatch = input.match(/(\d{4}-\d{2}-\d{2})/);
    if (dateMatch) {
      const result = await bookingAPI.getAvailability(dateMatch[1]);
      return `Available slots for ${dateMatch[1]}: ${result.slots.join(', ')}`;
    }
  }
  
  if (input.includes('book') || input.includes('schedule')) {
    // Manual extraction of email and date
    const emailMatch = input.match(/([^\s@]+@[^\s@]+\.[^\s@]+)/);
    const dateMatch = input.match(/(\d{4}-\d{2}-\d{2})/);
    
    if (emailMatch && dateMatch) {
      const result = await bookingAPI.createBooking(emailMatch[1], dateMatch[1]);
      return `Booking created with ID: ${result.id}`;
    }
  }
  
  return "I couldn't understand your request.";
}

šŸš€ Step 2: With LangChain - Structured Approach

import { ChatOpenAI } from "@langchain/openai";
import { DynamicTool } from "@langchain/core/tools";
import { AgentExecutor, createOpenAIFunctionsAgent } from "langchain/agents";
import { ChatPromptTemplate } from "@langchain/core/prompts";

// Define tools for LangChain
const availabilityTool = new DynamicTool({
  name: "get_availability",
  description: "Check available time slots for a specific date",
  func: async (date) => {
    const result = await bookingAPI.getAvailability(date);
    return JSON.stringify(result);
  }
});

const createBookingTool = new DynamicTool({
  name: "create_booking",
  description: "Create a new booking with user email and date",
  func: async (input) => {
    const { userEmail, date } = JSON.parse(input);
    const result = await bookingAPI.createBooking(userEmail, date);
    return JSON.stringify(result);
  }
});

const rescheduleBookingTool = new DynamicTool({
  name: "reschedule_booking",
  description: "Reschedule an existing booking to a new date",
  func: async (input) => {
    const { bookingId, newDate } = JSON.parse(input);
    const result = await bookingAPI.rescheduleBooking(bookingId, newDate);
    return JSON.stringify(result);
  }
});

const cancelBookingTool = new DynamicTool({
  name: "cancel_booking",
  description: "Cancel an existing booking",
  func: async (bookingId) => {
    const result = await bookingAPI.cancelBooking(bookingId);
    return JSON.stringify(result);
  }
});

// Create LangChain agent
const tools = [availabilityTool, createBookingTool, rescheduleBookingTool, cancelBookingTool];
const llm = new ChatOpenAI({ temperature: 0 });

const prompt = ChatPromptTemplate.fromMessages([
  ["system", "You are a booking assistant. Help users check availability, create, reschedule, or cancel bookings."],
  ["human", "{input}"],
  ["placeholder", "{agent_scratchpad}"]
]);

const agent = await createOpenAIFunctionsAgent({ llm, tools, prompt });
const agentExecutor = new AgentExecutor({ agent, tools });

// Usage
const response = await agentExecutor.invoke({
  input: "Check availability for 2024-03-15 and book a slot for john@email.com"
});

console.log(response.output);

šŸ”§ Step 3: Advanced LangChain Features

// Using Structured Output for better parsing
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";

const BookingSchema = z.object({
  action: z.enum(['check_availability', 'create_booking', 'reschedule_booking', 'cancel_booking']),
  userEmail: z.string().email().optional(),
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  bookingId: z.string().optional(),
  newDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
});

// Memory for conversation context
import { BufferMemory } from "langchain/memory";
import { ConversationChain } from "langchain/chains";

const memory = new BufferMemory({
  returnMessages: true,
  memoryKey: "chat_history"
});

const conversationChain = new ConversationChain({
  llm,
  memory,
  prompt: new ChatPromptTemplate({
    messages: [
      ["system", "You are a booking assistant with access to previous conversation context."],
      ["placeholder", "{chat_history}"],
      ["human", "{input}"]
    ]
  })
});

// Multi-step booking process
const multiStepPrompt = ChatPromptTemplate.fromMessages([
  ["system", `You are a booking assistant. Follow these steps:
1. If user wants to book, first check availability
2. If slots available, ask for confirmation
3. Create booking only after confirmation
4. Always be helpful and clear`],
  ["placeholder", "{chat_history}"],
  ["human", "{input}"],
  ["placeholder", "{agent_scratchpad}"]
]);

const smartAgent = await createOpenAIFunctionsAgent({
  llm,
  tools,
  prompt: multiStepPrompt
});

const smartAgentExecutor = new AgentExecutor({
  agent: smartAgent,
  tools,
  memory,
  verbose: true
});

šŸ”— Step 4: Chain

In LangChain, a chain is a sequence of components—such as prompts, LLMs, and output parsers—connected together to process input and produce structured output. Chains let you build more complex workflows by composing simple building blocks.

import { PromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
import { JsonOutputParser } from "@langchain/core/output_parsers";
import { RunnableSequence } from "@langchain/core/runnables";

// Step 1: Validate input data
const validateInput = RunnableSequence.from([
  (input) => {
    if (!input.email || !input.date) {
      throw new Error("Email and date are required");
    }
    return input;
  }
]);

// Step 2: Format the prompt
const formatPrompt = PromptTemplate.fromTemplate(
  "Book a meeting for {email} on {date}. Check if the date is valid and return JSON with email, date, and status."
);

// Step 3: Call the LLM
const llm = new ChatOpenAI({ temperature: 0 });

// Step 4: Parse the response
const parser = new JsonOutputParser();

// Step 5: Validate the parsed response
const validateResponse = (parsed) => {
  if (!parsed.email || !parsed.date || !parsed.status) {
    throw new Error("Invalid response format");
  }
  return parsed;
};

// Step 6: Check availability before booking
const checkAvailability = async (validated) => {
  try {
    const availability = await bookingAPI.getAvailability(validated.date);
    if (!availability.available || availability.slots.length === 0) {
      return {
        ...validated,
        apiStatus: "failed",
        error: `No available slots for ${validated.date}`
      };
    }
    return { ...validated, availability, apiStatus: "available" };
  } catch (error) {
    return { ...validated, apiStatus: "failed", error: error.message };
  }
};

// Step 7: Call the booking API (only if available)
const callBookingAPI = async (result) => {
  if (result.apiStatus !== "available") {
    return result; // Pass through if availability check failed
  }
  try {
    const bookingResult = await bookingAPI.createBooking(result.email, result.date);
    return { ...result, bookingId: bookingResult.id, apiStatus: "success" };
  } catch (error) {
    return { ...result, apiStatus: "failed", error: error.message };
  }
};

// Step 8: Format the final response
const formatResponse = (result) => {
  if (result.apiStatus === "success") {
    return `Booking created successfully! ID: ${result.bookingId}`;
  } else if (result.apiStatus === "available") {
    return `Slot is available for ${result.date}. Proceeding to booking...`;
  } else {
    return `Booking failed: ${result.error}`;
  }
};

// Compose the complex chain
const bookingChain = validateInput
  .pipe(formatPrompt)
  .pipe(llm)
  .pipe(parser)
  .pipe(validateResponse)
  .pipe(checkAvailability)
  .pipe(callBookingAPI)
  .pipe(formatResponse);

// Usage
const result = await bookingChain.invoke({
  email: "user@email.com",
  date: "2024-01-15"
});
// result = "Booking created successfully! ID: 12345" or error message

This complex chain demonstrates the real advantage of LangChain: you can compose multiple steps into a single, reusable workflow. Here, we check availability before booking, and only proceed if a slot is available. Each step handles a specific concern (validation, formatting, API calls, error handling), making the code modular and easy to test.