← Back to DevBytes

Building a Lead Qualification Agent with LangChain: Complete Guide

What is a Lead Qualification Agent?

A Lead Qualification Agent is an AI-powered system that automatically evaluates incoming sales leads to determine their likelihood of converting into paying customers. Built with LangChain, this agent leverages large language models (LLMs) to analyze lead data, ask clarifying questions, score prospects against predefined criteria, and route high-value opportunities to the right sales representatives — all without manual intervention.

Traditional lead qualification relies on sales development representatives (SDRs) manually reviewing forms, conducting discovery calls, and applying frameworks like BANT (Budget, Authority, Need, Timeline) or MEDDIC. An agent automates this process by:

Why a Lead Qualification Agent Matters

Manual lead qualification is slow, inconsistent, and expensive. Sales teams spend up to 60% of their time on unqualified leads. An AI agent delivers:

For developers, LangChain provides the perfect framework to build such agents. Its modular architecture lets you compose LLM calls, tools, memory, and retrieval capabilities into a coherent system that mimics a skilled SDR's reasoning process.

Architecture Overview

Our Lead Qualification Agent uses a ReAct (Reasoning + Acting) pattern implemented with LangChain's agent framework. Here's the high-level architecture:

The agent follows this workflow:

1. Receive lead (webhook, API, or manual input)
2. Extract structured information from raw input
3. Enrich with external data (Clearbit, Apollo, LinkedIn)
4. Compare against ideal customer profile (ICP) criteria
5. Generate qualification questions based on missing information
6. Conduct conversational qualification if needed
7. Score the lead using multi-factor model
8. Store result and route to appropriate sales tier
9. Log outcome for future model improvement

Setting Up Your Environment

Start by installing the required dependencies. Create a new Python project and set up a virtual environment:

# Create project directory
mkdir lead-qualification-agent
cd lead-qualification-agent

# Create virtual environment
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install core dependencies
pip install langchain langchain-openai langchain-community
pip install chromadb pydantic python-dotenv
pip install httpx pandas  # For API calls and data handling

Create a .env file to store API keys securely:

# .env file
OPENAI_API_KEY=your-openai-api-key-here
CLEARBIT_API_KEY=your-clearbit-api-key  # Optional: for enrichment
CRM_API_URL=https://your-crm-instance.com/api
CRM_API_KEY=your-crm-api-key

Load environment variables in your application entry point:

# main.py
from dotenv import load_dotenv
load_dotenv()

import os
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_react_agent
from langchain.tools import tool
from langchain.memory import ConversationBufferMemory
from langchain.prompts import PromptTemplate

Building the Core Components

1. Define the Lead Data Model

Use Pydantic to create a structured, validated lead object that the agent can reason about reliably:

# models.py
from pydantic import BaseModel, Field, field_validator
from typing import Optional, List
from enum import Enum
from datetime import datetime

class LeadSource(str, Enum):
    WEB_FORM = "web_form"
    LINKEDIN = "linkedin"
    REFERRAL = "referral"
    COLD_OUTBOUND = "cold_outbound"
    EVENT = "event"

class LeadStatus(str, Enum):
    NEW = "new"
    QUALIFYING = "qualifying"
    QUALIFIED = "qualified"
    DISQUALIFIED = "disqualified"
    NURTURE = "nurture"

class CompanyInfo(BaseModel):
    name: str
    industry: Optional[str] = None
    employee_count: Optional[int] = None
    revenue_range: Optional[str] = None  # e.g., "1M-10M", "10M-50M"
    tech_stack: List[str] = Field(default_factory=list)
    funding_stage: Optional[str] = None

class Lead(BaseModel):
    id: str = Field(default_factory=lambda: f"lead_{datetime.now().timestamp()}")
    first_name: str
    last_name: str
    email: str
    title: str
    company: CompanyInfo
    source: LeadSource
    status: LeadStatus = LeadStatus.NEW
    budget_mentioned: Optional[str] = None
    authority_level: Optional[str] = None  # e.g., "decision_maker", "influencer"
    need_description: Optional[str] = None
    timeline: Optional[str] = None
    qualification_score: Optional[float] = None
    notes: List[str] = Field(default_factory=list)
    
    @field_validator("email")
    def validate_email(cls, v):
        if "@" not in v:
            raise ValueError("Invalid email address")
        return v.lower()

