در قسمت قبلی «از دستیار کدنویس تا همکار هوشمند»، با مسیر توسعه ابزار تولید مستندات توسط AI آشنا شدیم. حالا توی این قسمت، میخوایم ببینیم که چطور میتونیم دانش و دادههای داخلی سازمان (کانتکست) رو در اختیار LLMها قرار بدیم.

وقتی این بُعد تازه به قابلیت مدلها اضافه بشه، میتونیم کارهای خیلی پیچیدهتری رو به کمکشون انجام بدیم.
بعد از معرفی ابزار GitHub Copilot، استفاده از LLMها بهعنوان دستیارهای برنامهنویسی به شکل جدی مطرح شد؛ بهطوری که برنامهنویسی، تبدیل به یکی از اهداف اصلی توسعهدهندگان این مدلها شد. برای مثال، یکی از بنچمارکهای اصلی برای ارزیابی همین مدلها، SWE-BENCH است که شامل تسکهای برنامهنویسی روی issueهای پروژههاست. همینطور شرکت Anthropic، که مدلهای Claudeش خیلی معروف هستند، تمرکز اصلیاش رو بر روی این بازار مشتریان قرار داد و با وجود اینکه قیمت مدلهایش بسیار بالاتر هست، چون عملکرد بهتری در زمینه تولید کد داشتند، همیشه جزو اولین مدلهای این زمینه بودن.
ما هم در دیوار این هدف رو برای خودمون گذاشتیم که با استفاده از هوش مصنوعی، بهرهوری مهندسی رو افزایش بدیم. در شروع سرویسهای مکالمهمحور مثل ChatGPT رو آوردیم و باهاشون کار کردیم. به مرور سرویسهایی مثل Copilot و Cursor هم امتحان کردیم. تجربهمون تا مدتی به این صورت بود که هر ابزار جدیدی که میاومد تعدادی از مشکلات و اذیتهایی که با ورژنهای قدیمیتر داشتیم برطرف میکرد. برای مثال در کار با ChatGPT باید توضیحات خیلی مفصلی از مسئله ارائه میدادیم و تمام کدهای مورد نیازشو کپی پیست میکردیم و کد خروجیش رو داخل محیط توسعهمون میآوردیم و مشکلات سینتکسی که داشت رو برطرف میکردیم. برای دیباگ هم لاگهای خروجیش رو باز به GPT میدادیم. این تجربه کاربری رفت و برگشتی تا حد خوبی در محصولاتی مثل Cursor برطرف شد اما همچنان مشکلات بزرگ دیگری داشتیم.
Cursor برای پروژههای کوچک و self-contained خیلی خوب عمل میکرد. اما برای پروژههای داخل یک سازمان به مشکل میخورد. مشکل این بود که همه اطلاعات مورد نیاز داخل پروژه در دسترس نیست و یا پیدا کردنش برای کسی که دانش قبلی از سازمان و کانونشنهای پروژه نداره کار سادهای نیست. خیلی از جاها هم زمان و هزینهای که ایجنتها برای پیدا کردن داده مورد نظر میذاشتن خیلی بالا بود و حتی ممکن بود Context Window مدل به طور کامل پر بشه و به نتیجه نرسه. تمام محصولات دیگری که امتحانشون کردیم، هر چقدر هم پیشرفته بودن و از مدلهای بهتر و توکن بیشتری استفاده میکردن، باز هم مشکل اصلی پابرجا بود. اینکه کانتکست دیواری و دانش کتابخانههای داخل سازمانی رو نداشتن و عملکردشون به همین علت، بهینه نبود. در خیلی از موارد هم مستندات معتبری داشتیم و یا ساخته بودیم که به خوبی ازشون استفاده نمیشد.
مکانیزم پیشنهادی برای حل کردن این مسائل در مدلهای زبانی قابلیت استفاده از ابزار (tool calling) در عاملهای (agents) هوشمصنوعی بود. اینطور که خود مدل بسیاری از دادههایی که نیاز داره رو به دست بیاره، و خودش اکشنهایی که پیشنهاد میده رو انجام بده و خروجیشون رو بررسی کنه. این یعنی مسئولیت از دوش کاربر استفاده کننده برداشته بشه.
برای همین تصمیم گرفتیم اول سعی کنیم منابع داده مختلفی رو که داریم رو با استفاده از ابزارهایی که امکانش هست، به LLMها وصل کنیم. اما باید برای هر سرویسی، به طور جداگانه، داخل Agent Libraryها و با استفاده از SDK خود شرکتها ابزار رو به عنوان یک تابع یا اندپوینتی که صدا زده میشه، اضافه کنیم. کار قابل انجامی بود اما یکپارچه نبود و خیلی از سرویسهای انحصاری هم قابلیت تغییر و اضافه کردن ابزار به این شکل رو به ما نمیدادن.
اینجا بود که با MCP آشنا شدیم. MCP یک پروتکل باز بر پایه JSON-RPC v2 هست که توسط Anthropic معرفی شده برای اینکه تعامل LLMها با بقیه APIهای موجود رو استاندارد کنه و از وقتی عرضه شده، استقبال زیادی داشته. در حال حاضر تقریبا هر LLM Application پراستفادهای ازش پشتیبانی میکنه (لیست محصولاتی که از MCP پشتیبانی میکنند). برای همین ما هم تصمیم گرفتیم تعدادی سرور MCP توسعه بدیم و ببینیم که به مدلها در انجام تسکشون کمک میکنه یا نه.
یک مسئله بزرگ وجود داره که اصلا محدود به LLMها نیست و شاید خود شما هم در شرکتتون الان این مسئله رو داشته باشید، مسئله محدودیت دسترسی به دانش داخلی هست. برای مثال در دیوار هم اوایل از سرچ گیتلب و یا ابزارهایی مثل grep یا rg داخل پروژهها استفاده میکردیم. این روش زمان زیادی میبرد و در اکثر مواقع چیزی رو که دنبالش بودیم پیدا نمیکردیم و مجبور بودیم از افراد مختلف پرس و جو کنیم و دانش سینه به سینه بین افراد و تیمها منتقل میشد. چند باری هم که خواستیم این مسئله رو با روشهایی مثل semantic search یا Source Code Embedding و RAG حل کنیم ناموفق بودیم.
در مراحل بعدی بیشتر تمرکزمون رو روی این گذاشتیم که ببینیم خودمون زمانی که دنبال قطعه کد خاصی میگردیم چطور عمل میکنیم و به راههایی از جنس exact text search رسیدیم. دنبال ابزاری میگشتیم که همون کاری که grep و rg برای ما میکرد در سطح کل کدبیس به صورت static و با قابلیت فیلتر دقیقتر روی پارامترهای مدنظر برنامهنویسی ارائه بده. توی این مسیر به سرویس Zoekt رسیدیم که دقیقا همین قابلیتها رو روی کدبیس به ما ارائه میده. این ابزار با زبان کوئری سادهای که ارائه میده، میتونه روی نام فایل، پروژه و زبان برنامهنویسی فیلتر کنه و با استفاده از regexهای نسبتا ساده جستوجوی دقیقی انجام بده. و ما دیدیم که نیاز نیست چرخ رو از اول اختراع کنیم.
سرویس Code Search از شرکت Sourcegraph هم از Zoekt به عنوان هسته اصلی سرچش استفاده میکنه. و البته UI خیلی بهتری هم داره و فیلترهای دقیقتر و کوئریبیلدر راحتتر ی هم ارائه میده. همچنین قابلیتهایی که در ابزارهایی مثل محصولات JetBrains هست مثل LSP و Finding Usage & Definition رو روی تمام زبانهای برنامهنویسی در سطح کل کدبیس ارائه میده و این قابلیتها رو در زبان کوئریش هم اضافه میکنه (مثلا میشه روی symbolها ی برنامه نویسی و موجود در AST سرچ کرد). با این ابزارها به راحتی میشه فهمید چه کدی کجا تعریف شده و API مد نظرمون در کجا داره صدا زده میشه.
در گام بعدی، میخواستیم به مدلها استفاده از این ابزار و این نحوه کد سرچ کردن رو یاد بدیم. اگه شما هم دیده باشید ابزارهای برنامه نویسی، برای اینکه اطلاعاتی که دنبالش هستن رو پیدا کنن، بیشترشون داخل پوشه پروژه shell باز میکنن و دستورهایی مثل cat, grep, tail استفاده میکنن. ما طبق بررسیها و آزمون و خطایی که داشتیم به این نتیجه رسیدیم که سه ابزار باید آماده کنیم که مدلها برای سرچ کردن ازش استفاده کنن.
search_prompt_guide: ابزاریه که یک توضیح مفصل از سینتکس کوئری Code Search به همراه مثال میده و سرور MCP و استفادهاش رو به مدل معرفی میکنه. علاوه بر اون یه بخش داخل پرامپت برای دانش داخل سازمانی هم در نظر گرفتیم که نکاتی قبیل اینکه پروژهها و کتابخانههای اصلی کجا قرار دارن و معماری سیستم و پروژهها چی هست رو داخل خودش داره. مدل همیشه از این ابزار، به عنوان اولین ابزار، قبل از جستجو استفاده میکنه.
search: مشابه همینه که مدل به نوار جستجو در Code Search دسترسی داره و هر کوئریای که نیاز داره رو میتونه بزنه و ما هم همون رو مستقیم به کدسرچ میدیم. البته خروجیای که میده رو ساده سازی میکنیم و چند خط بالا و پایین خطی از کد که match شده هم بر میگردونیم و یا جوابها رو زمانی که تعداد نتایج بالا باشه فیلتر میکنیم.
fetch_content: این رو در پی توسعه سرور MCP و مدتی بعد از ورژن اول بهش رسیدیم. دیدیم خیلی از مواقع جواب مدل کامل داخل یک فایل وجود داره و اگر این قابلیت رو بهش بدیم که تمام محتوای یک فایل رو بگیره بهش کمک میکنه. در بسیاری از موارد هم اگر فایلهای یک آدرس از پروژه رو بدونه میتونه سرچ بعدی و یا فایل بعدیای که میخواد محتواش رو بخونه بهتر پیدا کنه. به خصوص اگر پروژه README یا ai doc مناسب داشته باشه ( اشاره به مستندسازی برای پروژهها به کمک هوشمصنوعی)، با خوندن اونها خیلی دید بهتری نسبت به سوالی که داره و جایی که جواب سوالش هست پیدا میکنه.
جزئیات دقیقتر از پرامپت این ابزارها هم اینجا در دسترسه: https://github.com/divar-ir/code-context-provider/blob/master/src/prompts/prompts.yaml
یعنی تموم شد؟ همینقدر راحت همه چیز کار کرد؟ نه راستش! پرامپت و توضیحاتی که ما برای این ابزار میگذاشتیم، خیلی روی خروجی و کیفیت کار تاثیرگذار بود. مسئله این بود که خیلی مطمئن نبودیم که چه تعداد از جوابهایی که داره میده خوب و کمک کننده هست و چقدر هم جواب اشتباه داره میده. پس سراغ این رفتیم ساختار قابل اطمینانی برای ارزیابی داشته باشیم.
برای ارزیابی عملکرد سرور MCP، یک دیتاست از سوال و جواب روی کدبیس دیوار درست کردیم. جواب سوالها رو به صورت سرچ دستی و یا از مصاحبه با تیمهایی که روی سرویس آنبورد بودن پر کردیم. بعدش یک ساختار نوشتیم که ارزیابی کارکرد سرور رو به این شکل انجام بدیم:
یک ایجنت بالا میاریم و بهش MCP کد سرچ رو به عنوان ابزار میدیم.
بخش سوال آیتم دیتاست رو بهش میدیم و ازش میخوایم که جوابش رو پیدا کنه و توضیحاتش رو در قالب مشخصی بهمون برگردونه
سپس جواب ایجنتمون رو با بخش جواب توی دیتاست (با استفاده از داوری LLM) مقایسه میکنیم و بهش نمره قبول/رد میدیم. برای اینکه بتونیم مانیتورینگ راحتتر داشته باشیم از سرویس Langfuse استفاده کردیم که مخصوص LLM Application Observability هست و trace تمام tool callهایی که داخل یک نوبت روی سرور ما انجام میشه رو بهمون نشون میده. ما از طریق این میتونیم به راحتی مسیری رو که مدل برای جستوجو انتخاب کرده رو تحلیل کنیم و ببینیم مسیر مناسبی رو انتخاب کرده و در آخر به جواب رسیده یا نه. بعد از این هر تغییری که روی پرامپتها یا پیاده سازی داشتیم این فرایند ارزیابی رو اجرا میکردیم و درصد موفقیتش رو به عنوان متریک در نظر میگرفتیم.
در خیلی از موارد، سرویسهایی که با کدسرچ تعامل میکنن مدل قویای پشتشون نیست و یا محدودیت tool call دارند. در شرایطی هم، خروجیهایی که از codesearch میگیرن، کانتکستشون رو با دیتای نامفهوم پر میکنه و حتی اگر به جواب سوالی که دارن برسن، نمیتونن تسکی که با اون جواب میخوان انجام بدن رو تکمیل کنن. برای حل این موضوع از معماری MCP in MCP استفاده کردیم و یک سرور MCP ارائه دادیم که پشتش در واقع یک ایجنت برنامه نویسی هست که به MCP کدسرچ ما دسترسی داره. در ورژن فعلی این ایجنت همون ایجنت فرایند ارزیابیمون هست که محدودیت روی فرمت خروجیش رو کمتر کردیم و به عنوان MCP کانتکست ارائهاش دادیم. به نوعی پیاده سازی ما پیرو مفهوم و ابزار Deep Search بود که خیلی از محصولات اضافه کرده بودن. این MCP سرور شامل ابزارهای زیر بود:
query_reformatter: یک فاکتور خیلی تاثیرگذار در کیفیت جواب سرور ما، درستی اولین سوالی بود که مدل از MCP میپرسید که تاثیر زیادی روی ادامه مسیر جستجو روی اون موضوع داره. مواردی مثل زبان برنامهنویسیای که مدل دنبالشه یا اسم پروژهای که داخلش دنبال کد میگرده، اگر اشتباه یا نامفهوم گفته بشه، میتونه سرچ رو خیلی تحت تاثیر بگذاره. از طرف دیگه هم مدل خیلی اطلاعات محدودی از معماری و stack تکنولوژیای که ما استفاده میکنیم داره که اگر بدونه میتونه سوال دقیقتری رو بپرسه. کار این ابزار اینه که سوال اولیه مدل رو میگیره و با استفاده از یک ایجنت با دسترسی به کدسرچ سعی میکنه حدس بزنه منظور دقیق درخواست کننده چی بوده و تعدادی گزینه، که هر کدوم از یک ورژن دقیق شده سوال اولیه بوده، رو به عنوان خروجی بر میگردونه تا مدل از بین این گزینهها سوالی رو که به هدفی که داره نزدیکتره در کوئری بعدی بپرسه.
agentic_search: ورودی این ابزار سوالیه که از کدبیس داریم برای مثال: چه سرویسی وظیفه احراز هویت رو داره؟ چه کسی RPC X رو صدا میزنه؟ چطور از فیچرفلگ داخل کد Golang استفاده کنم؟ سوالهایی از این قبیل که بعد از دقیق شدن توسط query_reformatter از سرور کانتکست پرسیده میشن و این ابزار یک گزارش کامل به همراه قطعه کد و فایلها و پروژههایی که به عنوان منبع ازشون جواب رو استخراج کرده بر میگردونه.
کمتر از دو سه هفته میشه که ما این سرویس رو داخل شرکت ارائه دادیم و بازخورد مثبتی که تا الان گرفتیم خیلی بالا بوده. بهترین رابط کاربریمون دستیار جدا شدهای هست که وظیفهاش پاسخ دادن به سوالهای فنی از کدبیس دیواره و مسئله آنبوردینگ و رفت و برگشت سوال بین چند تیم یا دانش داخلی که سینه به سینه منتقل میشده رو تا حد خوبی بر طرف کرده. یک ایجنت بررسی تغییرات (code review) هم اضافه کردیم که روی اکثر پروژهها روشن شده و هر تغییری که روی کد انجام میشه، قبل از اضافه شدن به پروژه و ریلیزش به طور دقیق بررسی میکنه و بازخورد میده.
در زمینه ایجنتهای برنامهنویسی اما هنوز به نتیجه مدنظرمون نرسیدیم که محصولاتی مثل Cursor همراه ابزارهای داخلی خودشون به کمک ابزار ما هم تسکها رو انجام بدن. اگر سرورهای MCP کدسرچ رو بهشون بدیم در استفاده از ابزار سرچ داخلی خودشون و ابزار سرچ ما گمراه میشن و اگر سرور کانتکست رو بهشون بدیم خیلی خوب عمل میکنن اما به دلیل زمانی که میبره (حدود ۲ دقیقه به ازای هر agentic_search) چرخه توسعه رو خیلی کند میکنه. و فعلا بهترین استفاده از این ابزار همین پرسیدن از دستیار کد زمانی که سوال پیچیدهتری از کدبیس داریم هست.
در چند ماه اخیر مفهوم ایجنتهای برنامهنویسی داخل CLI مثل Claude Code، Amp Code و Gemini CLI خیلی جدیتر بهشون پرداخته شده. این ایجنتها میتونن در پسزمینه یا داخل محیط ترمینال وظایف برنامهنویسی رو انجام بدن. اینها ایجنتهای بهینهسازی شدهای هستن که قابلیتهایی نظیر Planning، Context Summarization و Intelligent Model Choosing بهشون اضافه شده. طبق تجربهای که باهاشون داشتیم، از سرور کدسرچ خیلی خوب استفاده میکنند و احتمال بالایی وجود داره که بتونن جایگزین بخش سرور کانتکست ما بشن. همچنین ایجنتهایی که تسکهای برنامهنویسی انجام میدن مثل ایجنت تست نویس میتونن از این ابزارها به عنوان قالب شروع استفاده کنن.
بهتره که ابزار مشابه سرچ کد رو روی مخازن دیگر اطلاعات سازمان از جمله مستندات داخلی، پروپوزالها، Post Mortemها و منابع دیتایی داشته باشیم. که باعث میشه این اطلاعات هم در دسترستر و قابل استفادهتر باشه و بهرهوری افراد سازمان که در وظیفهشون به داشتن دانش از این منابع نیاز دارند افزایش پیدا بکنه.
کارکردهای مشابه Code Review که با این ابزارها ممکن میشه مثل تست نرمافزار، دیباگ، افزایش مشاهدهپذیری و CI/CD بررسی و پیاده سازی کنیم.
در بسیاری از موارد سرویسهای منسوخ شده و یا نامگذاری بد پروژهها به همراه مستندات غیر معتبر و منقضی شده تاثیر منفی روی عملکرد ابزارهای ما داشتن. این مسیر AI Enabled شدن یک سازمان موضوع Deprecation رو برامون جدیتر کرده که به همراه مستندسازی پروژهها برای دسترسی بهتر مدلها نیاز به پاک کردن پروژههای استفاده نشده و نامگذاری بهتر دیده میشه.
اگر کارهایی که اینجا گفته شد براتون جالب بود و میخواستید خودتون کارهای مشابهی انجام بدید ما پیادهسازیمون از سرورهای MCP رو اوپن سورس کردیم:
همچنین اگر دوست دارید پیادهسازی مشابهی برای ارزیابی سرورهای کدسرچ و سرور کانتکست داشته باشید میتونید از این پروژه الهام بگیرید و استفاده کنید: