Галоўная / Артыкулы

Практычныя прыказкі: стварэнне локальнага каналу RAG высокай якосці для прымення ў практыцы — 100% безкоштовна, без…

Практычныя прыказкі: стварэнне локальнага каналу RAG высокай якосці для прымення ў практыцы — 100% безкоштовна, без дагавораў, пераканальвання і можлівасцяў для вставкі коду для команд, якія выкарыстоўваюць гэты патэрн.

Выдавец: REACTAPP.TOP21 хв чытання4736 слоў

Крыніцы: Building a Production-Grade Local RAG Pipeline — 100% Free, No Cloud Required

У гэтым керавані зноў ствараецца парадокс ад сыр'ёў да рабочай сістэмы для: стварэння локальнага каналу RAG высокай якосці — 100% безкоштовна, без патрэбы ў хмарах. Акцэнт ставіцца на практычныя крокі, чысткія перакананні і код, які можна проста дадаць у рэпозытарый без неабязковасці здогадвацца пра мету. У стадзіі агляду неабходна практычна візначыць інпуты, адпаведальную особу за крок і крытэрыя завершэння прычынамі перад зменым коду. Аператары должны магчымае перадзванаць крок з вядомай точкі контролю без неабязковасці здагадвацца пра схованы стан. Неабходна адначасова задокументаваць шлях успеху і шлях вяснавання. Перапрыбуткі, людзкія перакананні і апрацоўка некорэктных паведамленняў ёсць часткай продукту, а не чымсь, што дадаецца пазней.

Чаму гэта важна

Калі працюеце над этапам «Чаму гэта важна», спачатку запісайце умовы кантракту: неабяжлівыя даны, сігнал успеху і тое, што выходзіць пад частыя неудачы. Такі спіс контроля дапамагае заліцьварыць пазнейшыя змены ў кодзе. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выйшоў, неудача должна вказваць на адну конкрэтную адпаведальнасць, а не на заплутаны ланцужок задач. Перад налаштаваннем запітоў пераканайцеся, як працуе сістэма з воспамінанням на фіксаванай сэтцы запітаў. Частая зміна запітоў рэдка калі вярнайце слабкую эфектыўнасць выкарыстоўвання інформацыі.

Полны тэхнічны стак

Калі працюеце над стадзіяй «The Complete Tech Stack», спачатку запісайце контракт: неабяжлівыя даныя, сігнал успеху і тое, што выходзіць у разе частковага нэуспеху. Такі спіс контроля дапамагае залічыць пазнейшыя змены коду чыстымі. Спрыятліваце гэтую стадзію як контракт межу вхіднымі данымі і перакананымі выходнымі рэзультатамі. Даўце назвы артыфактам, задаце крытэрыя успеху і адмовіцеся ад тыхоўскага частковага завершэння. Замерыўце рэкалі на фіксаванай сэтке запытаў прычым падлашоўвання прапаза. Частая змена прапаза рэдка калі вярнуе слабую якасць адзысквання інформацыі.

Прыямыя прычынкі

Калі працюеце над стадзіяй «Прыёмныя умовы», спачатку запісайце «контракт»: неабяжлівыя даныя, сігнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі спіс пераконвае ў тым, што пазнейшыя змены коду будуць чыстымі. Запісвайце час выканання і кост токена або запыту праза функцыйнальнымі рэзултатамі. Відразы коста з самага пачатку запобегае неспакоўным рахункам, калі працэс пераходзіць з дэмаверсіі ў спяльныя асяроддзя. Замерьце ступень воскрэсення інформацыі на фіксаванай сэтке запытаў прычым рэгулюванню прапозыцый. Частае змены прапозыцый рэдка калі-небудзь выправляюць слабыя аспекты пошуку інформацыі. Калі працюеце над стадзіяй «Прыёмныя умовы», спачатку запісайце «контракт»: неабяжлівыя даныя, сігнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі спіс пераконвае ў тым, што пазнейшыя змены коду будуць чыстымі. Аддокументавайце як «шчаслівы» шлях, так і шлях вяснавання. Перапрыбуткі, людзкія контралі і обработка некорэктных паведамленняў ёсць часткай продукту, а не пазнейшым дапрацоўкам.

Частка 1 — Наладжэнне асяроддзя

Этап налагоджэння супакоўкі ў частцы 1 працюе найэфектывней, калі яго спрыяваць як мерыемую структуру. Зафіксавайце адну ідэальную версію, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перш чым расшырваць масштабы. Валідзіце невялікія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выходзіць, прычына неудачы павінна вказываць на адну конкрэтную адпаведальнасць, а не на заплутаны ланцюг задач. Раздзеліце правілы частковай обработкі дадзеных ад правіл ўтрымання іх. Змена адных не павінна вымагаць перапісву іншых, калі зменяюцыся показнікі якосці.

Крок 1: Стварыце праект

Першы шаг – стварэнне сцэны – работае найкрашча, калі яго спрыяюць як до мерыям падлеглую паверхню. Зафіксавце адны «золаты» прымер, адну справу з бягамі і прыметкі па адварцыю перш чым расшырваце масштабы. Спрыяйце гэтам шагу як кантракту межаў уваходных дадзеных і перакананыя выходныя рэзультаты. Даўце назвы артыфактам, задаце крытэрыя успеху і адмовіцеся ад мовчанкавага частковага завершэння. Раздзеліце політыку часткавага абрабатвання дадзеных ад політыкі ўтрымання іх. Змена адной з яных не павінна прымусваць перапісванне другой, калі зменяюцыся паказнікі якосці.