2. Create the Qualification Scoring Engine

The scoring engine applies configurable rules to calculate a lead score between 0 and 100. This is deterministic logic that complements the LLM's judgment:

# scoring.py
from typing import Dict, Any
import json

class QualificationScorer:
    """Multi-factor scoring engine for lead qualification."""
    
    def __init__(self, icp_criteria: Dict[str, Any]):
        """
        icp_criteria example:
        {
            "target_industries": ["SaaS", "Fintech", "Healthcare"],
            "min_employees": 50,
            "max_employees": 1000,
            "decision_maker_titles": ["CEO", "CTO", "VP Engineering", "Director of IT"],
            "budget_threshold": 50000,  # minimum annual budget
            "positive_signals": ["hiring_engineers", "raised_funding", "using_competitor"],
            "negative_signals": ["using_legacy_system", "no_digital_presence"]
        }
        """
        self.icp = icp_criteria
        self.weights = {
            "industry_match": 20,
            "company_size": 15,
            "authority_level": 25,
            "budget_indicator": 20,
            "timeline_urgency": 10,
            "positive_signals": 10
        }
    
    def score(self, lead_data: Dict[str, Any]) -> Dict[str, Any]:
        scores = {}
        breakdown = {}
        
        # Industry match
        industry = lead_data.get("company", {}).get("industry", "")
        scores["industry_match"] = (
            20 if industry in self.icp.get("target_industries", []) else 5
        )
        breakdown["industry_match"] = {
            "value": scores["industry_match"],
            "reason": f"Industry '{industry}' {'matches' if scores['industry_match']==20 else 'does not match'} target industries"
        }
        
        # Company size
        employees = lead_data.get("company", {}).get("employee_count", 0)
        min_emp = self.icp.get("min_employees", 0)
        max_emp = self.icp.get("max_employees", 100000)
        if min_emp <= employees <= max_emp:
            scores["company_size"] = 15
            breakdown["company_size"] = {"value": 15, "reason": f"{employees} employees within ICP range"}
        elif employees > 0:
            scores["company_size"] = 7
            breakdown["company_size"] = {"value": 7, "reason": f"{employees} employees outside ICP range {min_emp}-{max_emp}"}
        else:
            scores["company_size"] = 3
            breakdown["company_size"] = {"value": 3, "reason": "Unknown employee count"}
        
        # Authority level
        title = lead_data.get("title", "").lower()
        decision_titles = [t.lower() for t in self.icp.get("decision_maker_titles", [])]
        if any(dt in title for dt in decision_titles):
            scores["authority_level"] = 25
            breakdown["authority_level"] = {"value": 25, "reason": f"Title '{title}' indicates decision-making authority"}
        elif "manager" in title or "lead" in title:
            scores["authority_level"] = 15
            breakdown["authority_level"] = {"value": 15, "reason": "Mid-level influencer detected"}
        else:
            scores["authority_level"] = 5
            breakdown["authority_level"] = {"value": 5, "reason": "Limited authority based on title"}
        
        # Budget indicator
        budget = lead_data.get("budget_mentioned", "")
        if budget:
            try:
                budget_num = int("".join(c for c in budget if c.isdigit()))
                if budget_num >= self.icp.get("budget_threshold", 50000):
                    scores["budget_indicator"] = 20
                    breakdown["budget_indicator"] = {"value": 20, "reason": f"Mentioned budget ${budget_num} meets threshold"}
                else:
                    scores["budget_indicator"] = 8
                    breakdown["budget_indicator"] = {"value": 8, "reason": f"Budget ${budget_num} below threshold"}
            except ValueError:
                scores["budget_indicator"] = 10
                breakdown["budget_indicator"] = {"value": 10, "reason": "Budget mentioned but amount unclear"}
        else:
            scores["budget_indicator"] = 0
            breakdown["budget_indicator"] = {"value": 0, "reason": "No budget information provided"}
        
        # Timeline urgency
        timeline = lead_data.get("timeline", "").lower()
        urgent_keywords = ["immediately", "urgent", "asap", "this month", "this quarter"]
        if any(kw in timeline for kw in urgent_keywords):
            scores["timeline_urgency"] = 10
            breakdown["timeline_urgency"] = {"value": 10, "reason": "Urgent timeline indicated"}
        elif timeline:
            scores["timeline_urgency"] = 5
            breakdown["timeline_urgency"] = {"value": 5, "reason": "Timeline mentioned but not urgent"}
        else:
            scores["timeline_urgency"] = 0
            breakdown["timeline_urgency"] = {"value": 0, "reason": "No timeline provided"}
        
        # Positive signals
        positive_count = 0
        for signal in self.icp.get("positive_signals", []):
            if signal.lower() in str(lead_data).lower():
                positive_count += 1
        scores["positive_signals"] = min(10, positive_count * 5)
        breakdown["positive_signals"] = {"value": scores["positive_signals"], "reason": f"{positive_count} positive signals detected"}
        
        total_score = sum(scores.values())
        return {
            "total_score": total_score,
            "max_possible": 100,
            "breakdown": breakdown,
            "qualification_level": (
                "hot" if total_score >= 75 else
                "warm" if total_score >= 50 else
                "cool" if total_score >= 30 else
                "cold"
            )
        }

