در ارزیابی متن حاصل از گفتار مهندسی، ممکن است بیشتر واژه‌ها درست باشند ولی مقدار، واحد یا شناسهٔ دستگاه عوض شده باشد. این درس سه اسلاتِ ازپیش‌جداشدهٔ ولتاژ، جریان و شناسه را با مرجع مقایسه می‌کند. پنج جفت خروجی را خودمان ساخته‌ایم تا نوع اختلاف روشن باشد. هیچ صوت، مدل ASR، اندازه‌گیری یا فرمان واقعی وجود ندارد؛ موضوع، قرارداد ارزیابی همین دادهٔ کوچک است.

۱. خطای واژه و تطبیق اسلات پرسش‌های متفاوت‌اند

جدایی خطای واژه از درست‌بودن اسلات بحرانی
تصویر مفهومی: جدایی خطای واژه از درست‌بودن اسلات بحرانی؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

اسلات در اینجا یک فیلد مشخصِ اطلاعات است. مرجعِ همهٔ نمونه‌ها ولتاژ ۳٫۳ ولت، جریان ۱۲ میلی‌آمپر و شناسهٔ D07 دارد. خروجی فرضی را هم به همین سه فیلد تقسیم کرده‌ایم؛ کد از جملهٔ آزاد اسلات استخراج نمی‌کند. برای اینکه عدد نهایی معنی داشته باشد، در هر جفت باید بدانیم مرجع چگونه برچسب خورده و کدام فیلد مورد انتظار است.

مقالهٔ SLURP در EMNLP 2020 ارزیابی موجودیت‌ها و مقدار متنی آن‌ها را جدا از صرفِ اختلاف رونوشت بررسی می‌کند. آن پژوهش مربوط به دادهٔ انگلیسی است و سنجهٔ SLU-F1 پیشنهاد می‌دهد. درس ما آن سنجه را پیاده نمی‌کند؛ تنها انگیزهٔ بررسی جداگانهٔ اطلاعات ساختاری را می‌گیرد و برای سه فیلدِ ساختگی، قواعد ساده و مستقلی تعریف می‌کند.

محاسبهٔ WER و هم‌ترازی واژه‌ها همچنان برای بررسی رونوشت مفید است. اینجا آن محاسبه تکرار نمی‌شود. تبدیل V به mV ممکن است فقط یک واژه را تغییر دهد، اما مقدار تفسیرشده را هزار برابر عوض کند؛ در مقابل، نوشتن یک مقدار با واحد هم‌ارز می‌تواند متن متفاوت و مقدار یکسان بسازد. هر دو نوع ارزیابی باید نام و قرارداد روشن داشته باشند.

۲. قرارداد محدودِ شکل عدد و واحد را اعلام کنیم

قرارداد محدود شکل عدد و واحد اندازه‌گیری
تصویر مفهومی: قرارداد محدود شکل عدد و واحد اندازه‌گیری؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

برای ولتاژ و جریان، ورودی دقیقاً دو فیلد number و unit دارد. number باید رشته باشد؛ عدد شناور و bool پذیرفته نمی‌شوند. رقم‌های ASCII، فارسی و عربی و نشانهٔ اعشار «٫» پشتیبانی می‌شوند. پس از نگاشت صریح، علامت مثبت یا منفی اختیاری، حداکثر شش رقم صحیح و حداکثر سه رقم اعشار مجاز است. صفر ابتداییِ اضافه، فاصلهٔ ابتدا یا انتها و نمایش علمی پذیرفته نیست.

فقط چهار صورت نوشتاریِ گفتاری در جدول داریم: «سه ممیز سه»، «سه هزار و سیصد»، «دوازده» و «صفر ممیز صفر یک دو». این جدول دستور زبان عدد فارسی نیست؛ مثلاً «سیزده» خارج از قرارداد است. unit نیز از فهرست صریح V، mV، A، mA و نام‌های فارسیِ تعیین‌شده انتخاب می‌شود. تفاوت حروف کوچک و بزرگ یا صورتِ نیم‌فاصله‌دار را خودکار حدس نمی‌زنیم.

