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

Што зламалася ў карэкцыйным RAG-канвееры для пытанняў па YouTube на LangGraph

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

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

Крыніцы: Building a Corrective RAG Pipeline for YouTube Q&A: What Broke and Why?

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

  1. Мадэль-асэценавальнік, якая ацэнюе, чы розглядаваны фрагмент ўзаемна адносіцца да запіту карыстальніка.
  2. Програма-перапісваўшык запітоў, якая пераформулюе запіт, каб было лёгчэ знайсці неабходныя даны.
  3. Альтэрнатыва у вачынку пошуку ў інтэрнэце, калі жаданыя даны не ўзаемна адносяцца да запіту.

Праект, які лежыць у основе гэтых працоў, — гэта бот для адпаведзення на запыты ў відэах на YouTube. Вы даеце яму URL відэа і запыт, а ён дае адпаведзь. Ён таксама справляецца з відэамі, у якіх няма транскрыпцыі, што мае значэнне, калі бо толькі загрузчык відэаў з YouTube ад LangChain працюе толькі тады, калі транскрыпцыя ужо існуе. Прабег задачы такі: завантажыць відэа, перакластыць яго за дапамогою мадэлі распазнавання мовы (Whisper), увключыць тэкст, зберагчы вектары ў ChromaDB, а пасля адпавядаць на запыты за дапамогою графа LangGraph.

Паколькі задача выключала становы адстэйк, павторныя спробы і запасны варыянты, LangGraph падходзіў для яе лепшая, чым ланцюг LangChain або сукупнасць умовных оперэйтораў if-else. Работа з вузламі, рэшткамі і спяльным об’ектам стану таксама адкрывае, як на самай працэ функцыонуе застосунак RAG. Некалькі памылак на шляху навучылі больш, чым успехі, і яны пераказаны ніжэй.

Разбіўка на часткі

