Skip to content
reaatech

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).