پرش به محتوای اصلی
پرش به محتوای مقاله

پیاده‌سازی چت‌بات محلی RAG با .NET 8 و Ollama در ۳۰ دقیقه

·۱ تیر ۱۴۰۵۱۲ دقیقه مطالعه
راهنما
ساخت چت‌بات RAG محلی با .NET 8، Ollama و React در ۳۰ دقیقه
ساخت چت‌بات RAG محلی با .NET 8، Ollama و React در ۳۰ دقیقه
اشتراک‌گذاری
واقعاً چه چیز جدید است؟

حذف کامل لایه‌ی دیتابیس برداری (Vector DB) و جایگزینی آن با محاسبات Brute-Force در حافظه برای مقیاس‌های کوچک، که سرعت پیاده‌سازی RAG را به زیر ۳۰ دقیقه می‌رساند.

تصور کنید تمام اسناد محرمانه شرکت را بدون ترس از نشت داده یا پرداخت هزینه‌های ماهانه، در اختیار یک دستیار هوشمند قرار دهید. اگر اکنون برای هر پرسش از مدل‌های ابری هزینه پرداخت می‌کنید، این معماری محلی می‌تواند صورت‌حساب شما را به طور کامل حذف کند.

طبق گزارش منتشر شده در ۲۲ ژوئن ۲۰۲۶، آویناش زالا (Avinash Zala)، مهندس ارشد، مستندات فنی یک سیستم را منتشر کرد که با ترکیب .NET 8، Ollama و React، امکان پرس‌وجو از فایل‌های PDF را بدون نیاز به اتصال به اینترنت فراهم می‌کند. او برای اثبات کارایی، یک سند ۴۰ صفحه‌ای از مشخصات API داخلی را آپلود کرد و در مورد محدودیت نرخ درخواست‌ها (Rate Limit) برای اندپوینت جست‌وجو سؤال کرد. سیستم در حدود ۳ ثانیه پاسخ داد: «۱۰۰ درخواست در دقیقه برای هر کلید API، با امکان افزایش لحظه‌ای (Burst) تا ۲۰۰ درخواست»، و دقیقاً به بخش ۴.۲ سند ارجاع داد.

بیشتر ابزارهای «چت با PDF» امروزی، اسناد را به سرورهای OpenAI یا گوگل می‌فرستند. این رویکرد سه مشکل حیاتی ایجاد می‌کند: هزینه‌های بالای API، احتمال نشت داده‌های حساس و پدیده «گم‌شدن در میان» (lost-in-the-middle) که در آن مدل‌ها جزئیات را در پنجره‌های متنی بزرگ فراموش می‌کنند. به عنوان مثال، اگر یک تیم ۵۰ بار در روز از یک ابزار ابری استفاده کند، هزینه‌ها می‌تواند به ۴۵ دلار در ماه برای هر کاربر برسد. علاوه بر این، فایل‌های PDF اغلب حاوی داده‌های مشتریان، قیمت‌گذاری‌های داخلی یا ویژگی‌های منتشر نشده‌ای هستند که هرگز نباید وارد خط لوله آموزشی مدل‌های ابری شوند.

در همین راستا، همان‌طور که در تحلیل‌های پیشین ما درباره‌ی امنیت مدل‌های بازمتن اشاره کردیم، انتقال پردازش به لبه (Edge) تنها راه تضمین حریم خصوصی است. این رویکردی است که در به‌روزرسانی‌های اخیر برای پایداری عملیاتی AI و امنیت React نیز بر اهمیت کنترل لایه‌های دسترسی تأکید شده بود. سیستم تولید بازیابی‌افزا (RAG) — شبیه دانش‌آموزی که قبل از جواب دادن، اول کتاب درسی را باز می‌کند و از آن نقل می‌آورد — این مشکل را حل می‌کند. در این روش، تنها ۳ تا ۵ تکه از مرتبط‌ترین متون به مدل ارسال می‌شود که باعث افزایش ۱۰۰ برابری بهره‌وری هزینه و امنیت می‌گردد. هدف نهایی شفاف است: آپلود PDF، پرسیدن سؤال، و دریافت پاسخ با ارجاعات در کمتر از ۵ ثانیه، بدون خروج داده از لپ‌تاپ و بدون صورت‌حساب ماهانه.

معماری محلی سیستم