3. Build the Tool Set for the Agent

LangChain tools are functions decorated with @tool that the agent can invoke during its reasoning loop. Each tool has a clear name and description so the LLM knows when to use it:

# tools.py
from langchain.tools import tool
from typing import Optional, Dict, Any
import json
import httpx
from datetime import datetime, timedelta

# Mock CRM database for demonstration
MOCK_CRM: Dict[str, Dict[str, Any]] = {}

@tool
def lookup_existing_lead(email: str) -> str:
    """Search CRM for existing lead by email. Returns lead history if found, or 'not_found'."""
    email_lower = email.lower().strip()
    if email_lower in MOCK_CRM:
        lead_data = MOCK_CRM[email_lower]
        return json.dumps({
            "found": True,
            "lead_id": lead_data.get("id"),
            "status": lead_data.get("status"),
            "previous_interactions": lead_data.get("interactions", []),
            "last_contacted": lead_data.get("last_contacted", "never"),
            "notes": lead_data.get("notes", [])
        }, indent=2)
    return json.dumps({"found": False, "message": "No existing lead found with this email"})

@tool
def enrich_company_info(company_name: str) -> str:
    """Fetch enriched company data from external APIs. Returns firmographic and technographic data."""
    # In production, call Clearbit, Apollo, or LinkedIn APIs here
    # For demo, return simulated enrichment data
    enrichment_db = {
        "acme corp": {
            "industry": "SaaS",
            "employee_count": 350,
            "revenue_range": "50M-100M",
            "tech_stack": ["Salesforce", "AWS", "Slack", "HubSpot"],
            "funding_stage": "Series C",
            "recent_news": ["Hiring 50 engineers", "Launched new product line"],
            "linkedin_url": "https://linkedin.com/company/acme-corp"
        },
        "globaltech solutions": {
            "industry": "Fintech",
            "employee_count": 1200,
            "revenue_range": "200M-500M",
            "tech_stack": ["Azure", "Datadog", "PagerDuty", "Kubernetes"],
            "funding_stage": "Public",
            "recent_news": ["Opened European office", "Won industry award"]
        }
    }
    
    company_key = company_name.lower().strip()
    for db_key, data in enrichment_db.items():
        if company_key in db_key or db_key in company_key:
            return json.dumps(data, indent=2)
    
    # Simulate API call for unknown companies
    simulated_data = {
        "industry": "Technology",
        "employee_count": 250,
        "revenue_range": "10M-50M",
        "tech_stack": ["Unknown"],
        "funding_stage": "Private",
        "recent_news": [],
        "note": "Limited enrichment data available for this company"
    }
    return json.dumps(simulated_data, indent=2)