جدول پیشوندهای SI در NIST، milli با نماد m را یک‌هزارم معرفی می‌کند. کد، واحد را همراه نوع کمیت می‌سنجد: واحد جریان در اسلات ولتاژ نامعتبر است، حتی اگر عددش همان باشد. حد مطلق هزار ولت و صد آمپر فقط کران انتخابیِ ورودی این ارزیابی است و هیچ توصیه‌ای دربارهٔ طراحی یا بهره‌برداری مدار نمی‌دهد.

۳. مقدار هم‌ارز را با حساب کسری مقایسه کنیم

مقایسهٔ مقدار هم‌ارز با واحد متفاوت
تصویر مفهومی: مقایسهٔ مقدار هم‌ارز با واحد متفاوت؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

در نمونهٔ اول، خروجی ۳۳۰۰ میلی‌ولت و ۰٫۰۱۲ آمپر می‌دهد. هر دو با مرجع برابرند: ۳۳۰۰ تقسیم بر هزار، ۳٫۳ ولت و ۱۲ تقسیم بر هزار، ۰٫۰۱۲ آمپر است. مقدار پایهٔ دقیق ولتاژ سی‌وسه‌دهم و جریان سه‌دویست‌وپنجاهم می‌شود. همراه شناسهٔ درست D07، هر سه اسلاتِ این نمونه تطبیق دارند؛ یکسان‌بودن رشتهٔ خام شرط ما نیست.

مسیر اصلی رقم‌های صحیح و اعشاری را به صورت و مخرج صحیح تبدیل می‌کند و از Fraction پایتون استفاده می‌کند. ابتدا float نمی‌سازیم؛ مثلاً ساختن کسر از floatِ ۰٫۱ می‌تواند کسر نمایش دودویی را وارد کند. برابریِ این مثال دقیق است و تلرانس اندازه‌گیری، تعداد رقم معنی‌دار یا گردکردن برای مقادیر نزدیک تعریف نشده‌اند.

در نمونهٔ دوم، جریان ۱۲ آمپر آمده است. واحد از نوع جریان و مقدار در کران قراردادی است، اما با ۱۲ میلی‌آمپر برابر نیست؛ وضعیت mismatch می‌گیرد. تبدیل همهٔ واحدها به شکل نوشتاری مشترک بدون اعمال ضریب، چنین اختلافی را پنهان می‌کند. در همین حال، اسلات ولتاژ و شناسه درست‌اند؛ گزارش باید نشان دهد کدام فیلد مشکل دارد.

۴. کد کامل و رد ابهام عدد و شناسه

رد ورودی مبهم و حفظ تفاوت شناسهٔ دستگاه
تصویر مفهومی: رد ورودی مبهم و حفظ تفاوت شناسهٔ دستگاه؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

فایل critical_speech_slots.py با Python 3.14.5 اجرا شده است. رشتهٔ 3,3 را حدس نمی‌زند و نامعتبر می‌شمارد؛ کاما، جداکنندهٔ هزارگان، NaN، بی‌نهایت و صورت‌های پشتیبانی‌نشده نیز رد می‌شوند. شناسه دقیقاً D و دو رقم ASCII است؛ D07 با D70 متفاوت است و D7 به D07 تبدیل نمی‌شود. نرمال‌سازی عدد، مجوز نرمال‌سازی شناسه نیست.

"""Synthetic pre-extracted voltage/current/device slot evaluation only.

Not ASR, free-form Persian parsing, a WER scorer or an electrical decision rule.
"""
from copy import deepcopy
from decimal import Decimal, localcontext
from fractions import Fraction
import json
import platform
import re

SLOTS = ('voltage', 'current', 'device')
DIGITS = str.maketrans('۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩٫', '01234567890123456789.')
WORD_NUMBERS = {'سه ممیز سه': '3.3', 'سه هزار و سیصد': '3300',
                'دوازده': '12', 'صفر ممیز صفر یک دو': '0.012'}
