Building a personal AI chatbot and integrating it into your website can significantly enhance user engagement and provide interactive assistance to your visitors. In this post, I'll walk through the process of creating and deploying a custom chatbot using modern web technologies.
Tech Stack Overview
For this project, we'll use:
- OpenAI API: For natural language processing and response generation
- React: For building the chat interface
- TypeScript: For type-safe development
- Supabase: For storing chat history and managing API keys
- Shadcn UI: For building a beautiful chat interface
- Remix: For server-side rendering and API routes
Setting Up the Foundation
1. Project Structure
First, let's organize our chatbot-related code:
app/
├── components/
│ └── chat/
│ ├── chat-window.tsx
│ ├── message-input.tsx
│ ├── message-list.tsx
│ └── message-bubble.tsx
├── lib/
│ └── chat/
│ ├── types.ts
│ ├── openai.ts
│ └── chat-store.ts
└── routes/
└── api/
└── chat.ts
2. Data Model
In Supabase, we'll create tables for storing chat history:
create table chat_messages (
id uuid default uuid_generate_v4() primary key,
session_id uuid not null,
content text not null,
role text not null,
created_at timestamp with time zone default timezone('utc'::text, now())
);
create table chat_sessions (
id uuid default uuid_generate_v4() primary key,
user_id uuid references auth.users,
created_at timestamp with time zone default timezone('utc'::text, now())
);
Building the Chat Interface
1. Chat Components
The chat interface consists of several key components:
interface Message {
id: string;
content: string;
role: 'user' | 'assistant';
createdAt: Date;
}
export function ChatWindow() {
const [messages, setMessages] = useState<Message[]>([]);
const [isLoading, setIsLoading] = useState(false);
return (
<div className="flex flex-col h-[600px] w-full max-w-2xl mx-auto rounded-lg border">
<MessageList messages={messages} />
<MessageInput
onSend={async content => {
// Handle message sending
}}
isLoading={isLoading}
/>
</div>
);
}
2. API Integration
We'll create a server-side API route to handle chat requests:
import { OpenAI } from 'openai';
import { json } from '@remix-run/node';
export async function action({ request }: ActionArgs) {
const { content } = await request.json();
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const completion = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [{ role: 'user', content }],
});
return json({
message: completion.choices[0].message,
});
}
Adding Advanced Features
1. Context Management
To make the chatbot more intelligent, we'll maintain conversation context:
interface ChatContext {
messages: Message[];
systemPrompt: string;
}
function buildChatContext(messages: Message[]): ChatContext {
return {
messages,
systemPrompt: `You are a helpful assistant on my personal website.
You can help visitors learn about my projects, experience, and interests.`,
};
}
2. Stream Responses
Implementing streaming for a more dynamic experience:
export async function streamResponse(messages: Message[]) {
const stream = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: messages.map(m => ({ role: m.role, content: m.content })),
stream: true,
});
return new ReadableStream({
async start(controller) {
for await (const chunk of stream) {
controller.enqueue(chunk.choices[0]?.delta?.content || '');
}
controller.close();
},
});
}
Deployment and Integration
1. Environment Setup
Create a .env
file with necessary configurations:
OPENAI_API_KEY=your_api_key
SUPABASE_URL=your_supabase_url
SUPABASE_ANON_KEY=your_supabase_key
2. Chat Widget Integration
Add the chat widget to your website layout:
export function Layout({ children }: { children: React.ReactNode }) {
const [isChatOpen, setIsChatOpen] = useState(false);
return (
<div className="min-h-screen">
{children}
<button
onClick={() => setIsChatOpen(true)}
className="fixed bottom-4 right-4 p-4 rounded-full bg-primary"
>
<MessageCircle className="w-6 h-6" />
</button>
{isChatOpen && (
<Dialog open={isChatOpen} onOpenChange={setIsChatOpen}>
<DialogContent>
<ChatWindow />
</DialogContent>
</Dialog>
)}
</div>
);
}
Performance Optimization
Lazy Loading
- Load chat components only when needed
- Use React.lazy() for code splitting
Caching
- Cache common responses in Supabase
- Implement rate limiting for API calls
Error Handling
- Graceful fallbacks for API failures
- Clear error messages for users
Security Considerations
API Key Protection
- Never expose API keys in client-side code
- Use server-side API routes for OpenAI calls
Rate Limiting
- Implement request limits per session
- Monitor usage patterns for abuse
Data Privacy
- Clear data retention policies
- Option for users to clear chat history
Future Improvements
Here are some ways to enhance the chatbot:
Personalization
- Train on your personal content
- Add memory of past conversations
Multi-modal Support
- Handle image inputs
- Support voice interactions
Analytics
- Track common questions
- Measure response quality
Conclusion
Building a personal chatbot is an exciting way to make your website more interactive and helpful for visitors. The key is to start simple and gradually add features based on user feedback and needs.
Feel free to check out the chatbot in action by clicking the chat icon in the bottom right corner of this page. The source code is available on my GitHub repository!
Building a personal AI chatbot and integrating it into your website can significantly enhance user engagement and provide interactive assistance to your visitors. In this post, I'll walk through the process of creating and deploying a custom chatbot using modern web technologies.
Tech Stack Overview
For this project, we'll use:
Setting Up the Foundation
1. Project Structure
First, let's organize our chatbot-related code:
2. Data Model
In Supabase, we'll create tables for storing chat history:
create table chat_messages ( id uuid default uuid_generate_v4() primary key, session_id uuid not null, content text not null, role text not null, created_at timestamp with time zone default timezone('utc'::text, now()) ); create table chat_sessions ( id uuid default uuid_generate_v4() primary key, user_id uuid references auth.users, created_at timestamp with time zone default timezone('utc'::text, now()) );
Building the Chat Interface
1. Chat Components
The chat interface consists of several key components:
interface Message { id: string; content: string; role: 'user' | 'assistant'; createdAt: Date; } export function ChatWindow() { const [messages, setMessages] = useState<Message[]>([]); const [isLoading, setIsLoading] = useState(false); return ( <div className="flex flex-col h-[600px] w-full max-w-2xl mx-auto rounded-lg border"> <MessageList messages={messages} /> <MessageInput onSend={async content => { // Handle message sending }} isLoading={isLoading} /> </div> ); }
2. API Integration
We'll create a server-side API route to handle chat requests:
import { OpenAI } from 'openai'; import { json } from '@remix-run/node'; export async function action({ request }: ActionArgs) { const { content } = await request.json(); const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }); const completion = await openai.chat.completions.create({ model: 'gpt-3.5-turbo', messages: [{ role: 'user', content }], }); return json({ message: completion.choices[0].message, }); }
Adding Advanced Features
1. Context Management
To make the chatbot more intelligent, we'll maintain conversation context:
interface ChatContext { messages: Message[]; systemPrompt: string; } function buildChatContext(messages: Message[]): ChatContext { return { messages, systemPrompt: `You are a helpful assistant on my personal website. You can help visitors learn about my projects, experience, and interests.`, }; }
2. Stream Responses
Implementing streaming for a more dynamic experience:
export async function streamResponse(messages: Message[]) { const stream = await openai.chat.completions.create({ model: 'gpt-3.5-turbo', messages: messages.map(m => ({ role: m.role, content: m.content })), stream: true, }); return new ReadableStream({ async start(controller) { for await (const chunk of stream) { controller.enqueue(chunk.choices[0]?.delta?.content || ''); } controller.close(); }, }); }
Deployment and Integration
1. Environment Setup
Create a
.env
file with necessary configurations:2. Chat Widget Integration
Add the chat widget to your website layout:
export function Layout({ children }: { children: React.ReactNode }) { const [isChatOpen, setIsChatOpen] = useState(false); return ( <div className="min-h-screen"> {children} <button onClick={() => setIsChatOpen(true)} className="fixed bottom-4 right-4 p-4 rounded-full bg-primary" > <MessageCircle className="w-6 h-6" /> </button> {isChatOpen && ( <Dialog open={isChatOpen} onOpenChange={setIsChatOpen}> <DialogContent> <ChatWindow /> </DialogContent> </Dialog> )} </div> ); }
Performance Optimization
Lazy Loading
Caching
Error Handling
Security Considerations
API Key Protection
Rate Limiting
Data Privacy
Future Improvements
Here are some ways to enhance the chatbot:
Personalization
Multi-modal Support
Analytics
Conclusion
Building a personal chatbot is an exciting way to make your website more interactive and helpful for visitors. The key is to start simple and gradually add features based on user feedback and needs.
Feel free to check out the chatbot in action by clicking the chat icon in the bottom right corner of this page. The source code is available on my GitHub repository!