mkdir local-rag
cd local-rag
uv init

Шаг 2: Стварэнне і актывацыя віртуальной асяроддзя

Крок 2 «Стварыць і размістыць» працуе найэфектывней, калі яго розглядаць як параметр, які можна вымерыць. Зберагчыце адны ідэальны прыклад працы, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану, перш чым расшырваць масштабы. Запісвайце час выканання задач і кост токеноў або запытаў разам з функцыйнальнымі рэзултатамі. Відразы костаў з самага пачатку запобегае неспакойным рахункам, калі працэс пераходзіць з дэмаверсіі ў спяльныя асяроддзя. Раздзеляйце правілы частковага апрацоўкі дадзеных і правілы ўтрымання інформацыі. Змена адных не павінна вымагаць перапісвы іншых, калі зменяюцца паказнікі якасці.

uv venv
.venv\Scripts\activate

Крок 2 «Стварыць і размістыць» працуе найэфектывней, калі яго розглядаць як параметр, які можна вымерыць. Зберагчыце адны ідэальны прыклад працы, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану, перш чым расшырваць масштабы. Дакументавайце як шлях успеху, так і шлях вярнэння да нормальнага стану. Перапрыбуткі, людзкі контроль і апрацоўка некорэктных паведамленняў ёсць часткай продукту, а не етапамі далейшай налады.

Крок 3: Установіць усе залежнасці

Для трэцьяго крока – установкі ўсіх элементаў на пэўны ўровень – неабходна загадзя вызначыць параметры входных дадзеных, адпаведальнага за крок і крытэрыя завершэння працы перад змінайом коду. Аператары должны магчымае перзапускаць крок з вядомай точкі контролю, не прабуючы спадарацца пра схованы стан. Лепш выбіраць маленькія, тэставаныя елементы замест большых скрыптов. Калі крок не выконваецца, прычына неудачы должна вказываць на адну конкрэтную прычыну, а не на заплутаную структуру працэса. Прытамульвайце тыя часткі тэксту, якія фактычна лежаць у падставе адпаведнай адказы. Без ціх цытатаў аператары не зможуць разлічыць галюцынацію ад працягу індэксавання.

uv add google-genai pypdf chromadb rich python-dotenv huggingface_hub fpdf2

Крок 4: Стварэнне структуры праекта

Для крока 4 «Стварыць этап» неабяжна пазначыць вхідныя даны, адпаведальнага за крок і крытэрыі завершэння пры перадзеяванні коду. Аператары должны магчымаць перзапуск крока з вядомай точкі контролю, не падозрываючы прыхованы стан. Спрыяйце цэму этапу як кантракту межа вхіднымі данымі і перакананымі выходнымі рэзультатамі. Дайце назвы артыфактам, пазначыце перакананні успеху і адмовіцеся ад беззвучнага частковага завершэння. Апісуйце тыя часткі, якія фактычна лежалі в основе адпаведнай адказы. Без ціх апісаў аператары не зможуць разлічыць галюцынацію ад прасоў у індэксаванні.

mkdir pdfs
mkdir pdfs\versions
type nul > local_rag.ipynb
type nul > .env
type nul > .gitignore
local-rag/
├── .venv/                    ← virtual environment (never commit)
├── pdfs/                     ← drop your PDFs here
│   └── versions/             ← test PDFs for CDC testing
├── chroma_db/                ← auto-created on first ingest
├── memory_checkpoints/       ← auto-created on first memory session
├── staleness_registry.json   ← auto-created
├── chunk_registry.json       ← auto-created
├── local_rag.ipynb           ← your notebook
├── .env                      ← API keys (never commit)
├── .gitignore
└── pyproject.toml

Крок 5: Наладжыць ключы API

Для этапы «Настроўка API» па кроку 5 неабходна перад змянай коду вказацьці інпутаў, адпаведальнага за крок і крэтарыяў завершэння. Аперацыйныя працавнікі должны магчымае запускаць крок з вядомай точкі контролю, не падозрываючы прыхованы стан. Запісваць час выканання і кост токенаў або запытаў разам з функцыйнаімі рэзультатамі. Відразлівая відавальнасць костаў з самага пачатку запобегае неспакойным рахункам, калі працэс пераходзіць з дэмовай асяроддзя ў спакульнаныя сераверы. Указваць часткі тэксту, якія фактычна ляглі в основу адпаведнай адказы. Без цых цітатаў аперацыйныя працавнікі не можуць разлічыць галюцынацію ад прасоў у індэксаванні. Для этапы «Настроўка API» па кроку 5 неабходна перад змянай коду вказацьці інпутаў, адпаведальнага за крок і крэтарыяў завершэння. Аперацыйныя працавнікі должны магчымае запускаць крок з вядомай точкі контролю, не падозрываючы прыхованы стан. Аддзеўна задокументаваць шлях успеху і шлях вярнення. Перапрыбуткі, людзкія контролі і обработка некорэктных паведамленняў ёсць часткай продукту, а не чымсь, што дадаецца пазней.

GEMINI_API_KEY=your_gemini_key_here
HF_API_KEY=your_huggingface_token_here
.env
chroma_db/
memory_checkpoints/
staleness_registry.json
chunk_registry.json
__pycache__/
.venv/
*.pyc

Шаг 6: Налаштаванне VS Code

