d provides the user interface including a chat widget, dashboard, and landing page.
Step 1: Clone and Install
git clone https://github.com/ayw-ai/ayw-monorepo.git
cd ayw-monorepo
npm install
This uses npm workspaces to install dependencies across all apps.
Step 2: Set Up Environment Variables
Create .env files in both apps/backend and apps/frontend:
apps/backend/.env:
DATABASE_URL="postgresql://user:password@localhost:5432/ayw_db"
OPENAI_API_KEY="your-openai-api-key"
JWT_SECRET="your-jwt-secret"
FRONTEND_URL="http://localhost:3000"
PORT=4000
apps/frontend/.env:
VITE_API_URL="http://localhost:4000"
Step 3: Set Up the Database
cd apps/backend
npx prisma migrate dev --name init
npx prisma generate
This creates your database schema with tables for users, conversations, and messages.
Step 4: Start the Development Servers
In the root directory, run:
npm run dev
This concurrently starts:
- Backend server on
http://localhost:4000
- Frontend on
http://localhost:3000
- Chatbot service (when ready)
Step 5: Create Your First Bot Configuration
Now let's configure your first bot. Create a bot config in apps/backend/src/services/botConfig.ts:
export interface BotConfig {
name: string;
type: 'welcome' | 'support' | 'sales' | 'feedback';
systemPrompt: string;
temperature: number;
maxTokens: number;
}
export const welcomeBot: BotConfig = {
name: 'Welcome Bot',
type: 'welcome',
systemPrompt: `You are the AYW Welcome Bot. Your job is to:
1. Greet visitors warmly
2. Understand their intent (support, sales, feedback)
3. Route them to the appropriate specialist bot
4. Maintain a helpful, human-guided tone
Always be polite, concise, and guide users to the right destination.`,
temperature: 0.7,
maxTokens: 500
};
Step 6: Build the Chat Service with OpenAI
Create apps/backend/src/services/chatbotService.ts:
import OpenAI from 'openai';
import { PrismaClient } from '@prisma/client';
import { BotConfig, welcomeBot, supportBot, salesBot, feedbackBot } from './botConfig';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const prisma = new PrismaClient();
const botConfigs: Record<string, BotConfig> = {
welcome: welcomeBot,
support: supportBot,
sales: salesBot,
feedback: feedbackBot
};
export class ChatbotService {
async processMessage({ message, conversationId, botType, userId }: {
message: string;
conversationId?: string;
botType: string;
userId?: string;
}) {
const config = botConfigs[botType] || welcomeBot;
// Create or get conversation
let conversation;
if (conversationId) {
conversation = await prisma.conversation.findUnique({
where: { id: conversationId }
});
} else {
conversation = await prisma.conversation.create({
data: {
botType,
userId,
status: 'active'
}
});
}
// Save user message
await prisma.message.create({
data: {
conversationId: conversation.id,
role: 'user',
content: message
}
});
// Get conversation history
const history = await prisma.message.findMany({
where: { conversationId: conversation.id },
orderBy: { createdAt: 'asc' },
take: 10
});
// Call OpenAI with human-guided system prompt
const completion = await openai.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [
{ role: 'system', content: config.systemPrompt },
...history.map(msg => ({
role: msg.role as 'user' | 'assistant',
content: msg.content
}))
],
temperature: config.temperature,
max_tokens: config.maxTokens
});
const botResponse = completion.choices[0].message.content;
// Save bot response
await prisma.message.create({
data: {
conversationId: conversation.id,
role: 'assistant',
content: botResponse,
botType
}
});
return {
message: botResponse,
conversationId: conversation.id,
botType: config.type
};
}
}
Step 7: Build the Frontend Chat Interface
Create apps/frontend/src/pages/ChatPage.tsx:
import { useState, useRef, useEffect } from 'react';
import { Send, Bot, User } from 'lucide-react';
import { chatbotService } from '../services/chatbotService';
interface Message {
id: string;
role: 'user' | 'assistant';
content: string;
timestamp: Date;
}
export default function ChatPage() {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null);
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
const sendMessage = async () => {
if (!input.trim()) return;
const userMessage: Message = {
id: Date.now().toString(),
role: 'user',
content: input,
timestamp: new Date()
};
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsLoading(true);
try {
const response = await chatbotService.sendMessage({
message: input,
botType: 'welcome'
});
const botMessage: Message = {
id: (Date.now() + 1).toString(),
role: 'assistant',
content: response.message,
timestamp: new Date()
};
setMessages(prev => [...prev, botMessage]);
} catch (error) {
console.error('Failed to send message:', error);
} finally {
setIsLoading(false);
}
};
return (
<div className="flex flex-col h-screen max-w-4xl mx-auto p-4">
<header className="mb-4">
<h1 className="text-2xl font-bold">AYW Chatbot Demo</h1>
<p className="text-gray-600">Powered by human-guided AI</p>
</header>
<div className="flex-1 overflow-y-auto border rounded-lg p-4 space-y-4">
{messages.map(msg => (
<div
key={msg.id}
className={`flex ${msg.role === 'u
Pitfall Guide
- Prisma Schema Sync Failure: Running
npx prisma migrate dev without subsequently executing npx prisma generate leaves the TypeScript client out of sync with the database schema, causing runtime PrismaClient type errors.
- Unbounded Context Window: Omitting the
take: 10 constraint in prisma.message.findMany() allows conversation history to grow indefinitely, exhausting the LLM context window, increasing API costs, and degrading response latency.
- Environment Variable Leakage: Committing
.env files to version control exposes DATABASE_URL and OPENAI_API_KEY. Always enforce .env in .gitignore and validate CORS origins against FRONTEND_URL at the Express middleware level.
- Temperature Misconfiguration for Routing: Setting
temperature > 0.8 for intent-routing bots introduces stochastic behavior that breaks deterministic classification. Keep routing bots at 0.3β0.7 and reserve higher temperatures for creative/feedback bots.
- Conversation State Loss: Failing to persist and pass
conversationId across HTTP requests forces the backend to treat every message as a new session, destroying context continuity and breaking multi-turn routing logic.
- Blocking UI During Inference: Neglecting the
isLoading state or optimistic UI updates causes the frontend to freeze during OpenAI API latency. Always toggle loading states and implement scroll-to-bottom effects for smooth UX.
Deliverables
- π AYW Architecture Blueprint: Visual diagram detailing the monorepo workspace structure, Prisma ORM relationships, Express routing middleware, and OpenAI inference pipeline.
- β
Deployment Checklist: Step-by-step validation matrix covering environment validation, Prisma migration verification, CORS configuration, API key rotation, and frontend/backend health checks.
- βοΈ Configuration Templates: Pre-configured
.env samples, prisma/schema.prisma definitions for User, Conversation, and Message models, and typed BotConfig interfaces ready for production scaling.