UNITS = {'V': ('voltage', Fraction(1)), 'ولت': ('voltage', Fraction(1)),
         'mV': ('voltage', Fraction(1, 1000)), 'میلی\u200cولت': ('voltage', Fraction(1, 1000)),
         'A': ('current', Fraction(1)), 'آمپر': ('current', Fraction(1)),
         'mA': ('current', Fraction(1, 1000)), 'میلی\u200cآمپر': ('current', Fraction(1, 1000))}


def exact_number(text):
    if type(text) is not str or len(text) > 48:
        raise ValueError('number_format')
    normalized = WORD_NUMBERS.get(text, text).translate(DIGITS)
    if re.fullmatch(r'[+-]?(?:0|[1-9][0-9]{0,5})(?:\.[0-9]{1,3})?', normalized, re.ASCII) is None:
        raise ValueError('number_format')
    negative = normalized.startswith('-')
    unsigned = normalized.lstrip('+-')
    whole, dot, decimals = unsigned.partition('.')
    numerator = int(whole + decimals)
    return Fraction(-numerator if negative else numerator, 10 ** len(decimals))


def canonical_slot(name, value):
    if name == 'device':
        if type(value) is not str or re.fullmatch(r'D[0-9]{2}', value, re.ASCII) is None:
            raise ValueError('device_id')
        return value
    if name not in ('voltage', 'current'):
        raise ValueError('slot_name')
    if type(value) is not dict or set(value) != {'number', 'unit'}:
        raise ValueError('quantity_fields')
    unit = value['unit']
    if type(unit) is not str or unit not in UNITS:
        raise ValueError('unit')
    dimension, scale = UNITS[unit]
    if dimension != name:
        raise ValueError('unit_dimension')
    base = exact_number(value['number']) * scale
    if abs(base) > (1000 if name == 'voltage' else 100):
        raise ValueError('number_range')
    return dimension, base


def evaluate_cases(cases):
    if type(cases) is not list or not 1 <= len(cases) <= 16:
        raise ValueError('cases')
    seen = set()
    report = []
    counts = {'match': 0, 'missing': 0, 'invalid': 0, 'mismatch': 0}
    for case in cases:
        if type(case) is not dict or set(case) != {'case_id', 'reference', 'hypothesis'}:
            raise ValueError('case_fields')
        case_id = case['case_id']
        if type(case_id) is not str or re.fullmatch(r'[A-Za-z][A-Za-z0-9_]{0,15}', case_id, re.ASCII) is None or case_id in seen:
            raise ValueError('case_id')
        seen.add(case_id)
        reference, hypothesis = case['reference'], case['hypothesis']
        if type(reference) is not dict or set(reference) != set(SLOTS):
            raise ValueError('reference_fields')
        if type(hypothesis) is not dict or not set(hypothesis) <= set(SLOTS):
            raise ValueError('hypothesis_fields')
        try:
            gold = {name: canonical_slot(name, reference[name]) for name in SLOTS}
        except ValueError:
            raise ValueError('reference_value') from None
        outcomes, reasons = {}, {}
        for name in SLOTS:
            if name not in hypothesis:
                state = 'missing'
            else:
                try:
                    predicted = canonical_slot(name, hypothesis[name])
                    state = 'match' if predicted == gold[name] else 'mismatch'
                except ValueError as error:
                    state = 'invalid'
                    reasons[name] = str(error)
            outcomes[name] = state
            counts[state] += 1
        report.append({'case_id': case_id, 'slots': outcomes, 'invalid_reasons': reasons,
                       'all_slots_match': all(state == 'match' for state in outcomes.values())})
    case_matches = sum(row['all_slots_match'] for row in report)
    total_slots = 3 * len(cases)
    return {'per_case': report,
            'totals': {'slot_matches': counts['match'], 'slot_total': total_slots,
                       'case_matches': case_matches, 'case_total': len(cases),
                       'slot_accuracy': str(Fraction(counts['match'], total_slots)),
                       'all_slots_case_accuracy': str(Fraction(case_matches, len(cases))),
                       'missing': counts['missing'], 'invalid': counts['invalid'],
                       'mismatch': counts['mismatch']}}


