آنچه در این مقاله میخوانید
راهنمای ساخت دستیار هوش مصنوعی چندرسانهای با جاوااسکریپت
۸ آبان ۱۴۰۴
در این آموزش، قدم به قدم یاد خواهیم گرفت چگونه یک دستیار هوش مصنوعی چندرسانهای بسازیم که بتواند هم متن را پردازش کند و هم تصاویر را تولید کند. برای این کار از APIهای OpenAI استفاده خواهیم کرد و رابط کاربری مدرنی شبیه به ChatGPT را با React و Next.js پیادهسازی خواهیم کرد. این دستیار میتواند مکالمات طبیعی داشته باشه و با کمک قابلیتهای بینایی GPT-4o، تصاویر را تحلیل کند.
در پایان این آموزش، یک دستیار هوش مصنوعی کامل و کارآمد خواهید داشت که میتواند با کاربران حرف بزند و بر اساس توصیفهای متنی، تصاویر را بسازد.
با هوش مصنوعی لیارا، دسترسی سریع و پایدار به API هوش مصنوعی داشته باشید.
✅ ارائه API هوش مصنوعی✅ ادغام آسان با سرویسها و اپلیکیشنها✅ مقیاسپذیری و امنیت بالا
خرید و راهاندازی سرویس هوش مصنوعی
همچنین، لیارا از جمله نخستین سرویسهای میزبانی ابری ایرانی است که در زمینه ارائه خدمات مرتبط با هوش مصنوعی پیشتاز محسوب میشود. برای اطلاعات بیشتر، مستندات سرویس هوش مصنوعی لیارا را مشاهده کنید.
آنچه در ادامه خواهید خواند:
- پیشنیازها
- راهاندازی محیط توسعه
- ایجاد نقطه پایانی API
- ساخت رابط چت
- ادغام رابط چت
- تست دستیار هوش مصنوعی
- سوالات متداول
- جمع بندی
پیشنیازها
برای تکمیل این آموزش، به موارد زیر نیاز خواهید داشت:
- Node.js نسخه 18 یا بالاتر بر روی ماشین محلی شما نصب شده باشد. اگر مبتدی هستید نگران نباشید، از راهنمای آموزش نصب Nodejs روی ویندوز برای سیستم عاملتان استفاده کنید.
- یک ویرایشگر کد مانند Cursor نصب باشد.
- یک کلید API از OpenAI.
- آشنایی پایه با جاوااسکریپت و React. اگر نیاز به تقویت مهارتهایتان دارید، سری آموزشی ترفندهایی برای نوشتن کدهای کوتاه در JavaScript را بررسی کنید.
- یک حساب GitHub برای کنترل نسخه بسازید.
راهاندازی محیط توسعه
در این گام، یک پروژه جدید Next.js خواهیم ساخت و وابستگیهای لازم برای ساخت دستیار هوش مصنوعی را نصب خواهیم کرد.
ابتدا با دستور create-next-app یک اپلیکیشن Next.js جدید بسازید:
npx create-next-app@latest ai-assistant --typescript --tailwind --app
زمانی که سوالات از شما پرسیده شد، گزینههای زیر را انتخاب کنید:
Would you like to use ESLint? YesWould you like to use src/ directory? YesWould you like to customize the default import alias? No
این دستور یک پروژه Next.js جدید با TypeScript ، Tailwind CSS و وابستگیهای ضروری برای ساخت دستیارتان را ایجاد خواهد کرد.
حال به دایرکتوری پروژه برید:
cd ai-assistant
SDK OpenAI و وابستگیهای اضافی برای مدیریت رابط چت را نصب کنید:
npm install openai react-markdown remark-gfm
پکیج openai کلاینت رسمی جاوااسکریپت برای تعامل با APIهای OpenAI است. پکیجهای react-markdown و remark-gfm کمک میکنند پاسخهای فرمتشده از دستیار هوش مصنوعی را رندر کنید.
یک فایل .env.local در ریشه پروژه بسازید تا بتوانید کلید API OpenAI را ذخیره کنید:
touch .env.local
کلید API تان را به فایل .env.local اضافه کنید:
OPENAI_API_KEY=your_openai_api_key
عبارت your_openai_api_key را با کلید اصلیتان جایگزین کنید. این متغیر کلید را امن نگهمیدارد و از کد جدا میکند.
حال محیط توسعهتان با Next.js آماده است. در گام بعدی، نقطه پایانی API برای مدیریت درخواستهای هوش مصنوعی رو خواهیم ساخت.