Працюючы над шагам налаштавання, спачатку запішыце умовы працы: неабяжлівыя даны, сігнал успеху і тое, што выходзіць, калі адбываецца частковая памылка. Такі спіс контролю дапамагае залішацца чыстым пад час будучых змян у кодзе. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь шаг не выйшаў, памылка должна вказываць на адну конкрэтную прычыну, а не на заплутаную структуру задач. Перад налаштаваннем запітоў пераканайцеся, што сістэма правільна адпавядае на фіксаваныя запітанні. Часта змена запітоў рэдка калі-небудзь выправляе слабую якасць адпаведзей.

Ctrl+Shift+P → Python: Select Interpreter → .venv\Scripts\python.exe

Частка 2 — Падробны аналіз основнай структуры

Калі працуеце над стадзіяй «Core Pipeline», часткай 2, спачатку запісаце кантракт: неабяжлівыя вхідныя даны, сігнал успеху і тое, што выходзіць у разе частковага невыпання. Такі спіс контролю дапамагае заліцьварыць пазнейшыя змены коду. Спрэтавайцеся да гэтай стадзіі як да кантракта межа вхіднымі данымі і перакананымі выходнымі рэзультатамі. Даць назвы артыфактам, задаць крытэрыя успеху і не падзеўляйцеся частковым завершэнням без паведамлення. Змяроўваце рэкалі на фіксаванай сэтке запытаў прычым падбір прамптав. Частае змена прамптав рэдка калі вярнуе слабую эфектыўнасць адзысквання інформацыі.

Клетка 1 — Залежнасці (внутрь нотбука)

Калі працуеце над Залежнасцямі Клеткі 1 унутрь стадыі, спачатку запісайце контракт: неабяжлівыя вхідныя даны, сігнал успеху і тое, што выходзіць пад частковым невясненням. Такі спіс пераканаець у тым, што пазнейшыя змены коду будуць чыстымі. Запісвайце час выканання і кост токена або запыту празаўседле з рэзультатамі функцыянальнай працы. Візуабельнасць костаў з самага пачатку запобегае неспакойным рахункам, калі парадокс пераходзіць з дэмаверсіі ў спяльныя асяроддзя. Замерьце рэткі адзыв на фіксаваны набор запытаў прычым рэгулюванню прапазаў. Частыя змены прапазаў рэдка калі-небудзь вылечваюць слабую сістэму адзыва.

# Run once inside the notebook if uv add was not used externally
# %pip install google-genai pypdf chromadb rich python-dotenv huggingface_hub fpdf2

Калі працуеце над Залежнасцямі Клеткі 1 унутрь стадыі, спачатку запісайце контракт: неабяжлівыя вхідныя даны, сігнал успеху і тое, што выходзіць пад частковым невясненням. Такі спіс пераканаець у тым, што пазнейшыя змены коду будуць чыстымі. Дакументавайце як «шчаслівы» парадокс, так і парадокс вяснавання. Перапрыбуткі, людзкія контралі і обработка нераспакаваных паведамленняў ёсць часткай продукту, а не пазнейшым дапрацоўкам.

Cell 2 — Канфігурацыя

Этап канфігурацыі Cell 2 дае найлепшыя рэзультаты, калі яго спрытваць як меравальную плошчу. Зберагачыце адна ідэальная версія, адзін случай неудачы і прыметкі па поверненню да пачатковага стану пры расшырэнні масштаба. Вядзейце прывялікія, тэставальныя елементы замест большых скрыптов. Калі якісь крок не выйшае, прычына неудачы павінна вказываць на адну конкрэтную адпаведальнасць, а не на заплутаны ланцюг задач. Раздзеляйце правілы часткавання інфармацыі ад правіл яе выявлення. Змена адных не павінна прымусваць перапісванне іншых, калі змянююцца паказчыкі якосці.

import os
from dotenv import load_dotenv

load_dotenv()
GEMINI_API_KEY     = os.environ.get("GEMINI_API_KEY", "")
EMBED_DIM          = 768
HF_API_KEY         = os.environ.get("HF_API_KEY", "")
GEMINI_EMBED_MODEL = "gemini-embedding-001"
HF_LLM_MODEL       = "openai/gpt-oss-20b:groq"
CHROMA_DB_PATH     = "./chroma_db"
COLLECTION_NAME    = "local_rag"
CHUNK_SIZE         = 800
CHUNK_OVERLAP      = 120
TOP_K              = 5
EMBED_BATCH_SIZE   = 50
BATCH_SLEEP_SEC    = 0.3
LLM_MAX_NEW_TOKENS = 1024
LLM_TEMPERATURE    = 0.1
assert GEMINI_API_KEY, "❌ GEMINI_API_KEY not set"
assert HF_API_KEY,     "❌ HF_API_KEY not set"

Cell 3 — Выяўленне тэксту з PDF

Этап PDF-тэкста «Cell 3» працюе найкрашча, калі яго розглядаць як вімерную паверхню. Зафіксавце адны ідэальны прыклад, адну справу з бягамі і запіс пра вярнэнне да пачатковага стану, перш чым расшырваць масштабы. Разглядзіце гэты этап як кантракт межа вхіднымі данымі і паверынутымі выходнымі рэзультатамі. Даце назвы артыфактам, задаце крытэрыі успеху і не прабуйце прыймаць часткова завершаную працу без паведамлення. Раздзеліце правілы часткавання ад правіл нараджэння дадзеных. Змена адных не павінна вымагаць перапісву іншых, калі зменяюцца паказчыкі якосці.

from pypdf import PdfReader