def fixture():
    reference = {'voltage': {'number': '۳٫۳', 'unit': 'V'},
                 'current': {'number': '۱۲', 'unit': 'mA'}, 'device': 'D07'}
    predictions = [
      {'voltage': {'number': '3300', 'unit': 'mV'}, 'current': {'number': '0.012', 'unit': 'A'}, 'device': 'D07'},
      {'voltage': {'number': '3.3', 'unit': 'V'}, 'current': {'number': '12', 'unit': 'A'}, 'device': 'D07'},
      {'voltage': {'number': '3.3', 'unit': 'A'}, 'device': 'D70'},
      {'voltage': {'number': 'سه ممیز سه', 'unit': 'ولت'}, 'current': {'number': 'دوازده', 'unit': 'میلی\u200cآمپر'}, 'device': 'D07'},
      {'voltage': {'number': '3,3', 'unit': 'V'}, 'current': {'number': '۱۲', 'unit': 'mA'}, 'device': 'D07'}]
    return [{'case_id': 'Case' + str(i + 1), 'reference': deepcopy(reference), 'hypothesis': h}
            for i, h in enumerate(predictions)]


def rejected(call, message):
    try:
        call()
    except ValueError as error:
        return str(error) == message
    return False


def run_example():
    cases = fixture()
    before = deepcopy(cases)
    result = evaluate_cases(cases)
    expected = [
      {'voltage': 'match', 'current': 'match', 'device': 'match'},
      {'voltage': 'match', 'current': 'mismatch', 'device': 'match'},
      {'voltage': 'invalid', 'current': 'missing', 'device': 'mismatch'},
      {'voltage': 'match', 'current': 'match', 'device': 'match'},
      {'voltage': 'invalid', 'current': 'match', 'device': 'match'}]
    hand_total = {'slot_matches': 10, 'slot_total': 15, 'case_matches': 2,
                  'case_total': 5, 'slot_accuracy': '2/3', 'all_slots_case_accuracy': '2/5',
                  'missing': 1, 'invalid': 2, 'mismatch': 2}
    missing = deepcopy(cases[:1]); missing[0]['hypothesis'] = {}
    missing_report = evaluate_cases(missing)['totals']
    bad_reference = deepcopy(cases[:1]); bad_reference[0]['reference']['current']['unit'] = 'V'
    bad_reference_field = deepcopy(cases[:1]); del bad_reference_field[0]['reference']['current']
    extra = deepcopy(cases[:1]); extra[0]['hypothesis']['unknown'] = 'x'
    extra_case = deepcopy(cases[:1]); extra_case[0]['extra'] = True
    oracle_cases, oracle_ok = 0, True
    with localcontext() as context:
        context.prec = 40
        for name, unit, scale in [('voltage', 'V', Decimal(1)), ('voltage', 'mV', Decimal('0.001')),
                                   ('current', 'A', Decimal(1)), ('current', 'mA', Decimal('0.001'))]:
            for number in ['0', '0.001', '0.125', '-0.125', '3.3', '12', '99', '100', '-1']:
                expected_value = Fraction(Decimal(number) * scale)
                oracle_ok &= canonical_slot(name, {'number': number, 'unit': unit}) == (name, expected_value)
                oracle_cases += 1
    checks = {
      'independent_hand_five_case_status_matrix': [row['slots'] for row in result['per_case']] == expected,
      'hand_totals_and_two_distinct_denominators': result['totals'] == hand_total,
      'equivalent_voltage_and_current_units': canonical_slot('voltage', {'number': '3300', 'unit': 'mV'}) == ('voltage', Fraction(33, 10)) and canonical_slot('current', {'number': '12', 'unit': 'mA'}) == ('current', Fraction(3, 250)),
      'dimensions_cannot_match_by_same_numeric_value': rejected(lambda: canonical_slot('voltage', {'number': '3.3', 'unit': 'A'}), 'unit_dimension') and canonical_slot('voltage', {'number': '1', 'unit': 'V'}) != canonical_slot('current', {'number': '1', 'unit': 'A'}),
      'closed_spoken_forms_and_unit_aliases': canonical_slot('voltage', {'number': 'سه هزار و سیصد', 'unit': 'میلی\u200cولت'}) == ('voltage', Fraction(33, 10)) and canonical_slot('current', {'number': 'صفر ممیز صفر یک دو', 'unit': 'آمپر'}) == ('current', Fraction(3, 250)),
      'digit_sets_sign_and_decimal_precision': exact_number('٣٫٣') == exact_number('۳٫۳') == Fraction(33, 10) and exact_number('-0.125') == Fraction(-1, 8) and exact_number('+0.100') == Fraction(1, 10) and exact_number('-0') == 0,
      'device_identifiers_keep_zero_and_order': canonical_slot('device', 'D07') != canonical_slot('device', 'D70') and all(rejected(lambda value=value: canonical_slot('device', value), 'device_id') for value in ['D7', 'D007', 'd07', True]),
      'wrong_milli_prefix_is_a_value_mismatch': result['per_case'][1]['slots']['current'] == 'mismatch' and canonical_slot('current', {'number': '12', 'unit': 'A'}) != canonical_slot('current', {'number': '12', 'unit': 'mA'}),
      'all_missing_predictions_still_count_three_slots': missing_report['slot_total'] == 3 and missing_report['missing'] == 3 and missing_report['slot_accuracy'] == '0' and missing_report['case_total'] == 1,
      'invalid_predictions_stay_in_report_denominator': result['totals']['slot_total'] == 15 and result['totals']['invalid'] == 2 and result['per_case'][2]['invalid_reasons'] == {'voltage': 'unit_dimension'} and result['per_case'][4]['invalid_reasons'] == {'voltage': 'number_format'},
      'invalid_reference_value_or_missing_reference_aborts': rejected(lambda: evaluate_cases(bad_reference), 'reference_value') and rejected(lambda: evaluate_cases(bad_reference_field), 'reference_fields'),
      'dataset_case_and_unknown_field_contracts': rejected(lambda: evaluate_cases([]), 'cases') and rejected(lambda: evaluate_cases(cases * 4), 'cases') and rejected(lambda: evaluate_cases([cases[0], cases[0]]), 'case_id') and rejected(lambda: evaluate_cases(extra), 'hypothesis_fields') and rejected(lambda: evaluate_cases(extra_case), 'case_fields'),
      'ambiguous_or_unsupported_numeric_notation_reject': all(rejected(lambda text=text: exact_number(text), 'number_format') for text in ['3,3', '1,000', '۱٬۰۰۰', '1e3', 'NaN', 'inf', ' 3', '3 ', '۰۱', '−3', '1/2', '0.0001', '1000000', 'سیزده']),
      'unit_case_and_unlisted_spelling_reject': all(rejected(lambda unit=unit: canonical_slot('current', {'number': '1', 'unit': unit}), 'unit') for unit in ['ma', 'MA', 'میلی آمپر', 'amp', '']) and rejected(lambda: canonical_slot('current', {'number': '1', 'unit': []}), 'unit'),
      'quantity_type_bool_and_extra_fields_reject': rejected(lambda: canonical_slot('voltage', True), 'quantity_fields') and rejected(lambda: canonical_slot('voltage', {'number': '1', 'unit': 'V', 'confidence': 1}), 'quantity_fields') and all(rejected(lambda value=value: exact_number(value), 'number_format') for value in [True, 3.3, None]),
      'independent_decimal_conversion_oracle': oracle_cases == 36 and bool(oracle_ok),
      'input_unchanged_and_aggregate_order_invariant': cases == before and evaluate_cases(list(reversed(cases)))['totals'] == result['totals'],
      'explicit_base_value_bounds': canonical_slot('voltage', {'number': '1000', 'unit': 'V'}) == ('voltage', Fraction(1000)) and canonical_slot('current', {'number': '100', 'unit': 'A'}) == ('current', Fraction(100)) and rejected(lambda: canonical_slot('voltage', {'number': '1000.001', 'unit': 'V'}), 'number_range') and rejected(lambda: canonical_slot('current', {'number': '100.001', 'unit': 'A'}), 'number_range'),
    }
    assert len(checks) == 18 and all(type(value) is bool and value for value in checks.values()), checks
    return {'python': platform.python_version(), 'dependencies': 'Python standard library only',
            'scope': 'Synthetic pre-extracted three-slot comparison, own limited numeric/spoken-form policy; no ASR execution, WER recomputation or electrical action',
            'results': {'hand_report': result,
                        'canonical_reference': {'voltage': '33/10 V', 'current': '3/250 A', 'device': 'D07'},
                        'oracle_cases_inside_one_group': oracle_cases,
                        'normalization_policy': 'ASCII/Persian/Arabic digits and decimal marker plus four exact spoken aliases; no general Persian number parser'},
            'checks': checks}