@tool
def calculate_qualification_score(lead_json: str) -> str:
    """Calculate qualification score from lead JSON data. Input must be valid JSON with fields: 
    company (with industry, employee_count), title, budget_mentioned, timeline."""
    from scoring import QualificationScorer
    
    icp = {
        "target_industries": ["SaaS", "Fintech", "Healthcare", "DevOps"],
        "min_employees": 50,
        "max_employees": 2000,
        "decision_maker_titles": ["CEO", "CTO", "VP", "Director", "Head of"],
        "budget_threshold": 50000,
        "positive_signals": ["hiring", "funding", "growth", "scaling"],
        "negative_signals": ["legacy", "outsourcing"]
    }
    
    try:
        lead_data = json.loads(lead_json)
        scorer = QualificationScorer(icp)
        result = scorer.score(lead_data)
        return json.dumps(result, indent=2)
    except json.JSONDecodeError:
        return json.dumps({"error": "Invalid JSON input", "hint": "Provide valid JSON string"})
    except Exception as e:
        return json.dumps({"error": str(e)})

@tool
def save_qualified_lead(lead_json: str) -> str:
    """Save a qualified lead to the CRM. Only use when lead score >= 50. 
    Returns confirmation with lead ID."""
    try:
        lead_data = json.loads(lead_json)
        email = lead_data.get("email", "").lower().strip()
        lead_id = f"lead_{int(datetime.now().timestamp())}"
        
        MOCK_CRM[email] = {
            "id": lead_id,
            "data": lead_data,
            "status": "qualified",
            "qualified_at": datetime.now().isoformat(),
            "interactions": [],
            "notes": [f"Auto-qualified by AI agent at {datetime.now().isoformat()}"]
        }
        
        return json.dumps({
            "success": True,
            "lead_id": lead_id,
            "message": f"Lead {lead_data.get('first_name', 'Unknown')} {lead_data.get('last_name', '')} saved and qualified",
            "next_step": "Routing to appropriate sales representative"
        }, indent=2)
    except Exception as e:
        return json.dumps({"success": False, "error": str(e)})

@tool  
def generate_qualification_questions(lead_json: str) -> str:
    """Generate personalized qualification questions based on missing ICP criteria in lead data.
    Use when you need to gather more information from the lead."""
    try:
        lead_data = json.loads(lead_json)
        questions = []
        
        if not lead_data.get("budget_mentioned"):
            questions.append("What budget range have you allocated for a solution like this?")
        if not lead_data.get("timeline"):
            questions.append("What's your expected timeline for implementing a solution?")
        if not lead_data.get("need_description"):
            questions.append("Can you describe the primary challenge or need you're looking to address?")
        if not lead_data.get("authority_level") or lead_data.get("authority_level") not in ["decision_maker"]:
            questions.append("Who else on your team would be involved in evaluating and purchasing a solution?")
        
        if not questions:
            questions.append("I have all the information I need. Let me summarize your requirements.")
        
        return json.dumps({
            "questions": questions,
            "missing_fields": [
                f for f in ["budget_mentioned", "timeline", "need_description", "authority_level"]
                if not lead_data.get(f)
            ]
        }, indent=2)
    except Exception as e:
        return json.dumps({"error": str(e)})

4. Set Up the Vector Store for Historical Pattern Matching

A ChromaDB vector store helps the agent compare new leads against previously qualified and disqualified leads to identify patterns:

# vector_store.py
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
import json