def extract_text_from_pdf(pdf_path: str) -> tuple[str, int]:
    reader = PdfReader(pdf_path)
    pages = []
    for i, page in enumerate(reader.pages):
        text = page.extract_text()
        if text and text.strip():
            pages.append(f"[Page {i + 1}]\n{text.strip()}")
    return "\n\n".join(pages), len(reader.pages)

Cell 4 — Часткаванне за прынцыпам «скользячага вікна»

Этап «Cell 4 Sliding Window» працюе найкрашча, калі яго розглядаць як вімерную паверхню. Зафіксавце адны прыклад ідэальнага функцыонавання, адны прыклад неудачы і запіс пра вярнэнне да пачатковага стану перш чым расшырваць масштабы. Запісвайце часы выканання і косты токеноў або запытак праза функцыйнальныя рэзултаты. Відразлівае паказанне костаў з’являецца перашкоду неспакою, калі працэс пераходзіць з дэмаверсіі ў спяльныя асяроддзя. Раздзеліце політыку часткавага апрацоўвання дадзеных ад політыкі ўтрымання іх. Змена адной з яных не павінна вымагаць перапісвання другой, калі зменяюцца паказнікі якосці.

def chunk_text(text: str) -> list[str]:
    chunks, start = [], 0
    while start < len(text):
        end   = start + CHUNK_SIZE
        chunk = text[start:end].strip()
        if len(chunk) >= 80:
            chunks.append(chunk)
        start += CHUNK_SIZE - CHUNK_OVERLAP
    return chunks

Этап «Cell 4 Sliding Window» працюе найкрашча, калі яго розглядаць як вімерную паверхню. Зафіксавце адны прыклад ідэальнага функцыонавання, адны прыклад неудачы і запіс пра вярнэнне да пачатковага стану перш чым расшырваць масштабы. Дакументавайце як шлях ідэальнага функцыонавання, так і шлях вярнэння да нормы. Перапрыбуткі, людзкія контрольныя пункты і апрацоўка некорэктных паведамленняў ёсць часткай продукту, а не чымсь, што дадаецца пазней.

Cell 5 — Gemini Embeddings

Для этапы Cell 5 Gemini Embeddings неабяжна практыка: перш чым зменіць код, неабяжна адзначыць вхідныя даны, адпаведальнага за шаг і крытэрыі завершэння. Аперацыяныя працавнікі должны магчымаць перзапуск шага з вядомай точкі контролю, не падозрываючы схованы стан. Лепш выбіраць маленькія, тэставаныя елементы замест большых скрыптов. Калі шаг не выйшаў, прычына неабяжна адносіцца да конкрэтнай адпаведальнасці, а не да заплутанага ланца задач. Раздзеляйце стварэнне кліента ад цыклу перадачы паведамленняў, каб было можна змяніць прадаўцоў без перапісвання машыны стану размовы.

from google import genai
from google.genai import types

genai_client = genai.Client(api_key=GEMINI_API_KEY)
def embed_documents_batch(chunks: list[str]) -> list[list[float]]:
    all_embeddings = []
    for i, chunk in enumerate(chunks, 1):
        result = genai_client.models.embed_content(
            model=GEMINI_EMBED_MODEL,
            contents=chunk,
            config=types.EmbedContentConfig(
                task_type="RETRIEVAL_DOCUMENT",
                output_dimensionality=EMBED_DIM,
            ),
        )
        all_embeddings.append(result.embeddings[0].values)
    return all_embeddings
def embed_query(text: str) -> list[float]:
    result = genai_client.models.embed_content(
        model=GEMINI_EMBED_MODEL,
        contents=text,
        config=types.EmbedContentConfig(
            task_type="RETRIEVAL_QUERY",
            output_dimensionality=EMBED_DIM,
        ),
    )
    return result.embeddings[0].values

Cell 6 — Хранэнне і выкарыстоўванне дадзеных у ChromaDB

Для этапы зберагчэння ChromaDB у клетцы 6 неабяжна прадзефінаваць вхідныя даны, адпаведальнага за крок і крытэрыі завершэння пры змяне коду. Аперацыйныя працавнікі павінны магчымаць перзапуск кроку з вядомай точкі контролю, не падозрываючы прыхованы стан. Спрыяйце гэтай этапы як даговору межа вхіднымі данымі і перакананымі выходнымі рэзультатамі. Даўце назвы артыфактам, прадзефінаваць пераканання ў успеху і адмовіцеся ад тыхнай частковай рэалізацыі без паведамлення. Цытавайце тыя часткі, якія фактычна лежалі в основе адпаведнай адказы. Без цітатаў аперацыйныя працавнікі не зможуць адразліць галюцынацыю ад прасоўкі ў індэксаванні.

import uuid, chromadb

def get_collection():
    client = chromadb.PersistentClient(path=CHROMA_DB_PATH)
    return client.get_or_create_collection(
        name=COLLECTION_NAME,
        metadata={"hnsw:space": "cosine"},
    )
def store_in_chroma(chunks, embeddings, doc_name):
    collection = get_collection()
    ids        = [str(uuid.uuid4()) for _ in chunks]
    metadatas  = [{"source": doc_name, "chunk_index": i}
                  for i in range(len(chunks))]
    collection.add(ids=ids, embeddings=embeddings,
                   documents=chunks, metadatas=metadatas)
    return len(chunks)
