RAG, które nie jest przestarzałe: Świeże dane z sieci i pętle odświeżania

System RAG jest tak świeży, jak jego ostatnie przeszukiwanie. Matematyka pobierania jest łatwa — trudniejsza jest pętla odświeżania, która ponownie przeszukuje, dzieli i osadza każde źródło według własnego zegara, zanim odpowiedzi staną się nieaktualne.

Generacja wspomagana pobieraniem jest teraz obecna w około połowie systemów AI w przedsiębiorstwach — adopcja wzrosła do 51% w najnowszych badaniach, z 31% rok wcześniej. Jednak większość projektów RAG ma ten sam tryb awarii: zostały raz zapełnione, a potem świat poszedł dalej. Matematyka pobierania (osadź zapytanie, przeszukaj wektory, umieść najlepsze fragmenty w prompt) jest łatwa, dobrze udokumentowana. Trudniejsza część, ta, która decyduje, czy twój asystent jest godny zaufania, czy pewnie się myli, to świeżość — pipeline, który ciągle ponownie przeszukuje, dzieli i osadza każde źródło, zanim jego odpowiedzi staną się nieaktualne. Ten przewodnik dotyczy budowania tej pętli odświeżania, z wprowadzaniem markdown i TTL-ami dla każdego źródła jako jego rdzeniem.

Nieaktualne pobieranie jest gorsze niż brak pobierania

Cała obietnica RAG polega na tym, że model odpowiada na podstawie pobranych dowodów, a nie zamrożonych danych treningowych. Złam świeżość, a złamiesz obietnicę: bot wsparcia cytuje politykę zwrotów z poprzedniego kwartału, asystent ds. cen podaje nieaktualną kwotę, współpracownik badawczy pomija aktualizację, która zmieniła odpowiedź. Co gorsza, model stwierdza to z pełnym przekonaniem, ponieważ pobrany fragment wygląda autorytatywnie. Nieaktualny indeks nie zawodzi głośno — zawodzi cicho i wiarygodnie, co jest najniebezpieczniejszym rodzajem. Utrzymanie aktualności korpusu nie jest miłym dodatkiem; to różnica między ugruntowaniem a halucynowaniem z dodatkowymi krokami.

Wprowadzaj markdown, nie surowy HTML

Jakość wszystkiego, co jest dalej, jest ustalana przy wprowadzaniu. Surowy HTML to katastrofa dla pobierania — nawigacja, banery cookie, stopki i tagi skryptów stają się szumem, który zanieczyszcza wyszukiwanie podobieństw i marnuje tokeny kontekstu. Czyste podejście to wprowadzanie markdown: przeszukaj każdą stronę i wyodrębnij tylko główną treść jako strukturalny markdown, odrzucając boilerplate, zanim cokolwiek zostanie podzielone. Nasze Scraper API zwraca bezpośrednio markdown, renderuje JavaScript na żądanie i rotuje IP, aby zablokowane strony nie zostawiały dziur w twoim korpusie — zastępuje to kruche warstwy wprowadzania Puppeteer i BeautifulSoup, na których opiera się większość stosów RAG. Punkt końcowy web-data-for-LLMs łączy przeszukiwanie, czyszczenie i strukturę w jedno wywołanie stworzone dokładnie do tego.

import requests

# markdown-first ingestion: clean text, JS rendered, IPs rotated
def fetch_markdown(url):
    r = requests.post(
        "https://api.quantumproxies.io/scrape",
        headers={"Authorization": f"Bearer {QP_KEY}"},
        json={"url": url, "format": "markdown", "render": True},
        timeout=60,
    )
    doc = r.json()
    return {
        "url": url,
        "markdown": doc["markdown"],       # no nav, no boilerplate
        "fetched_at": doc["fetchedAt"],     # timestamp for TTL logic
        "content_hash": doc["contentHash"], # skip re-embed if unchanged
    }

Zasilaj swój stos RAG czystymi danymi z sieci

Dziel małe, noś metadane