class LeadVectorStore:
    """Vector store for historical lead qualification patterns."""
    
    def __init__(self, persist_directory: str = "./lead_vector_db"):
        self.embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
        self.persist_directory = persist_directory
        self.text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=500,
            chunk_overlap=50
        )
        self.vectorstore = None
    
    def initialize(self):
        """Load or create the vector store."""
        try:
            self.vectorstore = Chroma(
                embedding_function=self.embeddings,
                persist_directory=self.persist_directory,
                collection_name="lead_qualification_history"
            )
        except Exception:
            # Create new if doesn't exist
            self.vectorstore = Chroma(
                embedding_function=self.embeddings,
                persist_directory=self.persist_directory,
                collection_name="lead_qualification_history"
            )
        return self
    
    def add_historical_leads(self, leads: list):
        """Add historical lead outcomes to the vector store."""
        documents = []
        metadatas = []
        
        for lead in leads:
            # Create a rich text representation
            doc_text = f"""
            Lead: {lead.get('first_name', '')} {lead.get('last_name', '')}
            Title: {lead.get('title', '')}
            Company: {lead.get('company', {}).get('name', '')}
            Industry: {lead.get('company', {}).get('industry', '')}
            Employees: {lead.get('company', {}).get('employee_count', 'Unknown')}
            Budget: {lead.get('budget_mentioned', 'Not mentioned')}
            Timeline: {lead.get('timeline', 'Not mentioned')}
            Need: {lead.get('need_description', 'Not described')}
            Outcome: {lead.get('outcome', 'unknown')}  # 'qualified', 'disqualified', 'nurture'
            Score: {lead.get('qualification_score', 'N/A')}
            """
            
            chunks = self.text_splitter.split_text(doc_text)
            for i, chunk in enumerate(chunks):
                documents.append(chunk)
                metadatas.append({
                    "lead_id": lead.get("id", f"historical_{i}"),
                    "outcome": lead.get("outcome", "unknown"),
                    "score": lead.get("qualification_score", 0),
                    "chunk_index": i
                })
        
        if documents:
            self.vectorstore.add_texts(
                texts=documents,
                metadatas=metadatas
            )
        return self
    
    def find_similar_leads(self, lead_description: str, k: int = 5) -> str:
        """Find similar historical leads and their outcomes."""
        if not self.vectorstore:
            return json.dumps({"error": "Vector store not initialized"})
        
        results = self.vectorstore.similarity_search_with_score(
            lead_description, k=k
        )
        
        similar_leads = []
        for doc, score in results:
            similar_leads.append({
                "content": doc.page_content,
                "similarity_score": round(float(score), 4),
                "outcome": doc.metadata.get("outcome", "unknown"),
                "original_score": doc.metadata.get("score", "N/A")
            })
        
        return json.dumps({
            "similar_leads_found": len(similar_leads),
            "results": similar_leads,
            "insight": "Use these historical outcomes to inform qualification decision"
        }, indent=2)

Assembling the Agent

Now we compose all components into a working agent. The key is the system prompt that defines the agent's reasoning process and tool usage strategy:

# agent.py
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_react_agent
from langchain.memory import ConversationBufferMemory
from langchain.prompts import PromptTemplate
from langchain.callbacks import StdOutCallbackHandler
from tools import (
    lookup_existing_lead,
    enrich_company_info,
    calculate_qualification_score,
    save_qualified_lead,
    generate_qualification_questions
)
from vector_store import LeadVectorStore