if __name__ == '__main__':
    print(json.dumps(run_example(), ensure_ascii=False, indent=2))

مرجع مستقل برای سی‌وشش تبدیل کوچک از Decimalِ رشته‌ای، ضریب مستقل یک یا یک‌هزارم و localcontext با دقت چهل استفاده می‌کند. این مقایسه یک گروه از هجده گروه اجرایی است. ماتریس دستیِ پنج جفت نیز جدا بررسی شده است؛ زیرحالت‌ها یا اجرای دوباره، تعداد بررسی جدید نمی‌سازند. هیچ کتابخانهٔ تشخیص گفتار یا سنجش WER اجرا نشده است.

نمونهٔ سوم برای ولتاژ، واحد A دارد و invalid می‌گیرد؛ جریانش missing و شناسهٔ D70 نسبت به مرجع mismatch است. نمونهٔ چهارم با دو صورتِ گفتاریِ مجاز هر سه فیلد را درست می‌دهد. در نمونهٔ پنجم فقط ولتاژِ دارای کاما invalid است. اگر خود مرجع نامعتبر یا ناقص باشد، ارزیابی خطا می‌دهد؛ خطای مرجع به موفقیت یا حذف بی‌اعلان نمونه تبدیل نمی‌شود.

۵. مخرج‌ها و چهار پرسش پاسخ‌دار را روشن گزارش کنیم