Gdy masz czysty markdown, podziel go na fragmenty o wielkości około 300 do 500 tokenów — wystarczająco małe, aby pobrany fragment był ściśle na temat, wystarczająco duże, aby zachować spójną myśl. Użyj rekurencyjnego dzielnika (LangChain lub LlamaIndex mają taki), który respektuje granice zdań i nagłówków, zamiast ciąć w połowie słowa. Krytycznym, często pomijanym krokiem są metadane: każdy fragment musi zawierać URL źródła, znacznik czasu i hash treści. Te metadane umożliwiają świeżość — pobieranie może filtrować według aktualności, a twoja pętla odświeżania może określić, które fragmenty należą do źródła, które właśnie się zmieniło.

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=1800, chunk_overlap=200,  # ~400 tokens per chunk
)

def to_chunks(doc):
    parts = splitter.split_text(doc["markdown"])
    return [{
        "text": p,
        "source": doc["url"],
        "fetched_at": doc["fetched_at"],   # drives recency filtering
        "ttl": ttl_for(doc["url"]),          # per-source refresh clock
    } for p in parts]

Osadź te fragmenty z aktualnym modelem — text-embedding-3-large, modelem Cohere lub otwartą wersją Sentence-BERT — i przechowuj wektory wraz z metadanymi w bazie danych wektorów, takiej jak Pinecone, Weaviate, Milvus lub FAISS. Dostosuj progi podobieństwa i, co ważne, włącz filtrowanie metadanych, aby zapytanie mogło wymagać, na przykład, tylko fragmentów pobranych w ciągu ostatnich 30 dni dla pytania o cenę.

Diagram pętli odświeżania świeżości RAG od przeszukiwania markdown przez dzielenie i osadzanie do indeksowania i ponownego przeszukiwania sterowanego TTL
Pętla, która utrzymuje uczciwość RAG: przeszukiwanie markdown, małe fragmenty bogate w metadane i TTL, który wyzwala ponowne osadzanie przed dryfem.

TTL-e dla każdego źródła: serce świeżości

Błąd, który popełnia większość zespołów, polega na odświeżaniu wszystkiego według jednego harmonogramu — nocnym pełnym ponownym przeszukiwaniu, które jest jednocześnie zbyt wolne dla cen i marnotrawne dla dokumentów referencyjnych. Różne źródła starzeją się w różnym tempie, więc nadaj każdemu czas życia, który odpowiada temu, jak szybko jego treść faktycznie się zmienia. Aktualne ceny i stany magazynowe mogą wymagać TTL w minutach; wiadomości, ogłoszenia i wątki na forach — godzinę; rankingi i katalogi — dzień; dokumentacja, polityki i materiały referencyjne — tydzień lub więcej. Oznacz każdy fragment TTL-em jego źródła, a harmonogram ponownie przeszukuje tylko te źródła, których zegar wygasł. Hash treści to twój zawór efektywności: jeśli ponownie przeszukana strona jest identyczna bajtowo, pomiń całkowicie krok osadzania i po prostu zresetuj znacznik czasu.

import time

TTL = {"prices": 300, "news": 3600, "catalog": 86400, "docs": 604800}

def needs_refresh(chunk, now=None):
    now = now or time.time()
    return (now - chunk["fetched_at"]) > chunk["ttl"]

# refresh only what has expired; re-embed only what actually changed
for src in due_sources(TTL):
    doc = fetch_markdown(src.url)
    if doc["content_hash"] != src.last_hash:
        reindex(to_chunks(doc))     # re-chunk + re-embed
    src.touch(doc["content_hash"])  # reset the clock either way

To jest pętla, która oddziela demo od systemu produkcyjnego. Utrzymuje przewidywalność pobierania, zużywa moc obliczeniową tylko tam, gdzie świat faktycznie się zmienił, i pozwala ci na złożenie prawdziwej gwarancji świeżości dla każdego źródła. Dla szerszego obrazu ugruntowania LLM-ów na żywych danych, zobacz nasz przewodnik po zasilaniu LLM-ów świeżymi danymi z sieci, a dla przekształcenia jednej strony w cytowalną bazę wiedzy, budowanie bazy wiedzy bota wsparcia.