У будзь-якай сістэме RAG разбіўка на часткі — гэта этап, які тыха паспяшаеся, калі ён выкананы некорэктна. Частаўскія падходы ёсць такія:

  1. Рэкурсыўнае раздзелэнне на сімвалы праходзіць па спісе раздзелачоў і розрывае тэкст, пакуль фрагменты не западзяць у ліміт розмеру.
  2. Семантычная разбіўка групавае рэчэнні па значэнні, напрыклад, шляхом ацэнкі сэмантычнай супадзімасці рэчэнняў за дапамогою мадэлі у стыле BERT і з’еднання найбліжэйшых да яе.
  3. Раздзелэнне, спецыфічнае для дакумента, выконваецца ў спадарожніку выхіднага матэрыялу і ёсць тыповым для коду.
  • Хіерархічна сегментацыя на «бацьку» і «дзеця» дазволяе заставіць большы сегмент-«бацку» і меншыя сегменты-«дзеці», вырваныя з яго.
  • Сэмантычная сегментацыя спачатку здавалася правільным выборам. Відэа пакрываюць кожную можлівую тэму, таму групаванне па значэнні здавалася разумным. Але пазней з’явіўся якісь страннацкі факт: для транскрыпцый трываласцю менш дзесяты хвілін сэрвіс часта вяртаў толькі адзін сегмент. Дакладныя даследаванняя паяснілі прычыну. Короткія відэа зазвычай прасвятляюць адну тэму дакладна, і калі весь тэкст прысвечаны адной рэчы, сэмантычная сегментацыя не мае чаго дзеліць.

    Пасля гэтага была выкарыстоўвана рэкурсыўная сегментацыя па сімволах, але з’явілася ўжо другая памылка — значэнне chunk_overlap заставілі равным нулю. Самыя сегменты былі прыемныя, але якасць рэсультатаў падгорэла. Без перакрыцця сегмент не меў ніякіх падказак пра тое, што было раней, таму мадэль генеравання не мела кантэксту пра тое, да чаго належыць сегмент, і часта спалучала дэталі адна з другой.

    Узгоджэній підхід перэдбачав рэкурсівне падзелу з рэальнім перэкрытцям, без семантычного часткового розбітця для гэтага віпадку вікорыстання. Ён ефектывно апрацоўвав як короткі, так і довгі відэо і створував крашчы фрагменты транскрыпціі. На невелікій вібірці з дэсяты запітыв із параметром k=5 серэдній ранг взаемносты склав 0,81. Гэтая вібірка ёсць дужэ малою, тому іі слід розглядаты як прыблізній показнік, а не як стандарт оцінкі.

    Вібір мадэлі ембеддынгу

    Бот мае апрацоўваты будзь-які відэо, тому тэматыка охоплюе всі сферы. Мадэль ембеддынгу, спеціалізована на певній сферы, можа швідко віявіты недостатню ефектывнісць, калі відэо не належыць до областы іі навчання. Для загального вікорыстання, з урахуванням скромніх тэхнічніх можлівостэй, була обрана мадэль all-MiniLM-L6-v2 — швідка і легка мадэль, якая прыйнятно працюе майжэ всюды.

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

    Рашэння — падтрымваць значэнне chunk_size ніжэй за ліміт штырэня. Калі дзельніце даныя па сімволах, старацца не перасічыць ліміт токэнаў, выкорыстоўваючы прыблізныя правіла: аднаму токэну паўстаноўляе прыблізна чатыры сімволы. Дзельнік, налаштаваны на 800 сімвоў, дае апошнё адпрацаванне або 200 токэнаў, што падходзіць.

    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=800,      # Notice: 800 characters limit here (~200 tokens)
        chunk_overlap=200,
        length_function=len,
        is_separator_regex=False,
    )
    docs = text_splitter.create_documents([text])
    sentence_transformer = HuggingFaceEmbeddings(
        model_name="sentence-transformers/all-MiniLM-L6-v2"
    )
    vector_store = Chroma(
        collection_name=get_config("vectorDB_collection_name"),
        embedding_function=sentence_transformer,
    )
    

    Калі два найпашыяныя проблемы адгаваркі, наступныя стосуюцца самага LangGraph.

    Стан LangGraph і ChromaDB

    Кожны вузел у прыкладніце LangGraph дзельняе даныя праз об’ект стану. Кожны вузел прымець стан і яго адкорэгуе. Існуе два распашыяныя спосабы яго адзначэння:

    1. TypedDict — схема з падказкамі пра тыпы для слоўнікаў. Яна дазволяе статычную пераканальцоўку, але не валідацыю пад час выканання.
    2. Pydantic — інструмент, які валідуе даныя пад час выканання і стварае адказы на несуразнасці тыпоў.

    Калі стан являе сабою Pydantic BaseModel, кожны атрыбут пераканальваецца. Калі ў яго падаць складны об’ект, напрыклад кліента ChromaDB, адразу ж з’яўляецца адказ. Установка параметра arbitrary_types_allowed=True у класе стану дазволяе прыймаць спецыяльныя тыпы без жадных проблем.

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

    Безпечнейшы падход — гэта выключыць ChromaDB з ціклу стану цэлкам. Гэта таксама яшчэ чыстэйшая архітэктура, адколі рэсурс базы дадзеных не павінен быць заплутаны з звычнымі дадзенымі. Узлы, якім патрэбна база дадзеных, все рава можаць да яе дасягнуць праз параметр config, які пасылаецца функцыі aupdate_state():

    app.aupdate_state(
        config={
            "configurable": {
                "thread_id": thread_id,
                "recursion_limit": 30,
                "vector_store": vector_store,      # Pass the Vector DB here
            }
        }
    )
    

    Асінхронная выкананне і пастка input()

    Адна з галоўных пераваг LangGraph — асінхронная выкананне. Ён прыменяе асінхронныя аналагі сінхронных вызваў: ainvoke() замест invoke() і aupdate_state() замест update_state(). З іхнім адпаведна можна ствараць асінхронныя вузлы і прыстройства, якія рэагуюць быстрэй.

    Самы просты спосаб разгародзіць гэта — вызваць input() унутры вузла. Гэта блакуе выкананне пакуль чакае на вінаватара, таму нават абсалютна асінхронная прыстройка втрачае перавагі асінхроннасці.

    Рашэнне — збіраць даныя, якія вводзі вінаватары, за межамі вузлаў і адправляць іх у граф праз aupdate_state():

    await app.aupdate_state(
        config={
            "configurable": {"thread_id": thread_id, "recursion_limit": 30}
        },  # adjust as needed
        values={
            "user_query": user_query  # Send the user inputs using the 'values' property
        },
    )
    

    Локальная або хмарная транскрыпцыя

    Транскрыпцыя ўсё час являецца вачоўкай цэлага праекту, але стала вузькім месцам. Першая спроба выкарыстоўвала локальную мадэль перакладу мовы на тэкст whisper-small, якая мела два проблемы:

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

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

    Даныя не былі конфідэнцыйнымі, таму пераход на паслужбу воблака праз API Groq быў лёгкі для абяснення. Гэта рашыла оба проблемы. Точнасць паднялася, таму што Groq выдае whisper-large-v3-turbo, які ў большасці случаў прыгожэй за whisper-small. Затрымка зменшылася, таму што моцнейшая хмарная апаратыра зменіла час адпаведзення да працягу апошніх 0.15 секунд.

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

    Короткі чартак перагляду

    Вышэўшыя урокі можна стыснуць у калькі пераглядоў, якія варта адрабаты пры перадзеўліванні такой парадогі:

    • Выведзіце, сколькі фрагментаў стварытае тыповае кораткае транскрыпцыя. Колькасць адна — гэта сігнал парадзеўлівання, незалежна ад таго, што обяцвае срэдства нарывання.
  • Паказваце, чы розмер фрагменту, які выдэляець дадзеныя, пасля перакладу ў токэны, застаецца ніжым за ліміт вхідных дадзеных для мадэлі эмбеддынгу. Адсечка ніколі не вызывае памылак, таму яе трэба спецыяльна пераглядаць.
  • Прачытайце калькі выкараных фрагментаў аднарозова. Калі які-небудзь з іх не мае сэнсу без своіх суседзей, значы перакрыцча занадта маленькае.
  • Не включайце кліентаў і з’яносы, такія як хранальнік вектараў, у стан графа, а перадавайце іх праз настройкі.
  • Перад тым, як зваліць відсутнасць шырокай працэздатнасі на фрамворк, перагляньце вузлы графа на наявнасць блакуючых вызоў, такіх як input() аб сінхронныя запіты да сеткі.
  • Замерьце час транскрыпціі на самай машыне, дзе яна будзе выканана, і вярніцеся да таго, чы даныя дазволяюць вікорыстоўваць хоставаную службу.
  • Што не ўключаецца

    Этыя зьмісты паводзяцца толькі некалькамі з проблем, якія выступаюць у першай аплікацыі LangGraph. Іншыя тэмы захоўваюцься на аднальны розгляд: запыткі сістэмы, ацэнка сістэмы і кожнага елемента, стварэнне канцэнтраў REST і ўстановка ў хмару.