def retrieve_context(query: str) -> list[dict]:
    collection      = get_collection()
    query_embedding = embed_query(query)
    results = collection.query(
        query_embeddings=[query_embedding],
        n_results=TOP_K,
        include=["documents", "metadatas", "distances"],
    )
    chunks = []
    for doc, meta, dist in zip(results["documents"][0],
                               results["metadatas"][0],
                               results["distances"][0]):
        chunks.append({
            "text":        doc,
            "source":      meta.get("source", "unknown"),
            "chunk_index": meta.get("chunk_index", -1),
            "score":       round(1 - dist, 4),
        })
    return sorted(chunks, key=lambda x: x["score"], reverse=True)

Клетка 7 — Адканаўленне прыемкі

Для этапы працы Cell 7 Running Ingestion неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крытэрыя завершэння пры зміне коду. Аператары должны магчыма было перзапускаць крок з вядомай точкі контролю, не падозрываючы прыхованы стан. Запісваць час выканання і вартасць токена або запыту разам з функцыйнальнымі рэзултатамі. Відразы вартасці з самага пачатку запобегае неспакоўным рахункам, калі працэс пераходзіць з дэмовай асяроддзя ў спяльныя сераўеры. Указваць тые часткі тэксту, якія фактычна ляглі в основу адпаведнай адпаведзі. Без цых цытатаў аператары не можуць розразліць галюцынацію і прычыну, якая вынікла ў процэсе індэксавання.

PDF_PATH = "./pdfs/attention.pdf"

raw_text, page_count = extract_text_from_pdf(PDF_PATH)
chunks               = chunk_text(raw_text)
embeddings           = embed_documents_batch(chunks)
stored               = store_in_chroma(chunks, embeddings,
                                        os.path.basename(PDF_PATH))

Для этапы апрацоўкы даных у Клетцы 7 неабяжна пазначыць вхідныя даны, адпаведнага адпаведальнага за крок і крэтыры завершэння пры змяне коду. Аперацыйныя працавнікі должны магчымаць перзапуск кроку з вядомай точкі контролю, не прабуючы выявіць схованы стан. Неабяжна задокументаваць як шлях успеху, так і шлях вярнення да нормальнага стану. Перапрыбуткі, людзкія перакрыцці і апрацоўка некорэктных паведамленняў ёсць часткай продукту, а не чымсь, што дадаецца пазней.

Клетка 8 — LLM: gpt-oss-20b праз HuggingFace

Калі працюеце над стадзіяй Cell 8 LLM gpt-oss-20b, спачатку запісайце угоду: неабяжлівыя даны, сігнал успеху і тое, што выходзіць у разе частковага неяксамоства. Такі спіс пераканальвае ў тым, каб пазнейшыя змены коду былі чыстымі. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок неяксамоўць, неяксамоства должны адносіцца да адной адпаведальнасці, а не да заплутанага ланцоўка. Зберагайце у кэшы стабільныя інструкцыі сістэмы і схемы інструментаў. Перадзесланне ідэнтычных прамаўляючых частак — частая прычына вітрацы рэсурсаў.

from huggingface_hub import InferenceClient

hf_client = InferenceClient(api_key=HF_API_KEY)
def build_messages(query: str, context_chunks: list[dict]) -> list[dict]:
    context_str  = "\n\n---\n\n".join([
        f"[Source: {c['source']} | Chunk #{c['chunk_index']} | "
        f"Relevance: {c['score']}]\n{c['text']}"
        for c in context_chunks
    ])
    return [
        {"role": "system",  "content": SYSTEM_MSG},
        {"role": "user",    "content":
            f"CONTEXT:\n{context_str}\n\nQUESTION:\n{query}"},
    ]
def generate_answer(messages: list[dict]) -> str:
    completion = hf_client.chat.completions.create(
        model=HF_LLM_MODEL,
        messages=messages,
        max_tokens=LLM_MAX_NEW_TOKENS,
        temperature=LLM_TEMPERATURE,
    )
    return completion.choices[0].message.content.strip()

Cell 9–11 — The ask() Pipeline and REPL

Калі працюеце над этапам «Cell 9 11 The stage», спачатку запісайце умовы кантракту: неабяжлівыя даны, сігнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі спіс контроля дапамагае заліцьварыць пазнейшыя змены ў кодзе. Спрэцьвачваеце гэты этап як кантракт межа данымі і перакананымі выходамі. Дайце назвы рэзультатам, задаце крытэрыя успеху і не прымайце часткова завершання без паведамлення. Змяркуйце рэгрэсію на фіксаванай сэтке запытанняў прычым падбір запытань. Частая змена запытань рэдка калі вядомае слабую ступеню адзысквання інформацыі.

query → embed_query() → ChromaDB cosine search → top-5 chunks
      → build_messages() → generate_answer() → printed answer
1. attention.pdf chunk #34  [██████████████████████░░░░░░░░]  0.7335

Частка 3 — Памяць размовы

Калі працюеце над стадзіяй «Пам’ятка размовы» часткі 3, спачатку запішыце контракт: неабяжлівыя даны, сігнал успеху і тое, што выходзіць у разе частковага невыпання. Такі спіс перакладзець пазнейшыя змены коду ў правільным напрамку. Запісвайце часы выканання і кост токеноў або запіта праза функцыональныя рэзултаты. Відразлівасць костаў з самага пачатку запобегае неспакойным рахункам, калі працэс пераходзіць з дэмаверыянту ў спакульнае сераўысце. Замерьце спроможнасць адзычэння на фіксаванай сэтке запытаў прычым рэгулювання прапаза. Частыя змены прапаза рэдка калі вядуць да павышэння якасці адзычэння даных.

Проблема