Warstwy TTL pokazujące minuty dla cen i stanów magazynowych, godzinne dla wiadomości, dzienne dla katalogów i tygodniowe dla dokumentów referencyjnych
Odświeżaj każde źródło według zegara, na którym jego treść faktycznie się zmienia — minuty dla cen, tydzień dla dokumentów referencyjnych.

Pobieraj dla pokrycia, nie tylko podobieństwa

Ostatnia aktualizacja w czasie zapytania. Czysta podobieństwo wektorów pomija dokładne terminy — kody produktów, ciągi błędów, nazwy własne — które wyszukiwanie słów kluczowych trafia w punkt. Hybrydowe pobieranie łączy oba, a następnie stosuje twój filtr aktualności, aby zapytanie wrażliwe na czas preferowało świeże fragmenty. Zaloguj pobrane fragmenty z każdą odpowiedzią, abyś mógł sprawdzić, z jakich dowodów model faktycznie korzystał; ta śledzialność to, co czyni RAG wyjaśnialnym, i to, jak złapiesz nieaktualny fragment, zanim zrobi to użytkownik. Techniki z wydobywania wspomaganego LLM dobrze się tu sprawdzają, gdy potrzebujesz strukturalnych pól z pobranych stron zamiast prozy.

Często zadawane pytania

Jak utrzymać świeżość danych w pipeline RAG?

Przypisz każdemu źródłu czas życia, który odpowiada temu, jak szybko jego treść się zmienia, oznacz każdy fragment URL-em źródła i znacznikiem czasu, i uruchom harmonogram, który ponownie przeszukuje tylko wygasłe źródła. Użyj hash treści, aby pominąć ponowne osadzanie stron, które faktycznie się nie zmieniły, i włącz filtrowanie aktualności przy pobieraniu, aby zapytania wrażliwe na czas preferowały świeże fragmenty.

Dlaczego wprowadzać markdown zamiast HTML dla RAG?

Surowy HTML zawiera nawigację, banery, stopki i skrypty, które stają się szumem, zanieczyszczając wyszukiwanie podobieństw i marnując tokeny kontekstu. Wprowadzanie markdown jako pierwsze wyodrębnia tylko główną treść, więc fragmenty są czyste i na temat. API do skrapowania, które zwraca renderowany markdown, usuwa kruche niestandardowe parsowanie, na którym polega większość stosów RAG.

Jaką wielkość fragmentu powinienem użyć dla RAG?

Około 300–500 tokenów to powszechny złoty środek: wystarczająco małe, aby pobrany fragment pozostał ściśle istotny, wystarczająco duże, aby zachować pełną myśl. Użyj rekurencyjnego dzielnika, który respektuje granice zdań i nagłówków, dodaj małe nakładanie się, aby kontekst nie został utracony na szwach, i dołącz metadane źródła do każdego fragmentu.

Czy potrzebuję proxy do budowy pipeline danych RAG?

Jeśli przeszukujesz otwartą sieć na jakąkolwiek skalę, tak — strony ograniczają szybkość i blokują powtarzające się żądania z jednego IP, pozostawiając luki w twoim korpusie. API do skrapowania lub rotujące proxy utrzymują niezawodność przeszukiwania, dzięki czemu źródła odświeżają się całkowicie i zgodnie z harmonogramem, co jest dokładnie tym, na czym polegają TTL-e dla każdego źródła.

RAG żyje lub umiera na świeżości. Wprowadzaj czysty markdown, dziel małe z metadanymi i napędzaj ponowne osadzanie z TTL-ami dla każdego źródła, aby każde źródło odświeżało się według własnego zegara. Zbuduj tę pętlę raz, a twój asystent będzie odpowiadał z dzisiejszej sieci, a nie z zeszłomiesięcznego zrzutu.

Zbuduj świeży pipeline RAG na Scraper API