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:
- Email Sentiment → A pipe to analyze the sentiment of the incoming email
- Summarizer → Summarizes the content of the email and make it less wordy for you
- Decision Maker → Decides if the email needs a response, the category and priority of the response
- Pick Email Writer → An AI pipe that picks the tone for writing the response of the email
- 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.