
چطور کاری کنیم مدل از روی سندهای خودت جواب بدهد
دو صحنه که احتمالاً هر دو را دیدهای. اول: جزوههای یک درس را داری و از دستیارِ هوش مصنوعی میپرسی «توی جزوه دربارهٔ فلان موضوع چی گفته؟» — و جواب میگیری که به هیچ فایلی دسترسی ندارد. دوم، که بدتر است: همان سؤال، و این بار جوابی کامل و مرتب و مطمئن که در هیچ صفحهای از جزوهات وجود ندارد.
هر دو یک ریشه دارند: مدل متنهای تو را ندیده است. آنچه میداند میانگینی از حجمِ عظیمی متنِ عمومی است، و وقتی چیزی را نمیداند سکوت گزینهٔ طبیعیاش نیست (مدلهای زبانی چطور کار میکنند؟). جوابِ بدیهی — «همهاش را کپی کن داخلِ چت» — برای یک صفحه کار میکند و برای پنجاه صفحه نه، به سه دلیلِ جدا از هم.
چرا کپیکردنِ همهچیز جواب نمیدهد
۱. پنجره سقف دارد. سقفی بهنامِ context window که با token اندازهگیری میشود، نه با صفحه. یک صفحهٔ پرِ فارسی حدودِ ۱۵۰۰ تا ۲۵۰۰ نویسه است؛ ۱۲۰ صفحه یعنی حدودِ ۲۵۰٬۰۰۰ نویسه، و چون فارسی با tokenizerهای رایج نزدیکِ ۱٫۳ نویسه در هر توکن است (توکن چیست)، حدودِ ۱۹۰٬۰۰۰ توکن — تخمین، نه اندازهگیری. همان محتوا به انگلیسی حدودِ یکسوم میشود: سقف برای ما سه برابر زودتر میرسد.
۲. هزینه در هر سؤال از نو تکرار میشود. ده سؤال روی یک متن یعنی آن متن ده بار فرستاده و شمرده میشود؛ «یک بار خواند و یادش ماند» وجود ندارد. ۲۰ سؤال روی ۱۹۰٬۰۰۰ توکن یعنی حدودِ ۳٫۸ میلیون توکنِ ورودی؛ با پنج تکهٔ مربوط در هر نوبت (حدودِ ۳۵۰۰ توکن) همان ۲۰ سؤال حدودِ ۷۰٬۰۰۰ توکن میشود: بیش از پنجاه برابر کمتر.
۳. پیدا کردن در انبوه سختتر است. مدلی که پنجاه صفحه جلویش گذاشتهای جواب را کممطمئنتر پیدا میکند از مدلی که یک پاراگرافِ درست جلویش گذاشتهای؛ بارها اندازهاش گرفتهاند و الگو تکرارشونده است — اولِ متن و آخرِ متن بهتر پیدا میشوند و وسط دقت افت میکند. زمینهٔ کوتاه فقط ارزانتر نیست، دقیقتر هم هست.
ایده، در یک جمله
بهجای فرستادنِ همهچیز: اول تکههای مربوط را پیدا کن، بعد سؤال را فقط با همانها بپرس. اسمِ فنیاش RAG است، کوتاهشدهٔ Retrieval-Augmented Generation؛ اسم از خودِ ایده ترسناکتر است.
و نکتهای که بیشترین سوءتفاهم از آن میآید: مدل هیچ تغییری نمیکند — آموزش نمیبیند و چیزی «یاد نمیگیرد». فقط متنی که همراهِ سؤال جلویش میگذاری فرق میکند. کلِ ماجرا یک موتورِ جستوجوست که به یک چت وصل شده.
مکانیزم، گام به گام
تکهکردن (chunking)
سند را باید شکست، چون واحدی که جستوجو میکنی و واحدی که به مدل میدهی یکی است: تکه. و برشِ بد هیچ جوابی نمیدهد — اگر جملهٔ کلیدی وسطِ مرز بیفتد، یک تکه به «تعریفِ گشتاور عبارت است از» ختم میشود و تکهٔ بعدی با فرمول شروع: نیمی از جواب، بیآنکه ناقص بهنظر برسد.
اندازه یک بدهبستانِ واقعی است. تکهٔ بزرگ زمینهٔ کاملتری میدهد ولی جستوجو را کُند میکند، چون تکهای که دربارهٔ چهار موضوع حرف میزند به هیچکدام «خیلی نزدیک» نیست؛ تکهٔ کوچک تیز است ولی ممکن است جواب داخلش جا نشود. برای فارسی ۳۰۰ تا ۱۲۰۰ نویسه با ۱۰ تا ۲۰ درصد همپوشانی نقطهٔ شروعِ معقولی است.
و دو تصمیم که اثرشان از خودِ اندازه بیشتر است: بر اساسِ ساختار ببُر نه شمارشِ نویسه، و مسیرِ سرفصل را به تکه بچسبان — «حداکثر ۳۰ درجه» بهتنهایی بیمعناست:
فصل ۴ › بازوی رباتیک › محدودیتِ زاویهٔ مفصل
حداکثر ۳۰ درجه ...
تبدیل به عدد (embedding)
embedding تابعی است که یک متن میگیرد و یک فهرستِ اعداد با طولِ ثابت برمیگرداند — معمولاً چند صد تا چند هزار عدد — با یک قاعده و فقط همان یکی: متنهایی که دربارهٔ یک موضوعاند، فهرستهای نزدیک به هم میگیرند. مثلِ مختصاتِ روی نقشه، با این تفاوت که بهجای دو عدد چند صد عدد داری و هیچ محوری اسم ندارد.
و اینجا باید صریح بود، چون معمولاً با «هوش مصنوعی میفهمد» رد میشوند: چیزی نمیفهمد. «چرخِ جلو لق میزند» و «لقیِ چرخِ جلو» تقریباً هیچ اشتراکِ حرفی ندارند ولی نزدیکِ هم میافتند، چون در متنِ آموزشی در همسایگیِ کلمههای مشابهی آمدهاند. این هندسهٔ آماریِ همنشینیِ کلمههاست، نه فهم — و برای پیدا کردنِ پاراگرافِ درست کافی است.
محدودیتش هم از همینجا میآید: متنهای متضاد نزدیکِ هم میافتند. «باتری را وصل کن» و «باتری را وصل نکن» تقریباً همسایهاند، چون نفی یک کلمه است و هندسه منطق نیست.
سه قاعده که رعایتنکردنشان همهچیز را بیصدا خراب میکند: تکهها و سؤال باید با یک مدلِ واحد تبدیل شوند (دو مدل یعنی دو دستگاهِ مختصات)؛ اگر مدل عوض شد همه باید از نو تبدیل شوند؛ و مدل باید فارسی دیده باشد — خروجیِ مدلِ چندزبانه و مدلِ انگلیسی هر دو یک فهرستِ عدد است و فقط با آزمایش میفهمی کدام است.
جستوجوی شباهت
رایجترین سنجه cosine similarity است: هر دو فهرست را به طولِ ۱ میرساند، عددهای متناظر را در هم ضرب میکند و جمع میزند؛ حاصل بینِ ۱- و ۱ است و ۱ یعنی همجهت. کلِ جستوجو همین است — بردارِ سؤال را با بردارِ همهٔ تکهها بسنج، مرتب کن، k تای بالا را بردار (معمولاً ۳ تا ۸). یک جستوجوی معمولی است، نه چیزی جادویی: برای چند هزار تکه چند میلیون ضرب، یعنی چند میلیثانیه.
و این جستوجو هیچ آستانهای ندارد مگر خودت بگذاری: top-k همیشه k چیز برمیگرداند، حتی وقتی هیچکدام ربطی به سؤال ندارند. عددِ آستانه هم به مدل بستگی دارد و از مقاله کپی نمیشود؛ امتیازِ پنج تکهٔ برترِ بیست سؤالِ واقعی را چاپ کن و مرزش را در متنِ خودت پیدا کن.
پرامپتِ نهایی
تکههای بازیابیشده و سؤال کنارِ هم میروند. این پرامپت را آدم تایپ نمیکند؛ کدِ تو در لحظه میسازدش:
فقط و فقط بر اساسِ «متنهای زیر» جواب بده.
اگر جواب در این متنها نبود، بنویس: در متنهای دادهشده نبود.
حدس نزن و از دانشِ عمومیات استفاده نکن.
انتهای هر جمله، شمارهٔ متنی را که استفاده کردی داخل [] بگذار.
--- متن 1 | فایل: jozve-fasl-4.pdf | صفحهٔ 31 ---
فصل ۴ › بازوی رباتیک › انتخابِ گیربکس
...
--- متن 2 | فایل: jozve-fasl-2.pdf | صفحهٔ 9 ---
...
سؤال: برای بازوی ربات چه نسبتِ گیربکسی پیشنهاد شده؟
سه تصمیم در همین قالب: دستور اول، سؤال آخر، چون انتهای پنجره تازهترین چیزی است که مدل دیده؛ هر متن یک شناسهٔ صریح دارد، چون بدونِ شناسه «ارجاع بده» یعنی «چیزی شبیهِ ارجاع بنویس»؛ و «اگر نبود بگو نبود» تشریفاتی نیست، چون بدونِ آن پیشفرضِ مدل جوابدادن است. جزئیاتِ این بخش خودش مهارتی مستقل است: پرامپتنویسی.
این روش در برابرِ fine-tuning
«چرا مدل را روی دادههای خودم آموزش ندهم؟» این کار اسم دارد — fine-tuning — و جوابِ درست این نیست که بد است، این است که کارِ دیگری میکند: fine-tuning شکل را یاد میدهد، بازیابی محتوا را میرساند. لحن و قالب و سبک آموختنیاند؛ «حداکثر چند روز است» یک واقعیت است، نه یک الگو.
آزمونِ قاطع، تغییرِ یک واقعیت است. در بازیابی یک فایل را ویرایش میکنی و همان یکی را دوباره تکه و تبدیل: چند دقیقه. در fine-tuning باید دادهٔ آموزشی را بازسازی کنی و از نو آموزش بدهی — و واقعیتِ قدیمی هم مطمئناً پاک نشده، چون در وزنها حل شده. ضمناً مدلِ fine-tuneشده نمیتواند منبع بدهد، و صدها تا هزاران نمونهٔ آموزشی میخواهد که ساختنشان از خودِ آموزش گرانتر تمام میشود.
ولی تصویر بدونِ طرفِ دیگر ناقص است. fine-tuning جوابِ درست است وقتی شکلِ خروجی باید هر بار دقیقاً یکسان باشد و هیچ پرامپتی تحمیلش نمیکند؛ وقتی میخواهی مدلی کوچکتر کارِ محدودی را در حدِ مدلی بزرگتر انجام دهد؛ یا وقتی سبک واقعاً ناآشناست، مثلِ یک قراردادِ برچسبگذاریِ داخلی. پس رقیبِ هم نیستند — fine-tuning برای شکل، بازیابی برای واقعیت — و اشتباهِ گرانِ رایج دقیقاً وارونهاش است.
چه چیزی را درست میکند و چه چیزی را نه
وقتی متنِ درست جلوی مدل باشد جوابهای ساختگی محسوس کم میشوند، چون مدل دیگر مجبور نیست شکاف را از دانشِ عمومیاش پر کند. ولی از بین نمیروند، و سه مسیرِ خطا میماند: بازیابی چیزی پیدا نکرده و مدل با همان لحنِ مطمئن از حافظهاش جواب میدهد؛ بازیابی متنِ اشتباه آورده و مدل صادقانه از رویش جواب میدهد (بدترین حالت، چون جوابِ غلط همراهِ یک ارجاع میرسد و شبیهِ چیزی است که تأیید شده)؛ یا متن درست بوده و مدل بد خواندهاش — یک نفی را ندیده، یک شرط را نادیده گرفته، یا سطرِ جدول را از ستونِ اشتباه خوانده.
پس دو عادت، هیچکدام اختیاری. جملهٔ «اگر در متنها نبود، بگو نبود» مسیرِ اول را بهشدت کم میکند ولی تضمینش نمیکند؛ یک دستور یک درخواست است، نه یک قفل. و هر جواب باید منبعش را با خودش بیاورد — مکانیزمِ ایمنیِ واقعی همین است: نه چون مدل صادق میشود، بلکه چون تو در پنج ثانیه میتوانی چک کنی؛ و ارجاعی که نشود بازش کرد ارجاع نیست.
پس آن ادعای رایج که «RAG مشکلِ توهم را حل میکند» جایی غلط است که اهمیت دارد: خطا را از «ساختگی» به «متنِ اشتباه» منتقل میکند، و نوعِ دوم چون ارجاع دارد راحتتر باور میشود — توهم در هوش مصنوعی.
یک مثالِ کامل
جزوههای یک ترم: ۸ فایل، حدودِ ۱۲۰ صفحه.
ورودی: 8 فایل، ~120 صفحه -> ~250,000 نویسه
تکهکردن: ~900 نویسه، 150 همپوشانی -> ~330 تکه
هر تکه -> یک بردار 768 عددی
حافظهٔ لازم: 330 × 768 × 4 بایت ≈ 0.97 مگابایت
۷۶۸ طولِ متعارفِ بردار است نه یک الزام، و ۴ بایت اندازهٔ یک عددِ اعشاری. نتیجه: کلِ «پایگاهِ دانش» زیرِ یک مگابایت است — یک فایل و یک حلقه کافی است. هر تکه اینشکلی ذخیره میشود:
id: jozve-04#chunk-137
file: jozve-fasl-4.pdf
page: 31
heading: فصل ۴ › بازوی رباتیک › انتخابِ گیربکس
text_raw: <متنِ اصلی، دستنخورده — همین به مدل داده میشود>
text_norm: <متنِ یکدستشده — فقط برای جستوجو>
vector: [0.021, -0.114, 0.008, ...] (768 عدد)
سؤال که میرسد — «برای بازوی ربات چه نسبتِ گیربکسی پیشنهاد شده؟» — با همان تابعِ یکدستسازی یکدست میشود، با همان مدل به بردار تبدیل میشود، با هر ۳۳۰ بردار سنجیده میشود، و پرامپتِ بالا با text_rawِ پنج تکهٔ برتر ساخته میشود. و اگر جواب در جزوه نباشد، خروجیِ درست «در متنهای دادهشده نبود» است — که شکست نیست، دقیقاً چیزی است که برایش زحمت کشیدهای.
فارسی: بخشی که تقریباً هیچجا نوشته نمیشود
کلِ این مسیر در پایینترین لایه مقایسهٔ رشتهها و بردارهاست. و در فارسی یک کلمهٔ واحد بهطورِ روزمره چند رشتهٔ متفاوت است:
"میروم" و "میروم" و "می روم" -> سه رشتهٔ متفاوت
"کتاب" (ک فارسی) و "كتاب" (ك عربی) -> دو رشته، یک شکل روی صفحه
"خانهٔ" و "خانهی" و "خانه ی" -> سه رشتهٔ متفاوت
"۱۲۳" و "١٢٣" و "123" -> سه رشتهٔ متفاوت
سطرِ دوم بدترین است چون با چشم دیده نمیشود: «ی» و «ک»ِ فارسی با «ي» و «ك»ِ عربی روی صفحه تقریباً یکشکلاند. متنی که از چند منبع جمع شده تقریباً همیشه هر دو را دارد، و صفحهکلیدِ گوشیِ کسی که سؤال را تایپ میکند هم ممکن است نسخهٔ عربی بفرستد.
پیش از تکهکردن، اینها را یکدست کن
NFKC (نرمالسازیِ یونیکد) -> شکلهای نمایشی به حرفِ پایه
ي U+064A (عربی) -> ی U+06CC (فارسی)
ك U+0643 (عربی) -> ک U+06A9 (فارسی)
١٢٣ / ۱۲۳ / 123 -> یک شکلِ واحد
ـ U+0640 (کشیده) و اعراب -> حذف
U+00A0 و فاصلهٔ تکراری -> یک فاصلهٔ معمولی
U+200C (نیمفاصله) -> یک تصمیمِ صریح (پایین)
NFKC حروفِ جدا-جدای خروجیِ PDF را به حرفِ پایه برمیگرداند ولی نویسهٔ عربی را به فارسی تبدیل نمیکند. و تلهٔ PDF پرتکرارترین دلیلِ خرابی است: استخراج از PDFهای فارسی مرتب حروفِ جدا، ترتیبِ وارونهٔ کلمات، یا ناپدیدشدنِ نیمفاصلهها تولید میکند. دویست نویسه از متنِ استخراجشده را چاپ کن و با چشمِ خودت بخوان — وگرنه ممکن است ساعتها اندازهٔ تکه را تنظیم کنی در حالی که ورودی از اول خراب بوده.
دو نسخه از هر متن، و نیمفاصله
یکدستسازی برای پیدا کردن است، نه برای نشاندادن. پس text_norm را در جستوجو بهکار ببر و text_raw را در پرامپت بگذار. و بندِ دوم، که پرتکرارترین باگِ این حوزه است: هر تابعی که روی تکهها اجرا کردی، بیکموکاست روی سؤال هم اجرا کن — این خطا هیچ پیامی نمیدهد؛ فقط کیفیتِ جوابها بیدلیل پایین میآید.
نیمفاصله سختترین موردِ فهرست است. برای جستوجوی کلیدواژهای حذفِ کامل همهٔ املاها را روی یک شکل جمع میکند؛ برای embedding بهتر است املا را یکدست و درست کنی، چون مدل روی فارسیِ درست آموزش دیده. تفاوتشان هم این است که جستوجوی کلیدواژهای قاطع شکست میخورد — «كتاب» با کافِ عربی هرگز با «کتاب» تطبیق نمیخورد — ولی embedding تنزلِ نرم دارد.
خرابیهایی که باید انتظارشان را داشته باشی
۱. بازیابی چیزِ مربوطی برنمیگرداند. جواب کلی است، یا مدل میگوید در متن نبود در حالی که هست. یا کلمههای سؤال با کلمههای سند همپوشانی ندارند (کاربر میپرسد «چرا داغ میکنه؟» و جزوه نوشته «اتلافِ توانِ حرارتی در درایور»)، یا سؤال حولِ یک شناسهٔ دقیق است — شمارهٔ ماده، کدِ قطعه — که embedding در تفکیکش ضعیف است. چه کار کنی: تکههای بازیابیشده را چاپ کن؛ کنارِ جستوجوی برداری یک جستوجوی کلیدواژهایِ ساده هم بگذار و نتایج را ادغام کن، چون همین ترکیب کدها و شمارهها را میگیرد؛ و سؤال را دو سه جور بازنویسی کن و هرکدام را جدا جستوجو کن.
۲. جواب درست است، ارجاع غلط. یا مدل چند متن گرفته، از یکی فهمیده و به دیگری ارجاع داده؛ یا — جدیتر — از حافظهٔ خودش جواب داده و نزدیکترین ارجاع را چسبانده تا دستورِ تو را راضی کند. چه کار کنی: به هر متن شناسهٔ صریح بده، و از هر چند جواب یکی را باز کن و بخوان. اگر جواب درست بود ولی هیچکدام از متنها آن را نداشتند، این ایرادِ قالببندی نیست: مدل از حافظهاش جواب داده — این بار درست درآمده، دفعهٔ بعد نه.
۳. تکه از جواب کوچکتر است. جوابهای نصفه: تعریفی بدونِ فرمولش، «شرایط عبارتاند از:» و بعد هیچ، جدولی که نیمی از سطرهایش نیست. علتش برشِ مبتنی بر عدد است که از وسطِ یک ساختار رد شده. چه کار کنی: روی ساختار ببُر؛ جدول و فهرست را هرگز نصف نکن؛ و یک ترفندِ ارزان — همسایهها را هم بردار: وقتی تکهٔ ۱۳۷ انتخاب شد، ۱۳۶ و ۱۳۸ را هم به پرامپت اضافه کن.
۴. سؤال به دو سند همزمان نیاز دارد. «کدامشان ارزانتر است؟» یا «فرقِ روشِ فصل ۲ با فصل ۵ چیست؟» — جوابی میگیری که مطمئن بهنظر میرسد و فقط یک طرف را پوشش داده، چون top-k تمایل دارد k تکه دربارهٔ یک چیز برگرداند: شبیهترین تکهها به یک سؤالِ مقایسهای معمولاً چند پاراگراف دربارهٔ همان طرفی است که در سند بیشتر توضیح داده شده. چه کار کنی: سؤال را بشکن و برای هر طرف جداگانه بازیابی کن؛ یا از انتخابی استفاده کن که در هر گام تکهای را برمیدارد که هم مربوط باشد و هم با انتخابهای قبلی متفاوت (MMR). و بدان که سؤالی مثلِ «چند بار به فلان موضوع اشاره شده؟» ساختاراً جواب نمیگیرد: جوابش به همهٔ سندها نیاز دارد و بازیابی ۵ تکه از ۳۳۰ تکه را میبیند.
از کجا شروع کنی
۱. با ده صفحه شروع کن، نه کلِ آرشیو — و متنی که خودت خوب میشناسی؛ تا نتوانی درستیِ جواب را قضاوت کنی، نمیفهمی سامانهات خوب کار میکند یا بد.
۲. متنِ استخراجشده را با چشمِ خودت ببین: دویست نویسه چاپ کن و نیمفاصلهها، «ی» و «ک»، و ترتیبِ کلمهها را چک کن. اگر اینجا خراب باشد، هیچ تنظیمِ بعدی نجاتش نمیدهد.
۳. تابعِ یکدستسازی را بنویس، با دو آزمون — سه املای «میروم» یکی شوند، و «کتاب» با کافِ فارسی و عربی یکی — و همان تابع را روی سؤال هم اجرا کن.
۴. ساده تکه کن، ولی سرفصل را به هر تکه بچسبان.
۵. مدلِ embedding را همان اول امتحان کن: یک جمله، بازنویسیِ همان جمله با کلمههای متفاوت، و یک جملهٔ بیربط. اگر بازنویسی نزدیکتر نبود، پیش از هر کارِ دیگری مدل را عوض کن.
۶. جستوجو را با یک حلقه بنویس، نه با پایگاهِ دادهٔ برداری.
۷. بیست سؤالِ واقعی با جوابِ درستشان بنویس. تنها گامی که نظر را به اندازهگیری تبدیل میکند. بعد دو چیز را جدا بسنج: بازیابی متنِ درست را آورد؟ مدل از رویش درست جواب داد؟ اگر ندانی کدام خراب بوده، کورکورانه تنظیم میکنی.
۸. بهینهسازیها را آخر بگذار — جستوجوی ترکیبی، بازچینش، بازنویسیِ سؤال، تنوعِ نتایج؛ هرکدام فقط وقتی ارزش دارند که اندازه گرفته باشی مشکلی که حل میکنند واقعاً مشکلِ توست.
و در آخر
آنچه این روش عوض میکند مدل نیست، سؤال است: جای «از هرچه میدانی جوابِ این را بده»، میگویی «این چند پاراگراف را بخوان و فقط از روی همینها جواب بده». همین جابهجایی جواب را از یک حدسِ باورپذیر به چیزی تبدیل میکند که میشود بازش کرد و دید از کجا آمده. و تقریباً همهٔ کیفیتِ نهایی در بخشِ خستهکنندهاش تعیین میشود: اینکه متن چطور استخراج شده، چطور بریده شده، و چقدر یکدست است.
اگر میخواهی این مسیر را قدمبهقدم و با تمرین جلو بروی، دورهٔ ژرفا همین موضوع را در عمق باز میکند؛ و اگر هنوز مطمئن نیستی از کجای هوش مصنوعی شروع کنی، نقشهٔ راهِ یادگیری هوش مصنوعی ترتیبِ پیشنهادی را میدهد.