این سیستم کاملاً روی localhost اجرا می‌شود و هیچ وابستگی ابری ندارد. معماری از سه بخش تشکیل شده است:

  • Ollama: روی پورت http://localhost:11434 گوش می‌دهد.
  • .NET API: روی پورت http://localhost:5000 فعال است.
  • React Dev Server: روی پورت http://localhost:5173 اجرا می‌شود.

هیچ داده‌ای از دستگاه خارج نمی‌شود. تنها تماس شبکه خارجی، دریافت وابستگی‌های npm برای React است که قابلیت کش کردن برای استفاده آفلاین را دارد. جریان کامل فرآیند از آپلود PDF شروع شده، از طریق PdfService (با استفاده از کتابخانه PdfPig) پردازش می‌شود، به یک VectorStore (در حافظه) منتقل می‌گردد و با بهره‌گیری از EmbeddingService (از طریق اندپوینت /embed اولاما) و ChatService (خط لوله RAG)، پاسخ نهایی را از طریق رابط کاربری React و مدل llama3.2 ارائه می‌دهد.

استخراج و تکه‌بندی PDF

استخراج متن توسط PdfPig انجام می‌شود؛ یک کتابخانه PDF خالص C# که هیچ وابستگی بومی (Native) ندارد. این خط لوله شامل دو سرویس است: PdfService برای استخراج و تکه‌بندی، و EmbeddingService برای برداری‌سازی نتایج. متد ExtractAndChunk وظیفه مدیریت تبدیل جریان خام (Raw Stream) به تکه‌های پردازش شده را بر عهده دارد.

زالا برای مدیریت متون از استراتژی «تکه‌بندی مبتنی بر کلمه» به جای کاراکتر یا توکن استفاده کرده است تا اندازه قطعات پیش‌بینی‌پذیر باشد. این کار توسط متد ChunkText انجام می‌شود که متن را با استفاده از StringSplitOptions.RemoveEmptyEntries بر اساس فضای خالی (Space) تقسیم می‌کند تا ورودی‌ها قبل از پردازش پاک‌سازی شوند.

  • اندازه تکه (Chunk Size): ۵۰۰ کلمه (حدود ۶۵۰ توکن). این مقدار کاملاً در محدوده ورودی مدل بردارساز قرار می‌گیرد. زالا اشاره می‌کند که اگرچه تکه‌بندی آگاه از توکن (Token-aware) «صحیح‌تر» است، اما نیاز به یک وابستگی اضافی (Tokenizer) دارد؛ برای پنجره متنی ۸ هزار توکنی مدل nomic-embed-text، تکه‌بندی مبتنی بر کلمه یک جایگزین «ساده‌لوحانه اما مؤثر» است.
  • هم‌پوشانی (Overlap): ۵۰ کلمه. این بخش حیاتی است؛ زیرا تضمین می‌کند زمانی که یک جمله کلیدی در مرز دو تکه قرار گرفته، هر دو تکه شامل «کلمات پل» باشند. این امر اجازه می‌دهد شباهت کسینوسی هر دو طرف را شناسایی کند و از دست رفتن پاسخ‌هایی که بین دو تکه تقسیم شده‌اند جلوگیری می‌کند.

برداری‌سازی محلی

بخش EmbeddingService در واقع یک پوشش (Wrapper) سبک دور اندپوینت /api/embeddings در اولاما است. پیاده‌سازی آن بسیار ساده است: استفاده از یک شیء EmbeddingRequest (برای تعریف مدل و پرامپت) و تجزیه EmbeddingResponse برای بازگرداندن یک آرایه اعشاری (float[]).

این سرویس از مدل nomic-embed-text استفاده می‌کند؛ یک مدل بردارساز با ۱۳۷ میلیون پارامتر که بردارهایی با ۷۶۸ بُعد تولید می‌کند. روی تراشه M1، هر تکه متن در حدود ۵۰ میلی‌ثانیه پردازش می‌شود. از آنجایی که VectorStore این داده‌ها را به صورت float[] می‌بیند، می‌توان مدل را تنها با تغییر یک رشته متنی در کد، به هر مدل دیگری جایگزین کرد.

به نقل از مستندات پروژه، یک نکته کلیدی در تنظیمات Program.cs قرار دارد: پیکربندی OllamaService با یک BaseAddress خاص و افزایش Timeout کلاینت به ۵ دقیقه (client.Timeout = TimeSpan.FromMinutes(5)). دلیل این کار این است که زمان پیش‌فرض HttpClient (۱۰۰ ثانیه) در طول چرخه‌های تولید طولانی یا هنگام «راه‌اندازی سرد» (Cold Start) مدل‌ها به شدت ناکافاست و منجر به شکست در ارتباط می‌شود.

