Build AI Email Agents: Composable Multi-agent
A step-by-step guide to build a composable multi agent architecture using Langbase SDK.
In this guide, you will build an AI email agent that uses multiple Langbase agent 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
You will build a basic Node.js application that will use the Langbase SDK to connect to the AI agent pipes and generate responses using parallelization and prompt-chaining agent architectures.
Flow reference architecture
There are two flows in the email agent, i.e., User Email Flow and Spam Email Flow.
The User Email Flow is a normal email flow where the user sends an email, and the AI agent analyzes the email sentiment, summarizes the email content, decides the email needs a response, picks the tone of the response email, and generates the response email.
The Spam Email Flow is a spam email flow where the AI agent analyzes the email sentiment, summarizes the email content, and decides that the email does not need a response.
Demo workflow: Click [Send spam email] or [Send valid email] to see how it works:
Let's get started!
Step #0Setup your project
Create a new directory for your project and navigate to it.
Project setup
mkdir ai-email-agent && cd ai-email-agent
Initialize the project
Initialize Node.js project and create an index.ts
file.
Initialize project
npm init -y && touch index.ts && touch agents.ts
Install dependencies
You will use the Langbase SDK to connect to the AI agent pipes and dotenv
to manage environment variables. So, let's install these dependencies.
Install dependencies
npm i langbase dotenv
Step #1Get Langbase API Key
Every request you send to Langbase needs an API key. This guide assumes you already have one. In case, you do not have an API key, please check the instructions below.
Create an .env
file in the root of your project and add your Langbase API key.
.env
LANGBASE_API_KEY=xxxxxxxxx
Replace xxxxxxxxx with your Langbase API key.
Step #3Add LLM API keys
If you have set up LLM API keys in your profile, the Pipe will automatically use them. If not, navigate to LLM API keys page and add keys for different providers like OpenAI, TogetherAI, Anthropic, etc.
Step #4Fork the AI agent pipes
Fork the following agent pipes needed for the AI email agent in Langbase dashboard:
- Email Sentiment → An agent 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 agent pipe that picks the tone for writing the response of the email
- Email Writer → An agent pipe that will write a response email
Step #4Sentiment Analysis
In our first step, you will analyze the email sentiment using the Langbase AI agent pipe. Go ahead and add the following code to your agents.ts
file:
Email sentiment agent
import { Langbase } from 'langbase';
import 'dotenv/config'
const langbase = new Langbase({ apiKey: process.env.LANGBASE_API_KEY! });
// Sentiment analysis
export const emailSentimentAgent = async (emailContent: string) => {
const response = await langbase.pipe.run({
stream: false,
json: true,
name: 'email-sentiment',
messages: [
{
role: 'system',
content: `You are a sentiment analyzer.
You will analyze user email sentiment.
Respond in JSON with "sentiment" key
Pick from happy | frustrated | neutral | sad`
},
{
role: 'user',
content: emailContent
}
]
});
const completion = JSON.parse(response.completion);
return completion.sentiment;
};
Let's take a look at what is happening in this code:
- Initialize the Langbase SDK with the API key from the environment variables.
- Define
emailSentimentAgent
function that takes an email and returns its sentiment analysis. - Set the
json
parameter totrue
to get the response in JSON format. - Set the
stream
parameter tofalse
because the content generation will be processed internally.
Step #5Summarize Email
Now let's write a function in the same agents.ts
file to summarize the email content.
Email summary agent
// Summarize email
export const emailSummaryAgent = async (emailContent: string) => {
const response = await langbase.pipe.run({
json: true,
stream: false,
name: 'summarizer',
messages: [
{
role: 'system',
content: `You are a content summarizer. You will summarize content
without loosing context into less wordy to the point version.`
},
{
role: 'user',
content: emailContent
}
]
});
const completion = JSON.parse(response.completion);
return completion.summary;
};
Let's break down the above code:
- Define a function
emailSummaryAgent
that takes an email and returns its summarized content. - Set the
json
parameter totrue
to get the response in JSON format. - Set the
stream
parameter tofalse
because the content generation will be processed internally.
Step #6Decision Maker
You are building a ReAct based architecture which means the sytem first reason our the info it has and then decide to act.
In this example, results of the email sentiment and summary are passed to the decision-making agent pipe to decide whether to respond to the email or not.
Go ahead and add the following code to your agents.ts
file:
Should respond to email agent
// Determine if a response is needed
export const shouldRespondToEmailAgent = async (summary: string, sentiment: string) => {
const response = await langbase.pipe.run({
json: true,
stream: false,
name: 'decision-maker',
messages: [
{
role: 'system',
content: `You are a decision maker that analyzes and decides if the given email requires a response or not.
Make sure to check if the email is spam or not. If the email is spam, then it does not need a response.
If it requires a response, based on the email urgency, decide the response date. Also define the response priority.
Use following keys and values accordingly
- respond: true | false
- category: primary | spam
- priority: urgent | high | medium | low`
},
{
role: 'user',
content: `Email Summary: ${summary}
Email sentiment: ${sentiment}`
}
],
});
const completion = JSON.parse(response.completion);
return completion.respond;
};
Let's go through the above code.
- Define a function
shouldRespondToEmailAgent
that takes the summarized content and sentiment of the email and returns a decision. - Set the
json
parameter totrue
to get the response in JSON format. - Set the
stream
parameter tofalse
because the content generation will be processed internally.
Step #7Pick Email Writer
In cases where the email needs a response, you will use the Pick Email Writer agent pipe to pick the tone of the email response.
Pick email writer agent
// Pick an email writer
export const pickEmailWriterAgent = async (summary: string, sentiment: string) => {
const response = await langbase.pipe.run({
json: true,
stream: false,
name: 'pick-email-writer',
messages: [
{
role: 'system',
content:
`You are an email tone picker that analyzes the input
and picks up the response email tone.
Pick from: professional | formal | informal | casual | friendly`
},
{
role: 'user',
content: `Email Summary: ${summary}
Email sentiment: ${sentiment}`
}
],
});
const completion = JSON.parse(response.completion);
return completion.tone;
};
Here's what you have done in this step:
- Created a function
pickEmailWriterAgent
that uses the summarized email and sentiment to pick one of the following tones for response:- Professional
- Formal
- Informal
- Casual
- Friendly
- Set the
json
parameter totrue
to get the response in JSON format. - Set the
stream
parameter tofalse
because the content generation will be processed internally.
Step #8Write Email Response
Finally, you will use the Email Writer agent pipe to generate the response email based on the tone picked in the previous step.
Write email response agent
// Generate an email reply
export const emailResponseAgent = async (writer: string, summary: string) => {
const { stream } = await langbase.pipe.run({
stream: true,
name: 'email-writer',
messages: [
{
role: 'system',
content: `You are an email writer that writes a concise
to the point well written email as a reply to a user email.`,
},
{
role: 'user',
content: `Write a response using the following information:
Email tone: ${writer}
User email: ${summary}`,
},
],
});
return stream;
};
Let's take a look at the above code:
- Created a function
emailResponseAgent
that takes the tone and summarized email and returns the response email. - Set the
stream
parameter totrue
to get the response in stream format as you will be writing the response to the console.
Step #9Final composable mult-agent workflow
Now that you have all these agents, you can combine these in multi agent workflow so they can help us
- analyze the email
- summarize the email
- decide if it needs a response
- pick the tone of the response
- generate the response email if needed.
In index.ts
file, let's import all our agents and define a workflow
that to run with user email and spam email.
Agent workflow
import { getRunner } from 'langbase';
import {
emailResponseAgent,
emailSentimentAgent,
emailSummaryAgent,
pickEmailWriterAgent,
shouldRespondToEmailAgent,
} from './agents';
import { stdout } from 'process';
const workflow = async (emailContent: string) => {
console.log('Email:', emailContent);
// parallelly run the agent pipes
const [emailSentiment, emailSummary] = await Promise.all([
emailSentimentAgent(emailContent),
emailSummaryAgent(emailContent),
]);
console.log('Sentiment:', emailSentiment);
console.log('Summary:', emailSummary);
const respond = await shouldRespondToEmailAgent(emailSummary, emailSentiment);
console.log('Respond:', respond);
if (!respond) {
return 'No response needed for this email.';
}
const writer = await pickEmailWriterAgent(emailSummary, emailSentiment);
console.log('Writer:', writer);
const emailStream = await emailResponseAgent(writer, emailSummary);
const runner = getRunner(emailStream);
runner.on('content', (content: string) => {
stdout.write(content);
});
};
const userEmail = `I'm really disappointed with the service I received yesterday. The product was faulty and customer support was unhelpful.`;
const spamEmail = `Congratulations! You have been selected as the winner of a $100 million lottery!`;
workflow(userEmail);
Here's what you have done in this step:
- Define a
workflow
function that takes an email content and runs the email sentiment, summary, decision-making, email writer, and email response agents. - You have used the
getRunner
function to get the stream of the email response and write it to the console. - You have also defined the
emailSentimentAgent
,emailSummaryAgent
,shouldRespondToEmailAgent
,pickEmailWriterAgent
, andemailResponseAgent
functions to run the respective agents.
Step #10Run the workflow
Run the email agent workflow by running the following command in terminal:
Initialize project
npx tsx index.ts
You should see the following output in the console:
Email Agent output
Email: I'm really disappointed with the service I received yesterday. The product was faulty and customer support was unhelpful.
Sentiment: frustrated
Summary: Disappointed with faulty product and unhelpful customer support.
Respond: true
Writer: formal
Subject: Re: Concern Regarding Faulty Product and Support Experience
Dear [User's Name],
Thank you for reaching out to us regarding your experience with our product and customer support. I sincerely apologize for the inconvenience you've encountered.
We strive to provide high-quality products and exceptional service, and it is disappointing to hear that we did not meet those standards in your case. Please rest assured that your feedback is taken seriously, and we are committed to resolving this matter promptly.
To assist you further, could you please provide details about the specific issues you're facing? This will enable us to address your concerns more effectively.
Thank you for bringing this to our attention, and I look forward to assisting you.
Best regards,
[Your Name]
[Your Position]
[Company Name]
[Contact Information]
This is how you can build an AI email agent using Langbase agent pipes.
Next Steps
- Build something cool with Langbase APIs and SDK.
- Join our Discord community for feedback, requests, and support.