ساخت ایجنت هوش مصنوعی متصل به دیتابیس که اطلاعات غلط نمیدهد
الگوی عملی ساخت ایجنت LLM برای کسبوکار: جدا کردن تشخیص نیت از تولید پاسخ، اعتبارسنجی خروجی مدل با اسکیما، پاسخ فقط از داده واقعی، ارجاع به انسان، لاگ و ارزیابی؛ از تجربه ساخت پیجیار.
چتباتهایی که فقط یک پرامپت و یک مدل زبانی هستند، در دمو خوباند و در کار واقعی خطرناک. مشتری میپرسد «این مدل موجود است؟» و چتبات با اطمینان کامل جواب میدهد «بله، با ۱۵ درصد تخفیف»؛ در حالی که نه موجود است و نه تخفیفی در کار است. مدل زبانی وقتی داده ندارد، داده میسازد. در ساخت پیجیار، که به دایرکتهای اینستاگرام فروشگاهها جواب میدهد، کل معماری حول یک قاعده ساخته شد: مدل هیچ واقعیتی را از خودش نمیگوید. این مقاله الگویی است که این قاعده را عملی میکند.
مشکل: یک پرامپت برای همهچیز
سادهترین پیادهسازی این است که پیام مشتری، چند خط درباره فروشگاه و شاید کل فهرست محصولات در یک پرامپت قرار بگیرد و مدل جواب بدهد. سه مشکل دارد. اول، مدل وقتی جواب را در داده نمیبیند، جواب محتمل میسازد. دوم، نمیتوانید بفهمید کجای کار خراب شد: فهم سؤال اشتباه بود یا داده یا نوشتن جواب؟ سوم، با بزرگ شدن کاتالوگ، پرامپت بزرگ، کند و گران میشود و دقت کم.
راهحل، شکستن کار به مرحلههایی است که هر کدام مسئولیت محدود و قابلآزمون دارند.
مرحله ۱: تشخیص نیت و استخراج پارامتر به JSON
در مرحله اول، مدل فقط یک کار دارد: بفهمد مشتری چه میخواهد و پارامترها را استخراج کند. خروجی متن آزاد نیست؛ JSON با ساختار مشخص است: نیت (استعلام قیمت، موجودی، پیگیری سفارش، سؤال عمومی، شکایت، نامشخص) و پارامترها (نام یا کد محصول، رنگ، شماره سفارش).
این خروجی قبل از هر استفادهای با یک اسکیما اعتبارسنجی میشود. اگر مدل نیتی خارج از فهرست برگرداند، یا پارامتر الزامی را نداشت، یا JSON خراب بود، کار به مسیر امن میرود (سؤال روشنکننده یا ارجاع به انسان)، نه اینکه با حدس ادامه پیدا کند. خروجی مدل را همیشه مثل ورودی کاربر، غیرقابل اعتماد در نظر بگیرید؛ تجربهام از یک خطای واقعی در همین زمینه را در یک خط جدید داخل رشته JSON که مقالهها را از بین میبرد نوشتهام.
import { z } from 'zod';
const Intent = z.object({
intent: z.enum(['price', 'stock', 'order_status', 'general', 'complaint', 'unknown']),
product: z.string().max(120).optional(),
color: z.string().max(40).optional(),
orderNo: z.string().regex(/^\d{4,12}$/).optional(),
confidence: z.number().min(0).max(1),
});
export async function understand(message: string) {
const raw = await llm.json({ system: INTENT_PROMPT, user: normalizeFa(message) });
const parsed = Intent.safeParse(raw);
if (!parsed.success || parsed.data.confidence < 0.6) return { intent: 'unknown' as const };
return parsed.data;
}مرحله ۲: داده واقعی، با کد معمولی
مرحله دوم اصلاً به مدل زبانی ربطی ندارد. بر اساس نیت و پارامترها، کد معمولی (همان کوئریها و APIهایی که پنل فروشگاه استفاده میکند) داده واقعی را میگیرد: قیمت فعلی، موجودی هر رنگ، وضعیت سفارش. اگر محصول پیدا نشد یا چند محصول با آن نام بود، این هم یک «واقعیت» است که به مرحله بعد میرود.
این جدایی مهمترین تصمیم معماری است. قیمت و موجودی را کد تعیین میکند، نه مدل. اگر قیمتی اشتباه نمایش داده شد، دنبال باگ در کوئری میگردید، نه در رفتار احتمالاتی یک مدل.
مرحله ۳: نوشتن پاسخ فقط از روی واقعیتها
در مرحله سوم، مدل یک فهرست واقعیت ساختاریافته و پیام اصلی مشتری را میگیرد و فقط باید آنها را به یک پاسخ طبیعی و مؤدبانه فارسی تبدیل کند. دستور صریح دارد که هیچ عدد، قیمت یا وعدهای خارج از واقعیتها ننویسد. و یک بررسی ساده بعد از تولید پاسخ: همه اعداد داخل پاسخ باید در واقعیتها وجود داشته باشند؛ اگر عددی در پاسخ بود که در داده نبود، پاسخ رد میشود.
لحن و طول پاسخ هم اینجا تنظیم میشود: پیام دایرکت باید کوتاه، دوستانه و به زبان محاورهای مؤدب باشد، نه متن رسمی و طولانی.
نکات کلیدی
- قیمت و موجودی فقط از دیتابیس، هیچوقت از مدل.
- هر عدد در پاسخ باید در داده ورودی وجود داشته باشد.
- پاسخ کوتاه، با لحن مخاطب کانال (دایرکت، تلگرام، سایت).
ارجاع به انسان، بخش اصلی طراحی است
ایجنت خوب میداند کجا نباید جواب بدهد. شکایت، درخواست مرجوعی، سؤالهای پزشکی یا حقوقی، مشتری عصبانی، و هر موقعیتی که داده کافی نیست، باید به اپراتور انسانی برسد؛ همراه با خلاصه گفتگو تا اپراتور مجبور نباشد از اول بخواند. در پیجیار این ارجاع یکی از نیتهاست، نه یک استثنا.
قاعده سادهای که پیشنهاد میکنم: هر جا که پاسخ اشتباه هزینه دارد (مالی، اعتبار، رابطه با مشتری)، پیشفرض ارجاع است. اتوماسیون باید کار تکراری و کمریسک را حذف کند، نه تصمیمهای حساس را.
فارسی محاورهای و ورودی واقعی
پیامهای واقعی مشتری با متن تمیز آموزشی فرق دارند: غلط تایپی، فینگلیش، ی و ک عربی، ارقام فارسی و عربی، شکلک و پیامهای چندتکه («سلام» … «این» … «قیمتش؟»). قبل از ارسال به مدل، متن را یکسانسازی کنید (ارقام، ی و ک، فاصلهها) و پیامهای پشتسرهم یک کاربر را در یک بازه کوتاه با هم جمع کنید تا مدل کل منظور را ببیند. درباره یکسانسازی متن فارسی در تایپوگرافی فارسی و RTL بیشتر نوشتهام.
ورکر جدا، صف و تلاش مجدد
فراخوانی مدل زبانی کند است و گاهی شکست میخورد (محدودیت سهمیه، قطعی شبکه، پاسخ خراب). اگر این کار در همان درخواستی انجام شود که پیام را دریافت میکند، هر کندی به کل سیستم سرایت میکند. در پیجیار پیامها در صف قرار میگیرند و یک ورکر جداگانه آنها را پردازش میکند، با تلاش مجدد محدود و فاصلهدار. اگر بعد از چند تلاش هم پاسخی تولید نشد، پیام به اپراتور میرسد؛ هیچ پیامی بیجواب گم نمیشود.
لاگ و ارزیابی: بدون اندازهگیری، بهبودی نیست
برای هر پیام، نیت تشخیصدادهشده، داده گرفتهشده و پاسخ نهایی ذخیره میشود. این لاگ دو کاربرد دارد: وقتی پاسخی اشتباه بود، دقیقاً معلوم است کدام مرحله خطا کرده؛ و مجموعهای از پیامهای واقعی جمع میشود که هر تغییر پرامپت یا مدل را میشود روی آن آزمود.
قبل از هر تغییر در پرامپت یا تعویض مدل، همان مجموعه پیامهای واقعی را دوباره اجرا و نتایج را مقایسه کنید. تغییری که یک سؤال را بهتر میکند، ممکن است ده سؤال دیگر را خراب کند و بدون این آزمون متوجه نمیشوید.
نکات کلیدی
- برای هر پیام: نیت، داده، پاسخ و زمان هر مرحله.
- مجموعه آزمون از پیامهای واقعی (بدون اطلاعات شخصی).
- هر تغییر پرامپت یا مدل، قبل از انتشار روی مجموعه آزمون.
جمعبندی
ایجنت قابل اعتماد یک پرامپت هوشمند نیست؛ یک خط تولید با مرحلههای جداست: تشخیص نیت با خروجی ساختاریافته و اعتبارسنجیشده، گرفتن داده واقعی با کد معمولی، نوشتن پاسخ فقط از واقعیتها، و ارجاع به انسان هر جا که اشتباه هزینه دارد. صف، تلاش مجدد و لاگ کامل، بقیه کار را قابل اعتماد میکنند. این الگو در پیجیار استفاده شده و اگر میخواهید بخشی از پاسخگویی یا کار تکراری کسبوکارتان خودکار شود، اتوماسیون هوش مصنوعی را ببینید.