Guide: Build a composable AI email agent

A step-by-step guide to build an AI email agent using the Langbase SDK.


In this guide, we will build an AI email agent that uses multiple Langbase pipes to:

  • Summarize an email
  • Analyze sentiment of the email
  • Decide whether the email needs a response or not
  • Pick the tone of the response email
  • Generate a response email


We will create a basic Next.js application that will use the Langbase SDK to connect to the AI pipes and generate responses in order that will be used by other pipes to complete the workflow of our agent.

Here is what the final agent will look like:

Let's get started!


Step #0Create a Next.js Application

To build the agent, we need to have a Next.js starter application. If you don't have one, you can create a new Next.js application using the following command:

npx create-next-app@latest ai-email-agent

This will create a new Next.js application in the ai-email-agent directory. Navigate to the directory and start the development server:

cd ai-email-agent
npm run dev

Once the development server is running, you can open the application in your browser at http://localhost:3000

Step #1Install Langbase SDK

Install the Langbase SDK in this project using npm or pnpm.

npm install langbase

Step #2Fork the AI pipes

Fork the following pipes needed for the AI email agent in Langbase dashboard:

  1. Email Sentiment → A pipe to analyze the sentiment of the incoming email
  2. Summarizer → Summarizes the content of the email and make it less wordy for you
  3. Decision Maker → Decides if the email needs a response, the category and priority of the response
  4. Pick Email Writer → An AI pipe that picks the tone for writing the response of the email
  5. Email Writer → A pipe that will write a response email

Now that you have the API keys for each of the pipes. You can use these keys to initiate pipes using the Langbase SDK. Copy and paste these keys in the .env.local file of your project.

# Replace `PIPE_API_KEY` with the copied API key of the `email-sentiment` pipe
LANGBASE_AI_PIPE_SENTIMENT_API_KEY="PIPE_API_KEY"

# Replace `PIPE_API_KEY` with the copied API key of the `summarizer` pipe
LANGBASE_AI_PIPE_SUMMARIZER_API_KEY="PIPE_API_KEY"

# Replace `PIPE_API_KEY` with the copied API key of the `decision-maker` pipe
LANGBASE_AI_PIPE_DECISION_MAKER_API_KEY="PIPE_API_KEY"

# Replace `PIPE_API_KEY` with the copied API key of the `pick-email-writer` pipe
LANGBASE_AI_PIPE_PICK_EMAIL_WRITER_API_KEY="PIPE_API_KEY"

# Replace `PIPE_API_KEY` with the copied API key of the `email-writer` pipe
LANGBASE_AI_PIPE_EMAIL_WRITER_API_KEY="PIPE_API_KEY"

Step #3Sentiment Analysis

Create a new file in app/api/sentiment/route.ts and add the following code:

import { Pipe } from 'langbase';

export const runtime = 'edge';

export async function POST(req: Request) {
	try {
		if (!process.env.LANGBASE_AI_PIPE_SENTIMENT_API_KEY) {
			throw new Error(
				'Please set LANGBASE_AI_PIPE_SENTIMENT_API_KEY in your environment variables.'
			);
		}

		// Get email from the client.
		const body = await req.json();
		const { email } = body;

		const pipe = new Pipe({
			apiKey: process.env.LANGBASE_AI_PIPE_SENTIMENT_API_KEY
		});

		const sentiment = await pipe.generateText({
			variables: [
				{
					name: 'email',
					value: email
				}
			]
		});

		// Parse JSON response from Langbase
		const response: JSON = JSON.parse(sentiment.completion);

		return Response.json(response);
	} catch (error: any) {
		console.error('Uncaught API Error:', error);
		return new Response(JSON.stringify(error), { status: 500 });
	}
}

Send a POST request to the /api/sentiment endpoint with the email content in the request body to get the sentiment analysis of the email.

const analyzeSentiment = async (email: string) => {
	const response = await fetch('/api/sentiment', {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json'
		},
		body: JSON.stringify({ email })
	});

	const sentimentAnalysis = await response.json();
	return sentimentAnalysis;
};

Step #4Summarize Email

Create a new file in app/api/summarize/route.ts and add the following code:

import { Pipe } from 'langbase';

export const runtime = 'edge';

export async function POST(req: Request) {
	try {
		if (!process.env.LANGBASE_AI_PIPE_SUMMARIZER_API_KEY) {
			throw new Error(
				'Please set LANGBASE_AI_PIPE_SUMMARIZER_API_KEY in your environment variables.'
			);
		}

		// Get email from the client.
		const body = await req.json();
		const { email } = body;

		const pipe = new Pipe({
			apiKey: process.env.LANGBASE_AI_PIPE_SUMMARIZER_API_KEY
		});

		const summary = await pipe.generateText({
			variables: [
				{
					name: 'content',
					value: email
				}
			]
		});

		return new Response(
			JSON.stringify({
				summary: summary.completion
			}),
			{ status: 200 }
		);
	} catch (error: any) {
		console.error('Uncaught API Error:', error);
		return new Response(JSON.stringify(error), { status: 500 });
	}
}

Send a POST request to the /api/summarize endpoint with the email content in the request body to get the summarized content of the email.

const summarizeEmail = async (email: string) => {
	const response = await fetch('/api/summarize', {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json'
		},
		body: JSON.stringify({ email })
	});

	const summarizedEmail = await response.json();
	return summarizedEmail;
};

Step #5Decision Making

Create a new file in app/api/respond/route.ts and add the following code:

import { Pipe } from 'langbase';

export const runtime = 'edge';