class LeadQualificationAgent:
    """Complete lead qualification agent using LangChain ReAct pattern."""
    
    def __init__(self, verbose: bool = True):
        self.llm = ChatOpenAI(
            model="gpt-4o",
            temperature=0.2,  # Low temperature for consistent qualification
            max_tokens=2000
        )
        
        # Register all tools
        self.tools = [
            lookup_existing_lead,
            enrich_company_info,
            calculate_qualification_score,
            save_qualified_lead,
            generate_qualification_questions
        ]
        
        # Initialize vector store
        self.vector_store = LeadVectorStore().initialize()
        
        # Set up conversation memory
        self.memory = ConversationBufferMemory(
            memory_key="chat_history",
            return_messages=True,
            output_key="output"
        )
        
        # Create the system prompt
        self.system_prompt = """You are an expert Lead Qualification Agent for a B2B SaaS company. 
Your job is to evaluate inbound leads and determine if they match our Ideal Customer Profile (ICP).

OUR ICP:
- Target industries: SaaS, Fintech, Healthcare, DevOps, Cybersecurity
- Company size: 50-2000 employees
- Decision makers: CTO, VP Engineering, Director of IT, CEO, Head of DevOps
- Budget threshold: $50,000+ annually
- Positive signals: Hiring engineers, recent funding, scaling infrastructure, using competitor products

QUALIFICATION WORKFLOW:
1. First, use lookup_existing_lead to check if this lead already exists in CRM
2. Use enrich_company_info to get firmographic and technographic data
3. Use generate_qualification_questions to identify what information is missing
4. If interacting with a user, ask the generated questions one at a time naturally
5. Once you have sufficient data, use calculate_qualification_score
6. If score >= 75 (hot): use save_qualified_lead and explain routing to sales team
7. If score 50-74 (warm): save and recommend nurture sequence
8. If score 30-49 (cool): save with nurture tag, recommend long-term nurture
9. If score < 30 (cold): explain disqualification reason, don't save

RULES:
- Always be professional and courteous
- Don't make up information — use tools to get real data
- When you don't know something, ask the lead directly
- Explain your qualification reasoning clearly
- Use calculate_qualification_score before making final determination
- Never save leads with score < 30 as qualified

You have access to the following tools: {tools}

Use this format:
Thought: I need to reason about what to do next
Action: the tool name to use (must be one of: {tool_names})
Action Input: the input for the tool (valid JSON or string as required)
Observation: the tool's output
... (repeat Thought/Action/Action Input/Observation as needed)
Thought: Now I can provide the final answer
Final Answer: Your comprehensive response to the user

Begin!"""
        
        # Create the prompt template
        self.prompt = PromptTemplate.from_template(
            self.system_prompt + "\n\nChat History:\n{chat_history}\n\nHuman: {input}\n\n{agent_scratchpad}"
        )
        
        # Create the ReAct agent
        self.agent = create_react_agent(
            llm=self.llm,
            tools=self.tools,
            prompt=self.prompt
        )
        
        # Create the executor
        self.executor = AgentExecutor(
            agent=self.agent,
            tools=self.tools,
            memory=self.memory,
            verbose=verbose,
            handle_parsing_errors=True,
            max_iterations=10,
            early_stopping_method="generate",
            callbacks=[StdOutCallbackHandler()] if verbose else []
        )
    
    def qualify_lead(self, lead_input: str) -> str:
        """Run qualification on a lead description or conversation turn."""
        result = self.executor.invoke({"input": lead_input})
        return result.get("output", "No output generated")
    
    def reset_conversation(self):
        """Reset memory for a new lead conversation."""
        self.memory.clear()
    
    def get_conversation_history(self) -> str:
        """Retrieve the full conversation history."""
        messages = self.memory.load_memory_variables({})
        return str(messages.get("chat_history", ""))

Running the Agent: Complete Example

Here's how to run the agent with a sample lead submission:

# run_qualification.py
from agent import LeadQualificationAgent
import json

def main():
    # Initialize the agent
    print("Initializing Lead Qualification Agent...")
    agent = LeadQualificationAgent(verbose=True)
    
    # Sample lead input (simulating a web form submission)
    lead_submission = """
    New lead from website contact form:
    Name: Sarah Chen
    Email: sarah.chen@globaltech.com
    Title: VP of Engineering
    Company: GlobalTech Solutions
    Message: We're scaling our infrastructure and looking for a monitoring solution. 
    Our current setup with Datadog is becoming too expensive at our scale. 
    We need something that can handle 1000+ microservices. Budget is around $150K annually.
    Looking to implement within the next 2 months.
    """
    
    print("\n=== Processing Lead Submission ===\n")
    result = agent.qualify_lead(lead_submission)
    
    print("\n=== Final Qualification Result ===\n")
    print(result)
    
    print("\n=== Conversation History ===\n")
    print(agent.get_conversation_history())

if __name__ == "__main__":
    main()

Expected output flow:

Initializing Lead Qualification Agent...

=== Processing Lead Submission ===

Thought: I need to evaluate this lead step by step. Let me start by checking CRM.
Action: lookup_existing_lead
Action Input: {"email": "sarah.chen@globaltech.com"}
Observation: {"found": false, "message": "No existing lead found with this email"}

