Онлайн час в Google Meet
Лого на 91. НЕГ „Проф. Константин Гълъбов“

12. клас · Избираем модул · Урок 26

RAG — чатбот върху собствени документи

Моделът отговаря по вашите документи: нарязване на парчета, вектори, търсене на най-близките, промпт с цитиран източник и честно „не знам“ — сглобени в прост чатбот в Colab.

Защо моделът не знае вашите неща

Моделът е обучен върху текстове до определена дата и не съдържа правилника на училището, конспекта за изпита или документацията на вашия проект. Попитан за тях, той ще произведе правдоподобен, но измислен отговор — видяхте го в урок 16.

Три изхода: да дообучим модела (скъпо и бавно), да залепим всички документи в промпта (не се побират и струват скъпо) или да намерим само нужните няколко абзаца и да подадем тях. Третото се нарича RAG и е точно търсенето по смисъл от урок 25 плюс едно извикване на модела.

RAG (retrieval-augmented generation)

Подход, при който преди отговора системата намира подходящи откъси от собствените документи и ги вмъква в промпта, а моделът отговаря само по тях.

парчета + векторивъпростърсенеk парчетапромптотговорМоделът не се дообучава — просто получава нужния текст в промпта.R (retrieval) — намиране · A (augmented) — обогатяване · G (generation) — генериране
Пет стъпки, от които само последната е работа на езиковия модел.

Подготовка на документите

Нарязване на парчета

Документите се режат на парчета (chunks) от няколко изречения до абзац. Твърде голямо парче носи шум и запълва прозореца; твърде малко губи контекста и става неразбираемо само по себе си.

  • Режете по естествени граници — празен ред, подзаглавие, — а не на сляпо по брой символи.
  • Пазете при всяко парче откъде е дошло: файл и номер на абзац. Това е източникът в отговора.
  • Добавете името на документа в началото на всяко парче — така „чл. 12“ знае, че е от правилника (урок 25, задача 2).
def narezhi(ime_fail, tekst):
    abzaci = [a.strip() for a in tekst.split("\n\n") if a.strip()]
    return [{"iztochnik": f"{ime_fail}, абзац {i + 1}", "tekst": a}
            for i, a in enumerate(abzaci)]

Вектори

Всяко парче получава вектор — със същата функция vektori от урок 25 (Gemini или локалният модел). При 30 парчета списък в паметта стига; при хиляди — таблицата с JSON колона от миналия урок.

Отговарянето

  1. Въпросът се превръща във вектор със същия модел като парчетата.
  2. Намират се k = 3 парчета с най-голяма косинусова близост.
  3. Строи се промпт: указание „отговори само по текста“, парчетата с източниците им, въпросът.
  4. Моделът отговаря; отговорът се показва заедно с източниците.
SISTEMNO = (
    "Отговаряш САМО въз основа на подадения текст. "
    "Ако отговорът не се съдържа в него, напиши точно: „Не се съдържа в документите.“ "
    "Накрая посочи източника в квадратни скоби.")

kontekst = "\n\n".join(f"[{p['iztochnik']}] {p['tekst']}" for p in namereni)
otg = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=f"ТЕКСТ:\n{kontekst}\n\nВЪПРОС: {vapros}",
    config=types.GenerateContentConfig(system_instruction=SISTEMNO, temperature=0))

Кога да каже „не знам“

Търсенето по вектори винаги връща нещо — и за „колко струва обядът“ ще намери „най-близкото“ парче, дори то да е за екскурзията. Затова „не знам“ има две линии:

  • Праг на близостта — ако най-близкото парче е под прага, моделът изобщо не се вика: отговорът е „не знам“ веднага. Пести лимит и не дава на модела повод да измисля.
  • Указанието в промпта — ако парчетата са минали прага, но отговорът все пак го няма в тях, моделът трябва да го каже.
PRAG = 0.45          # различен за всеки модел — измерете го в задача 3

namereni = nameri(vapros, k=3)
if namereni[0]["blizost"] < PRAG:
    return "Не се съдържа в документите.", []

Къде се къса веригата

  • Търсенето не е намерило нужното — най-честият проблем. Моделът не може да отговори по текст, който не е получил.
  • Парчето е разкъсано — списък или таблица, срязани по средата, объркват отговора.
  • Противоречиви източници — стар и нов правилник; моделът няма как да знае кой е в сила, ако не му кажем.
  • Моделът игнорира текста и отговаря от своите знания. Срещу това помагат изричното указание и искането за цитат.

Чатботът

„Чатбот“ в Colab е цикъл: input → търсене → промпт → отговор → пак. Документите идват от самите ученици — текстови файлове, качени в тетрадката с files.upload(). Това е и ядрото на AI компонента в проекта ви от урок 27 нататък: същата верига, само документите и интерфейсът са ваши.

Какво трябва да запомниш

  • RAG не дообучава модела — подава му нужните откъси в промпта.
  • Документите се режат по естествени граници; всяко парче пази източника си.
  • Търсенето е по косинусова близост от урок 25; моделът получава само k най-близки парчета.
  • „Не знам“ има две линии: праг на близостта преди извикването и указание в промпта.
  • Грешен отговор → първо гледаме намерените парчета: ако верният текст не е там, вината е в търсенето.