با استفاده از Next.js و OpenAI Text Completion یک پرسش و پاسخ ساده با هوش مصنوعی بسازید
TL; DR
این اجرای API و اجرای فرانت اند در Github را بررسی کنید.
معرفی
OpenAI یک پلتفرم هوش مصنوعی قدرتمند است که به توسعه دهندگان اجازه می دهد تا برنامه های هوشمند ایجاد کنند. Next.js یک چارچوب محبوب React است که رندر سمت سرور و سایر ویژگی های پیشرفته را ارائه می دهد. در این وبلاگ، من یک برنامه پرسش و پاسخ ساده با استفاده از OpenAI و Next.js ایجاد خواهم کرد.
API تکمیل متن OpenAI از هوش مصنوعی برای تولید متنی شبیه به انسان بر اساس یک دستور داده شده استفاده میکند و آن را به ابزاری قدرتمند برای کارهایی مانند تولید نوشتن خلاقانه، ساخت رباتهای گفتگو و ایجاد مدلهای زبان تبدیل میکند. پردازش زبان پیشرفته و منابع دادهای گسترده آن را قادر میسازد تا متنی را تولید کند که بسیار شبیه به نوشتههای انسان است و آن را به منبعی ارزشمند برای کسبوکارها و توسعهدهندگان تبدیل میکند.
پیش نیازها
قبل از شروع، باید درک اولیه ای از React و Next.js داشته باشید. همچنین باید یک کلید OpenAI API داشته باشید. اگر کلید OpenAI API ندارید، می توانید برای یک حساب کاربری رایگان در وب سایت آنها ثبت نام کنید.
من فقط از TypeScript و TailwindCSS برای این پروژه استفاده خواهم کرد، اما شما می توانید از جاوا اسکریپت و هر چارچوب CSS که ترجیح می دهید استفاده کنید.
راه اندازی پروژه
یک پروژه NextJS را راه اندازی کنید
شما می توانید یک پروژه را همانطور که در مستندات رسمی NextJS پیشنهاد شده است ایجاد کنید.
npx create-next-app@latest
# or
yarn create next-app
اما اگر مانند من هستید که ترجیح می دهید TypeScript و Tailwind داشته باشید، می توانید به جای آن این دیگ بخار را شبیه سازی کنید.
npx degit codegino/nextjs-tailwind-slim-boilerplate
Backend API را ایجاد کنید
یک کلاینت OpenAI قابل استفاده مجدد ایجاد کنید
// src/libs/openai.ts
import {Configuration, OpenAIApi} from 'openai';
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});
export const openai = new OpenAIApi(configuration);
هرگز کلید API خود را به مخزن خود متعهد نکنید. تو می توانی
.env
خارج از جعبه برای ذخیره کلید API خود.
یک کنترلر NextJS API ایجاد کنید
ما فقط برای رسیدگی به درخواستهای POST به API نیاز داریم، بنابراین اگر روش درخواست POST نباشد، میتوانیم خطا را برگردانیم.
ما می توانیم استفاده کنیم OpenAI
مشتری برای ایجاد یک تکمیل با استفاده از createCompletion
روش. ما می توانیم تنظیم کنیم prompt
به سوال، و مدل به text-davinci-003
. این temperature
و max_tokens
را می توان برای به دست آوردن نتایج متفاوت تنظیم کرد. ما می توانیم تنظیم کنیم n
به 1
تا فقط یک نتیجه به دست آورید (همچنین برای صرفه جویی در پول).
// src/pages/api/completion.ts
import {NextApiRequest, NextApiResponse} from 'next';
import {openai} from '../../libs/openai';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const {prompt} = req.body;
if (req.method !== 'POST') {
return res.status(400).json({error: 'Invalid request'});
}
const response = await openai.createCompletion({
prompt,
model: 'text-davinci-003',
temperature: 0.6,
max_tokens: 100,
n: 1,
});
return res.status(200).json({
data: response.data
});
}
برنامه را با استفاده از yarn dev
. نقطه پایانی جدید ایجاد شده را با استفاده از curl، Postman، Hoppscotch یا هر چیزی که ترجیح می دهید، آزمایش کنید.
ما باید داده های پاسخ را از OpenAI ببینیم.
پاسخ API را به روز کنید
از آنجایی که ما به کل پاسخ OpenAI نیاز نداریم، میتوانیم پاسخ را ساده کنیم تا فقط دادههای مورد نیاز خود را برگردانیم. زیرا تعداد نتایج را تنها با استفاده از یک عدد تنظیم می کنیم n: 1
، میتوانیم پاسخ را ساده کنیم تا فقط انتخاب اول را استخراج کنیم.
همچنین، میتوانیم خطوط جدید را از ابتدای پاسخ با استفاده از عبارت حذف کنیم replace
روش.
// src/pages/api/completion.ts
import {NextApiRequest, NextApiResponse} from 'next';
import {openai} from '../../libs/openai';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const {prompt} = req.body;
if (req.method !== 'POST') {
return res.status(400).json({error: 'Invalid request'});
}
const response = await openai.createCompletion({
prompt,
model: 'text-davinci-003',
temperature: 0.6,
max_tokens: 100,
n: 1,
});
- return res.status(200).json({
- data: response.data
- });
+ const firstResponse = response.data.choices[0];
+ return res.status(200).json({
+ data: {
+ ...firstResponse,
+ text: firstResponse.text.replace(/^\n+/, ''),
+ },
+ });
}
پاسخ باید برای فرانت اند ساده تر باشد
فرم سوال را ایجاد کنید
متغیرهایی برای ذخیره سوال و پاسخ ایجاد کنید
// src/pages/index.tsx
const [prompt, setPrompt] = useState('');
const [response, setResponse] = useState('');
هنگامی که کاربر روی دکمه کلیک می کند یک کنترل کننده عملکرد ایجاد کنید
// src/pages/index.tsx
const handleSubmit = async e => {
e.preventDefault();
const res = await fetch('/api/completion', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({prompt: prompt.trim()}),
}).then(res => res.json());
setResponse(res.data.text);
فرم را با یک ورودی و یک دکمه ارسال ایجاد کنید
// src/pages/index.tsx
return (
<div className="max-w-md mx-auto pt-20">
<h1 className="text-3xl font-bold text-center mb-6 bg-white">Ask Me Anything!</h1>
<form onSubmit={handleSubmit} className="bg-white shadow-md rounded p-8">
<div className="mb-4">
<label htmlFor="prompt" className="block text-gray-700 text-sm font-bold mb-2">
What do you want to know?
</label>
<input id="prompt" type="text" name="prompt" value={prompt} autoComplete="off"
onChange={e => setPrompt(e.target.value)} placeholder="How to ask a question?"
className="shadow border rounded w-full py-2 px-3 text-gray-700"
/>
</div>
<div className="flex gap-2">
<button type="submit" className="bg-blue-500 text-white font-bold py-2 px-4 rounded">
Ask
</button>
</div>
</form>
</div>
);
پاسخ را در صورت وجود ارائه دهید
// src/pages/index.tsx
return (
<div className="max-w-md mx-auto pt-20">
<h1 className="text-3xl font-bold text-center mb-6 bg-white">Ask Me Anything!</h1>
<form onSubmit={handleSubmit} className="bg-white shadow-md rounded p-8">
<div className="mb-4">
<label htmlFor="prompt" className="block text-gray-700 text-sm font-bold mb-2">
What do you want to know?
</label>
<input id="prompt" type="text" name="prompt" value={prompt} autoComplete="off"
onChange={e => setPrompt(e.target.value)} placeholder="How to ask a question?"
className="shadow border rounded w-full py-2 px-3 text-gray-700"
/>
</div>
+ {response && (
+ <div className="mb-4">
+ <h2 className="font-bold">Response</h2>
+ <p className="pl-2 text-gray-700 whitespace-pre-line">{response}</p>
+ </div>
+ )}
<div className="flex gap-2">
<button type="submit" className="bg-blue-500 text-white font-bold py-2 px-4 rounded">
Ask
</button>
</div>
</form>
</div>
);
(اختیاری) سبک جهانی را به صفحه اضافه کنید
// src/styles/tailwind
body {
background-image: radial-gradient(blue 0.1px, transparent 0.5px);
background-size: 10px 10px;
}
کل جزء باید به این شکل باشد
// src/pages/index.tsx
import {useState} from 'react';
const IndexPage = () => {
const [prompt, setPrompt] = useState('');
const [response, setResponse] = useState('');
const handleSubmit = async e => {
e.preventDefault();
const res = await fetch('/api/completion', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({prompt: prompt.trim()}),
}).then(res => res.json());
setResponse(res.data.text);
};
return (
<div className="max-w-md mx-auto pt-20">
<h1 className="text-3xl font-bold text-center mb-6 bg-white">Ask Me Anything!</h1>
<form onSubmit={handleSubmit} className="bg-white shadow-md rounded p-8">
<div className="mb-4">
<label htmlFor="prompt" className="block text-gray-700 text-sm font-bold mb-2">
What do you want to know?
</label>
<input id="prompt" type="text" name="prompt" value={prompt} autoComplete="off"
onChange={e => setPrompt(e.target.value)} placeholder="How to ask a question?"
className="shadow border rounded w-full py-2 px-3 text-gray-700"
/>
</div>
{response && (
<div className="mb-4">
<h2 className="font-bold">Response</h2>
<p className="pl-2 text-gray-700 whitespace-pre-line">{response}</p>
</div>
)}
<div className="flex gap-2">
<button type="submit" className="bg-blue-500 text-white font-bold py-2 px-4 rounded">
Ask
</button>
</div>
</form>
</div>
);
};
export default IndexPage;
در اینجا نشانه گذاری به نظر می رسد:
و در اینجا خروجی زمانی است که کاربر فرم را ارسال می کند:
نکته: می توانید از باد دم استفاده کنید
whitespace-pre-line
یا CSS وانیلیwhite-space: pre-line;
سبک برای حفظ خطوط جدید در پاسخ.
کمی UX را بهبود ببخشید
میتوانیم حالت بارگیری را به دکمه اضافه کنیم و فیلد ورودی را در حین پردازش درخواست غیرفعال کنیم. همچنین، به جای اسپم کردن Ask
را فشار دهید، ما می توانیم a اضافه کنیم Try Again
دکمه برای تنظیم مجدد فرم. به این ترتیب می توانیم برخی از اعتبارات ارزشمند OpenAI خود را ذخیره کنیم.
// src/pages/index.tsx
import {useState} from 'react';
const IndexPage = () => {
const [response, setResponse] = useState('');
const [prompt, setPrompt] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async e => {
e.preventDefault();
setLoading(true);
setResponse('');
const res = await fetch('/api/completion', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({prompt: prompt.trim()}),
}).then(res => res.json());
setResponse(res.data.text);
setLoading(false);
};
const handleTryAgain = () => {
setResponse('');
setPrompt('');
};
return (
<div className="max-w-md mx-auto pt-20">
<h1 className="text-3xl font-bold text-center mb-6 bg-white">
Ask Me Anything!
</h1>
<form onSubmit={handleSubmit} className="bg-white shadow-md rounded p-8">
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="prompt"
>
What do you want to know?
</label>
<input id="prompt" type="text" name="prompt" value={prompt}
onChange={e => setPrompt(e.target.value)} placeholder="How to ask a question?"
autoComplete="off" disabled={!!response || loading}
className="shadow border rounded w-full py-2 px-3 text-gray-700"
/>
</div>
{response && (
<div className="mb-4">
<h2 className="font-bold">Response</h2>
<p className="pl-2 text-gray-700 whitespace-pre-line">{response}</p>
</div>
)}
<div className="flex gap-2">
{response ? (
<button
onClick={handleTryAgain}
className="bg-blue-500 text-white font-bold py-2 px-4 rounded"
>
Try again
</button>
) : (
<button
type="submit"
disabled={!!loading || prompt.length < 5}
className="bg-blue-500 text-white font-bold py-2 px-4 rounded disabled:opacity-50"
>
{loading ? 'Thinking...' : 'Ask'}
</button>
)}
</div>
</form>
</div>
);
};
export default IndexPage;
و در اینجا یک تجربه کمی بهتر است:
کد منبع
کد منبع این پروژه در GitHub موجود است
نتیجه
در این پست وبلاگ، من نشان دادم که چقدر ساده است که از تکمیل متن OpenAI با Next.js استفاده کنید. ما نحوه ایجاد یک مسیر API را در Next.js، نحوه ایجاد یک درخواست API به OpenAI API با استفاده از openai
کتابخانه، و یک فرم سمت کلاینت برای پذیرش درخواستها و نمایش پاسخ از API ایجاد کنید. و این سه مرحله تمام چیزی است که ما برای ایجاد یک برنامه عالی تر نیاز داریم.
بعد چه است
- تولید تصویر با OpenAI
- پاسخ ها را در پایگاه داده ذخیره کنید