← Back to DevBytes

Building a Sales Outreach Agent with LangChain: Complete Guide

What is a Sales Outreach Agent?

A sales outreach agent is an AI-driven system that automates the process of identifying, researching, and contacting potential customers. It combines large language models (LLMs) with external tools—such as web search, CRM APIs, and email sending services—to generate highly personalized outreach messages at scale. Instead of manually looking up a lead's company, recent news, or job title, the agent handles the research and drafts a tailored email, then logs the interaction in your CRM.

With LangChain, you can build such an agent by connecting an LLM to a set of tools and a prompt strategy, giving it the ability to reason, act, and observe in a loop. The result is a powerful assistant that can handle the heavy lifting of sales prospecting while keeping the human in control for final approval.

Why LangChain for Sales Outreach Agents?

LangChain offers a modular framework specifically designed for composing LLM-powered applications. For a sales outreach agent, LangChain provides:

This combination lets you build a sales outreach agent that can research a lead, draft an email, and even schedule a follow-up, all while following your defined rules and tone of voice.

Core Components of the Agent

A complete sales outreach agent typically consists of:

Setting Up the Environment

First, install the required packages. Create a virtual environment and run:

pip install langchain langchain-openai langchain-community tavily-python
pip install openai  # if using OpenAI directly
pip install pydantic  # for tool definitions

Set your API keys as environment variables or in a .env file:

export OPENAI_API_KEY="your-openai-key"
export TAVILY_API_KEY="your-tavily-key"  # for web search

Step 1: Defining Tools for Sales Research

The agent needs tools to gather information about a lead. We'll create two: a web search tool using Tavily, and a simulated CRM lookup tool.

Web Search Tool

from langchain_community.tools.tavily_search import TavilySearchResults

# Limit to top 3 results for quick summaries
search_tool = TavilySearchResults(
    max_results=3,
    include_answer=True,
    include_raw_content=False,
    search_depth="basic"
)

Mock CRM Lookup Tool

This simulates fetching lead data from a CRM. In production, you'd replace it with an API call to HubSpot or Salesforce.

from langchain.tools import tool
from pydantic import BaseModel, Field

class CRMInput(BaseModel):
    email: str = Field(description="Lead email address to look up")

@tool("crm_lookup", args_schema=CRMInput)
def crm_lookup(email: str) -> str:
    """Fetch lead information from the CRM by email. Returns name, company, title, and past interactions."""
    # Mock data – replace with real API call
    mock_db = {
        "alex@example.com": "Name: Alex Rivera, Company: FinTech Labs, Title: VP of Engineering, Notes: Interested in API solutions",
        "jordan@example.com": "Name: Jordan Chen, Company: CloudScape, Title: CTO, Notes: Requested a demo last month"
    }
    return mock_db.get(email, "No CRM record found for this email.")

Step 2: Building the Email Drafting Chain

We'll create a reusable chain that takes research notes and produces a personalized email. This uses a ChatPromptTemplate with placeholders for the research context, sender info, and company details.

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

llm = ChatOpenAI(model="gpt-4o", temperature=0.7)

email_prompt = ChatPromptTemplate.from_messages([
    ("system", """You are a sales assistant for Acme Corp, a B2B SaaS platform.
Your task is to write a concise, friendly, and personalized outreach email.
Use the provided research to reference something specific about the lead or their company.
Keep the email under 150 words. Include a clear call-to-action for a 15-minute discovery call.
Format: Subject line, then email body."""),
    ("user", """
Research:
{research}

Lead Name: {lead_name}
Company: {company_name}
Sender: Alex from Acme Corp

Draft the email:""")
])

email_chain = email_prompt | llm | StrOutputParser()

This chain will be used later inside a tool that the agent can call.

Step 3: Assembling the Agent with LangChain's AgentExecutor

We'll use the create_openai_tools_agent helper, which is the modern recommended way. First, define the list of tools the agent can use.

Create the Email Drafting Tool

Wrap the email chain into a tool so the agent can invoke it with structured input.

from langchain.tools import tool

class EmailInput(BaseModel):
    lead_name: str = Field(description="Full name of the lead")
    company_name: str = Field(description="Company name")
    research: str = Field(description="Research summary about the lead/company")

@tool("draft_outreach_email", args_schema=EmailInput)
def draft_outreach_email(lead_name: str, company_name: str, research: str) -> str:
    """Generate a personalized sales outreach email using research notes."""
    result = email_chain.invoke({
        "research": research,
        "lead_name": lead_name,
        "company_name": company_name
    })
    return result

Agent Initialization

from langchain_openai import ChatOpenAI
from langchain.agents import create_openai_tools_agent, AgentExecutor
from langchain.tools import Tool

# Combine all tools
tools = [search_tool, crm_lookup, draft_outreach_email]

# LLM for agent reasoning
agent_llm = ChatOpenAI(model="gpt-4o", temperature=0)

# Create the agent
agent = create_openai_tools_agent(agent_llm, tools, 
    system_message="You are a helpful sales outreach agent. Research leads and draft personalized emails. Use the CRM first if an email is provided, then use web search for additional context, and finally draft the email."
)

agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

Step 4: Adding Memory for Multi-step Outreach

Memory prevents the agent from sending the same email twice or forgetting the lead's context during a long interaction. We'll add a simple ConversationSummaryBufferMemory.

from langchain.memory import ConversationSummaryBufferMemory
from langchain_openai import ChatOpenAI

# Memory LLM can be cheaper model
memory_llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
memory = ConversationSummaryBufferMemory(
    llm=memory_llm,
    max_token_limit=2000,
    memory_key="chat_history",
    return_messages=True
)

