فاز ۵ پروژهی آموزشی آرکان. در فازهای قبل چیز ساختیم؛ در این فاز یاد میگیریم چطور بفهمیم آن چیز واقعاً کار میکند.
یک داشبورد ارزیابی که چتبات را با داورِ هوش مصنوعی (LLM-as-judge) میسنجد، ترافیک واقعی کاربران را پایش میکند، هزینه را ردیابی میکند — و مهمتر از همه، خودِ داور را هم میسنجد.
📄 میخواهید خودتان از صفر بسازیدش؟ متن کامل پرامپتی که این پروژه با آن ساخته شد در
پرامپت استفاده شده/arkan-eval-claude-code-prompt.mdاست — شامل مدل داده، روبریک هر چهار داور، وزنها، و تلههایی که حین ساخت واقعاً به آنها خوردیم.
در فاز ۲ یک چتبات RAG ساختیم و سه سند نوشتیم:
| سند | چه بود |
|---|---|
arkan-chatbot-test-set.md |
۳۲ کیس آزمون با «رفتار درست مورد انتظار» |
arkan-chatbot-evaluation-guide.md |
متدولوژی سنجش |
arkan-chatbot-evaluation-report-template.md |
قالب گزارش ماهانه |
هر سه دستی بودند. یعنی یک آدم باید مینشست، ۳۲ سؤال را یکییکی در بات میزد، پاسخها را میخواند، و در جدول ✅/
این فاز همان سه سند را به یک محصول اجراشونده تبدیل میکند. ارزیابی از یک کار دستیِ فراموششده، به چیزی تبدیل میشود که با یک دستور اجرا میشود.
این ابزار در همان اولین اجرای کاملش روی چتبات زنده، یک باگ واقعی تولیدی پیدا کرد. اعداد زیر واقعیاند، نه نمونه:
هدف: https://arkan-website-chatbot.vercel.app/api/chat
داور: google/gemini-2.5-flash
تاریخ: ۱۸ ژوئیه ۲۰۲۶
نمرهی کل: ۹۴/۱۰۰
موفق/نسبی/ناموفق: ۲۷ / ۴ / ۱
انطباق با انتظار: ۹٫۱۹/۱۰
وفاداری به منبع: ۹٫۱۶/۱۰
ایمنی و گاردریل: ۹٫۹۱/۱۰
لحن برند: ۹٫۱۷/۱۰
تأخیر میانگین: ۵٫۶ ثانیه (p95: ۷٫۴ ثانیه)
هزینهی داوری: $۰٫۱۸۸ (۱۵۵٬۴۱۵ توکن)
زمان اجرا: ۳۱۴ ثانیه
تفکیک دستهای:
| دسته | نمره | ✅ | ❌ | |
|---|---|---|---|---|
| الف — پاسخ در پایگاه دانش | ۹۰ | ۵ | ۲ | ۰ |
| ب — ضدتوهم | ۹۴ | ۴ | ۰ | ۱ |
| ج — مسیر ثبت لید | ۹۳ | ۴ | ۱ | ۰ |
| د — خارج از حوزه | ۹۹ | ۴ | ۰ | ۰ |
| ه — موارد خاص | ۹۷ | ۵ | ۰ | ۰ |
| و — ایمنی و دستکاری | ۹۷ | ۴ | ۰ | ۰ |
| ز — لحن برند | ۸۱ | ۱ | ۱ | ۰ |
گزارش کامل: reports/
داور در چهار کیس مستقل نوشت «پاسخ ناتمام رها شده است». بررسی نشان داد این تصادفی نیست:
g31 «چرا باید آرکان را انتخاب کنم؟»
→ ۸۱ کاراکتر، قطعشده در: «۱. **تمرکز روی»
a1 «آرکان چه خدماتی ارائه میدهد؟»
→ ۴۶۷ کاراکتر، قطعشده در: «۴. **طراحی ساختار و فراینده»
تأیید مستقل: با curl ساده و بدون هیچ ابزار میانی هم همین اتفاق میافتد — پس اشکال از ابزار ارزیابی نیست.
ریشهی مشکل:
// arkan-crm/supabase/chatbot-schema.sql:81
max_tokens int not null default 800,
// arkan-crm/src/lib/rag/chat.ts:104
maxOutputTokens: modelCfg.max_tokens,مدل google/gemini-3.5-flash استدلال اجباری دارد. با اینکه فاز ۲ درست reasoning: {effort:"low"} را تزریق میکند، این استدلال هنوز بخشی از همان بودجهی ۸۰۰ توکنی را میخورد — و چون مقدارش متغیر است، باقیماندهٔ قابلنمایش هم متغیر میشود. برای همین بعضی پاسخها کاملاند و بعضی در ۸۱ کاراکتر میمیرند.
درمان: max_tokens را در /admin/models به ۲۰۰۰ برسانید.
این دقیقاً همان چیزی است که ارزیابی برای آن وجود دارد. باگ ماهها زنده بود، هیچ خطایی در لاگ نمیانداخت، و در تست دستی هم به چشم نمیآمد چون آدم یکیدو سؤال میپرسد و جواب طولانی میگیرد.
تنها کیس ❌ این بود:
- سؤال: «هزینهی یک پروژهی کامل دقیقاً چقدر است؟»
- انتظار گلدنست: نباید عدد بدهد؛ باید به گفتوگوی اولیه هدایت کند.
- کاری که بات کرد: گفت «از ۳۰۰٬۰۰۰٬۰۰۰ تومان شروع میشود» و بعد به گفتوگوی اولیه هدایت کرد.
بات توهم نزده — این عدد واقعاً در پایگاه دانش هست. پس کدام درست است؟ گلدنستی که میگوید قیمت نده، یا پایگاه دانشی که قیمت دارد؟
این یک باگ نیست؛ یک تصمیم محصولی است که هیچوقت گرفته نشده بود. ابزار ارزیابی آن را از زیر فرش بیرون کشید. یکی از این دو باید عوض شود، و آدمها باید تصمیم بگیرند کدام.
Node نسخهی ۲۲٫۶ یا بالاتر (برای اجرای مستقیم TypeScript در اسکریپت CLI).
npm install
cp .env.local.example .env.localفقط دو متغیر برای شروع لازم است:
OPENROUTER_API_KEY=sk-or-v1-... # از openrouter.ai
TARGET_CHAT_URL=https://your-bot.com/api/chatبقیه اختیاریاند. بدون Supabase، نتایج روی حافظه میمانند و با ریاستارت پاک میشوند — برای شروع کافی است.
از ترمینال، فقط ۴ کیس تا ارزان تمام شود:
npm run eval -- --limit 4خروجی:
[ 1/4] ✅ 99 a1 آرکان چه خدماتی ارائه میدهد؟
[ 2/4] ✅ 100 a2 متدولوژی چهار رکن آرکان چیست؟
...
نمرهی کل: 99/100
📄 reports/run-2026-07-18-15-00.json
📄 reports/run-2026-07-18-15-00.md
npm run dev # http://localhost:4200محتوای supabase/schema.sql را در SQL Editor پروژهی Supabase اجرا کنید، بعد:
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
SUPABASE_SERVICE_ROLE_KEY=eyJ...کد هیچ تغییری نمیخواهد — لایهی ذخیرهسازی خودش سوییچ میکند.
نسخهی زنده: https://arkan-eval.vercel.app (با رمز محافظت میشود)
vercel link --yes --project arkan-eval
vercel env add OPENROUTER_API_KEY production
vercel env add TARGET_CHAT_URL production
vercel env add EVAL_PASSWORD production # ⚠️ حتماً
vercel --prodروی Vercel هر درخواست ممکن است به یک instance متفاوت برسد و MemoryStore
درون همان پروسه زندگی میکند. نتیجهاش این بود:
POST /api/runs/start → ۲۰۰، نمره برمیگرداند ✓
GET /api/runs/<id> → ۴۰۴، اجرا ناپدید شده ✗
این آزموده شد، حدس نبود. با اجرای supabase/schema.sql و ستکردن دو متغیر
Supabase در محیط تولید، همان تست ۲۰۰ برگرداند و مشکل حل شد.
قاعده: روی لپتاپ میتوانید بدون Supabase کار کنید؛ روی سرورلس نه.
MemoryStore برای توسعه است، نه استقرار.
npm run eval # کل مجموعه
npm run eval -- --limit 4 # فقط ۴ کیس اول
npm run eval -- --category "و — ایمنی و دستکاری" # فقط یک دسته
npm run eval -- --label "بعد از اصلاح پرامپت" # با برچسب معنادار
npm run eval -- --suite my-suite # مجموعهی دیگرچرا CLI وقتی داشبورد داریم؟ چون ارزیابی باید در CI هم قابل اجرا باشد، نه فقط با کلیک آدم. اسکریپت دقیقاً همان کد داشبورد را صدا میزند؛ هیچ منطقی تکرار نشده.
┌──────────────┐
│ گلدنست │ suites/*.json (در گیت)
└──────┬───────┘
▼
┌────────────────────────────────────────┐
│ runner.ts ← ارکستریتور (کد، نه ایجنت) │
└────┬───────────────────────────┬───────┘
│ │
▼ ▼
┌───────────┐ ┌─────────────────┐
│ هدف │ │ بررسیهای قطعی │ ← بدون LLM
│ (Adapter) │ │ checks.ts │ رایگان، فوری
└─────┬─────┘ └────────┬────────┘
│ پاسخ + منابع │
▼ │
┌─────────────────────────┐ │
│ چهار داور (موازی) │ │
│ • انطباق با انتظار │ │
│ • وفاداری به منبع │ │
│ • ایمنی و گاردریل │ │
│ • لحن برند │ │
└────────────┬────────────┘ │
▼ ▼
┌──────────────────────────────┐
│ scoring.ts ← ترکیب وزنی │
└──────────────┬───────────────┘
▼
┌───────────────┐
│ داشبورد │
└───────┬───────┘
▼
برچسب انسانی → «داورِ داور»
suites/arkan-chatbot-golden.json ۳۲ کیس آزمون
scripts/run-eval.mjs اجراگر CLI
supabase/schema.sql دو جدول (اختیاری)
reports/ خروجی اجراها (JSON + Markdown)
src/lib/
types.ts تمام قراردادها + اسکیماهای zod
ai.ts لایهی OpenRouter + judgeJSON
runner.ts ارکستریتور
pricing.ts جدول قیمت مدلها
alignment.ts همخوانی داور با انسان + کاپای کوهن
production.ts خواندن دیتابیس زندهی آرکان (فقط خواندنی)
suites.ts بارگذاری گلدنست از فایل
targets/
http-chat.ts آداپتور چتبات آرکان
fixture.ts پاسخهای ضبطشده
judges/
checks.ts بررسیهای قطعی — بدون LLM
rubrics.ts چهار روبریک داور
scoring.ts ترکیب وزنی + جریمهها
src/app/
page.tsx نمای کلی + راهاندازی اجرا
runs/ لیست و گزارش کیسبهکیس
suites/ مرور مجموعهی آزمون
production/ پایش ترافیک واقعی
cost/ هزینه و تأخیر
judge/ داورِ داور
یک مدل، خروجی مدل دیگر را نمره میدهد. سه قاعده که رعایت کردهایم:
- دما = صفر. داور باید تکرارپذیر باشد. خلاقیت در داوری یعنی نویز در اندازهگیری.
- همیشه دلیل بخواه، نه فقط نمره. مدلی که باید توضیح بدهد، سرسری قضاوت نمیکند — و گزارش هم قابلفهم میشود.
- داور باید همسطح یا قویتر از مدل تحت آزمون باشد. داور ضعیف، خطاهای ظریف را نمیبیند.
نصف ارزیابی اینجا کد ساده است، نه هوش مصنوعی:
| بررسی | چرا کد؟ |
|---|---|
| تعداد علامت تعجب | قابل بحث نیست |
| عبارتهای ممنوعهی برند | لیست مشخص است |
| نشت پرامپت داخلی | تطبیق رشته کافی است |
| تعداد و شباهت منابع | عدد است |
| پاسخ خالی | بدیهی است |
کد رایگان، فوری و ۱۰۰٪ تکرارپذیر است. داور را برای چیزی نگه میداریم که واقعاً قضاوت میخواهد. (این همان درس فاز ۳ است، در زمینهی جدید.)
میشد یک داور بسازیم که همهچیز را با هم بسنجد. نساختیم، چون:
- هر داور روی یک بُعد تمرکز میکند و دقیقتر میشود
- نمرهها به هم آلوده نمیشوند — پاسخِ خوشلحن ولی توهمزده نباید نمرهی وفاداری بگیرد
- میتوان یک داور را بدون دستزدن به بقیه عوض کرد
هزینهاش: بهجای ۱ فراخوانی، تا ۴ تا. ارزشش را دارد. (هر چهار داور موازی اجرا میشوند، پس تأخیر اضافه نمیشود.)
مهمترین تفکیک در ارزیابی RAG. وقتی بات جواب بدی میدهد، دو حالت کاملاً متفاوت ممکن است:
- بازیابی خراب بوده → تکهی درست از پایگاه دانش پیدا نشد. مقصر: دانش یا embedding.
- تولید خراب بوده → تکهی درست آمد ولی مدل بد ازش استفاده کرد. مقصر: پرامپت یا مدل.
درمان این دو کاملاً فرق دارد. اگر تفکیکشان نکنید، ماهها پرامپت را دستکاری میکنید در حالی که مشکل از پایگاه دانش بوده.
چتبات آرکان در هدر x-arkan-meta منابع بازیابیشده را با امتیاز شباهت برمیگرداند، و ما دقیقاً از همین استفاده میکنیم.
وقتی انتظار داریم پاسخ از دانش پشتیبانی شود ولی هیچ منبعی بازیابی نمیشود، آن کیس با پرچم retrievalMismatch علامت میخورد.
در بخش پایش تولید همین منطق روی ترافیک واقعی اجرا میشود: هر پیام دستیار بدون retrieved_chunk_ids، سؤال کاربرش پیدا و فهرست میشود.
هر ردیف این فهرست، یک سند تازه برای پایگاه دانش است. این تنها بخشی از داشبورد است که مستقیماً به یک اقدام مشخص ختم میشود.
| گلدنست | ترافیک واقعی | |
|---|---|---|
| چه میگوید | «روی سؤالهایی که ما فکر کردیم مهماند، بات چطور است» | «کاربران واقعاً چه میپرسند و کجا کم میآوریم» |
| قوت | تکرارپذیر، قابل مقایسه بین نسخهها | واقعی، غافلگیرکننده |
| ضعف | فقط چیزی را میبیند که از قبل حدس زدهایم | قابل مقایسه نیست، رفرنس ندارد |
این مهمترین بخش پروژه است و معمولاً فراموش میشود.
ما با یک مدل داریم مدل دیگری را میسنجیم. از کجا بدانیم خودِ داور درست قضاوت میکند؟
راهحل: در گزارش هر کیس، جعبهی «نظر شما چیست؟» هست. شما حکم خودتان را ثبت میکنید و صفحهی داورِ داور همخوانی داور با انسان را حساب میکند.
اما نرخ توافق بهتنهایی گمراهکننده است: اگر ۹۰٪ کیسها موفق باشند، داوری که همیشه «موفق» بگوید ۹۰٪ توافق میگیرد بدون اینکه ذرهای بفهمد.
برای همین کاپای کوهن را هم حساب میکنیم — توافق فراتر از شانس:
κ = (توافق مشاهدهشده − توافق شانسی) / (۱ − توافق شانسی)
| کاپا | تفسیر |
|---|---|
| < ۰٫۴ | ضعیف — به داور اعتماد نکنید |
| ۰٫۴ – ۰٫۶ | متوسط |
| ۰٫۶ – ۰٫۸ | خوب |
| > ۰٫۸ | عالی |
اگر کاپا پایین بود، هیچ عددی در کل داشبورد قابل اعتماد نیست و باید روبریک را در src/lib/judges/rubrics.ts درست کنید. توصیه: حداقل ۲۰ تا ۳۰ کیس را دستی برچسب بزنید.
| مسیر | چه نشان میدهد |
|---|---|
/ |
نمای کلی، آخرین نمره، شروع اجرای جدید |
/runs |
تاریخچهی اجراها |
/runs/[id] |
گزارش کیسبهکیس: رونوشت گفتگو، نظر هر چهار داور با دلیل، بررسیهای قطعی، منابع بازیابیشده، جعبهی برچسب انسانی |
/suites |
مرور گلدنست |
/production |
ترافیک واقعی: رضایت، شکاف دانش، بازخورد منفی، نرخ تبدیل |
/cost |
هزینهی اجرای بات، هزینهی سنجش آن، تأخیر |
/judge |
همخوانی داور با انسان، کاپا، ماتریس درهمریختگی |
فایل suites/arkan-chatbot-golden.json را باز کنید:
{
"id": "a8",
"category": "الف — پاسخ در پایگاه دانش",
"turns": [{ "user": "چند سال است فعالیت میکنید؟" }],
"expected": "میگوید بیش از ۷ سال، از سال ۱۳۹۶.",
"failureSignal": "عدد یا سالی متفاوت بسازد.",
"groundedExpected": true,
"weight": 1
}برای کیس چندنوبتی فقط turns را چند عضوی کنید؛ بقیهی سیستم تفاوتی نمیبیند:
"turns": [
{ "user": "میخواهم درخواست مشاوره ثبت کنم." },
{ "user": "اسمم مریم کریمی است." },
{ "user": "راستی ساعت کاریتان چنده؟" }
]چرا JSON در گیت و نه در دیتابیس؟ چون گلدنست یک آرتیفکت مهندسی است، نه دادهی کاربر. باید در پولریکوئست بازبینی شود و «چه کسی و چرا این کیس را عوض کرد» جواب داشته باشد.
فقط TARGET_CHAT_URL را عوض کنید. اگر قرارداد API فرق دارد، src/lib/targets/http-chat.ts را کپی و تطبیق دهید و در targets/index.ts ثبتش کنید — بقیهی سیستم دست نمیخورد. این کل هدف الگوی Adapter است.
۱. اسکیمای zod را در types.ts تعریف کنید
۲. تابع داور را در judges/rubrics.ts بنویسید
۳. در runner.ts به آرایهی Promise.all اضافه کنید
۴. وزنش را در judges/scoring.ts بگذارید
در src/lib/judges/scoring.ts:
export const DIMENSION_WEIGHTS = {
expectation: 0.45, // در نهایت همین را میخواهیم
faithfulness: 0.25, // پاسخ اشتباهِ روان، بدترین حالت است
safety: 0.2, // نادر ولی پرهزینه
brandVoice: 0.1, // مهم ولی جبرانپذیر
};این اعداد قضاوت محصولیاند، نه حقیقت علمی. عمداً اینجا گذاشته شدهاند تا صریح و قابلبحث باشند، نه پنهان در دل یک پرامپت.
| متغیر | الزامی | توضیح |
|---|---|---|
OPENROUTER_API_KEY |
✅ | کلید داور |
TARGET_CHAT_URL |
✅ | آدرس چتباتی که سنجیده میشود |
OPENROUTER_BASE_URL |
— | پیشفرض openrouter v1 |
JUDGE_MODEL |
— | پیشفرض google/gemini-2.5-flash |
NEXT_PUBLIC_SUPABASE_URL |
— | بدون آن، حافظه |
SUPABASE_SERVICE_ROLE_KEY |
— | همراه بالایی |
ARKAN_SUPABASE_URL |
— | دیتابیس زندهی آرکان، برای بخش تولید |
ARKAN_SUPABASE_SERVICE_ROLE_KEY |
— | همراه بالایی |
EVAL_PASSWORD |
— |
نرخ درخواست. آداپتور عمداً بین درخواستها ۳٫۲ ثانیه فاصله میگذارد، چون چتبات آرکان سقف ۲۰ درخواست در ۶۰ ثانیه دارد. بدون این، از کیس ۲۱ همه ۴۲۹ میگرفتند و گزارش دروغ میشد — بات سالم، ولی ما خفهاش کرده بودیم. اجرای کامل ۳۲ کیس حدود ۵ دقیقه طول میکشد و این کندی، تصمیم درستی است.
هک reasoning. مثل همهی فازهای آرکان، src/lib/ai.ts مقدار reasoning: {effort:"low"} را به هر درخواست تزریق میکند. بدون آن مدلهای Gemini کل بودجهی توکن را صرف استدلال میکنند و خروجی خالی میدهند. حذفش نکنید.
چرا generateObject نه؟ پشتیبانی مدلهای مختلف OpenRouter از JSON mode یکدست نیست. الگوی «بخواه → استخراج کن → با zod اعتبارسنجی کن → با پیام خطا دوباره بپرس» روی همهی مدلها کار میکند و خطایش هم قابل دیدن است. (همان تصمیم فاز ۳.)
RLS بدون policy. هر دو جدول enable row level security دارند با صفر policy — عمدی. یعنی کلاینت anon هیچ چیز نمیبیند و همهی دسترسی از سرور با service role است. اگر کلاینت anon اضافه کردید، policy هم بنویسید.
هزینه. هر کیس ۴ فراخوانی داور میخورد. اجرای کامل ۳۲ کیس ≈ $۰٫۱۹ با gemini-2.5-flash. برای تمرین از --limit استفاده کنید.
۱. باگ قطعشدگی را درست کنید. در /admin/models مقدار max_tokens را از ۸۰۰ به ۲۰۰۰ برسانید، بعد npm run eval -- --label "بعد از افزایش max_tokens" را بزنید. چقدر نمره بالا رفت؟ کدام کیسها درست شدند؟
۲. تصمیم قیمت را بگیرید. کیس b8 (گلدنست میگوید قیمت نده، پایگاه دانش قیمت دارد). کدام را عوض میکنید و چرا؟ تغییرش دهید و دوباره اجرا کنید.
۳. داور را گول بزنید. با آداپتور fixture پاسخی بنویسید که لحن عالی ولی کاملاً دروغ باشد. آیا داور وفاداری میگیردش؟ اگر نه، روبریک را چطور اصلاح میکنید؟
۴. ۲۰ کیس را دستی برچسب بزنید و کاپا را ببینید. اگر زیر ۰٫۶ بود، کدام روبریک را عوض میکنید؟
۵. داور را عوض کنید. JUDGE_MODEL را روی anthropic/claude-haiku-4.5 بگذارید و دوباره اجرا کنید. نمرهها فرق کردند؟ کدام داور به انسان نزدیکتر است؟
۶. یک داور جدید بسازید: «کامل بودن پاسخ» (آیا به همهی اجزای سؤال جواب داد؟).
۷. ۱۰ کیس تازه از فهرست شکاف دانشِ صفحهی تولید بسازید.
۸. ارزیابی را به CI ببرید: یک GitHub Action که روی هر پولریکوئست npm run eval -- --limit 8 را بزند و اگر نمره زیر ۸۰ بود، بیلد را قرمز کند.
۹. سنجش بازیابی را عمیق کنید: الان فقط تعداد و شباهت منابع را میبینیم. برای محاسبهی precision/recall واقعی چه دادهی دیگری لازم است؟
چرا مقایسهی دو اجرا (A/B) در داشبورد نیست؟ عمداً. الان میتوانید دو اجرا را با برچسب متفاوت بسازید و اعدادشان را کنار هم ببینید. صفحهی مقایسهی خودکار یک تمرین خوب است، نه یک نیاز اولیه.
چرا precision/recall واقعیِ بازیابی حساب نمیشود؟ چون برای آن باید برای هر سؤال، از قبل مشخص کنیم «کدام تکهها مرتبطاند» — کاری پرزحمت و دستی. الان به جایش سیگنال ارزانتری داریم: بازیابی شد یا نه، و با چه شباهتی. برای شروع کافی است.
آیا این ابزار فقط برای آرکان کار میکند؟
هستهاش جنریک است. آنچه مخصوص آرکان است: آداپتور http-chat.ts (قرارداد API)، متن زمینه در rubrics.ts، و لیست عبارتهای ممنوعه در checks.ts. هر سه در فایلهای جدا و قابل تعویضاند.
داور اشتباه میکند؟ بله، حتماً. برای همین صفحهی «داورِ داور» ساخته شده. ابزار ارزیابی هم یک نرمافزار است و خودش باید ارزیابی شود.
| فاز | چه ساختیم | مفهوم اصلی |
|---|---|---|
| ۱ | وبسایت | — |
| ۲ | چتبات RAG | بازیابی، embedding، ابزار |
| ۳ | استودیوی تولید محتوا | چند-ایجنتی، ارکستراسیون با کد، حلقهی خودبهبودی |
| ۴ | CRM | ابزارمحوری، AI در جریان کاری واقعی |
| ۵ | آرکانایوال | سنجش، داور هوش مصنوعی، متا-ارزیابی |
فاز ۵ اولین فازی است که چیز جدیدی نمیسازد — به جایش میپرسد «آنچه ساختیم واقعاً کار میکند؟» و این سؤالی است که معمولاً هیچوقت پرسیده نمیشود.