ایجاد نقطه پایانی API
در این گام، یک مسیر API در Next.js میسازیم که ارتباط با API OpenAI را مدیریت کند و هم مکالمات متنی و هم درخواستهای تولید تصویر را پردازش کند.
ابتدا فایل جدیدی را برای نقطه پایانی API بسازید:
mkdir -p src/app/api/chat
touch src/app/api/chat/route.ts
فایل src/app/api/chat/route.ts را باز کنید و کد زیر را اضافه کنید تا درخواستهای API مدیریت بشوند:
import { NextRequest, NextResponse } from 'next/server';
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
export async function POST(request: NextRequest) {
try {
const { messages, mode, model = 'gpt-4o' } = await request.json();
// Validate input
if (!messages || !Array.isArray(messages) || messages.length === 0) {
return NextResponse.json(
{ error: 'Invalid messages format' },
{ status: 400 }
);
}
if (mode === 'chat') {
const completion = await openai.chat.completions.create({
model: model,
messages: messages,
temperature: 0.7,
max_tokens: 1000,
});
return NextResponse.json({
content: completion.choices[0].message.content,
role: 'assistant',
});
} else if (mode === 'image') {
const lastMessage = messages[messages.length - 1];
// Using DALL-E 3 for image generation
// Note: As of 2025, GPT-4o with vision can analyze images but doesn't generate them
// For image generation, we use DALL-E 3 which is the latest image generation model
const imageResponse = await openai.images.generate({
model: 'dall-e-3',
prompt: lastMessage.content,
n: 1,
size: '1024x1024',
quality: 'hd', // Use HD quality for better results
style: 'vivid', // More vibrant and detailed images
});
return NextResponse.json({
content: imageResponse.data[0].url,
role: 'assistant',
type: 'image',
});
}
return NextResponse.json({ error: 'Invalid mode' }, { status: 400 });
} catch (error) {
console.error('API Error:', error);
if (error instanceof OpenAI.APIError) {
return NextResponse.json(
{ error: `OpenAI API error: ${error.message}` },
{ status: error.status || 500 }
);
}
return NextResponse.json(
{ error: 'Failed to process request' },
{ status: 500 }
);
}
}
این نقطه پایانی درخواستهای POST را میپذیرد که شامل آرایه messages و پارامتر mode است. زمانی که mode برابر chat باشد، از GPT-4o برای تولید پاسخهای متنی استفاده میکند و زمانی که mode برابر image باشد، تصاویر را با DALL-E 3 تولید میکند. پارامتر temperature تصادفی بودن پاسخها را کنترل میکند، مقدار 0.7 تعادل خوبی بین خلاقیت و ثبات ایجاد میکند.
حال یک نقطه پایانی API را در اختیار دارید که میتواند درخواستهای متنی و تولید تصویر را مدیریت کند. در مرحله بعدی، رابط کاربری برای دستیار هوش مصنوعی را خواهیم ساخت.
ساخت رابط چت
در این گام، یک رابط چت جدید و پاسخگو با تاریخچه پیامها و مدیریت ورودی خواهیم ساخت که شبیه به طراحی ChatGPT باشد.
ابتدا یک کامپوننت جدید برای رابط چت بسازید:
mkdir -p src/components
touch src/components/ChatInterface.tsx
فایل src/components/ChatInterface.tsx را باز کنید و کد زیر را اضافه کنید:
'use client';
import { useState, useRef, useEffect } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
interface Message {
role: 'user' | 'assistant';
content: string;
type?: 'text' | 'image';
model?: string;
}
const AVAILABLE_MODELS = [
{ id: 'gpt-4o', name: 'GPT-4o', description: 'Great for most tasks' },
{ id: 'o3', name: 'o3', description: 'Uses advanced reasoning' },
{ id: 'o4-mini', name: 'o4-mini', description: 'Fastest at advanced reasoning' },
{ id: 'o4-mini-high', name: 'o4-mini-high', description: 'Great at coding and visual reasoning' },
];
export default function ChatInterface() {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [mode, setMode] = useState<'chat' | 'image'>('chat');
const [selectedModel, setSelectedModel] = useState('gpt-4o');
const [showModelDropdown, setShowModelDropdown] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null);
const textareaRef = useRef<HTMLTextAreaElement>(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
scrollToBottom();
}, [messages]);
// Auto-resize textarea
useEffect(() => {
if (textareaRef.current) {
textareaRef.current.style.height = 'auto';
textareaRef.current.style.height = textareaRef.current.scrollHeight + 'px';
}
}, [input]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!input.trim() || isLoading) return;
const userMessage: Message = {
role: 'user',
content: input,
model: selectedModel
};
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsLoading(true);
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
messages: [...messages, userMessage],
mode: mode,
model: selectedModel,
}),
});
const data = await response.json();
setMessages(prev => [...prev, { ...data, model: selectedModel }]);
} catch (error) {
console.error('Error:', error);
setMessages(prev => [...prev, {
role: 'assistant',
content: 'Sorry, I encountered an error. Please try again.',
model: selectedModel,
}]);
} finally {
setIsLoading(false);
}
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSubmit(e as any);
}
};
const clearConversation = () => {
setMessages([]);
};
return (
<div className="flex flex-col h-screen bg-gray-50">
{/* Header */}
<div className="bg-white border-b border-gray-200 px-4 py-2">
<div className="max-w-3xl mx-auto flex items-center justify-between">
<div className="flex items-center gap-3">
<h1 className="text-lg font-semibold">ChatGPT 4o</h1>
<div className="relative">
<button
onClick={() => setShowModelDropdown(!showModelDropdown)}
className="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-600 hover:text-gray-800 transition-colors"
>
<span>{AVAILABLE_MODELS.find(m => m.id === selectedModel)?.name}</span>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{showModelDropdown && (
<div className="absolute top-full left-0 mt-1 w-80 bg-white rounded-xl shadow-lg border border-gray-200 py-2 z-50">
<div className="px-4 py-2 text-sm text-gray-500">Models</div>
{AVAILABLE_MODELS.map(model => (
<button
key={model.id}
onClick={() => {
setSelectedModel(model.id);
setShowModelDropdown(false);
}}
className={`w-full text-left px-4 py-3 hover:bg-gray-50 flex items-center justify-between group ${
selectedModel === model.id ? 'bg-gray-50' : ''
}`}
>
<div>
<div className="font-medium text-sm">{model.name}</div>
<div className="text-xs text-gray-500">{model.description}</div>
</div>
{selectedModel === model.id && (
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
)}
</button>
))}
<div className="border-t border-gray-200 mt-2 pt-2">
<button className="w-full text-left px-4 py-3 hover:bg-gray-50 flex items-center justify-between text-sm">
<span>More models</span>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</button>
</div>
</div>
)}
</div>
</div>
<button
onClick={clearConversation}
className="p-2 text-gray-600 hover:text-gray-800 transition-colors"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
</button>
</div>
</div>
{/* Messages */}
<div className="flex-1 overflow-y-auto">
<div className="max-w-3xl mx-auto">
{messages.length === 0 && (
<div className="text-center py-24">
<h2 className="text-3xl font-semibold text-gray-900 mb-2">How can I help you today?</h2>
</div>
)}
{messages.map((message, index) => (
<div
key={index}
className={`group ${
message.role === 'user' ? '' : ''
} px-4 py-6`}
>
<div className="max-w-3xl mx-auto flex gap-4">
<div className={`w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 ${
message.role === 'user'
? 'bg-white border border-gray-300'
: 'bg-black text-white'
}`}>
{message.role === 'user' ? (
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
) : (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z"/>
</svg>
)}
</div>
<div className="flex-1 overflow-hidden">
<div className="font-semibold text-sm mb-1">
{message.role === 'user' ? 'You' : 'ChatGPT'}
</div>
{message.type === 'image' ? (
<img
src={message.content}
alt="Generated image"
className="max-w-full rounded-lg shadow-sm"
/>
) : (
<div className="prose prose-sm max-w-none text-gray-800">
<ReactMarkdown remarkPlugins={[remarkGfm]}>
{message.content}
</ReactMarkdown>
</div>
)}
</div>
</div>
</div>
))}
{isLoading && (
<div className="px-4 py-6">
<div className="max-w-3xl mx-auto flex gap-4">
<div className="w-8 h-8 rounded-full bg-black flex items-center justify-center flex-shrink-0">
<div className="flex space-x-1">
<div className="w-1.5 h-1.5 bg-white rounded-full animate-pulse" />
<div className="w-1.5 h-1.5 bg-white rounded-full animate-pulse" style={{ animationDelay: '0.2s' }} />
<div className="w-1.5 h-1.5 bg-white rounded-full animate-pulse" style={{ animationDelay: '0.4s' }} />
</div>
</div>
<div className="flex-1">
<div className="font-semibold text-sm mb-1">ChatGPT</div>
<div className="text-gray-600 text-sm">Thinking...</div>
</div>
</div>
</div>
)}
<div ref={messagesEndRef} />
</div>
</div>
{/* Input Area */}
<div className="border-t bg-white">
<div className="max-w-3xl mx-auto p-4">
<div className="flex gap-2 mb-3">
<button
onClick={() => setMode('chat')}
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all ${
mode === 'chat'
? 'bg-black text-white'
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
}`}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" />
</svg>
Chat
</button>
<button
onClick={() => setMode('image')}
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all ${
mode === 'image'
? 'bg-black text-white'
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
}`}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
Generate Image
</button>
</div>
<form onSubmit={handleSubmit} className="relative">
<textarea
ref={textareaRef}
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={
mode === 'chat'
? 'Message ChatGPT...'
: 'Describe the image you want to generate...'
}
className="w-full px-4 py-3 pr-12 bg-gray-100 border border-gray-200 rounded-2xl resize-none focus:outline-none focus:ring-2 focus:ring-black focus:border-transparent"
rows={1}
style={{ maxHeight: '200px' }}
disabled={isLoading}
/>
<button
type="submit"
disabled={isLoading || !input.trim()}
className={`absolute right-3 bottom-3 p-1.5 rounded-lg transition-all ${
isLoading || !input.trim()
? 'text-gray-300 cursor-not-allowed'
: 'text-white bg-black hover:bg-gray-800'
}`}
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 10l7-7m0 0l7 7m-7-7v18" />
</svg>
</button>
</form>
<p className="text-xs text-gray-500 mt-2 text-center">
{mode === 'chat'
? 'ChatGPT can make mistakes. Check important info.'
: 'AI-generated images may vary from your description.'}
</p>
</div>
</div>
</div>
);
}
این کامپوننت یک رابط چت تمامصفحه با تاریخچه پیامها، سوئیچینگ بین حالتهای چت و تولید تصویر و نشانگر بارگذاری ایجاد میکند. رابط از Tailwind CSS برای استایلینگ استفاده میکند و شامل اسکرول برای نگه داشتن پیامهای جدید است.
بدون هیچگونه پیکربندی پیچیده، هاست NodeJS را در چند ثانیه راهاندازی کنید!
✅ عملکرد بالا ✅ بدون نیاز به پیکربندی ✅ راهاندازی سریع
خرید هاست ابری NodeJS
ادغام رابط چت
فایل src/app/page.tsx را باز کنید و محتویاتش را با این جایگزین کنید:
import ChatInterface from '@/components/ChatInterface';
export default function Home() {
return (
<main className="h-screen">
<ChatInterface />
</main>
);
}
بعد، فایل layout را برای اضافه کردن هدر بهروزرسانی کنید. فایل src/app/layout.tsx را باز کنید و تغییر بدید:
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: 'AI Assistant - Powered by GPT-4o',
description: 'A multimodal AI assistant built with JavaScript',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<header className="bg-white border-b border-gray-200 px-4 py-3">
<div className="max-w-7xl mx-auto flex items-center justify-between">
<h1 className="text-xl font-semibold text-gray-800">
AI Assistant
</h1>
<span className="text-sm text-gray-500">
Powered by GPT-4o
</span>
</div>
</header>
{children}
</body>
</html>
);
}
حال یک خطای سفارشی برای مدیریت خطاهای زمان اجرا به صورت graceful بسازید:
touch src/app/error.tsx
کد مدیریت خطا را اضافه کنید:
'use client';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div className="flex h-screen items-center justify-center">
<div className="text-center">
<h2 className="text-2xl font-semibold text-gray-800 mb-4">
Something went wrong!
</h2>
<p className="text-gray-600 mb-4">{error.message}</p>
<button
onClick={reset}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Try again
</button>
</div>
</div>
);
}
تست دستیار هوش مصنوعی
در این گام، دستیار هوش مصنوعیتان را تست خواهید کرد تا مطمئن شوید درست کار میکند یا خیر.
سرور توسعه را شروع کنید:
npm run dev
به http://localhost:3000 در مرورگرتان برید.