گزارش جداگانهٔ خطای مقدار و واحد و شناسه
تصویر مفهومی: گزارش جداگانهٔ خطای مقدار و واحد و شناسه؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

پنج نمونه، پانزده اسلات مورد انتظار دارند. ده اسلات match، یک مورد missing، دو مورد invalid و دو مورد mismatch هستند؛ نسبت تطبیق اسلات‌ها ده‌پانزدهم، یعنی دوسوم است. فقط نمونه‌های اول و چهارم هر سه اسلات را درست دارند؛ نسبت نمونهٔ کاملاً منطبق دوپنجم است. خروجیِ گم‌شده یا نامعتبر در مخرج پانزده باقی می‌ماند.

{
  "python": "3.14.5",
  "dependencies": "Python standard library only",
  "scope": "Synthetic pre-extracted three-slot comparison, own limited numeric/spoken-form policy; no ASR execution, WER recomputation or electrical action",
  "results": {
    "hand_report": {
      "per_case": [
        {
          "case_id": "Case1",
          "slots": {
            "voltage": "match",
            "current": "match",
            "device": "match"
          },
          "invalid_reasons": {},
          "all_slots_match": true
        },
        {
          "case_id": "Case2",
          "slots": {
            "voltage": "match",
            "current": "mismatch",
            "device": "match"
          },
          "invalid_reasons": {},
          "all_slots_match": false
        },
        {
          "case_id": "Case3",
          "slots": {
            "voltage": "invalid",
            "current": "missing",
            "device": "mismatch"
          },
          "invalid_reasons": {
            "voltage": "unit_dimension"
          },
          "all_slots_match": false
        },
        {
          "case_id": "Case4",
          "slots": {
            "voltage": "match",
            "current": "match",
            "device": "match"
          },
          "invalid_reasons": {},
          "all_slots_match": true
        },
        {
          "case_id": "Case5",
          "slots": {
            "voltage": "invalid",
            "current": "match",
            "device": "match"
          },
          "invalid_reasons": {
            "voltage": "number_format"
          },
          "all_slots_match": false
        }
      ],
      "totals": {
        "slot_matches": 10,
        "slot_total": 15,
        "case_matches": 2,
        "case_total": 5,
        "slot_accuracy": "2/3",
        "all_slots_case_accuracy": "2/5",
        "missing": 1,
        "invalid": 2,
        "mismatch": 2
      }
    },
    "canonical_reference": {
      "voltage": "33/10 V",
      "current": "3/250 A",
      "device": "D07"
    },
    "oracle_cases_inside_one_group": 36,
    "normalization_policy": "ASCII/Persian/Arabic digits and decimal marker plus four exact spoken aliases; no general Persian number parser"
  },
  "checks": {
    "independent_hand_five_case_status_matrix": true,
    "hand_totals_and_two_distinct_denominators": true,
    "equivalent_voltage_and_current_units": true,
    "dimensions_cannot_match_by_same_numeric_value": true,
    "closed_spoken_forms_and_unit_aliases": true,
    "digit_sets_sign_and_decimal_precision": true,
    "device_identifiers_keep_zero_and_order": true,
    "wrong_milli_prefix_is_a_value_mismatch": true,
    "all_missing_predictions_still_count_three_slots": true,
    "invalid_predictions_stay_in_report_denominator": true,
    "invalid_reference_value_or_missing_reference_aborts": true,
    "dataset_case_and_unknown_field_contracts": true,
    "ambiguous_or_unsupported_numeric_notation_reject": true,
    "unit_case_and_unlisted_spelling_reject": true,
    "quantity_type_bool_and_extra_fields_reject": true,
    "independent_decimal_conversion_oracle": true,
    "input_unchanged_and_aggregate_order_invariant": true,
    "explicit_base_value_bounds": true
  }
}