ذخیره‌سازی برداری با روش Brute-Force

به جای استفاده از پایگاه‌داده‌های پیچیده مانند ChromaDB، Qdrant یا pgvector، زالا یک لیست در حافظه (In-memory list) همراه با یک Lock پیاده کرده است. کلاس VectorStore از یک List<DocumentChunk> و یک شیء خصوصی _lock استفاده می‌کند تا از خطاهای خواندن/نوشتن همزمان توسط تب‌های مختلف مرورگر جلوگیری کند. این Lock ضروری است زیرا متد AddChunks روی اندپوینت آپلود اجرا می‌شود، در حالی که /api/chat توسط کاربر فراخوانی می‌گردد.

این سیستم از فرمول استاندارد کتاب‌های درسی برای شباهت کسینوسی (Cosine Similarity) بهره می‌برد. محاسبه شامل حاصل‌ضرب نقطه‌ای (Dot Product) دو بردار تقسیم بر حاصل‌ضرب بزرگی آن‌هاست. اگر بزرگی هر یک از بردارها صفر باشد، شباهت صفر باز می‌گردد.

  • عملکرد: اسکن Brute-force روی ۱۰۰۰ تکه (با ۷۶۸ بُعد) نیازمند ۷۶۸ هزار ضرب در هر پرس‌وجو است که روی یک CPU مدرن تنها در ۵ میلی‌ثانیه اجرا می‌شود. پیچیدگی زمانی این روش $O(n * d)$ است که $n$ تعداد تکه‌ها و $d$ بُعد بردار است.
  • سقف مقیاس: این روش تا زمانی که سیستم از حدود ۵۰ هزار تکه (معادل ۲۰۰ فایل PDF بزرگ) فراتر نرود یا بودجه تأخیر (Latency) به زیر ۲۰ میلی‌ثانیه نرسد، کاملاً قابل استفاده است. زالا استدلال می‌کند که برای استفاده شخصی، یک Lock پنج‌خطی بسیار ارزان‌تر و بهینه‌تر از نصب یک دیتابیس کامل است.

خط لوله تولید پاسخ

متد ChatService.AnswerQuestionAsync فرآیند RAG را در ۵ گام مجزا اجرا می‌کند:
۱. برداری‌سازی (Embed): سؤال کاربر توسط مدل بردارساز محلی به بردار تبدیل می‌شود.
۲. جست‌وجو (Search): ۵ تکه مشابه (Top-K=5) از طریق شباهت کسینوسی استخراج می‌شوند. اگر هیچ تکه‌ای یافت نشود، سیستم پیام می‌دهد: «هیچ محتوای مرتبطی در اسناد آپلود شده یافت نشد. لطفاً ابتدا یک PDF آپلود کنید».
۳. ساخت پرامپت (Prompt Build): یک پرامپت سیستمی و یک پرامپت کاربر ساخته می‌شود. پرامپت سیستمی عبارت است از: «شما یک دستیار مفید هستید که به سؤالات بر اساس محتوای سند ارائه شده پاسخ می‌دهد. فقط و فقط از محتوای ارائه شده استفاده کنید. اگر محتوا اطلاعات کافی ندارد، این موضوع را ذکر کنید».
۴. تولید (Generate): پرامپت نهایی (شامل متون استخراج شده و سؤال) به مدل llama3.2 از طریق اولاما ارسال می‌شود.
۵. ارجاع (Reference): پاسخ نهایی به صورت یک ChatResponse همراه با منابع بازگردانده می‌شود. این منابع شامل نام سند، یک تکه ۲۰۰ کاراکتری (که در صورت طولانی‌تر بودن با ... کوتاه می‌شود)، امتیاز شباهت (رند شده تا ۴ رقم اعشار) و ایندکس تکه است.

یک نکته حیاتی، سخت‌گیری در پرامپت سیستمی است. زالا اشاره می‌کند که این محدودیت واحد، توهم (Hallucination) را تا ۸۰٪ کاهش می‌دهد. بدون آن، مدل llama3.2 ممکن است پاسخی کلی از داده‌های آموزشی خود بدهد (مثلاً محدودیت ۱۰۰ درخواست در دقیقه را صرفاً چون یک استاندارد رایج در صنعت است پیشنهاد کند)، به جای اینکه داده‌های دقیق موجود در PDF را استخراج کند.