Калі працюеце над стадзіяй рашэння проблемы, спачатку запісайце умовы: неабяжлівыя даны, сігнал успеху і тое, што выходзіць пад частыя неудачы. Такі спіс контролю дапамагае заліцьварыць пазнейшыя змены ў кодзе. Храніце настройкі парадульна ад коду прыемліка. Файлы сераўіса, хранальнікі секрэтных даных і флагі функцыйяў должны знаходзіцца ў аднам месцы, куды аператары можаць адбавіць аўдыт без неабяжлівага чытання всей структуры. Перад налаштаваннем запітоў пераканайцеся ў рэверыі на фіксаванай сэтке запытаў. Частае змены запытаў рэдка калі выправляюць слабую эфектыўнасць адзысквання інформацыі.

Рашэнне: ConversationMemory

Калі працуеце над стадзіяй ConversationMemory, спачатку запісайце умовы працы: неабяжлівыя даныя, сігнал успеху і тое, што выканаецца у разе частковага невыпалення. Такі спіс контроля дапамагае заліцьварыць пазнейшыя змены коду.

class ConversationMemory:
    def __init__(self, session_id: str = None):
        self.session_id = session_id or datetime.now().strftime("%Y%m%d_%H%M%S")
        self.filepath   = os.path.join(MEMORY_DIR, f"{self.session_id}.json")
        self.history    = []
        # Auto-loads if resuming an existing session
        if os.path.exists(self.filepath):
            self._load()

    def add_turn(self, question: str, answer: str, chunks: list[dict]):
        # Append user + assistant turns, checkpoint immediately
        ...
        self._save()
    def get_messages_with_history(self, query, context_chunks, system_msg):
        # Injects last 6 Q&A pairs into the message list before the current turn
        ...
# New session
memory = ConversationMemory()

# Resume yesterday's session
memory = ConversationMemory("20260413_104959")
Turn 4 question: "How does that compare to what you said about the BLEU score?"
Turn 4 answer: "The context also reports a BLEU score of 28.4 for the
               Transformer (big) on WMT 2014 English-to-German. This matches
               exactly what I previously stated."

Частка 4 — Адстэйканне старасці, CDC і надаўанне вагі свежасці

Калі працюеце над стадзіяй 4 «Сочыць за занепадам», спачатку запісайце угоду: неабяжлівыя даны, сігнал успеху і тое, што выходзіць пад часты няудача. Такі спіс пераканальвае ў тым, каб пазнейшыя змены коду былі чыстымі. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выйшае, няудача должна вказваць на адну адпаведальнасць, а не на заплутаны працэс. Перад налаштаваннем праблемаў пераканайце рэкалі на фіксаваным наборэ запытанняў. Частае змена праблемаў рэдка калі вярнайце слабкую сістэму аднаходжэння інфармацыі.

Рэальная проблема

Калі працюеце над стадзіяй «Рэальная проблема», спачатку запісайце контракт: неабяжлівыя вхідныя даны, сігнал успеху і тое, што выходзіць у разе частковага невыпання. Такі спіс контроля дапамагае заліцьварыць пазнейшыя змены ў кодзе. Спрэцьвачайце гэтую стадзію як контракт межа вхіднымі данымі і перакананымі выходнымі рэзультатамі. Дайце назвы артыфактам, задаце крытэрыя успеху і адмовіцеся ад тыхоўскага частковага завершэння. Замерайце рывень запамятовання на фіксаваным наборэ пытанняў прычым перэналаштаванні запрошэнняў. Частае змены запрошэнняў рэдка калі вылечваюць слабкую структуру адзысквання інформацыі.

Сачыце за старэнням (Клетка 14A)

Калі працуеце над стадзіяй Staleness Tracking Cell 14A, спачатку запісайце умовы кантракту: неабяжлівыя даныя, сігнал успеху і тое, што выходзіць пад частковыя неудачы. Такі спіс контроля дапамагае заліцвачыць пазнейшыя змены коду. Запісвайце час выканання і кост токена або запыту праз ці функцыйнальныя рэзултаты. Відразлівасць коста з самага пачатку запобегае неспакоўным рахункам, калі працэс пераходзіць з дэмовай асяроддзя ў спакоўныя серавісы. Замерьце рэткую здатнасць на фіксаваным наборэ запытаў прычыну налаштавання прапаза. Частыя змены прапаза рэдка калі вядуць да павышэння якасці выкарыстоўвання даных. Калі працуеце над стадзіяй Staleness Tracking Cell 14A, спачатку запісайце умовы кантракту: неабяжлівыя даныя, сігнал успеху і тое, што выходзіць пад частковыя неудачы. Такі спіс контроля дапамагае заліцвачыць пазнейшыя змены коду. Дакументавайце як «вялікі успех», так і шлях вярнення да нормы. Перапрыбуткі, людзкія контрольныя пункты і обработка некорэктных дадзеных ўжо ёсць часткай продукту, а не пазнейшым дапрацоўкам.

def compute_file_hash(pdf_path: str) -> str:
    sha = hashlib.sha256()
    with open(pdf_path, "rb") as f:
        for block in iter(lambda: f.read(65536), b""):
            sha.update(block)
    return sha.hexdigest()

CDC Engine (Cell 14B)

Этап Engine Cell 14B цэнтра CDC працюе наяўней, калі яго розглядаць як вімерную паверхню. Зафіксавайце адна «золатая» транскрыпцыю, адын прыклад неудачы і запіс пра вярнэнне да пачатковага стану перш чым расшырваць масштабы. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выходзіць, прычына неудачы павінна вказываць на адную адпаведальнасць, а не на заплутаны ланцюг задач. Раздзеліце правілы часткавання дадзеных ад правілаў ўзяць іх. Змена адных не павінна вымагаць перапісву іншых, калі зменяюцыся паказчыкі якосці.

