پاسخی درباره اسناد شما تنها به اندازه متنی که از آن ساخته شده است خوب است، و وقتی کل فایل‌ها را در یک چت می‌چسبانید، نمی‌توانید بگویید مدل دقیقاً از کدام بخش استفاده کرده است. GroupDocs.Search.Mcp که به‌صورت محلی در Docker اجرا می‌شود، به Claude Desktop، Cursor یا Copilot یک ابزار متفاوت می‌دهد: get_text_fragments فقط بخش‌هایی را که با یک پرس‌وجو مطابقت دارند برمی‌گرداند، به همراه چند کلمهٔ زمینه و فایلی که از آن آمده‌اند، بنابراین عامل پاسخ را از نقل‌قول‌ها می‌گیرد:

قراردادهای من دربارهٔ جریمه‌ها چه می‌گویند؟ بخش‌ها را نقل‌قول کنید، به‌طوری که زمینهٔ کافی برای درک هر یک وجود داشته باشد.

نسخهٔ گام‌به‌گام همراه با پیکربندی و عیب‌یابی در مستندات موجود است: How to get the matching passages instead of reading whole files .

چرا پاسخ‌ها از اسناد منحرف می‌شوند؟

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

یک قطعه (fragment) چگونه به‌نظر می‌رسد؟

فهرست (index) قبلاً متن هر سند را نگه داشته است، بنابراین سرور می‌تواند بخش اطراف هر مطابقت را برش دهد. این‌ها پاسخ‌های واقعی از آزمایش ما هستند، در برابر پنج قرارداد مصنوعی. پیش‌فرض‌ها، هشت کلمه در هر طرف، یک عبارت می‌دهند:

-> get_text_fragments {"query":"penalty","indexName":"contracts"}
<- === contracts/acme-master-services-agreement.pdf (1 occurrence(s)) ===
     [content] …before the end of the Term, a termination penalty of
   three monthly fees applies, payable within 30…

بیست کلمه در هر طرف کل بند را می‌دهد:

-> get_text_fragments {"query":"penalty","indexName":"contracts",
     "termsBefore":20,"termsAfter":20,"maxFragmentsPerDocument":1}
<- === contracts/acme-master-services-agreement.pdf (1 occurrence(s)) ===
     …convenience on 90 days written notice.
   12.3 If the Client terminates before the end of the Term, a termination
   penalty of three monthly fees applies, payable within 30 days of the
   termination date.
   14.2 Force majeure: neither party is…

termsBefore و termsAfter یک تنظیم‌کننده هستند: مقدار کوچک برای «کدام فایل‌ها و تقریباً کجا»، مقدار بزرگ برای «بند را نقل‌قول کن». maxFragmentsPerDocument و maxDocuments اندازهٔ پاسخ را محدود می‌کنند، بنابراین کلمه‌ای که دوصد بار ظاهر می‌شود، زمینه را پر نمی‌کند.

بودجه، کنار به کنار

Reading the files                        Searching the index
-------------------------------------    -------------------------------------
agent opens every document               search: which files match (JSON)
  context grows with the corpus          get_text_fragments: the passages
  long files crowd out the question        (~8 words either side, per file)
  repeated for every question            answer cites file + passage
                                         the index is reused next time

تفاوت با افزایش حجم مجموعه بزرگ می‌شود. برای سه فایل کوتاه تقریباً اهمیتی ندارد. برای یک پوشهٔ شامل قراردادهای طولانی، گزارش‌ها و ایمیل‌ها، قطعات برای یک سؤال چند صد کلمه هستند، در مقابل متن کامل هر سند کاندید.

وقتی کلمات متفاوت هستند

جستجوی تمام‑متن کلمات را مطابقت می‌دهد، که این ویژگی برای استوارسازی (grounding) است (هر نتیجه قابل توضیح است) و محدودیتی برای یادآوری (recall) است (یک مترادف یک عدم مطابقت است). کار عامل این است که انواع مختلف را امتحان کند، و می‌تواند ببیند هر نوع دقیقاً چه چیزی را مطابقت داده است:

-> search {"query":"renewal OR renews","indexName":"contracts"}
<- umbrella-office-lease.pdf, terms ["renewal", "renews"]

-> search {"query":"renewl","indexName":"contracts","fuzzy":true}
<- umbrella-office-lease.pdf, terms ["renewal", "renews", "rent"]

مطابقت مبهم (fuzzy) یک املای اشتباه را نجات داد و همچنین rent را وارد کرد. چون پاسخ فهرست اصطلاحاتی را که مطابقت داده‌اند نشان می‌دهد، یک عامل یا شما می‌توانید بیش‌مطابقت را ببینید به‌جای این‌که به آن اعتماد کنید. این شفافیت همان چیزی است که «استوار» باید به معنای آن باشد: نه فقط «پاسخ منبعی را ذکر می‌کند»، بلکه «می‌توانید ببینید منبع چگونه پیدا شده است».

یک پاسخ استوار چگونه به‌نظر می‌رسد

از نقل‌قول بخواهید، و پاسخ به شکلی می‌آید که می‌توانید در یک نگاه آن را تأیید کنید:

دو قرارداد شامل جریمهٔ خاتمه هستند. Acme MSA، بند 12.3: «a termination penalty of three monthly fees applies, payable within 30 days of the termination date.» قرارداد مشاورهٔ Globex، بخش 9: «the Client pays 50 percent of the fees remaining for the current phase.» NDA Initech به خاتمه و جریمه اشاره می‌کند اما می‌گوید جریمه‌ای وجود ندارد.

اگر خلاصه‌ای نقل‌قول را به‌درستی نخواند، خطای خواندن دقیقاً در کنار نقل‌قول قرار می‌گیرد.

محدودیت‌های صادقانه

  • در حالت ارزیابی یک فهرست حداکثر ۱۰۰ سند را نگه می‌دارد و یک جستجو حداکثر ۱۰ نتیجه برمی‌گرداند. حالت ارزیابی همچنین خطی «Evaluation only…» به متن فهرست برخی فرمت‌ها اضافه می‌کند که می‌تواند در قطعات ظاهر شود؛ برای حذف آن سرور را لایسنس بگیرید و فهرست را دوباره بسازید. ابتدا get_license_status را فراخوانی کنید.
  • زمینهٔ قطعه بر حسب کلمه شمارش می‌شود، نه جمله، بنابراین یک بخش می‌تواند در وسط یک جمله شروع شود؛ وقتی مهم است termsBefore را افزایش دهید.
  • OCR وجود ندارد: اسکن‌هایی که لایهٔ متنی ندارند، هیچ بخشی برای بازگرداندن ندارند.
  • فقط Docker؛ در لینوکس --user "$(id -u):$(id -g)" را اضافه کنید.

پرسش‌های متداول

چگونه می‌توانم Claude را وادار کنم منابع را از اسناد من نقل‌قول کند؟
به آن ابزاری بدهید که متن منبع را همراه با نام فایل برگرداند. get_text_fragments دقیقاً این کار را انجام می‌دهد و یک دستور مانند «quote the passage and its file» آن را به عادت تبدیل می‌کند.

آیا این باعث کاهش مصرف توکن می‌شود؟
بله، چون بارگذاری کل اسناد برای پاسخ به سؤال جلوگیری می‌شود: عامل فقط بخش‌های مطابقت یافته را می‌خواند. مقدار صرفه‌جویی بستگی به حجم فایل‌های شما دارد.

آیا عامل هنوز می‌تواند وقتی نیاز دارد کل فایل را بخواند؟
بله، با ابزارهای فایل خود. قطعات برای یافتن و نقل‌قول هستند؛ وقتی بخش کافی نیست، می‌توانید کل فایل را بخوانید.

عمیق‌تر بروید