انتخاب topK: 5 حدود ۲۵۰۰ کلمه زمینه (Context) فراهم می‌کند. استفاده از ۳ تکه برای سؤالات ترکیبی (مثلاً مقایسه محدودیت جست‌وجو در برابر آپلود) ناکافی بود و استفاده از ۱۰ تکه، نویز زیادی را برای پنجره متنی ۸ هزار توکنی llama3.2 ایجاد می‌کرد.

درس‌های آموخته شده از توسعه

زالا پنج چالش فنی اصلی را در طول توسعه برجسته کرد:

  • ماندگاری (Persistence): ذخیره‌ساز فعلی در حافظه است و با ریستارت پاک می‌شود. راهکار پیشنهادی، ذخیره بردارها در SQLite در مرحله AddChunks و بارگذاری آن‌ها در هنگام شروع برنامه است که تنها به ۳۰ خط کد نیاز دارد.
  • ترتیب استخراج (Extraction Order): کتابخانه PdfPig متن را به ترتیب جریان محتوا استخراج می‌کند. در مقالات آکادمیک یا صفحاتی با دو ستون، این کار باعث به‌هم‌ریختگی متن می‌شود (مثلاً نتیجه‌گیری قبل از مقدمه می‌آید). راهکار این است که از ReadingOrderDetector در PdfPig استفاده شود یا برای اسناد اسکن شده از Tesseract OCR استفاده گردد. این مورد فعلاً در README به عنوان یک محدودیت ذکر شده است.
  • خطاهای Timeout: زمان پیش‌فرض ۱۰۰ ثانیه‌ای HttpClient در .NET ناکافاست. مدل‌های محلی ممکن است ۸ تا ۱۵ ثانیه برای بارگذاری اولیه (از دیسک به رم) و ۳۰ تا ۶۰ ثانیه روی سیستم‌های CPU-only برای تولید پاسخ‌های طولانی زمان نیاز داشته باشند. زالا Timeout را به ۵ دقیقه افزایش داد تا حاشیه امنیت ۳ برابری ایجاد کند و مرحله Lazy Pull مدل‌ها را پوشش دهد.
  • ارجاعات UI: در حالی که بک‌اند متادیتای لازم را فراهم می‌کند، فرانت‌اند React هنوز منابع را به صورت درون‌متنی رندر نمی‌کند. هدف این است که ارجاعات به شکل «محدودیت نرخ ۱۰۰/دقیقه است [منبع: api-spec.pdf, تکه ۲۳, امتیاز ۰.۸۹]» نمایش داده شوند. این چالش‌های رابط کاربری یادآور محدودیت‌های تجربه کاربری در اپلیکیشن‌های ساخته شده با پروتکل MCP است که نشان می‌دهد حتی با هسته‌های پردازشی قدرتمند، نمایش بهینه اطلاعات همچنان یک چالش است.
  • اصطکاک نصب (Setup Friction): هرچند اجرا رایگان است، اما نصب اولیه نیازمند دانلود حدود ۲.۳ گیگابایت مدل است (nomic-embed-text حدود ۲۷۴ مگابایت و llama3.2 حدود ۲ گیگابایت). در سرعت‌های پایین (۱۰ مگابیت) یا اینترنت‌های محدود، این فرآیند ۳۰ تا ۶۰ دقیقه زمان می‌برد. همچنین فایروال‌های شرکتی ممکن است CDNهای ذخیره مدل‌ها را مسدود کنند. زمان raise-start روی لپ‌تاپ‌های ۵ سال پیش ممکن است از ۲۰ ثانیه بیشتر شود.

استقرار و پیاده‌سازی

برای کسانی که قصد استقرار این سیستم را دارند، پروژه در آدرس github.com/ZalaAvinash/AI-Document-Chatbot-RAG- در دسترس است. دو مسیر برای اجرا وجود دارد:

مسیر بومی (Native):
۱. نصب Ollama و دریافت مدل‌ها: ollama pull nomic-embed-text و ollama pull llama3.2.
۲. بک‌اند: ورود به پوشه backend و اجرای dotnet run (سوئگر در مسیر /swagger در دسترس است).
۳. فرانت‌اند: ورود به پوشه frontend و اجرای npm install و سپس npm run dev.

