A conversational AI agent built with LangGraph that converts social media conversations into qualified business leads for AutoStream — a fictional SaaS video editing platform.
Built as part of the ServiceHive × Inflx ML Intern assignment.
git clone https://github.com/your-username/autostream-agent cd autostream-agent
python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
python main.py
The AutoStream AI agent is built using LangGraph as a stateful workflow engine.
Below is the execution graph showing how each user message flows through the system:
The graph above is automatically generated from the compiled LangGraph DAG using
draw_mermaid_png().
You: hi there AutoStream: Hey! Welcome to AutoStream. I can help you with pricing, features, and plans.
You: what's included in the pro plan? AutoStream: The Pro Plan is $79/month and includes unlimited videos, 4K resolution, and AI captions.
You: that sounds great, I want to sign up for my YouTube channel AutoStream: Awesome! Let's get you started. Could I get your full name?
You: John Doe AutoStream: Thanks John! What's your email address?
You: john@example.com AutoStream: Perfect! Which creator platform are you on — YouTube, Instagram, TikTok?
You: YouTube AutoStream: You're all set, John! We've captured your details and our team will reach out to your YouTube account at john@example.com. Welcome to AutoStream!
Lead successfully captured
- Name: John Doe
- Email: john@example.com
- Platform: YouTube
The complete working demonstration of the AutoStream AI Agent can be viewed here:
The demo covers:
- Conversation flow
- Lead qualification
- Lead capture process
- Agent decision routing
LangGraph was chosen over a simple chain because this agent requires stateful multi-turn decision making — not just a single prompt-response cycle. The agent needs to remember whether it's mid-lead-collection, which fields it has already gathered, and what the user's intent was three turns ago.
LangGraph models this as a directed graph where each node is a pure function that
reads from and writes to a shared AgentState TypedDict. The graph re-enters at
classify_intent on every user turn, so intent is re-evaluated continuously —
meaning a user can switch from asking about pricing to signing up mid-conversation
and the agent handles it gracefully.
All memory lives in AgentState (defined in agent/state.py). It holds:
messages— full conversation history, using LangGraph'sadd_messagesreducer so messages are appended rather than overwritten each turnintent— re-classified on every turnlead_name,lead_email,lead_platform— filled incrementallylead_complete— flipped to True only when all 3 fields are confirmedretrieved_context— the RAG output, saved for auditability
State is passed into agent_graph.invoke(state) in main.py and the returned
state is persisted across turns in a local dict — no external database needed.
The knowledge base lives in rag/knowledge_base.json as a flat list of chunks.
The retriever in rag/retriever.py scores each chunk using keyword overlap against
the user query and returns the top-k as a plain string. This string is injected
directly into the system prompt via config/prompts.py before the LLM is called.
The LLM is instructed to answer ONLY from the provided context, which eliminates hallucination of fake prices or policies.
update_lead_state
│
▼
classify_intent
│
├─ greeting ──→ handle_greeting ──→ END
│
├─ product_inquiry ──→ rag_retrieval ──→ END
│
└─ high_intent
│
├─ first time ──→ qualify_lead ──→ collect_lead_info
│
└─ returning ──→ collect_lead_info
│
├─ fields missing ──→ END
│
└─ all 3 present ──→ capture_lead ──→ END
To deploy this agent on WhatsApp using the WhatsApp Business API:
1. Webhook setup Host a POST endpoint (e.g. using FastAPI) that WhatsApp calls on every incoming message. WhatsApp sends a JSON payload containing the sender's phone number and message text.
2. Session management
Replace the in-memory state dict in main.py with a persistent store
(Redis or a database) keyed by the user's phone number. On each webhook
call, load that user's state, run agent_graph.invoke(state), and save
the updated state back.
3. Sending replies
After invoke() returns, read state["messages"][-1].content and POST
it back to WhatsApp via the Send Message API using the sender's phone
number as the recipient.
4. Verification
WhatsApp requires a GET endpoint that handles the initial webhook
verification challenge (returning the hub.challenge token).
Example FastAPI sketch:
from fastapi import FastAPI, Request from agent import agent_graph import redis, json
app = FastAPI() r = redis.Redis()
@app.post("/webhook") async def webhook(request: Request): body = await request.json() phone = body["entry"][0]["changes"][0]["value"]["messages"][0]["from"] text = body["entry"][0]["changes"][0]["value"]["messages"][0]["text"]["body"]
raw = r.get(phone)
state = json.loads(raw) if raw else {
"messages": [], "intent": None,
"lead_name": None, "lead_email": None,
"lead_platform": None, "lead_complete": False,
"retrieved_context": None
}
from langchain_core.messages import HumanMessage
state["messages"].append(HumanMessage(content=text))
state = agent_graph.invoke(state)
r.set(phone, json.dumps(state))
reply = state["messages"][-1].content
# POST reply to WhatsApp Send Message API here
return {"status": "ok"}
autostream-agent/
├── agent/
│ ├── nodes/
│ │ ├── classify_intent.py
│ │ ├── handle_greeting.py
│ │ ├── rag_retrieval.py
│ │ ├── qualify_lead.py
│ │ ├── collect_lead_info.py
│ │ ├── capture_lead.py
│ │ ├── update_lead_state.py
│ │ └── __init__.py
│ ├── graph.py
│ ├── state.py
│ └── __init__.py
├── rag/
│ ├── knowledge_base.json
│ ├── retriever.py
│ └── __init__.py
├── tools/
│ ├── lead_capture.py
│ └── __init__.py
├── config/
│ ├── settings.py
│ └── prompts.py
├── main.py
├── .env.example
├── .gitignore
├── requirements.txt
└── README.md