Files · OpenAI Lead Intake with Intent Routing for SMBs
68 (1 binary, 594.1 kB total)attempt 1
README.md·6226 B·markdown
markdown
# OpenAI Lead Intake with Intent Routing for SMBs
> Chat-based lead intake that classifies buyer intent and routes qualified conversations to the right sales agent, with full session history.
A tutorialized reference solution from [reaatech.com](https://reaatech.com), demonstrating how to build production-grade AI systems with the `@reaatech/*` package family.
## Getting Started
### Prerequisites
- **Node.js** >= 22
- **pnpm** (install via `corepack enable && corepack prepare pnpm@latest --activate`)
### Setup
```bash
cp .env.example .env # then fill in your API keys
pnpm install
pnpm dev # starts Next.js dev server
pnpm test # vitest run with coverage
```
## How it works
The system processes each chat message through a 3-step pipeline:
1. **Classify intent** — `ConfidenceRouter` runs the `LLMClassifier` (OpenAI `gpt-4o-mini`) as the primary classifier. If it times out or returns low confidence, the `KeywordClassifier` (regex-based keyword matching on 5 intents: pricing, demo, support, general, unqualified) acts as a synchronous fallback.
2. **Route / clarify / fallback decision** — The router's decision tree evaluates confidence:
- `>= 0.8` → **ROUTE**: generate a reply and trigger a sales handoff via `SalesHandoffService`, creating a `LeadRecord`.
- `>= 0.3` but `< 0.8` → **CLARIFY**: generate a reply asking for more detail (no handoff).
- `< 0.3` → **FALLBACK**: serve an FAQ answer if one exists, otherwise generate a general reply.
3. **Session continuity & sales handoff** — `SessionService` (backed by `@reaatech/session-continuity`) persists the conversation with sliding-window compression. When routed, `SalesHandoffService` wraps the lead data and sends it to the configured `AGENT_HANDOFF_ENDPOINT` with exponential retry.
## Architecture
### Confidence-router decision tree
```
┌─────────────────────┐
│ User message │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ LLMClassifier │
│ (gpt-4o-mini, ≥ 2) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ KeywordClassifier │ ← fallback if LLM fails
│ (keyword, ≥ 1) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ ConfidenceRouter │
│ route: ≥ 0.8 │
│ clarify: ≥ 0.3 │
│ fallback: < 0.3 │
└──────────┬──────────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
ROUTE CLARIFY FALLBACK
│ │ │
┌───────▼───────┐ ┌────▼────┐ ┌───────▼───────┐
│ Generate reply │ │ Ask for │ │ FAQ answer or │
│ Sales handoff │ │ details │ │ general reply │
└───────────────┘ └─────────┘ └───────────────┘
```
### Session sliding-window compression
`SessionManager` is configured with an 8192-token budget and sliding-window compression (`targetTokens: 6000`). When the budget is exceeded, older messages are evicted to keep the conversation context within the token limit.
### Langfuse telemetry
Every request creates a Langfuse trace (`chat-handle`) with named spans for session loading, message ingestion, classification, and sales handoff. Traces are finalised and flushed before the response is returned.
## API Reference
### `POST /api/chat`
Processes a chat message through the intent-classification pipeline.
#### Request body
```json
{
"content": "string",
"sessionId": "string (optional)"
}
```
| Field | Type | Required | Description |
|-------------|----------|----------|-------------------------------------|
| `content` | `string` | yes | User's chat message |
| `sessionId` | `string` | no | Existing session ID for continuity |
#### Response body (200)
```json
{
"reply": "string",
"sessionId": "string",
"action": "clarify | route | fallback",
"lead": {
"id": "string",
"sessionId": "string",
"intent": "pricing | demo | support | general | unqualified",
"confidence": "number",
"messageCount": "number",
"summary": "string",
"createdAt": "string"
}
}
```
`lead` is only present when `action` is `"route"`.
#### Status codes
| Code | Description |
|------|----------------------------------------------|
| 200 | Success — response contains reply + metadata |
| 400 | Bad request — missing or invalid `content` |
| 500 | Server error — internal processing failure |
#### Example
```bash
curl -X POST http://localhost:3000/api/chat \
-H "Content-Type: application/json" \
-d '{"content": "How much does your starter plan cost?"}'
```
## Project layout
```
app/ Next.js App Router pages + API routes
src/ services, lib, adapters
tests/ vitest suite (mirrors src/)
packages/ API references for every dependency (read these first)
DEV_PLAN.md build plan for this recipe
```
## License
MIT — see [LICENSE](./LICENSE).