er between the LLM and the action executor. This prevents unauthorized discounts, ensures compliance, and handles fallback logic.
Implementation
The following TypeScript implementation outlines a CustomerAcquisitionEngine. It demonstrates signal processing, context retrieval, and safe action generation.
import { z } from 'zod';
import { OpenAI } from 'openai';
import { Pinecone } from '@pinecone-database/pinecone';
import { v4 as uuidv4 } from 'uuid';
// --- Types & Schemas ---
interface AcquisitionSignal {
id: string;
userId: string;
eventType: 'page_view' | 'checkout_abandon' | 'pricing_click';
payload: Record<string, unknown>;
timestamp: number;
}
const ActionSchema = z.object({
actionType: z.enum(['email_send', 'chat_trigger', 'discount_offer', 'no_op']),
content: z.string().max(500),
confidence: z.number().min(0).max(1),
metadata: z.record(z.unknown()).optional()
});
type Action = z.infer<typeof ActionSchema>;
// --- Services ---
class ContextRetriever {
constructor(private pinecone: Pinecone) {}
async getContext(userId: string, signal: AcquisitionSignal): Promise<string> {
const embedding = await this.embedSignal(signal);
const index = this.pinecone.Index('acquisition-context');
const results = await index.query({
vector: embedding,
topK: 3,
filter: { userId },
includeMetadata: true
});
return results.matches
.map(match => JSON.stringify(match.metadata))
.join('\n');
}
private async embedSignal(signal: AcquisitionSignal): Promise<number[]> {
// Implementation depends on embedding provider
// Returns vector representation of signal + payload
return [];
}
}
class AcquisitionEngine {
private openai: OpenAI;
private contextRetriever: ContextRetriever;
constructor(openai: OpenAI, contextRetriever: ContextRetriever) {
this.openai = openai;
this.contextRetriever = contextRetriever;
}
async processSignal(signal: AcquisitionSignal): Promise<Action> {
// 1. Retrieve Real-Time Context
const context = await this.contextRetriever.getContext(signal.userId, signal);
// 2. Generate Action via LLM with Structured Output
const response = await this.openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{
role: 'system',
content: `You are an acquisition agent.
Analyze the signal and context.
Return a structured action.
Max confidence threshold for offers is 0.85.
Never offer discounts > 20%.`
},
{
role: 'user',
content: `Signal: ${JSON.stringify(signal)}\nContext: ${context}`
}
],
response_format: { type: 'json_object' },
temperature: 0.1
});
const rawAction = JSON.parse(response.choices[0].message.content || '{}');
// 3. Validate and Guardrail
const validationResult = ActionSchema.safeParse(rawAction);
if (!validationResult.success) {
return this.generateFallbackAction(signal);
}
const action = validationResult.data;
// 4. Business Logic Checks
if (action.actionType === 'discount_offer' && action.confidence < 0.75) {
return { ...action, actionType: 'no_op', content: 'Confidence too low for discount.' };
}
return action;
}
private generateFallbackAction(signal: AcquisitionSignal): Action {
return {
actionType: 'email_send',
content: `We noticed you were looking at ${signal.payload.page}. Need help?`,
confidence: 0.5,
metadata: { source: 'fallback' }
};
}
}
Key Technical Patterns
- Structured Output: Using
response_format: { type: 'json_object' } ensures the LLM output is parseable, eliminating regex-based extraction errors.
- Zod Validation: The
ActionSchema acts as a runtime guardrail. Even if the LLM returns malformed data or violates constraints (e.g., excessive discount), the schema catches it, triggering a safe fallback.
- Low Temperature:
temperature: 0.1 reduces variance in decision-making, ensuring consistent acquisition logic for similar signals.
- Idempotency: The
signal.id must be used to deduplicate processing in the message consumer, preventing duplicate outreach.
Pitfall Guide
- Hallucinated Incentives: LLMs may generate discount codes or offers not authorized by business rules.
- Fix: Implement a deterministic validation layer that cross-references generated offers against an allowlist of active campaigns and margin thresholds. Never trust LLM output for financial values.
- Latency Budget Violations: Acquisition signals have a half-life. If the pipeline takes >300ms, the opportunity is lost.
- Fix: Optimize vector search latency by using approximate nearest neighbor (ANN) indices with appropriate
ef_construction settings. Cache user context embeddings. Use edge inference for scoring models where possible.
- Context Window Blowout: Feeding entire user histories into the prompt increases cost and latency while diluting relevance.
- Fix: Implement semantic filtering. Retrieve only the top-K most relevant context chunks. Summarize historical interactions before injection. Use sliding windows for session data.
- Lack of Feedback Loop: The system improves only if it learns from outcomes. Without tracking which actions led to conversions, the model drifts.
- Fix: Implement a feedback pipeline. When a conversion occurs, log the
signal_id and action_id. Use this data to fine-tune scoring models or update vector metadata weights.
- Privacy and PII Leakage: Sending raw user data to third-party LLM APIs can violate GDPR/CCPA.
- Fix: Scrub PII before prompt construction. Use tokenization for sensitive fields. Ensure your LLM provider offers zero-data-retention agreements. Hash user IDs before sending to external services.
- Over-Engineering the Prompt: Complex prompts with excessive instructions degrade performance and increase token usage.
- Fix: Keep system prompts focused on decision logic. Offload data formatting to the application layer. Use few-shot examples only for edge cases.
- Ignoring "No-Op" Scenarios: Forcing an action on every signal annoys users and increases churn.
- Fix: Include
no_op as a valid action class. Train the model to recognize low-intent signals where intervention is counterproductive.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| High Volume / Low Value Leads | Real-Time Contextual AI with no_op filtering | Maximizes scale while minimizing noise and cost per interaction. | Low per-unit cost; high infrastructure efficiency. |
| Low Volume / High Value Leads | AI Scoring + Human-in-the-Loop | LLM scores and prioritizes; human executes relationship-building. | Higher operational cost; maximizes deal size. |
| Strict Latency (<50ms) | Edge Inference + Deterministic Rules | Cloud LLM latency is prohibitive; use distilled models or rules. | Higher upfront model training cost; lower inference cost. |
| Multi-Channel Orchestration | Centralized Event Bus + Channel Adapters | Ensures consistent context across email, chat, and ads. | Moderate integration cost; unified attribution. |
Configuration Template
Use this YAML configuration to parameterize the acquisition engine. This allows runtime adjustments without code deployments.
acquisition:
engine:
model: "gpt-4o-mini"
temperature: 0.1
max_tokens: 256
context:
vector_store: "pinecone"
index: "acquisition-context"
top_k: 3
cache_ttl_seconds: 300
guardrails:
max_discount_percent: 20
min_confidence_email: 0.6
min_confidence_offer: 0.85
pii_redaction: true
routing:
fallback_strategy: "deterministic_rule"
retry_attempts: 2
dead_letter_queue: "acquisition-dlq"
metrics:
track_conversion_window_hours: 24
feedback_loop_enabled: true
Quick Start Guide
-
Initialize Project:
mkdir ai-acquisition-engine && cd ai-acquisition-engine
npm init -y
npm install openai @pinecone-database/pinecone zod uuid typescript ts-node
npx tsc --init
-
Configure Environment:
Create .env:
OPENAI_API_KEY=sk-...
PINECONE_API_KEY=...
PINECONE_ENVIRONMENT=us-east-1
-
Run Context Seeder:
Execute a script to populate the vector store with initial product and user data.
ts-node scripts/seed-context.ts
-
Deploy Consumer:
Start the message consumer that listens for signals and invokes the AcquisitionEngine.
ts-node src/consumer.ts
-
Verify Pipeline:
Send a test signal via the webhook or CLI. Check logs for action generation and guardrail validation. Confirm the action is routed to the execution channel (e.g., email service API).
AI customer acquisition is no longer about generating text; it is about engineering systems that deliver the right intervention at the right moment with mathematical precision. By implementing real-time context orchestration, strict guardrails, and closed-loop feedback, development teams can transform acquisition from a cost center into a scalable, data-driven growth engine.