def compute_chunk_hash(text: str) -> str:
    return hashlib.md5(text.encode("utf-8")).hexdigest()

def diff_chunks(old_registry: dict, new_chunks: list[str]) -> dict:
    new_hash_map = {compute_chunk_hash(c): c for c in new_chunks}
    old_hashes   = set(old_registry.keys())
    new_hashes   = set(new_hash_map.keys())
    return {
        "added":     {h: new_hash_map[h] for h in (new_hashes - old_hashes)},
        "removed":   {h: old_registry[h] for h in (old_hashes - new_hashes)},
        "unchanged": {h: old_registry[h] for h in (old_hashes & new_hashes)},
    }
v1 → v2 CDC result:
  ✅ Unchanged : 8   (kept — zero re-embedding cost)
  ➕ Added     : 6   (embedded + inserted)
  ➖ Removed   : 4   (deleted from ChromaDB)
  💰 API calls saved: 8/14 (57% reuse)

v2 → v3 CDC result:
  ✅ Unchanged : 10  (kept - zero re-embedding cost)
  ➕ Added     : 4   (embedded + inserted)
  ➖ Removed   : 2   (deleted from ChromaDB)
  💰 API calls saved: 10/14 (71% reuse)

Выбіранне дадзеных на аднойчынку з урахоўванням свежасці (Cell 14C)

Этап 14C з’явоўкі дадзеных за прыорытэтам свежасці працюе найкрашча, калі яго розглядаць як вімерную паверхню. Зафіксавайце адна ідэальная транскрыпцыя, адын прыклад неудачы і запіс пра відкатанне працы, перш чым расшырваць масштабы. Разглядзайце этап як кантракт між вхіднымі дадзенымі і паверыжанымі выходнымі рэзультатамі. Паказвайце назвы артыфактаў, задаюце крытэрыя успеху і адмовляйцеся ад безсловеснага частковага завершэння працы. Раздзеляйце правілы часткавага разбівання дадзеных і правілы ўзяць іх з памяці. Змена адных не должна прымусваць перапісванне іншых, калі змянююцыся паказнікі якасці.

def recency_decay(ingested_at_str: str, half_life_days: float = 30) -> float:
    ingested  = datetime.fromisoformat(ingested_at_str)
    days_gone = (datetime.now() - ingested).total_seconds() / 86400
    λ         = math.log(2) / half_life_days
    return round(math.exp(-λ * days_gone), 4)
blended = alpha * cosine_score + (1 - alpha) * recency_score
# Default: 0.85 * cosine + 0.15 * recency

Частка 5 — Тэставанне з PDF-файламі з калькамі

Тэставанне часткі 5 з атраментамі работае наяўней, калі яго спрыяваць як мерыемую паверхню. Зберагуце адна ідэальная транскрыпцыя, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перад расшырэнням масштаба. Запісвайце часы выканання і косты токеноў або запытак па боку ад функцыйнальных рэзультатаў. Візуабельнасць костаў з самага пачатку запобегае неспакойным рахункам, калі працэс пераходзіць з дэмовай асяроддзя ў спяльныя сераўеры. Раздзеліце політыку частковага апрацоўкі дадзеных ад політыкі ўзяць іх. Змена адной з яных не должна прымусваць перапісванне другой, калі зменяюцыся паказнікі якосці. Тэставанне часткі 5 з атраментамі работае наяўней, калі яго спрыяваць як мерыемую паверхню. Зберагуце адна ідэальная транскрыпцыя, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перад расшырэнням масштаба. Дакументавайце як успішны, так і вярнучыся шляхы. Перапрыбуткі, людзкія контрольныя пункты і апрацоўка некорэктных паведамленняў ёсць часткай продукту, а не чымсь, што дадаецца пазней.

TEST 1: Full ingest of v1
TEST 2: Staleness check — same file, correctly skipped
TEST 3: Baseline queries against v1
TEST 4: Copy v2 over active file → CDC kicks in
TEST 5: Same queries now return v2 content, newer chunks visible in recency scores
TEST 6: Copy v3 over active file → second CDC cycle
TEST 7: Recency verification — v3 chunks score highest across the board
TEST 8: Full stack test — weighted retrieval + conversation memory combined

Рэзультаты і паўнастаішчаныя адпаведзі

У стадыі Рэзультатаў і парадактаваных адказоў неабходна перад змянай коду вызначыць інпуты, адпаведальную особу за крок і крэтыры завершэння. Аперацыяныя працавнікі должны магчымае перайсці на гэты крок з вядомага пункта контролю, не спрабоўваючы здагадвацца пра схованы стан. Лепш выбіраць маленькія, тэставаныя елементы замест большых скрыптаў. Калі крок не выйшоў, прычына неудачы должна вказываць на адзін конкрэтны аспект, а не на заплутаны ланцужок задач. Прытамульвайце тыя часткі тэксту, якія фактычна лежалі в основе адказа. Без цых цытатаў аперацыяныя працавнікі не зможуць адразніць галюцынацыю ад працягу ў індэксаванні.

Падсумак эфектыўнасці

