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

قرارداد قطعه و شاهد را پیش از بازیابی تعریف کنیم

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

متن آزمایش «راهنما: برق قطع؛ سپس کابل جدا شود.» است؛ این جمله صرفاً دادهٔ ساختگی برنامه است، نه دستورالعمل تعمیر یا ایمنی. طول آن در قرارداد ما ۳۴ نقطهٔ کد است. قطعه یک رکورد شامل شمارهٔ ترتیبی، آغاز، پایان و متن استخراج‌شده دارد. محدودهٔ [start,end) از صفر شروع می‌شود، آغاز را شامل می‌شود و پایان را شامل نمی‌شود. بنابراین طول قطعه باید دقیقاً برابر تفاضل دو مرز باشد.

شاهدِ انتخابی «برق قطع؛ سپس» در محدودهٔ [8,20) قرار دارد و ۱۲ نقطهٔ کد دارد. این شاهد کوچک، نمونه‌ای برای آزمودن محصورشدن یک عبارت است؛ کامل‌بودن معنایی پاسخ را تعریف نمی‌کند. ورودی تابع علاوه بر مرزها، نقل‌قول موردانتظار را هم می‌گیرد. اگر برش سند با نقل‌قول یکسان نباشد، گزارش تولید نمی‌شود. برای سندی با عبارت تکراری، محل وقوع باید صریح باشد؛ انتخاب نخستین نتیجهٔ جست‌وجوی رشته جای این قرارداد را نمی‌گیرد.

برای جایگاه این مرحله در سامانه، راهنمای RAG و پایگاه دانش سازمانی را ببینید. آزمون این مقاله فقط پیش‌شرط مکانیِ آماده‌سازی شاهد را بررسی می‌کند؛ رتبه‌بندی، استناد پاسخ و مجوز دسترسی به سند آزمون‌های جداگانه می‌خواهند.

واحد offset؛ نقطهٔ کد با بایت و توکن فرق دارد

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

طبق مستندات رسمی str پایتون، رشته دنباله‌ای تغییرناپذیر از نقاط کد Unicode است. پس len و برش این برنامه بر همین واحد کار می‌کنند. هیچ تبدیل واحدی به بایت، توکن مدل یا تعداد حروفِ دیده‌شده در صفحه انجام نمی‌دهیم. نوشتن واژهٔ مبهم «کاراکتر» در قرارداد بین دو سرویس می‌تواند همین تفاوت را پنهان کند.

کاوشگر مستقل «می‌رود🙂é» در فایل کد به‌صورت صریح، نیم‌فاصله و علامت ترکیبی دارد. خروجی اجرا ۹ نقطهٔ کد، ۲۰ بایت UTF-8 و ۱۰ واحد کد UTF-16 است. حرف لاتین و اعراب ترکیبی آن دو نقطهٔ کد جدا هستند؛ پنجرهٔ یک‌واحدی می‌تواند آن‌ها را از هم جدا کند. Unicode HOWTO پایتون نیز تفاوت نمایش ظاهری و تعداد نقاط کد را توضیح می‌دهد. این قطعه‌ساز، مرز واژه یا خوشهٔ نویسه‌ای را حفظ نمی‌کند.

در مرجع Text Split مایکروسافت، خروجی offset برای UTF-8، UTF-16 و CodePoint تفکیک شده است. این منبع اهمیت نام‌گذاری واحد را روشن می‌کند؛ برنامهٔ ما پیاده‌سازی آن سرویس نیست. همچنین متن را پاک‌سازی یا نرمال نمی‌کنیم. اگر لایهٔ جست‌وجو از کلید نرمال‌شدهٔ فارسی استفاده کند، offset آن کلید را بدون نگاشت معتبر به سند اصلی منتقل نکنید.

پنجره، هم‌پوشانی و پایان سند را بدون ابهام بسازیم

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

اندازهٔ پنجره را ۱۶ و هم‌پوشانی را ۴ نقطهٔ کد می‌گذاریم. گام حرکت ۱۲ است. محدوده‌های موردانتظار، با محاسبهٔ دستی، [0,16)، [12,28) و [24,34) هستند. قطعهٔ آخر فقط ده واحد دارد. هنگامی که پایان یک قطعه به پایان سند رسید، حلقه متوقف می‌شود؛ ادامه‌دادن صرفاً برای رسیدن به شروع بعدی، دنباله‌ای زائد ایجاد می‌کند.

