Steg 9 (R4): unified migrate-corpus.mjs --write over engineering/governance/ infrastructure/security. 327 filer mutert, verified=null, prosa byte-identisk (fra første ## seksjon), advisor urørt (0 endringer). To applier-fixes oppdaget under kjøring (TDD, RED→GREEN): - insertHeaderFields: anker faller nå tilbake når en meta-linje selv passerer 500B (2 filer pakket et avsnitt i **Status:** → Type/Source landet utenfor scan-vinduet, applierens post-write-assertion fanget + restaurerte). - normalizeStaleVerified: fjerner nå ALLE stale non-date **Verified:** i 500B-vinduet, inkl. stray body-dup rett under --- (9 mlops-genaiops-filer var ellers falskt "verified"/fresh, droppet fra worklist). Operatør-godkjent utvidelse av carve-out; kun stray metadata-linjer, aldri prosa. test-transform-criterion: precondition oppdatert til post-migrasjons-sannhet (fila bærer nå Source). Suite 728/728 grønn.
612 lines
No EOL
25 KiB
Markdown
612 lines
No EOL
25 KiB
Markdown
# Query Understanding and Expansion
|
||
|
||
**Last updated:** 2026-06-19 | Verified: MCP 2026-06-19
|
||
**Status:** GA
|
||
**Category:** RAG Architecture & Semantic Search
|
||
**Type:** reference
|
||
**Source:** https://learn.microsoft.com/azure/databricks/generative-ai/tutorials/ai-cookbook/quality-rag-chain
|
||
|
||
---
|
||
|
||
## Innhold
|
||
|
||
- [Introduksjon](#introduksjon)
|
||
- [Kjernekomponenter](#kjernekomponenter)
|
||
- [Arkitekturmønstre](#arkitekturmønstre)
|
||
- [Beslutningsveiledning](#beslutningsveiledning)
|
||
- [Integrasjon med Microsoft-stakken](#integrasjon-med-microsoft-stakken)
|
||
- [Offentlig sektor (Norge)](#offentlig-sektor-norge)
|
||
- [Kostnad og lisensiering](#kostnad-og-lisensiering)
|
||
- [For arkitekten (Cosmo)](#for-arkitekten-cosmo)
|
||
- [Multi-Query RAG](#multi-query-rag)
|
||
- [Kilder og verifisering](#kilder-og-verifisering)
|
||
|
||
## Introduksjon
|
||
|
||
Query understanding er den kritiske fasen i en RAG-løsning som transformerer brukerens spørsmål til optimaliserte søkespørringer før retrieval-steget. Mens direkte bruk av brukerens originale spørsmål kan fungere for enkle queries, gir query understanding betydelig forbedring av retrieval-kvalitet gjennom teknikker som query expansion, intent classification, query rewriting og sub-query decomposition.
|
||
|
||
Målet med query understanding er tredelt: (1) **forstå brukerens intensjon** gjennom kontekstuell analyse, (2) **reformulere spørringen** til å treffe den terminologien som faktisk brukes i dokumentene, og (3) **utvide søkerommet** gjennom synonymer, relaterte termer og hypoteser. Dette steget er spesielt viktig i multi-turn conversations hvor kontekst fra tidligere meldinger må integreres, og i domener med spesialisert terminologi hvor brukerens ordvalg ikke matcher dokumentenes språk.
|
||
|
||
Microsoft-stakken tilbyr flere verktøy for å implementere query understanding: Azure OpenAI for LLM-basert reformulering, Azure AI Search med innebygd fuzzy search og synonym mapping, og Semantic Kernel for orchestration av multi-step query pipelines. Kostnaden varierer basert på tilnærming — regelbaserte transformasjoner er gratis, mens LLM-basert query expansion krever ekstra tokens per spørring (typisk 200-500 input tokens + 100-300 output tokens per reformulering).
|
||
|
||
## Kjernekomponenter
|
||
|
||
Query understanding består av flere teknikker som kan kombineres i en pipeline:
|
||
|
||
### 1. Intent Classification
|
||
|
||
Klassifisering av brukerens intensjon i forhåndsdefinerte kategorier for å velge riktig retrieval-strategi.
|
||
|
||
| Kategori | Eksempel | Handling |
|
||
|----------|----------|----------|
|
||
| **Factual lookup** | "Hva er hovedstaden i Frankrike?" | Single-shot keyword search |
|
||
| **Complex analysis** | "Sammenlign økonomiske effekter av X og Y" | Query decomposition + multi-source retrieval |
|
||
| **Troubleshooting** | "Feilen ORA-12154 i produksjon" | Entity extraction → filter på feilkode |
|
||
| **Conversational followup** | "Hva med kostnaden?" | Context integration fra chat history |
|
||
|
||
**Implementering:** Bruk Azure OpenAI med strukturert output (JSON mode) for å klassifisere intent. Alternativt: lightweight classification model (fine-tuned BERT) for lavere latency.
|
||
|
||
### 2. Query Expansion
|
||
|
||
Utviding av spørringen med synonymer, relaterte termer og alternative formuleringer for bredere coverage.
|
||
|
||
**Teknikker:**
|
||
|
||
- **Synonym mapping:** Azure AI Search støtter synonym maps — definer f.eks. `"AI, kunstig intelligens, maskinlæring"` for automatisk expansion ved indexing-tid
|
||
- **LLM-basert expansion:** Be GPT om "generer 3-5 alternative formuleringer av dette spørsmålet"
|
||
- **Domain-specific thesaurus:** Bruk bransjespesifikke ordbøker (f.eks. medisinsk terminologi)
|
||
|
||
**Trade-off:** Økt recall (finner flere relevante dokumenter), men potensielt lavere precision (mer støy). Kombiner med reranking.
|
||
|
||
### 3. Query Rewriting
|
||
|
||
Reformulering av spørringen for å adressere problemer som vaghet, manglende keywords, unødvendige ord eller uklar semantikk.
|
||
|
||
**Anvendelser:**
|
||
|
||
| Problem | Originalt spørsmål | Rewritten query |
|
||
|---------|-------------------|-----------------|
|
||
| **Vagueness** | "Vis meg skjemaet" | "Vis medarbeider onboarding request form" (fra chat context) |
|
||
| **Stavefeil** | "Azur AI serch" | "Azure AI Search" |
|
||
| **Missing context** | "Hva med kostnaden?" | "Hva er kostnaden for Azure OpenAI GPT-4 i Norge?" |
|
||
| **Unnecessary words** | "Jeg lurer virkelig på om kanskje..." | "Hvilke lisenser trengs for Power Automate AI Builder?" |
|
||
|
||
**Implementering:** Multi-turn chat med `create_history_aware_retriever` i LangChain/Semantic Kernel — dette genererer standalone queries fra conversation history.
|
||
|
||
### 4. Sub-query Decomposition
|
||
|
||
Dekomponering av komplekse spørsmål i flere enkle sub-queries som kjøres uavhengig, deretter aggregering av resultater.
|
||
|
||
**Eksempel:**
|
||
|
||
```
|
||
Original: "Hvordan fungerer elbiler, og hvordan sammenlignes de med fossile biler?"
|
||
|
||
Sub-queries:
|
||
1. "Hvordan fungerer elektriske kjøretøy? Forklar motor og batteri."
|
||
2. "Hva er fordeler og ulemper med elbiler vs bensin/diesel?"
|
||
3. "Sammenlign total eierkostnad for elbil og fossil bil over 5 år"
|
||
```
|
||
|
||
**Decomposition-strategi:**
|
||
|
||
1. **Klassifiser** spørringen som "simple" eller "complex" (via LLM prompt)
|
||
2. **Dekomponér** komplekse spørsmål i ordered sub-queries (minst → mest avhengig)
|
||
3. **Kjør** hver sub-query parallelt mot vector store
|
||
4. **Aggregér** top-N resultater fra alle sub-queries som accumulated context
|
||
5. **Kjør** original query med accumulated context til LLM
|
||
|
||
**Når bruke:** Multi-part questions, sammenligninger, "hvordan X og hva er effekten av Y"-spørsmål.
|
||
|
||
### 5. Filter Extraction
|
||
|
||
Identifisering og ekstraksjon av strukturerte filtre fra naturlig språk for metadata-filtrering.
|
||
|
||
**Eksempler:**
|
||
|
||
| Spørring | Ekstraherte filtre |
|
||
|----------|-------------------|
|
||
| "Artikler om AI fra siste 6 måneder" | `date >= 2025-08-01` |
|
||
| "Power Automate-dokumentasjon på norsk" | `language = 'no', product = 'Power Automate'` |
|
||
| "Sikkerhetspolicyer for helsesektoren" | `sector = 'healthcare', category = 'security'` |
|
||
|
||
**Krav:** Metadata må være tilgjengelig i search index (konfigureres ved indexing). Kombiner med Azure AI Search `$filter` parameter.
|
||
|
||
### 6. HyDE (Hypothetical Document Embeddings)
|
||
|
||
Teknikk hvor LLM genererer hypotetiske svar på spørringen, deretter brukes embedding av disse svarene (ikke spørringen) til vector search.
|
||
|
||
**Hvorfor fungerer det:** Answer-to-answer similarity er ofte høyere enn question-to-answer similarity i embedding space.
|
||
|
||
**Implementering:**
|
||
|
||
```python
|
||
# 1. Generer hypotetisk svar
|
||
hypothetical_answer = llm.generate(f"Answer this: {user_query}")
|
||
|
||
# 2. Embed svaret (ikke spørringen)
|
||
answer_embedding = embedding_model.embed(hypothetical_answer)
|
||
|
||
# 3. Søk mot vector store
|
||
results = vector_store.similarity_search_by_vector(answer_embedding, k=5)
|
||
```
|
||
|
||
**Trade-off:** Ekstra LLM-kall (kostnad + latency), men potensielt bedre retrieval for ambiguous queries.
|
||
|
||
## Arkitekturmønstre
|
||
|
||
### Mønster 1: Single-LLM Query Rewriting
|
||
|
||
**Beskrivelse:** Én LLM-prompt tar originalspørring + chat history → genererer optimalisert søkequery.
|
||
|
||
**Fordeler:**
|
||
|
||
- Enkel å implementere
|
||
- Lavere latency (ett LLM-kall)
|
||
- Kostnadseffektiv
|
||
|
||
**Ulemper:**
|
||
|
||
- Kan feile ved komplekse multi-step logic
|
||
- Mindre kontroll over individuelle steg
|
||
|
||
**Når bruke:** Simple chatbots, enkle FAQs, begrenset domene.
|
||
|
||
### Mønster 2: Multi-step Pipeline (Intent → Extract → Rewrite)
|
||
|
||
**Beskrivelse:** Tre sekvensielle LLM-kall: (1) klassifiser intent, (2) ekstraher entities/filters, (3) rewrite basert på (1) og (2).
|
||
|
||
**Fordeler:**
|
||
|
||
- Finere kontroll over hvert steg
|
||
- Bedre debugging (kan inspisere output fra hvert steg)
|
||
- Høyere kvalitet for komplekse scenarios
|
||
|
||
**Ulemper:**
|
||
|
||
- Høyere latency (3x LLM-kall)
|
||
- Høyere kostnad
|
||
- Mer kompleks kode
|
||
|
||
**Når bruke:** Support bots, komplekse B2B applications, multi-intent queries.
|
||
|
||
### Mønster 3: Hybrid (Rules + LLM)
|
||
|
||
**Beskrivelse:** Regelbaserte transformasjoner for common cases, LLM for edge cases.
|
||
|
||
```
|
||
IF query matches regex "Hva er <term>?"
|
||
THEN: Simple keyword search on <term>
|
||
ELSE IF query contains "sammenlign X og Y"
|
||
THEN: Decompose into [query about X, query about Y]
|
||
ELSE:
|
||
THEN: LLM-based rewriting
|
||
```
|
||
|
||
**Fordeler:**
|
||
|
||
- Optimalt cost/performance ratio
|
||
- Raskere for common patterns (ingen LLM-kall)
|
||
- Deterministisk oppførsel for kjente cases
|
||
|
||
**Ulemper:**
|
||
|
||
- Krever vedlikehold av rule set
|
||
- Fallback til LLM kan gi inkonsistens
|
||
|
||
**Når bruke:** High-volume production systems, kostnadsoptimalisering, deterministic requirements.
|
||
|
||
## Beslutningsveiledning
|
||
|
||
### Beslutningstabell: Hvilken teknikk når?
|
||
|
||
| Scenario | Anbefalt teknikk | Hvorfor |
|
||
|----------|-----------------|---------|
|
||
| Multi-turn chat | Query rewriting med chat history | Kontekstualisering av "Hva med X?" |
|
||
| Stavefeil og typos | Fuzzy search (Azure AI Search innebygd) | Ingen LLM-kostnad, rask |
|
||
| Vage spørsmål | Query expansion + reranking | Bredere coverage → rerank for precision |
|
||
| Komplekse sammenligninger | Sub-query decomposition | Parallell retrieval fra multiple sources |
|
||
| Spesialisert terminologi | Domain-specific synonym maps | Fast, deterministisk |
|
||
| Ambiguous queries | HyDE | Bedre embedding similarity |
|
||
| Filter-baserte spørsmål | Filter extraction + metadata filtering | Direkte $filter på index |
|
||
|
||
### Vanlige feil
|
||
|
||
1. **Over-rewriting:** Endrer brukerens intensjon ved aggressiv reformulering
|
||
- **Løsning:** Behold original query i parallel search, rerank combined results
|
||
2. **Expansion without reranking:** For bredt søk uten prioritering
|
||
- **Løsning:** Alltid kombiner expansion med cross-encoder reranker
|
||
3. **Missing context in multi-turn:** Glemmer tidligere chat history
|
||
- **Løsning:** Bruk `ConversationBufferMemory` (LangChain) eller Semantic Kernel chat history
|
||
4. **LLM hallucination i query rewriting:** Legger til fakta som ikke er i original query
|
||
- **Løsning:** Instruer LLM eksplisitt: "Do NOT add facts not present in the original query"
|
||
5. **Sub-query decomposition for simple queries:** Unødvendig overhead
|
||
- **Løsning:** Klassifiser query først (simple vs complex) før decomposition
|
||
|
||
### Røde flagg
|
||
|
||
- 🚩 **Høy kostnad:** > 1000 tokens per query for rewriting (optimaliser prompts)
|
||
- 🚩 **Latency > 2s:** For mange sekvensielle LLM-kalls (vurder parallelisering eller caching)
|
||
- 🚩 **Retrieval precision < 60%:** Query understanding hjelper ikke hvis underliggende chunks er dårlige
|
||
- 🚩 **User frustration:** Brukere reformulerer spørsmål 3+ ganger → query understanding feiler
|
||
|
||
## Integrasjon med Microsoft-stakken
|
||
|
||
### Azure OpenAI
|
||
|
||
**Query rewriting med chat history:**
|
||
|
||
```python
|
||
from langchain.chains import create_history_aware_retriever
|
||
from langchain_openai import AzureChatOpenAI
|
||
|
||
llm = AzureChatOpenAI(
|
||
azure_deployment="gpt-4",
|
||
temperature=0
|
||
)
|
||
|
||
retriever_chain = create_history_aware_retriever(
|
||
llm=llm,
|
||
retriever=vector_store.as_retriever(),
|
||
prompt=ChatPromptTemplate.from_messages([
|
||
("system", "Given chat history and latest user question, formulate a standalone question."),
|
||
MessagesPlaceholder("chat_history"),
|
||
("human", "{input}")
|
||
])
|
||
)
|
||
```
|
||
|
||
**Intent classification med structured output:**
|
||
|
||
```python
|
||
completion = client.chat.completions.create(
|
||
model="gpt-4",
|
||
messages=[
|
||
{"role": "system", "content": "Classify user intent. Return JSON: {\"category\": \"factual|complex|troubleshooting\"}"},
|
||
{"role": "user", "content": user_query}
|
||
],
|
||
response_format={"type": "json_object"}
|
||
)
|
||
intent = json.loads(completion.choices[0].message.content)["category"]
|
||
```
|
||
|
||
### Azure AI Search
|
||
|
||
**Fuzzy search for typos (innebygd):**
|
||
|
||
```http
|
||
POST https://{search-service}.search.windows.net/indexes/{index-name}/docs/search?api-version=2026-04-01
|
||
{
|
||
"search": "Azur~ AI~ Serch~", // ~ = fuzzy, default edit distance 2 (max 2, Damerau-Levenshtein); ~1 for 1 edit, ~0 for exact
|
||
"queryType": "full"
|
||
}
|
||
```
|
||
|
||
**Synonym mapping:**
|
||
|
||
```json
|
||
{
|
||
"name": "ai-synonyms",
|
||
"format": "solr",
|
||
"synonyms": [
|
||
"AI, kunstig intelligens, maskinlæring, machine learning",
|
||
"RAG, retrieval augmented generation, retrieval-basert generering",
|
||
"embedding, vektor, vector"
|
||
]
|
||
}
|
||
```
|
||
|
||
Koble til index via `synonymMaps` property på searchable fields.
|
||
|
||
### Semantic Kernel
|
||
|
||
**Multi-step query pipeline:**
|
||
|
||
```csharp
|
||
var kernel = Kernel.CreateBuilder()
|
||
.AddAzureOpenAIChatCompletion(deploymentName, endpoint, apiKey)
|
||
.Build();
|
||
|
||
// Step 1: Intent classification
|
||
var intentPlugin = kernel.ImportPluginFromPromptDirectory("IntentPlugin");
|
||
var intent = await kernel.InvokeAsync(intentPlugin["ClassifyIntent"],
|
||
new() { ["query"] = userQuery });
|
||
|
||
// Step 2: Query rewriting based on intent
|
||
var rewritePlugin = kernel.ImportPluginFromPromptDirectory("RewritePlugin");
|
||
var rewrittenQuery = await kernel.InvokeAsync(rewritePlugin[$"Rewrite{intent}"],
|
||
new() { ["query"] = userQuery, ["history"] = chatHistory });
|
||
|
||
// Step 3: Execute search with rewritten query
|
||
var results = await vectorStore.SearchAsync(rewrittenQuery.ToString());
|
||
```
|
||
|
||
### Copilot Studio
|
||
|
||
**Bruk "Create search query" node:** Automatisk query rewriting med conversation history — no-code løsning.
|
||
|
||
**Konfigurasjon:**
|
||
|
||
1. Legg til "Create search query" node i topic
|
||
2. Input: Current user message + conversation history
|
||
3. Output: Rewritten standalone query
|
||
4. Bruk output i "Search" eller "Custom connector" node
|
||
|
||
### Power Automate med AI Builder
|
||
|
||
**GPT-vurdering for query classification:** Bruk "Create text with GPT" action til å klassifisere intent før conditional branching.
|
||
|
||
## Offentlig sektor (Norge)
|
||
|
||
### Flerspråklige spørsmål
|
||
|
||
**Utfordring:** Brukere spør på norsk, nynorsk, samisk — dokumenter kan være på engelsk eller blandede språk.
|
||
|
||
**Løsninger:**
|
||
|
||
1. **Query translation:** Oversett spørring til engelsk før vector search (hvis dokumenter primært engelsk)
|
||
```python
|
||
translated_query = azure_translator.translate(user_query, to="en")
|
||
```
|
||
2. **Multilingual embeddings:** Bruk `text-embedding-3-large` (støtter 100+ språk) — spørring og dokumenter i ulike språk matcher i samme vector space
|
||
3. **Language-specific indexes:** Separate indexes per språk med language-specific analyzers
|
||
|
||
**Tilgjengelighet:**
|
||
|
||
- **Klarspråk:** Query rewriting bør forenkle, ikke komplisere — sjekk WCAG 2.1 readability guidelines
|
||
- **Voice input:** Håndter ASR-feil (stavefeil) via fuzzy search
|
||
|
||
### GDPR og personvern
|
||
|
||
**Filter ut PII i queries:** Bruk Azure AI Language PII detection på user query FØR logging eller videre prosessering.
|
||
|
||
```python
|
||
from azure.ai.textanalytics import TextAnalyticsClient
|
||
|
||
pii_result = text_analytics_client.recognize_pii_entities([user_query])
|
||
redacted_query = pii_result[0].redacted_text # Bruk denne videre
|
||
```
|
||
|
||
## Kostnad og lisensiering
|
||
|
||
### Prismodell
|
||
|
||
**Query understanding kostnader (per 1000 queries):**
|
||
|
||
| Tilnærming | Kostnad (NOK) | Latency | Kvalitet |
|
||
|------------|---------------|---------|----------|
|
||
| **Regelbasert (fuzzy, synonyms)** | 0 | ~50ms | Middels |
|
||
| **Single LLM rewrite (GPT-4o-mini)** | ~2 NOK | ~300ms | Høy |
|
||
| **Multi-step pipeline (3x GPT-4o-mini)** | ~6 NOK | ~800ms | Svært høy |
|
||
| **HyDE (GPT-4 Turbo)** | ~15 NOK | ~1.5s | Variabel |
|
||
|
||
**Antagelser:**
|
||
- GPT-4o-mini: 0.45 NOK / 1M input tokens, 1.80 NOK / 1M output tokens
|
||
- 250 input tokens + 150 output tokens per LLM-kall
|
||
|
||
### Optimaliseringstips
|
||
|
||
1. **Cache rewrites:** For common queries, cache rewritten version (Redis/Azure Cache)
|
||
2. **Batch processing:** Ved bulk-queries (f.eks. analytics), batch LLM-calls med `max_tokens` limit
|
||
3. **Fallback til billigere modeller:** GPT-4o-mini for simple rewrites, GPT-4 for complex decomposition
|
||
4. **Prompt optimization:** Reduser system prompt lengde — hver token repeteres per query
|
||
|
||
### Lisenser
|
||
|
||
| Komponent | Lisens/SKU |
|
||
|-----------|-----------|
|
||
| **Azure OpenAI** | Pay-as-you-go eller Provisioned Throughput Units (PTU) |
|
||
| **Azure AI Search** | Basic tier støtter synonym maps, Standard+ for semantic ranker |
|
||
| **Semantic Kernel** | Open-source (MIT) — ingen lisenskostnad |
|
||
| **Copilot Studio** | Per user/session (inkludert i M365 Copilot eller standalone) |
|
||
|
||
**ROI-vurdering:** Query understanding øker retrieval precision med 15-30% (typisk) → færre irrelevante svar → høyere user satisfaction → lavere support cost. Bryt-even oftest ved 5000+ queries/måned.
|
||
|
||
## For arkitekten (Cosmo)
|
||
|
||
### Spørsmål å stille kunden
|
||
|
||
1. **"Hva er typiske brukerqueries i løsningen deres? Kan jeg få 20-50 eksempler?"**
|
||
- Analysér for common patterns (multi-turn, typos, vage spørsmål)
|
||
2. **"Finnes det bransjespesifikk terminologi eller forkortelser brukerne bruker?"**
|
||
- Vurdér domain-specific synonym maps eller custom entity extraction
|
||
3. **"Hvor lang chat history er relevant? 3 messages? 10 messages?"**
|
||
- Påvirker context window size for query rewriting
|
||
4. **"Hva er budsjettet for retrieval per måned? Antall forventede queries?"**
|
||
- Bestemmer LLM-taktikk (GPT-4 vs GPT-4o-mini vs regelbasert)
|
||
5. **"Hva er akseptabel latency for et søk? 500ms? 2s? 5s?"**
|
||
- Single-step vs multi-step pipeline trade-off
|
||
6. **"Støtter løsningen kun norsk, eller multiple språk?"**
|
||
- Multilingual embeddings vs translation-basert approach
|
||
7. **"Hva er konsekvensen av feil retrieval? Kritisk (medical) eller informasjonell?"**
|
||
- Høy confidence threshold + decomposition for kritiske domener
|
||
8. **"Har dere eksisterende analytics på søkeadferd? Click-through rates?"**
|
||
- Evaluer current baseline før implementering av query understanding
|
||
|
||
### Fallgruver
|
||
|
||
- ❌ **Over-engineering:** Ikke bruk HyDE + decomposition + expansion for enkle FAQ-bots
|
||
- ❌ **Ignoring baseline:** Implementer alltid A/B test: query understanding ON vs OFF
|
||
- ❌ **LLM-avhengighet:** Regelbasert løser 60-70% av cases billigere
|
||
- ❌ **No reranking:** Query expansion uten reranker gir mer støy, ikke bedre svar
|
||
- ❌ **Context loss:** Å "reformulere" kan miste nyanser i original query
|
||
- ❌ **Token bloat:** System prompts på 1000+ tokens repeteres per query — optimaliser
|
||
- ❌ **Single evaluation metric:** Ikke kun precision — vurder recall, MRR, user satisfaction
|
||
|
||
### Anbefalinger per modenhetsnivå
|
||
|
||
**Nivå 1 — Starter (MVP):**
|
||
|
||
- Azure AI Search fuzzy search (innebygd, gratis)
|
||
- Synonym maps for top 10-20 vanlige termer
|
||
- Ingen LLM-basert query understanding (hold kostnad lav)
|
||
|
||
**Nivå 2 — Etablert (Production):**
|
||
|
||
- Single LLM rewrite med chat history (GPT-4o-mini)
|
||
- Filter extraction for common metadata (dato, kategori)
|
||
- A/B testing query understanding ON/OFF
|
||
|
||
**Nivå 3 — Optimert (Enterprise):**
|
||
|
||
- Multi-step pipeline: Intent → Extract → Rewrite
|
||
- Sub-query decomposition for komplekse spørsmål
|
||
- Cross-encoder reranking
|
||
- Continuous evaluation med precision@k, recall@k, MRR
|
||
|
||
**Nivå 4 — Avansert (Innovation):**
|
||
|
||
- HyDE for ambiguous queries
|
||
- Fine-tuned query classifier (BERT) for lavere latency
|
||
- Custom embedding model fine-tuned på domain data
|
||
- Real-time feedback loop (user clicks → retraining signal)
|
||
|
||
## Multi-Query RAG
|
||
|
||
### Konsept
|
||
|
||
Multi-Query RAG genererer *multiple spørringsvariasjoner* fra én brukerquery og kjører dem parallelt mot søkeindeksen. Resultatene dedupliseres og fusjoneres via **Reciprocal Rank Fusion (RRF)** for å gi bredere dekning enn en enkelt query.
|
||
|
||
### Forskjell fra Query Expansion
|
||
|
||
| Aspekt | Query Expansion | Multi-Query RAG |
|
||
|--------|----------------|-----------------|
|
||
| **Output** | Én beriket query | Multiple separate queries |
|
||
| **Søk** | Ett søk med utvidet query | Parallelle søk per variant |
|
||
| **Fusjon** | Ikke nødvendig | RRF / set union |
|
||
| **Dekning** | Dypere på ett konsept | Bredere over flere konsepter |
|
||
| **Kostnad** | 1 søk + 1 LLM-kall | N søk + 1 LLM-kall |
|
||
|
||
### Implementering i Azure
|
||
|
||
**Steg 1: Generer query-variasjoner med LLM**
|
||
|
||
```python
|
||
from openai import AzureOpenAI
|
||
|
||
client = AzureOpenAI(...)
|
||
|
||
def generate_query_variants(original_query: str, n: int = 3) -> list[str]:
|
||
response = client.chat.completions.create(
|
||
model="gpt-4o-mini",
|
||
messages=[{
|
||
"role": "system",
|
||
"content": f"""Generate {n} different search queries that capture
|
||
different aspects of the user's question. Return one query per line.
|
||
Original question: {original_query}"""
|
||
}],
|
||
temperature=0.7
|
||
)
|
||
variants = response.choices[0].message.content.strip().split("\n")
|
||
return [original_query] + variants[:n]
|
||
```
|
||
|
||
**Steg 2: Parallelle søk + RRF-fusjon**
|
||
|
||
```python
|
||
import asyncio
|
||
from collections import defaultdict
|
||
|
||
async def multi_query_search(queries: list[str], k: int = 5):
|
||
# Kjør parallelle søk
|
||
tasks = [search_async(q, top=k*2) for q in queries]
|
||
all_results = await asyncio.gather(*tasks)
|
||
|
||
# Reciprocal Rank Fusion
|
||
rrf_scores = defaultdict(float)
|
||
rrf_constant = 60
|
||
|
||
for results in all_results:
|
||
for rank, doc in enumerate(results):
|
||
rrf_scores[doc["id"]] += 1.0 / (rank + rrf_constant)
|
||
|
||
# Sorter og returner top-k
|
||
sorted_docs = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
|
||
return sorted_docs[:k]
|
||
```
|
||
|
||
### RRF-algoritmen
|
||
|
||
Azure AI Search bruker allerede RRF internt for hybrid search (BM25 + vektor). Multi-Query RAG legger til et ekstra lag:
|
||
|
||
```
|
||
RRF_score(d) = Σ_i (1 / (rank_i(d) + k))
|
||
|
||
hvor:
|
||
i = indeks over alle queries (original + varianter)
|
||
rank_i(d) = dokumentets posisjon i query i
|
||
k = konstant (typisk 60)
|
||
```
|
||
|
||
### Når bruke Multi-Query RAG
|
||
|
||
| Scenario | Bruk multi-query? | Begrunnelse |
|
||
|----------|-------------------|-------------|
|
||
| Brede, tematiske spørsmål | Ja | Dekker flere aspekter |
|
||
| Presise, fakta-baserte queries | Nei | Single query er tilstrekkelig |
|
||
| Tvetydig intent | Ja | Varianter fanger ulike tolkninger |
|
||
| Høyt volum (>10K queries/dag) | Vurder kostnad | N×søk-kostnad |
|
||
| Lav recall i eksisterende system | Ja | Øker sannsynlighet for relevante treff |
|
||
|
||
### Kostnad
|
||
|
||
Multi-Query RAG med 3 varianter = ~3x søkekostnad + 1 LLM-kall for generering.
|
||
- LLM-kall (gpt-4o-mini): ~0.01 NOK per query
|
||
- Ekstra søk: ~0.03 NOK per ekstra søk
|
||
- **Total ekstra kostnad:** ~0.10 NOK per query (for 3 varianter)
|
||
|
||
---
|
||
|
||
## Kilder og verifisering
|
||
|
||
**Microsoft Learn (Verified — search-query-fuzzy re-verifisert via MCP 2026-06-19):**
|
||
|
||
1. [Azure Databricks: Improve RAG chain quality — Query understanding](https://learn.microsoft.com/en-us/azure/databricks/generative-ai/tutorials/ai-cookbook/quality-rag-chain#query-understanding)
|
||
**Konfidens:** Verified — Multi-step query understanding (intent, entity extraction, rewriting)
|
||
|
||
2. [Azure Architecture: RAG Information retrieval — Query translation](https://learn.microsoft.com/en-us/azure/architecture/ai-ml/guide/rag/rag-information-retrieval#query-translation)
|
||
**Konfidens:** Verified — Query augmentation, decomposition, rewriting, HyDE
|
||
|
||
3. [Azure AI Search: Fuzzy search to correct misspellings and typos](https://learn.microsoft.com/en-us/azure/search/search-query-fuzzy)
|
||
**Konfidens:** Verified — Fuzzy query expansion med Damerau-Levenshtein distance
|
||
|
||
4. [Azure AI Search: Simple query syntax](https://learn.microsoft.com/en-us/azure/search/query-simple-syntax)
|
||
**Konfidens:** Verified — Keyword search, phrase search, boolean operators
|
||
|
||
5. [Azure Arc Edge RAG: Search type parameters](https://learn.microsoft.com/en-us/azure/azure-arc/edge-rag/search-types#search-type-parameters)
|
||
**Konfidens:** Verified — Query expansion, sub-query generation, hypothetical answer generation
|
||
|
||
6. [Copilot Studio: Create search query](https://learn.microsoft.com/en-us/microsoft-copilot-studio/authoring-create-search-query)
|
||
**Konfidens:** Verified — No-code query rewriting med conversation history
|
||
|
||
**RAG Experiment Accelerator (GitHub — Microsoft):**
|
||
|
||
7. [microsoft/rag-experiment-accelerator](https://github.com/microsoft/rag-experiment-accelerator)
|
||
**Konfidens:** Verified — Query classification, decomposition, rewriting prompts (Python)
|
||
|
||
**Seksjoner med baseline-kunnskap (modellkunnskap, ikke MCP-verifisert):**
|
||
|
||
- **Semantic Kernel C# eksempel:** Baseline — ikke funnet i MCP-søk, basert på SK pattern library
|
||
- **Token cost estimater:** Baseline — basert på Azure OpenAI pricing (januar 2026)
|
||
- **ROI-tall (15-30% precision improvement):** Baseline — industry benchmarks, ikke Microsoft-spesifikk data
|
||
|
||
**Totalt antall kilder:** 7 Microsoft Learn URLer (Verified) + 1 GitHub repo (Verified) = 8 kilder
|
||
|
||
|
||
### Simple Query Syntax for RAG (oppdatert 2026-06)
|
||
|
||
Azure AI Search simple query syntax er default parser for full-text søk i RAG:
|
||
|
||
**Boolske operatorer (tegn-basert):**
|
||
- `+` — AND (påkrevd term)
|
||
- `|` — OR (alternativ term)
|
||
- `-` — NOT (ekskluder term) — `searchMode=all` anbefales for presis NOT-atferd
|
||
|
||
**Prefix queries:** `lingui*` — matcher "linguistic", "linguini" etc.
|
||
|
||
**Phrase search:** `"eksakt frase"` — krever eksakt ordrekkefølge
|
||
|
||
**Begrensninger:** Ingen fuzzy search, ingen suffix/infix wildcard (bruk full Lucene syntax for det).
|
||
|
||
**Bruk i RAG query expansion:**
|
||
Simple syntax egner seg for keyword-delen av hybrid queries. For agentic RAG bruker LLM query planning til å generere fokuserte subqueries som kombinerer full-text + vector search parallelt. |