# Rebuild agent executor with memory
agent = create_openai_tools_agent(
    agent_llm,
    tools,
    system_message="You are a sales outreach agent. Use memory to avoid repeating information and track conversation.",
    extra_prompt_messages=[("system", "Memory: {chat_history}")] 
    # Note: in practice, you inject memory via the prompt or agent's prompt template.
    # For simplicity, we'll manually prepend history when invoking.
)
agent_executor = AgentExecutor(agent=agent, tools=tools, memory=memory, verbose=True)

Now the agent remembers previous research and drafted emails across calls.

Step 5: Integrating Real-world APIs (CRM & Email)

Replace mock tools with actual API integrations. Below are examples for HubSpot (CRM) and SendGrid (email sending).

CRM Tool – HubSpot Example

import requests
from langchain.tools import tool

@tool("hubspot_lookup")
def hubspot_lookup(email: str) -> str:
    """Search HubSpot for a contact by email and return key fields."""
    api_key = os.getenv("HUBSPOT_API_KEY")
    url = f"https://api.hubapi.com/crm/v3/objects/contacts/search"
    headers = {"Authorization": f"Bearer {api_key}"}
    payload = {
        "filterGroups": [{
            "filters": [{
                "propertyName": "email",
                "operator": "EQ",
                "value": email
            }]
        }],
        "properties": ["firstname", "lastname", "company", "jobtitle", "hs_notes"]
    }
    resp = requests.post(url, json=payload, headers=headers)
    if resp.status_code == 200:
        data = resp.json()
        if data.get("total") > 0:
            contact = data["results"][0]["properties"]
            return f"Name: {contact['firstname']} {contact['lastname']}, Company: {contact['company']}, Title: {contact['jobtitle']}, Notes: {contact['hs_notes']}"
    return "Contact not found in HubSpot."

Email Sending Tool – SendGrid Example

from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

@tool("send_email_via_sendgrid")
def send_email_via_sendgrid(to_email: str, subject: str, body: str) -> str:
    """Send an email using SendGrid. Returns success or failure message."""
    sg = SendGridAPIClient(os.getenv("SENDGRID_API_KEY"))
    message = Mail(
        from_email="alex@acmecorp.com",
        to_emails=to_email,
        subject=subject,
        plain_text_content=body
    )
    response = sg.send(message)
    if response.status_code in [200, 202]:
        return f"Email sent successfully to {to_email}."
    else:
        return f"Failed to send email: {response.status_code}"

Add these tools to the agent's tool list alongside research and drafting.

Running the Agent: Full Workflow Example

Below is a complete script that researches a lead, drafts an email, and (optionally) sends it, with memory and real-world tool integration.

import os
from langchain_openai import ChatOpenAI
from langchain.agents import create_openai_tools_agent, AgentExecutor
from langchain.memory import ConversationSummaryBufferMemory
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain.tools import tool

# --- Environment ---
os.environ["OPENAI_API_KEY"] = "your-key"
os.environ["TAVILY_API_KEY"] = "your-key"

# --- Tools (mock CRM + real search + email drafting) ---
search_tool = TavilySearchResults(max_results=3)

class CRMInput(BaseModel):
    email: str

@tool("crm_lookup", args_schema=CRMInput)
def crm_lookup(email: str) -> str:
    # Replace with HubSpot API in production
    mock_db = {
        "alex@example.com": "Name: Alex Rivera, Company: FinTech Labs, Title: VP Engineering, Notes: Interested in API solutions"
    }
    return mock_db.get(email, "No CRM record found.")

# --- Email drafting chain ---
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

email_prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a sales assistant for Acme Corp..."),
    ("user", "Research:\n{research}\n\nLead Name: {lead_name}\nCompany: {company_name}\nDraft email:")
])
llm_draft = ChatOpenAI(model="gpt-4o", temperature=0.7)
email_chain = email_prompt | llm_draft | StrOutputParser()

class EmailInput(BaseModel):
    lead_name: str
    company_name: str
    research: str

@tool("draft_outreach_email", args_schema=EmailInput)
def draft_outreach_email(lead_name: str, company_name: str, research: str) -> str:
    return email_chain.invoke({"research": research, "lead_name": lead_name, "company_name": company_name})

# --- Agent setup ---
tools = [search_tool, crm_lookup, draft_outreach_email]
agent_llm = ChatOpenAI(model="gpt-4o", temperature=0)
memory = ConversationSummaryBufferMemory(
    llm=ChatOpenAI(model="gpt-3.5-turbo", temperature=0),
    max_token_limit=2000,
    memory_key="chat_history",
    return_messages=True
)
agent = create_openai_tools_agent(agent_llm, tools,
    system_message="You are a sales outreach agent. Research leads, draft personalized emails. Use CRM first, then web search."
)
agent_executor = AgentExecutor(agent=agent, tools=tools, memory=memory, verbose=True)

# --- Example invocation ---
response = agent_executor.invoke({
    "input": "Research the lead with email alex@example.com and company FinTech Labs. Draft an outreach email for Alex Rivera, VP Engineering."
})
print(response["output"])

When you run this, the agent will:

  1. Call crm_lookup to get existing CRM data.
  2. Call search_tool to gather recent news about FinTech Labs.
  3. Combine research and invoke draft_outreach_email.
  4. Return the final personalized email.

Best Practices for Sales Outreach Agents

Conclusion

Building a sales outreach agent with LangChain transforms a tedious, manual process into an intelligent, scalable pipeline. By combining LLM reasoning with tools for research, CRM lookup, and email drafting, you can generate highly relevant outreach messages that respect each lead's context. LangChain's agent architecture lets you swap tools, add memory, and integrate real APIs with minimal code changes. Start with the provided template, customize the prompts to your brand voice, and always keep a human review step. As you refine the agent, you'll see improved reply rates and a more efficient sales process.

— Ad —

Google AdSense will appear here after approval

← Back to all articles