در این قرارداد آموزشی، اندازهٔ پنجره عدد صحیح دقیق بین یک و ۱۰۲۴ است؛ هم‌پوشانی عدد صحیح نامنفی و کوچک‌تر از پنجره است. bool، float و رشتهٔ عددی پذیرفته نمی‌شوند. این محدودیت، گام مثبت را تضمین می‌کند. ورودی نیز رشتهٔ دقیق و حداکثر ۴۰۹۶ نقطهٔ کد است. سند خالی هیچ قطعه‌ای ندارد؛ سند کوتاه‌تر از پنجره فقط یک قطعه دارد. این‌ها سقف‌های قراردادی مثال‌اند، نه محدودیت عمومی پایگاه دانش.

راهنمای قطعه‌بندی اسناد مایکروسافت هم‌پوشانی را یکی از انتخاب‌های حفظ زمینه معرفی می‌کند و وابستگی انتخاب‌ها به محتوا را مطرح می‌کند. اندازه‌ها و قواعد این مثال مستقل‌اند؛ آن‌ها را به‌عنوان تنظیم پیشنهادی Azure یا مقدار بهینهٔ مدل معرفی نمی‌کنیم. برنامه فقط کتابخانهٔ استاندارد می‌خواهد و بدون شبکه اجرا می‌شود.

"""Offline, authored code-point chunking and evidence-span acceptance tests.

This is not a tokenizer, retrieval evaluation, production RAG service or an
implementation of Azure Text Split. No model, network or document download runs.
"""
import json
import platform


def chunk_text(text, window, overlap):
    """Keep the exact source str; return nonempty half-open code-point slices."""
    if type(text) is not str:
        raise TypeError('text must be an exact str')
    if len(text) > 4096:
        raise ValueError('teaching input limit is 4096 code points')
    if type(window) is not int or type(overlap) is not int:
        raise TypeError('window and overlap must be exact int, not bool')
    if not 1 <= window <= 1024 or not 0 <= overlap < window:
        raise ValueError('require 1<=window<=1024 and 0<=overlap<window')
    chunks = []
    start = 0
    while start < len(text):
        end = min(start + window, len(text))
        chunks.append({'ordinal': len(chunks), 'start': start, 'end': end,
                       'text': text[start:end]})
        if end == len(text):
            break  # Do not emit a redundant tail already covered by this chunk.
        start = end - overlap
    return chunks


def evidence_report(text, window, overlap, start, end, expected_quote):
    """Verify one declared occurrence; no find()-based occurrence guessing."""
    chunks = chunk_text(text, window, overlap)
    if type(start) is not int or type(end) is not int:
        raise TypeError('span endpoints must be exact int, not bool')
    if not 0 <= start < end <= len(text):
        raise ValueError('require a nonempty half-open source span')
    if type(expected_quote) is not str:
        raise TypeError('expected quote must be an exact str')
    if text[start:end] != expected_quote:
        raise ValueError('expected quote does not match this source occurrence')
    containing = [c['ordinal'] for c in chunks
                  if c['start'] <= start and end <= c['end']]
    # This diagnostic is intentionally different from single-chunk containment.
    union_complete = all(any(c['start'] <= p < c['end'] for c in chunks)
                         for p in range(start, end))
    return {'span': [start, end], 'quote': expected_quote,
            'containing_ordinals': containing,
            'all_span_code_points_in_chunk_union': union_complete}


def fails(error, function, *args):
    try:
        function(*args)
    except error:
        return True
    return False


def closed_form_ranges(n, window, overlap):
    """Independent count/stride oracle, not the production end-update loop."""
    if n == 0:
        return []
    stride = window - overlap
    count = 1 + (max(0, n - window) + stride - 1) // stride
    return [(j * stride, min(j * stride + window, n)) for j in range(count)]