Для этапы падчытання эфектыўнасці неабходна перад змянай коду задаць вхідныя даны, адпаведальнага за крок і крэтырыя завершэння. Аперацыёныя працавнікі должны магчымае запускаць крок з вядомай точкі контролю, не падозрываючы прыхованы стан. Спрыяць гэтай этапы як даговору межа вхіднымі данымі і падтверджанымі выходнымі рэзультатамі. Даць назвы артыфактам, задаць пераканальнікі успеху і адмовіцца ад беззвучнага частковага завершэння. Цітаваць тыя часткі, якія фактычна лежалі в основе адпаведнай адказы. Без цітатаў аперацыёныя працавнікі не можуць адразніць галюцынацыю ад прасоў у індэксаванні.

Вядомыя абмежэнні

У стадыі «Вядомыя лімітаванні» неабходна дэфініцыя вхідных дадзеных, адпаведнага адпаведальніка за крок і крэтарыяў выходу пры перадзеіснаванні коду. Аператары должны магчымае перадзеіснаваць крок з вядомага пункта контролю, не спрабоўваючы здагадвацца пра захаваны стан. Запісвайце час выканання і кост токенаў або запытаў разам з функцыйнальнымі рэзултатамі. Відразы костаў з самага пачатку запобегае неспакойным рахункам, калі працэс пераходзіць з дэмаверсіі ў спяльныя асяроддзя. Указвайце тыя часткі тэксту, якія фактычна лежалі в основе адпаведзьбы. Без цых цытатаў аператары не можуць разлічыць галюцынацію ад прасоў у індэксаванні. У стадыі «Вядомыя лімітаванні» неабходна дэфініцыя вхідных дадзеных, адпаведнага адпаведальніка за крок і крэтарыяў выходу пры перадзеіснаванні коду. Аператары должны магчымае перадзеіснаваць крок з вядомага пункта контролю, не спрабоўваючы здагадвацца пра захаваны стан. Дакументавайце як «шчаслівы» шлях, так і шлях вяснавання проблем. Перапрыбуткі, людзкія контралі і обработка некорэктных паведамленняў ёсць часткай продукту, а не чымсь, што дадаецца пазней.

Што вы створылі

Калі працуеце на стадыі «Што вы створылі», спачатку запісайце умовы: неабяжлівыя даныя, сігнал успеху і тое, што выканаецца у разе частковага нявыполнення. Такі спіс контроля дапамагае заліцварыць пазнейшыя змены ў кодзе. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выйшоў, прычына нявыполнення павінна вказываць на адну конкрэтную адпаведальнасць, а не на заплутаны ланцужок задач. Перад налаштаваннем запітоў пераканайцеся, наскількі эфектыва сістэма адпаведзядае на фіксаваны набор запітаў. Частыя змены запітоў рэдка калі вядуць да павышэння якосці адпаведзенняў.

PDF on disk
 └► SHA256 hash check (staleness)
      ├► Unchanged → skip
      └► Changed → CDC diff
           ├► Unchanged chunks → kept in ChromaDB (zero API cost)
           ├► Removed chunks → deleted from ChromaDB
           └► Added chunks → embed (Gemini) → store (ChromaDB)
                                                    ↓
User question
 └► embed_query() [RETRIEVAL_QUERY task type]
      └► ChromaDB cosine search (TOP_K × 3 candidates)
           └► recency_decay() per chunk
                └► blended score re-ranking
                     └► top-5 chunks as context
                          └► ConversationMemory.get_messages_with_history()
                               └► gpt-oss-20b via Groq/HuggingFace
                                    └► grounded answer + checkpoint to disk

Чэк-ліст для эксплуатацыі

Стадыя чэк-ліста для эксплуатацыі будзе эфектыва, якшо ёй ставіцца задача стварыць меркаваную базу дадзеных. Запісайце адны ідэальны прыклад працы, адзін прыклад нявыполнення і прыметкі па адкату перш чым расшырваце сферу працы.

Зберагаюце канфігурацыю праза ў кодзе прыемлікага програмнага забезпечэння. Файлы сяродавішча, храненні секрэтных дадзенаў і флагі функцыйяй должны знаходзіцца ў аднам месцы, куды аператары можаюць адбавіць аўдыт без неабяжнага чытання всей структуры.

Раздзеляйце політыку часткавання дадзенаў ад політыкі ўзяць іх. Змена адной з яных не должна прымусваць перапісванне другой, калі зменяюцыся паказнікі якосці.

Калі дозволяе бюджэт, адбавяйце тэст на працэсанне крытычнага маршруту ў сістэме CI з вікорыстанням фіксатываў, а не рэальных платных API.

Документавайце як шлях успеху, так і шлях вяснавання проблем. Перапрыбуткі, людзкія контрольныя пункты і обработка непрацюючых паведамленняў ёсць часткай продукту, а не чымсь, што дадаецца пазней.

Раздзеляйце політыку часткавання дадзенаў ад політыкі ўзяць іх. Змена адной з яых не должна прымусваць перапісванне другой, калі зменяюцыся паказнікі якосці.

Перш чым запускаць даную структуру, заморозьце версіі, зафіксавце «золаты» транскрыпты для крытычных етапаў і паказвце способы вярнення да пачатковага стану. У спільных серэдавішчах неабходны ліміты частоты запуска, пераканання ў належнасці тэрыторыі і чысткі власнік для змены секрэтных даных. Валіце надзейнасць працы над красавімі разовымі дэманстрацыямі.

Прыметкі для 8d172e929623: не кладзіце ключы прадаўцаў у рэпазітарый, задаце верхнюю межу токенав для кожной сесіі і зберагачыце транскрыпты празаўседы ў фіксатырах ацэнкі, каб пазнейшыя замены мадэляў заставаліся пораўнанымі.