مسیر داکر (Sugerred for non-.NET users):
اجرای دستور docker-compose up --build. این روش محیط Ollama و دانلودهای اولیه را به صورت خودکار مدیریت می‌کند، هرچند اولین اجرا به دلیل دانلود مدل‌ها حدود ۵ دقیقه زمان می‌برد.

این پیاده‌سازی، پارادایم هوش مصنوعی را از «پرداخت برای توکن» به «مدیریت محاسبات محلی» تغییر می‌دهد. این موضوع ثابت می‌کند که برای کارهای تخصصی با حریم خصوصی بالا مانند تحلیل مشخصات API، قراردادهای فروش، اسناد تطبیق (Compliance) یا مقالات پژوهشی، یک مدل محلی کوچک و مستند (Grounded)، عملکردی بهتر از یک مدل ابری عظیم دارد. در تست روی چهار نوع سند مختلف، چت‌بات همواره پاسخ‌ها را در کمتر از ۵ ثانیه و با ارجاعات قابل تایید ارائه کرد. نبود توهم در این سیستم به این دلیل است که مدل مجبور است منابع خود را ذکر کند و این امر تأیید پاسخ را لحظه‌ای می‌کند.

گام بعدی شما

  • اگر به امنیت داده‌ها اهمیت می‌دهید، مخزن گیت‌هاب ZalaAvinash/AI-Document-Chatbot-RAG- را بررسی کنید.
  • برای اجرا، ابتدا Ollama را نصب و مدل‌های llama3.2 و nomic-embed-text را Pull کنید.
  • برای دور زدن پیچیدگی‌های نصب .NET، از نسخه Docker-compose استفاده کنید.

اما داستان سخت‌افزاری این تحول حتی شگفت‌انگیزتر است؛ برای درک اینکه چگونه NPUهای جدید این زمان‌های انتظار را حذف می‌کنند، به تحلیل ما درباره‌ی تراشه‌های نسل جدید مراجعه کنید.

چرا این موضوع مهم است؟

این معماری با تکیه بر تخصص در بهینه‌سازی لبه، ثابت می‌کند که حریم خصوصی مطلق و هزینه صفر در استنتاج، دیگر یک تضاد نیستند. این موضوع برای سازمان‌هایی که با داده‌های حساس سر و کار دارند، یک الگوی جایگزین برای خروج از وابستگی به венدورهای ابری است.

تأثیر برای ایران

به‌دلیل تحریم‌های APIهای ابری، این مدل‌های محلی و وزن‌های باز بهترین گزینه برای توسعه‌دهندگان ایرانی هستند تا بدون نیاز به پرداخت ارزی یا VPN، سامانه‌های RAG بسازند.

·نگاه ما
تحریریه دات‌هوش

تغییر رویکرد از پایگاه‌داده‌های برداری پیچیده به یک لیست ساده در حافظه، نشان می‌دهد که برای بسیاری از کاربردهای عملی، «بیش‌مهندسی» (Over-engineering) مانع اصلی است. این پروژه ثابت می‌کند که مدل‌های زبانی کوچک (SLM) وقتی با یک سیستم بازیابی دقیق ترکیب شوند، در تسک‌های تخصصی از مدل‌های غول‌پیکر ابری پیشی می‌گیرند، چون تمرکز را از «احتمالات زبانی» به «داده‌های مرجع» منتقل می‌کنند.

منابع

این گزارش با خط‌لولهٔ خودکار دات‌هوش از منابع معتبر جهانی تدوین و زیر نظر تحریریه منتشر شده است. روش کار ما

گفتگو

پنج‌شنبه‌های هوش‌محور

بسته‌ی هفتگی دات‌هوش

۵ خبر، ۲ ابزار، ۱ پرامپت در هر شماره. به‌زودی راه‌اندازی می‌شود — هر پنج‌شنبه صبح.

خبر کلیدی
ابزار کاربردی
پرامپت حرفه‌ای
تحلیل پژوهش
به‌زودی
زاویه‌ی ایرانی
به‌زودی
تمرین این هفته
به‌زودی

راهنماهای دات‌هوش

راهنماهای کاربردیِ دات‌هوش برای کار با هوش مصنوعی — از همین‌جا شروع کنید:

دات‌هوش

راهنمای فارسی هوش مصنوعی — با نگاه به ایران

اخبار روزانه، معرفی ابزارها و مدل‌ها، و آموزشِ کار با هوش مصنوعی؛ همیشه با این پرسش که از ایران چه چیزی کار می‌کند و چه چیزی نه.