File size: 8,404 Bytes
3295172
 
204691d
 
 
3295172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
897d4ed
3295172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
897d4ed
 
3295172
897d4ed
3295172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
897d4ed
3295172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
---
title: AutoStream AI Sales Agent
emoji: 
colorFrom: gray
colorTo: red
sdk: docker
app_file: app.py
pinned: false
---

# AutoStream AI Sales Agent

A **LangGraph**-powered conversational AI agent that acts as an intelligent sales assistant for **AutoStream** — an automated video editing SaaS for content creators.

Built for the ServiceHive / Inflx ML Intern assignment: _Social-to-Lead Agentic Workflow_.

---

## Features

| Capability | Implementation |
|---|---|
| Intent detection | LLM classifier — 4 labels: `greeting`, `inquiry`, `high_intent`, `collecting` |
| RAG knowledge retrieval | BM25 search over structured JSON knowledge base |
| Lead qualification | Progressive field collection (name → email → platform) |
| Tool execution guard | `mock_lead_capture` fires **only** after all 3 fields collected |
| State persistence | Full conversation history via LangGraph `AgentState` across turns |
| Multi-LLM support | Groq llama-3.3-70b (default) · Claude 3 Haiku · GPT-4o-mini · Gemini 2.0 Flash |

---

## Quick Start

### 1. Clone & install

```bash
git clone <repo-url> autostream-agent
cd autostream-agent
pip install -r requirements.txt
```

### 2. Configure API key

```bash
cp .env.example .env
# Edit .env — add ONE of:
#   GROQ_API_KEY=gsk_...           ← llama-3.3-70b-versatile (default, free + fast)
#   ANTHROPIC_API_KEY=sk-ant-...   ← Claude 3 Haiku
#   OPENAI_API_KEY=sk-...          ← GPT-4o-mini
#   GOOGLE_API_KEY=...             ← Gemini 2.0 Flash
```

### 3. Run

```bash
# Interactive chat
python main.py

# Scripted demo (for recording the demo video)
python demo.py

# Debug mode — shows intent label + lead state after every turn
DEBUG=1 python main.py
```

---

## Example Conversation

```
You:   Hi, tell me about your pricing.
Agent: AutoStream has two plans:
       • Basic ($29/mo) — 10 videos/month, 720p, email support
       • Pro ($79/mo)   — Unlimited videos, 4K, AI captions, 24/7 support

You:   I want to sign up for Pro for my YouTube channel.
Agent: That's fantastic! Let's get you set up. What's your full name?

You:   Alex Johnson
Agent: Great to meet you, Alex! What's your email address?

You:   alex@example.com
Agent: Almost there! Which creator platform are you primarily on?

You:   YouTube
Lead captured successfully: Alex Johnson, alex@example.com, YouTube

Agent: You're all set, Alex! Our team will reach out to alex@example.com shortly.
       Start your free trial at autostream.io/signup
```

---

## Architecture

### Why LangGraph?

LangGraph was chosen over AutoGen because this workflow requires a **deterministic state machine** with clearly defined transitions. The lead qualification pipeline has discrete stages — greeting, inquiry, high-intent detection, field collection, and capture — that map directly onto LangGraph's node/edge model. LangGraph makes conditional routing explicit (no ambiguous agent-to-agent negotiation) and provides typed state that is immutable between graph invocations, preventing hidden side effects.

### How state is managed

State is a typed Python dict (`AgentState`) with these fields: conversation `messages`, classified `intent`, `collecting_lead` flag, individual lead fields (`lead_name`, `lead_email`, `lead_platform`), a `lead_captured` boolean, and `rag_context` for the current turn's retrieval result.

The `messages` field uses LangGraph's `add_messages` reducer — new messages are **appended** on each node invocation rather than replacing the list, preserving full conversation history across all turns. All other fields are plain values updated by returning a partial dict from each node. Between CLI turns, the full state dict is kept in memory and re-passed to `app.invoke()`.

### Graph topology