پرسش اول: آیا ۳۳۰۰ میلی‌ولت با ۳٫۳ ولت برابر است؟ پاسخ: طبق ضریب یک‌هزارم، بله؛ تطبیق روی مقدار پایهٔ کسری انجام می‌شود. این برابری دربارهٔ دو رشتهٔ مرجع و خروجی است، نه صحت یک اندازه‌گیری واقعی.

پرسش دوم: اگر خروجی هر سه فیلد را ندهد چه می‌شود؟ پاسخ: هر سه missing می‌شوند و همان سه اسلات در مخرج می‌مانند. خالی‌بودن خروجی پیش‌بینی، علت حذف نمونه از ارزیابی نیست.

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

پرسش چهارم: آیا دوسوم، کیفیت یک محصول صوتی است؟ پاسخ: خیر؛ فقط نتیجهٔ پنج جفت دست‌نویس است. برای ارزیابی گفتار فارسی داده، برچسب و آزمون مستقل لازم است. منابع در ۲۴ مهر ۱۴۰۵ خوانده شدند؛ مقالهٔ SLURP مربوط به نوامبر ۲۰۲۰ و آخرین به‌روزرسانی صفحهٔ NIST، ۱۳ اوت ۲۰۲۵ است. این مطلب آموزش پایه و بدون ادعای خبر تازه است.

منابع و مطالعهٔ بیشتر

بررسی منابع: ۲۴ مهر ۱۴۰۵. تحلیل‌ها و پیشنهادهای اجرایی، جمع‌بندی نویسنده‌اند.