export async function POST(req: Request) {
	try {
		if (!process.env.LANGBASE_AI_PIPE_DECISION_MAKER_API_KEY) {
			throw new Error(
				'Please set LANGBASE_AI_PIPE_DECISION_MAKER_API_KEY in your environment variables.'
			);
		}

		// Get email summary and sentiment from the client.
		const body = await req.json();
		const { summary, sentiment } = body;

		const pipe = new Pipe({
			apiKey: process.env.LANGBASE_AI_PIPE_DECISION_MAKER_API_KEY
		});

		const shouldRespond = await pipe.generateText({
			variables: [
				{
					name: 'summary',
					value: summary
				},
				{
					name: 'sentiment',
					value: sentiment
				}
			]
		});

		// Parse JSON response from Langbase
		const decision: JSON = JSON.parse(shouldRespond.completion);

		return Response.json(decision);
	} catch (error: any) {
		console.error('Uncaught API Error:', error);
		return new Response(JSON.stringify(error), { status: 500 });
	}
}

Send a POST request to the /api/respond endpoint with the summarized content and sentiment analysis of the email in the request body to get the decision of whether to respond to the email or not.

const shouldRespondToEmail = async (summary: string, sentiment: string) => {
	const response = await fetch('/api/respond', {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json'
		},
		body: JSON.stringify({ summary, sentiment })
	});

	const shouldRespond = await response.json();
	return shouldRespond;
};

Step #6Pick Email Writer

Create a new file in app/api/pick-email-writer/route.ts and add the following code:

import { Pipe } from 'langbase';

export const runtime = 'edge';

export async function POST(req: Request) {
	try {
		if (!process.env.LANGBASE_AI_PIPE_PICK_EMAIL_WRITER_API_KEY) {
			throw new Error(
				'Please set LANGBASE_AI_PIPE_PICK_EMAIL_WRITER_API_KEY in your environment variables.'
			);
		}

		// Get email summary and sentiment from the client.
		const body = await req.json();
		const { summary, sentiment } = body;

		const pipe = new Pipe({
			apiKey: process.env.LANGBASE_AI_PIPE_PICK_EMAIL_WRITER_API_KEY
		});

		const writer = await pipe.generateText({
			variables: [
				{
					name: 'summary',
					value: summary
				},
				{
					name: 'sentiment',
					value: sentiment
				}
			]
		});

		// Parse JSON response from Langbase
		const decision: JSON = JSON.parse(writer.completion);

		return Response.json(decision);
	} catch (error: any) {
		console.error('Uncaught API Error:', error);
		return new Response(JSON.stringify(error), { status: 500 });
	}
}

Send a POST request to the /api/pick-email-writer endpoint with the summarized content and sentiment analysis of the email in the request body to pick the tone of our email response.

const pickEmailWriter = async (summary: string, sentiment: string) => {
	const response = await fetch('/api/pick-email-writer', {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json'
		},
		body: JSON.stringify({ summary, sentiment })
	});

	const writer = await response.json();
	return writer;
};

Step #7Write Email Response

Create a new file in app/api/write-email/route.ts and add the following code:

import { Pipe } from 'langbase';

export const runtime = 'edge';

export async function POST(req: Request) {
	try {
		if (!process.env.LANGBASE_AI_PIPE_EMAIL_WRITER_API_KEY) {
			throw new Error(
				'Please set LANGBASE_AI_PIPE_EMAIL_WRITER_API_KEY in your environment variables.'
			);
		}

		// Get writer and email summary from the client.
		const body = await req.json();
		const { writer, emailSummary } = body;

		const pipe = new Pipe({
			apiKey: process.env.LANGBASE_AI_PIPE_EMAIL_WRITER_API_KEY
		});

		const { stream: emailReply } = await pipe.streamText({
			variables: [
				{
					name: 'writer',
					value: writer
				},
				{
					name: 'user_email_summary',
					value: emailSummary
				}
			]
		});

		return new Response(emailReply.toReadableStream(), { status: 200 });
	} catch (error: any) {
		console.error('Uncaught API Error:', error);
		return new Response(JSON.stringify(error), { status: 500 });
	}
}

Send a POST request to the /api/write-email endpoint with the writer and summarized content of the email in the request body to stream the email response.

const generateEmailReply = async (writer: string, emailSummary: string) => {
	const response = await fetch('/api/email-writer', {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json'
		},
		body: JSON.stringify({ writer, emailSummary })
	});

	if (!response.ok) {
		const error = await response.json();
		toast.error(error);
		return;
	}

	if (response.body) {
		const stream = fromReadableStream(response.body);

		for await (const chunk of stream) {
			const content = chunk?.choices[0]?.delta?.content || '';
			content && setEmailReply(prev => prev + content);
		}
	}

	return emailReply;
};

Step #8Build the Email Agent

Now that we have all the pipes in place, we can start sending the email content to our agent and generate a response email if needed.

const sendEmailToAgent = async (email: string) => {
	// Set loading state to true
	setIsLoading(true);

	// Analyze sentiment and summarize email in parallel
	const [sentimentAnalysis, emailSummary] = await Promise.all([
		analyzeSentiment(email),
		summarizeEmail(email)
	]);
	const { sentiment } = sentimentAnalysis;
	const { summary } = emailSummary;

	// Make a decision about the email response
	const { respond, category, byWhen, priority } =
		await shouldRespondToEmail(summary, sentiment);

	if (!respond) {
		// If no, then stop the pipeline
		setIsLoading(false);
		return;
	}

	// If yes, then pick email writer
	const { tone } = await pickEmailWriter(summary, sentiment);

	// Generate the email response
	const reply = await generateEmailReply(tone, summary);
	setIsLoading(false);
	return reply;
};

Live Demo

You can check out the live demo of this project here. Complete code of the agent is available on GitHub.