```
User message


classify_intent ──► (greeting / high_intent / unknown)
     │                        │
     │ inquiry                │
     ▼                        │
retrieve_rag                  │
     │                        │
     └──────────┬─────────────┘

          generate_response ──► END

     collecting │
     ▼          │
extract_lead_fields
     │  incomplete
     │──────────────► generate_response ──► END
     │  complete

capture_lead ──► generate_response ──► END
```

### RAG pipeline

The knowledge base (`knowledge_base/autostream_kb.json`) contains company info, plan details, policies, and FAQs. On startup, `KnowledgeBaseRetriever` chunks each entry into a document and builds a **BM25Okapi** index (`rank-bm25`). On each inquiry turn, the top-3 highest-scoring chunks are injected into the LLM system prompt as grounded context. BM25 was chosen over vector embeddings to avoid heavy dependencies (no 500 MB model download) while still providing relevance-ranked retrieval suitable for this knowledge base size.

---

## WhatsApp Webhook Integration

To deploy this agent on WhatsApp Business:

### 1. Set up WhatsApp Business API

Register a Meta for Developers account, create a WhatsApp Business App, and obtain a `PHONE_NUMBER_ID` and `WHATSAPP_TOKEN`.

### 2. Build a webhook server (FastAPI example)

```python
from fastapi import FastAPI, Request
import httpx, redis, json
from agent.graph import get_app
from agent.state import AgentState
from langchain_core.messages import HumanMessage

app_server = FastAPI()
redis_client = redis.Redis()
agent = get_app()

@app_server.get("/webhook")
async def verify(hub_mode: str, hub_verify_token: str, hub_challenge: str):
    if hub_verify_token == "YOUR_VERIFY_TOKEN":
        return int(hub_challenge)

@app_server.post("/webhook")
async def receive_message(request: Request):
    body = await request.json()
    msg_obj = body["entry"][0]["changes"][0]["value"]["messages"][0]
    phone   = msg_obj["from"]
    text    = msg_obj["text"]["body"]

    # Load or initialise session state from Redis
    raw = redis_client.get(f"session:{phone}")
    state: AgentState = json.loads(raw) if raw else _initial_state()

    # Run agent
    state = agent.invoke({
        **state,
        "messages": state["messages"] + [HumanMessage(content=text)],
    })

    # Persist updated state
    redis_client.setex(f"session:{phone}", 86400, json.dumps(state, default=str))

    # Send reply via WhatsApp Cloud API
    reply = next(m.content for m in reversed(state["messages"]) if hasattr(m, "content"))
    async with httpx.AsyncClient() as client:
        await client.post(
            f"https://graph.facebook.com/v18.0/{PHONE_NUMBER_ID}/messages",
            headers={"Authorization": f"Bearer {WHATSAPP_TOKEN}"},
            json={"messaging_product": "whatsapp", "to": phone,
                  "text": {"body": reply}},
        )
    return {"status": "ok"}
```

### 3. Expose & register

Deploy the server (e.g. Railway, Fly.io) and register the public URL in the Meta Developer Console as your webhook endpoint.

---

## Project Structure

```
autostream-agent/
├── knowledge_base/
│   └── autostream_kb.json      # Pricing, policies, FAQs
├── rag/
│   ├── __init__.py
│   └── retriever.py            # BM25 retriever
├── agent/
│   ├── __init__.py
│   ├── state.py                # AgentState TypedDict
│   ├── llm.py                  # Multi-provider LLM factory
│   ├── tools.py                # mock_lead_capture
│   ├── nodes.py                # Graph node functions
│   └── graph.py                # LangGraph assembly + compile
├── main.py                     # Interactive CLI
├── demo.py                     # Scripted demo for video recording
├── requirements.txt
├── .env.example
└── README.md
```

---

## Evaluation Mapping

| Criterion | Where |
|---|---|
| Agent reasoning & intent detection | `agent/nodes.py``classify_intent` |
| Correct use of RAG | `rag/retriever.py` + `agent/nodes.py``retrieve_rag` |
| Clean state management | `agent/state.py` + LangGraph `add_messages` |
| Proper tool calling logic | `agent/nodes.py``capture_lead` + `_route_after_extract` guard |
| Code clarity & structure | Modular single-responsibility files |
| Real-world deployability | WhatsApp webhook section above |