سؤالات متداول
در ادامه به سوالاتی که امکان دارد در این زمینه برای شما بدون پاسخ بماند، جوابهای کوتاه اما مفیدی دادهایم که با استفاده از آن میتوانید به سوال خود پاسخ صحیحی را بدهید.
آیا میتوانم یک دستیار هوش مصنوعی کاملا کارآمد با جاوااسکریپت بسازم؟
بله، کاملا ممکن اسن. با پیشرفتهای کتابخانهها و چارچوبهای جاوااسکریپت، میتوانید از ابزارهایی مانند OpenAI Dialogflow یا Botpress برای ساخت دستیار مکالمهای استفاده کنید تا ورودیهای کاربر را بفهمد و پاسخ بدهد.
بهترین کتابخانه جاوااسکریپت برای آموزش چتبات هوش مصنوعی چیست؟
بهترین کتابخانه بسته به نیازهای پروژه شما دارد. گزینههای محبوب عبارتند از:
- OpenAI: مدلهای متنوعی برای تولید متن، ایجاد تصویر و غیره را ارائه دهد.
- Dialogflow: پلتفرمی توسعهیافته توسط گوگل برای ساخت رابطهای مکالمهای.
- Botpress: چارچوب منبعباز برای ساخت دستیارهای هوش مصنوعی مکالمهای. SDK جاوااسکریپت برای ایجاد چتباتها با ویژگیهای پیشرفته مانند پردازش زبان طبیعی (NLP) و ادغام یادگیری ماشین (ML) دارد.
آیا لازم است از یادگیری ماشین برای آموزش چتبات در جاوااسکریپت استفاده کنم؟
هرچند یادگیری ماشین (ML) میتواند قابلیتهای چتبات را بهتر کند، اما ضروری نیست. میتوانید یک چتبات ساده با سیستمهای مبتنی بر قوانین یا درختهای تصمیمگیری بدون ML بسازید. اما اگر بخواید چتبات پیشرفتهتری بسازید که از تعاملات کاربر یاد بگیرد و با گذشت زمان بهبود پیدا کند، ادغام تکنیکهای ML مانند NLP و یادگیری عمیق مفید خواهد بود.
جمع بندی
در این آموزش، با موفقیت یک دستیار هوش مصنوعی چندرسانهای با جاوااسکریپت ساختید و مستقر کردید که میتواند متن را پردازش کند و تصاویر تولید کند. ادغام APIهای OpenAI با Next.js و React را بررسی کردید و رابط صحیحی شبیه به ChatGPT برای مدیریت مکالمات و تولید تصویر ساختید.
برای گامهای بعدی، میتونید قابلیتهای بیشتری مانند تحلیل تصاویر با بینایی GPT-4o اضافه کنید یا دستیارتان را با سرویسهای دیگه ادغام کنید تا کاربردیتر شود.