Thought: Good, this is a new lead. Now let me enrich the company data.
Action: enrich_company_info
Action Input: {"company_name": "GlobalTech Solutions"}
Observation: {
  "industry": "Fintech",
  "employee_count": 1200,
  "revenue_range": "200M-500M",
  "tech_stack": ["Azure", "Datadog", "PagerDuty", "Kubernetes"],
  "funding_stage": "Public",
  "recent_news": ["Opened European office", "Won industry award"]
}

Thought: Excellent enrichment data. Let me now check what qualification questions to ask.
The lead already provided budget ($150K), timeline (2 months), and need description.
Let me calculate the qualification score now.

Action: calculate_qualification_score
Action Input: {"lead_json": "{\"first_name\": \"Sarah\", \"last_name\": \"Chen\", \"email\": \"sarah.chen@globaltech.com\", \"title\": \"VP of Engineering\", \"company\": {\"name\": \"GlobalTech Solutions\", \"industry\": \"Fintech\", \"employee_count\": 1200, \"revenue_range\": \"200M-500M\", \"tech_stack\": [\"Azure\", \"Datadog\", \"PagerDuty\", \"Kubernetes\"], \"funding_stage\": \"Public\"}, \"budget_mentioned\": \"$150K annually\", \"timeline\": \"within the next 2 months\", \"need_description\": \"Scaling infrastructure, looking for monitoring solution to replace Datadog for 1000+ microservices\"}"}
Observation: {
  "total_score": 85,
  "max_possible": 100,
  "breakdown": {
    "industry_match": {"value": 20, "reason": "Industry 'Fintech' matches target industries"},
    "company_size": {"value": 15, "reason": "1200 employees within ICP range"},
    "authority_level": {"value": 25, "reason": "Title 'VP of Engineering' indicates decision-making authority"},
    "budget_indicator": {"value": 20, "reason": "Mentioned budget $150000 meets threshold"},
    "timeline_urgency": {"value": 10, "reason": "Urgent timeline indicated"},
    "positive_signals": {"value": 10, "reason": "2 positive signals detected"}
  },
  "qualification_level": "hot"
}

Thought: Score of 85 - this is a HOT lead. I should save it immediately.
Action: save_qualified_lead
Action Input: {"lead_json": "{\"first_name\": \"Sarah\", \"last_name\": \"Chen\", \"email\": \"sarah.chen@globaltech.com\", \"title\": \"VP of Engineering\", \"company\": {\"name\": \"GlobalTech Solutions\", \"industry\": \"Fintech\", \"employee_count\": 1200}, \"qualification_score\": 85, \"qualification_level\": \"hot\", \"budget_mentioned\": \"$150K annually\", \"timeline\": \"within the next 2 months\"}"}
Observation: {"success": true, "lead_id": "lead_1700000000", "message": "Lead Sarah Chen saved and qualified"}

=== Final Qualification Result ===

Here's my qualification assessment for Sarah Chen from GlobalTech Solutions:

**Qualification Score: 85/100 — HOT LEAD ⭐**

Key factors:
- ✅ Industry: Fintech matches our ICP perfectly
- ✅ Company size: 1,200 employees within our target range
- ✅ Authority: VP of Engineering is a strong decision-maker
- ✅ Budget: $150K exceeds our $50K threshold
- ✅ Timeline: 2-month implementation indicates urgency
- ✅ Signals: Scaling infrastructure, replacing competitor (Datadog)

**Recommendation: Immediate routing to Enterprise sales team.**

This lead has been saved to CRM (lead_id: lead_1700000000). 
Sarah should be contacted within 24 hours. The discovery call should focus on 
their microservices monitoring needs and Datadog migration path.

Priority: Highest — all criteria exceeded.

Building a REST API for the Agent

To integrate with web forms and CRMs, wrap the agent in a FastAPI endpoint:

# api.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr
from typing import Optional
from agent import LeadQualificationAgent
import uvicorn

app = FastAPI(title="Lead Qualification Agent API")
agent = LeadQualificationAgent(verbose=False)

class LeadSubmission(BaseModel):
    first_name: str
    last_name: str
    email: EmailStr

— Ad —

Google AdSense will appear here after approval

← Back to all articles