Produkcja RAG w Azure: dzielenie na fragmenty, wyszukiwanie hybrydowe, filtry i cytaty
Aplikacje biznesowe wymagają fragmentów uwzględniających strukturę, wyszukiwania hybrydowego, filtrów ACL, ponownego rankowania, spersonalizowanych promptów oraz oceny — a nie tylko demonstracji w formacie PDF.
Demony RAG wyglądają prosto: PDF → fragmenty → embeddingi → baza wektorowa → zapytanie do modelu. Potem przychodzi pięć tysięcy dokumentów i ktoś pyta o zasady zwrotu kosztów podróży zagranicznych. Trudna jest pewna integracja odpowiednich dowodów z modelem.
1. Zacznij od danych, a nie od LLM
Zbadaj źródła, kontrolę dostępu, aktualność i formaty przed wyborem modelu do rozmów. Złe korpusy generują błędne, płynne odpowiedzi.
2. Dzielenie na fragmenty: nie dziel bezmyślnie co N znaków
Najlepiej stosować podział z uwzględnieniem struktury (tytuły, strony, tabele) z nakładaniem się fragmentów. Tabele opłat i klauzule polityk nie tolerują prostych okien znakowych.
3. Generowanie embeddingów
Ustal jedną metodę implementacji embeddingów; zapisuj wymiary i identyfikator modelu przy każdym indeksie. Mieszanie modeli potajemnie pogarsza dokładność wyników.
document = {
"document_id": "HR-2026-001",
"title": "Employee Benefits Policy",
"department": "HR",
"country": "India",
"version": "2026.1",
"effective_date": "2026-01-01",
"source": "HR Portal"
}
def chunk_text(text, size=1000):
return [
text[i:i + size]
for i in range(0, len(text), size)
]
def chunk_by_sections(document):
chunks = []
for section in document.sections:
chunks.append({
"title": section.title,
"content": section.text,
"document_id": document.id
})
return chunks
pip install openai azure-identity
from openai import OpenAI
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
token_provider = get_bearer_token_provider(
DefaultAzureCredential(),
"https://ai.azure.com/.default"
)
client = OpenAI(
base_url="https://YOUR-RESOURCE.openai.azure.com/openai/v1/",
api_key=token_provider
)
response = client.embeddings.create(
model="text-embedding-3-small",
input="Employees are eligible after completing 12 months of service."
)
vector = response.data[0].embedding
{
"id": "HR-2026-001-004",
"document_id": "HR-2026-001",
"title": "Employee Benefits Policy",
"content": "...",
"country": "India",
"version": "2026.1",
"content_vector": embedding
}
from azure.search.documents import SearchClient
from azure.search.documents.models import VectorizedQuery
query = "Can I work remotely from another country?"
query_vector = client.embeddings.create(
model="text-embedding-3-small",
input=query
).data[0].embedding
vector_query = VectorizedQuery(
vector=query_vector,
k_nearest_neighbors=10,
fields="content_vector"
)
results = search_client.search(
search_text=query,
vector_queries=[vector_query],
top=5,
select=["title", "content", "document_id", "country"]
)
results = search_client.search(
search_text=query,
vector_queries=[vector_query],
filter="country eq 'India'",
top=5
)
context = "\n\n".join(
f"Source: {r['title']}\n{r['content']}"
for r in results
)
prompt = f"""
You are an enterprise policy assistant.
Answer the user's question using only the provided evidence.
If the evidence does not contain the answer, say that you don't have enough information.
Do not invent policies.
Evidence: {context}
Question: {query}
"""
{
"document_id": "HR-2026-001",
"page": 14,
"section": "Eligibility",
"content": "..."
}
def answer_question(question):
# 1. Embed the question
vector = embed(question)
# 2. Hybrid retrieval
candidates = search(
query=question,
vector=vector,
top=20
)
# 3. Apply metadata/business filters
candidates = apply_filters(candidates)
# 4. Rerank
ranked = rerank(question, candidates)
# 5. Keep only useful evidence
evidence = ranked[:5]
# 6. Build grounded prompt
prompt = build_prompt(
question,
evidence
)
# 7. Generate answer
response = generate(prompt)
# 8. Return answer + citations
return {
"answer": response,
"sources": extract_sources(evidence)
}
test_cases = [
{
"question": "What is the India travel allowance?",
"expected_source": "travel-policy-india.pdf"
},
{
"question": "How long is parental leave?",
"expected_source": "leave-policy-2026.pdf"
}
]
4. Przechowywanie embeddingów w Azure AI Search
Zachowuj tekst, wektory oraz metadane nadające się do filtrowania (id dokumentu, produkt, lokalizacja, grupa ACL, updated_at).
5. Hybrydowe wyszukiwanie jest lepsze od wyłącznie wektorowego dla większości przedsiębiorstw
Łącz intensywną podobieństwo z algorytmem BM25/słowami kluczowymi w przypadku identyfikatorów, kodów oraz rzadkich nazw własnych.
6. Filtrowanie przed marnowaniem kontekstu
Zastosuj filtry bezpieczeństwa i dotyczące dzierżawy w zapytaniu – a nie po tym, jak model już obejrzał zabronione fragmenty.
7>Wyniki wyszukiwania nie stanowią ostatecznego kontekstu
Ponownie uporządkuj wyniki, usuń duplikaty i dostosuj ich liczbę do dostępnej puli tokenów. Więcej fragmentów ≠ lepsze odpowiedzi.
8. Tworzenie promptu na podstawie dostępnych dowodów
Instrukcje powinny wymagać odpowiedzi wyłącznie z podanych fragmentów tekstu oraz wskazywać, gdy brakuje dowodów.
9. Uzasadnienie z cytatami
Należy podać identyfikatory fragmentów lub stron, aby użytkownicy mogli to sprawdzić. Brak cytowania stanowi zagrożenie.
10. Proces gotowy do wdrożenia
Przyjmowanie danych → dzielenie na fragmenty → włączanie informacji → tworzenie indeksu → hybrydowe wyszukiwanie → filtrowanie → ponowna klasyfikacja → generowanie odpowiedzi → cytowanie źródeł → ocena.
11. Co zwykle się psuje
Nieodpowiednie dzielenie na fragmenty; słabe metadane; wyszukiwanie wyłącznie na podstawie wektorów; zbyt duży kontekst; brak zestawu do oceny; brak cytatów; brak informacji o uprawnieniach dostępu; potajemna zmiana modeli do włączania informacji; brak informacji o aktualności danych.
Zakończenie
RAG w produkcji to wyszukiwanie informacji z integracją punktu końcowego LLM – a nie odwrotnie. Inwestuj w dzielenie na fragmenty, hybrydowe wyszukiwanie, filtry, cytowanie źródeł oraz ocenę; model do rozmów jest jedynie ostatnim etapem.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki negatywne ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmienią się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki negatywne ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmienią się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki negatywne ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmienią się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki negatywne ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmienią się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki negatywne ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmienią się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki ACL-negative. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmieniają się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki ACL-negative. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmieniają się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki ACL-negative. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmieniają się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki ACL-negative. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmieniają się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki ACL-negative. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmieniają się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw „złotych” pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki o wyniku negatywnym ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmienią się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw „złotych” pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki o wyniku negatywnym ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmienią się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw „złotych” pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki o wyniku negatywnym ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmienią się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw „złotych” pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki o wyniku negatywnym ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmienią się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw „złotych” pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki o wyniku negatywnym ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmienią się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki negatywne ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmieniają się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki negatywne ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmieniają się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki negatywne ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmieniają się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki negatywne ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmieniają się ustawienia dzielenia na fragmenty lub embeddingu.
Zachowaj zestaw złotych pytań dla każdej domeny: wyszukiwania według dokładnego ID, pytania w formie parafrazy oraz przypadki negatywne ACL. Zautomatyzuj je w środowisku CI za każdym razem, gdy zmieniają się ustawienia dzielenia na fragmenty lub embeddingu.
Ocena powinna znajdować się w tym samym repozytorium co konfiguracja dzielenia na fragmenty: gdy ktoś „tylko lekko zmienia” parametry nakładania się, system powinien zawieść się, zanim użytkownicy to zauważą.
Hybrydowe wyszukiwanie wciąż wymaga dobrych analizatorów oraz list synonimów dla żargonu specjalistycznego; same embeddingi nie pomogą w przypadku błędnie wpisanego kodu polityki.
Zdokumentuj specyfikację pola ACL na jednej stronie, aby każde nowe zadanie importu generowało te same dane, których oczekuje filtr wyświetlania.
Ocena powinna znajdować się w tym samym repozytorium co konfiguracja dzielenia na fragmenty: gdy ktoś „tylko lekko zmienia” parametry nakładania się, system powinien zawieść się, zanim użytkownicy to zauważą.