def run_example():
    text = 'راهنما: برق قطع؛ سپس کابل جدا شود.'
    chunks4 = chunk_text(text, 16, 4)
    chunks8 = chunk_text(text, 16, 8)
    quote = 'برق قطع؛ سپس'
    evidence4 = evidence_report(text, 16, 4, 8, 20, quote)
    evidence8 = evidence_report(text, 16, 8, 8, 20, quote)
    intervals4 = [(c['start'], c['end']) for c in chunks4]
    intervals8 = [(c['start'], c['end']) for c in chunks8]
    covered = {p for c in chunks4 for p in range(c['start'], c['end'])}
    no_overlap = chunk_text(text, 16, 0)
    unicode_probe = 'می\u200cرود🙂e\u0301'
    units = {'code_points': len(unicode_probe),
             'utf8_bytes': len(unicode_probe.encode('utf-8')),
             'utf16_code_units': len(unicode_probe.encode('utf-16-le')) // 2}
    small_cases = 0
    closed_form_agrees = True
    for n in range(21):
        for width in range(1, 7):
            for shared in range(width):
                actual = chunk_text('x' * n, width, shared)
                closed_form_agrees &= [(c['start'], c['end']) for c in actual] == closed_form_ranges(n, width, shared)
                small_cases += 1
    span_cases = 0
    set_oracle_agrees = True
    for shared in (4, 8):
        cs = chunk_text(text, 16, shared)
        chunk_sets = [set(range(c['start'], c['end'])) for c in cs]
        for left in range(len(text)):
            for right in range(left + 1, len(text) + 1):
                expected_ids = [j for j, positions in enumerate(chunk_sets)
                                if set(range(left, right)) <= positions]
                actual = evidence_report(text, 16, shared, left, right, text[left:right])
                set_oracle_agrees &= actual['containing_ordinals'] == expected_ids
                span_cases += 1
    repeated = 'خاموش خاموش'
    checks = {
        'hand_ranges_window16_overlap4': intervals4 == [(0, 16), (12, 28), (24, 34)],
        'hand_chunk_texts_include_spaces_punctuation_and_short_tail':
            [c['text'] for c in chunks4] == ['راهنما: برق قطع؛', 'قطع؛ سپس کابل جد', 'ل جدا شود.'],
        'every_chunk_is_exact_original_half_open_slice': all(c['text'] == text[c['start']:c['end']] for c in chunks4),
        'ordinals_lengths_and_source_boundaries_are_consistent':
            [c['ordinal'] for c in chunks4] == [0, 1, 2] and
            all(0 <= c['start'] < c['end'] <= 34 and len(c['text']) <= 16 for c in chunks4) and
            chunks4[0]['start'] == 0 and chunks4[-1]['end'] == 34,
        'all34_code_points_covered_with8_extra_stored_occurrences':
            covered == set(range(34)) and sum(len(c['text']) for c in chunks4) == 42,
        'union_coverage_does_not_imply_single_chunk_evidence':
            evidence4['all_span_code_points_in_chunk_union'] is True and evidence4['containing_ordinals'] == [],
        'overlap8_contains_this_same_span_and_stores58_occurrences':
            intervals8 == [(0, 16), (8, 24), (16, 32), (24, 34)] and
            evidence8['containing_ordinals'] == [1] and sum(len(c['text']) for c in chunks8) == 58,
        'zero_overlap_is_lossless_union_without_repeated_occurrences':
            ''.join(c['text'] for c in no_overlap) == text and
            [(c['start'], c['end']) for c in no_overlap] == [(0, 16), (16, 32), (32, 34)],
        'whole_document_window_returns_one_identical_chunk':
            chunk_text(text, 34, 0) == [{'ordinal': 0, 'start': 0, 'end': 34, 'text': text}],
        'larger_window_and_exact_boundary_do_not_emit_redundant_tail':
            len(chunk_text(text, 100, 3)) == 1 and len(chunk_text('x' * 16, 16, 4)) == 1,
        'empty_document_returns_no_chunks_and_cannot_have_nonempty_evidence':
            chunk_text('', 16, 4) == [] and fails(ValueError, evidence_report, '', 16, 4, 0, 0, ''),
        'one_code_point_and_maximal_allowed_overlap_make_progress':
            len(chunk_text('ی', 16, 15)) == 1 and
            [(c['start'], c['end']) for c in chunk_text('abcd', 2, 1)] == [(0, 2), (1, 3), (2, 4)],
        'unicode_units_differ_and_combining_mark_is_not_grapheme_safe':
            units == {'code_points': 9, 'utf8_bytes': 20, 'utf16_code_units': 10} and
            ''.join(c['text'] for c in chunk_text(unicode_probe, 1, 0)) == unicode_probe and
            unicode_probe[2] == '\u200c' and unicode_probe[7:8] == 'e' and unicode_probe[8:9] == '\u0301',
        'repeated_quote_uses_declared_occurrence_and_rejects_mismatch':
            evidence_report(repeated, 6, 0, 6, 11, 'خاموش')['containing_ordinals'] == [1] and
            fails(ValueError, evidence_report, repeated, 6, 0, 1, 6, 'خاموش'),
        '441_small_document_cases_match_closed_form_interval_oracle': bool(closed_form_agrees) and small_cases == 441,
        '1190_nonempty_spans_match_independent_set_containment_oracle': bool(set_oracle_agrees) and span_cases == 1190,
        'invalid_text_window_overlap_types_values_and_input_bound_rejected':
            len(chunk_text('x' * 4096, 1024, 0)) == 4 and
            all(fails(TypeError, chunk_text, bad, 16, 4) for bad in (None, b'x', 1, ['x'])) and
            all(fails(TypeError, chunk_text, 'abc', w, o) for w, o in ((True, 0), (2.0, 0), (2, False), (2, 0.0))) and
            all(fails(ValueError, chunk_text, 'abc', w, o) for w, o in ((0, 0), (-1, 0), (1025, 0), (2, -1), (2, 2), (2, 3))) and
            fails(ValueError, chunk_text, 'x' * 4097, 16, 4),
        'invalid_span_types_bounds_and_quote_type_rejected':
            all(fails(TypeError, evidence_report, text, 16, 4, a, z, quote) for a, z in ((True, 20), (8, 20.0))) and
            all(fails(ValueError, evidence_report, text, 16, 4, a, z, quote) for a, z in ((-1, 20), (8, 8), (20, 8), (8, 35))) and
            fails(TypeError, evidence_report, text, 16, 4, 8, 20, b'quote'),
    }
    if len(checks) != 18 or not all(type(value) is bool and value for value in checks.values()):
        raise AssertionError(checks)
    return {'python': platform.python_version(),
            'scope': 'Authored offline Unicode-code-point chunks and exact-source evidence spans; no RAG model, tokenizer, Azure call, retrieval or physical safety test.',
            'fixture_text': text, 'source_code_points': len(text),
            'offset_unit': 'Unicode code points; zero-based half-open [start,end)',
            'chunks_overlap4': chunks4, 'evidence_overlap4': evidence4,
            'chunks_overlap8': chunks8, 'evidence_overlap8': evidence8,
            'stored_occurrences': {'overlap4': 42, 'overlap8': 58, 'unique_source': 34},
            'unicode_probe_units': units,
            'closed_form_fixture_cases': small_cases, 'set_oracle_span_cases': span_cases,
            'checks': checks}


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

پوشش سراسری سند با نگه‌داشتن شاهد در یک قطعه برابر نیست

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

برای هم‌پوشانی چهار، تمام ۳۴ موقعیت سند در اجتماع محدوده‌ها وجود دارند. مجموع طول قطعات ۴۲ است؛ بنابراین هشت رخداد ذخیره‌شدهٔ اضافی داریم. این عدد، تعداد بایت فایل یا هزینهٔ توکن مدل نیست. باوجود پوشش سراسری، هیچ قطعه‌ای شاهد [8,20) را کامل نگه نمی‌دارد: قطعهٔ اول زود تمام می‌شود و قطعهٔ دوم دیر شروع می‌شود.

با هم‌پوشانی هشت، گام هشت می‌شود و چهار محدودهٔ [0,16)، [8,24)، [16,32) و [24,34) داریم. اکنون قطعهٔ شمارهٔ یک، با شماره‌گذاری از صفر، شاهد انتخاب‌شده را کامل در بر می‌گیرد. مجموع طول‌ها ۵۸ و رخدادهای اضافی ۲۴ است. موفقیت این شاهد، تضمین همهٔ عبارت‌های ۱۲واحدی، بهبود بازیابی یا انتخاب اندازهٔ بهینه نیست؛ فقط نتیجهٔ همین داده و قرارداد است.

مرجع مستقل اول، تعداد قطعات و آغازها را با فرمول بسته و گام ثابت حساب می‌کند و در ۴۴۱ ترکیب کوچک با حلقه مقایسه می‌شود. مرجع دوم، موقعیت‌های هر قطعه را مجموعه می‌سازد و زیرمجموعه‌بودن شاهد را می‌سنجد؛ در برابر آزمون نامساوی مرزها، ۱۱۹۰ حالتِ شاهد بررسی می‌شود. این پیمایش‌ها دو گروه از ۱۸ بررسی نام‌دار هستند، نه هزاران آزمون نام‌دار تازه. خروجی زیر اجرای Python 3.14.5 را ثبت می‌کند.

{
  "python": "3.14.5",
  "scope": "Authored offline Unicode-code-point chunks and exact-source evidence spans; no RAG model, tokenizer, Azure call, retrieval or physical safety test.",
  "fixture_text": "راهنما: برق قطع؛ سپس کابل جدا شود.",
  "source_code_points": 34,
  "offset_unit": "Unicode code points; zero-based half-open [start,end)",
  "chunks_overlap4": [
    {
      "ordinal": 0,
      "start": 0,
      "end": 16,
      "text": "راهنما: برق قطع؛"
    },
    {
      "ordinal": 1,
      "start": 12,
      "end": 28,
      "text": "قطع؛ سپس کابل جد"
    },
    {
      "ordinal": 2,
      "start": 24,
      "end": 34,
      "text": "ل جدا شود."
    }
  ],
  "evidence_overlap4": {
    "span": [
      8,
      20
    ],
    "quote": "برق قطع؛ سپس",
    "containing_ordinals": [],
    "all_span_code_points_in_chunk_union": true
  },
  "chunks_overlap8": [
    {
      "ordinal": 0,
      "start": 0,
      "end": 16,
      "text": "راهنما: برق قطع؛"
    },
    {
      "ordinal": 1,
      "start": 8,
      "end": 24,
      "text": "برق قطع؛ سپس کاب"
    },
    {
      "ordinal": 2,
      "start": 16,
      "end": 32,
      "text": " سپس کابل جدا شو"
    },
    {
      "ordinal": 3,
      "start": 24,
      "end": 34,
      "text": "ل جدا شود."
    }
  ],
  "evidence_overlap8": {
    "span": [
      8,
      20
    ],
    "quote": "برق قطع؛ سپس",
    "containing_ordinals": [
      1
    ],
    "all_span_code_points_in_chunk_union": true
  },
  "stored_occurrences": {
    "overlap4": 42,
    "overlap8": 58,
    "unique_source": 34
  },
  "unicode_probe_units": {
    "code_points": 9,
    "utf8_bytes": 20,
    "utf16_code_units": 10
  },
  "closed_form_fixture_cases": 441,
  "set_oracle_span_cases": 1190,
  "checks": {
    "hand_ranges_window16_overlap4": true,
    "hand_chunk_texts_include_spaces_punctuation_and_short_tail": true,
    "every_chunk_is_exact_original_half_open_slice": true,
    "ordinals_lengths_and_source_boundaries_are_consistent": true,
    "all34_code_points_covered_with8_extra_stored_occurrences": true,
    "union_coverage_does_not_imply_single_chunk_evidence": true,
    "overlap8_contains_this_same_span_and_stores58_occurrences": true,
    "zero_overlap_is_lossless_union_without_repeated_occurrences": true,
    "whole_document_window_returns_one_identical_chunk": true,
    "larger_window_and_exact_boundary_do_not_emit_redundant_tail": true,
    "empty_document_returns_no_chunks_and_cannot_have_nonempty_evidence": true,
    "one_code_point_and_maximal_allowed_overlap_make_progress": true,
    "unicode_units_differ_and_combining_mark_is_not_grapheme_safe": true,
    "repeated_quote_uses_declared_occurrence_and_rejects_mismatch": true,
    "441_small_document_cases_match_closed_form_interval_oracle": true,
    "1190_nonempty_spans_match_independent_set_containment_oracle": true,
    "invalid_text_window_overlap_types_values_and_input_bound_rejected": true,
    "invalid_span_types_bounds_and_quote_type_rejected": true
  }
}

معیار پذیرش و چهار تمرین با پاسخ

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

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

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

تمرین دو: چرا پایان شاهد ۲۰ است، نه ۱۹؟ پاسخ: آخرین موقعیت شامل‌شده ۱۹ است و قرارداد نیمه‌باز، مرز پایان را یک واحد بعد ثبت می‌کند. طول عبارت برابر ۲۰ منهای هشت، یعنی ۱۲ است.

تمرین سه: آیا پنجرهٔ یک‌واحدی مرز نویسهٔ نمایشی را حفظ می‌کند؟ پاسخ: خیر؛ در کاوشگر، e و علامت ترکیبی به دو برش می‌روند. برای مرزبندی نمایشی به سیاست و ابزار مناسب خوشهٔ نویسه‌ای نیاز داریم.

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

منابع در ۲۴ مهر ۱۴۰۵ خوانده شده‌اند. این آموزش پایه و تألیفی است؛ تاریخ به‌روزرسانی صفحات، تاریخ نخستین انتشار پژوهش نیست. مستندات Python 3.14.8 مرجع بوده و اجرای ثبت‌شده با 3.14.5 انجام شده است.

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

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