יישום Multi-Query RAG לשיפור איכות האחזור
דמיינו: מערכת ה-RAG שלכם מחזירה תשובות לא רלוונטיות עבור 20% מהשאילתות רק בגלל ניסוח לקוי. כמהנדסים עם ניסיון עמוק ב-AI/ML, נתקלנו בבעיה זו עשרות פעמים. לדוגמה, השאילתה 'איך לפטר עובד' ו'הליך לסיום חוזה העסקה' הן זהות, אך המערכת רואה וקטורים שונים ומאבדת חצי מהמסמכים הרלוונטיים. RAG (Retrieval-Augmented Generation) היא טכניקה המשפרת את האחזור על ידי ניסוח מחדש אוטומטי של השאילתה המקורית בכמה דרכים. כל גרסה מופעלת דרך החיפוש, והתוצאות ממוזגות. זה מפחית את התלות של איכות התשובה בניסוח הספציפי של השאילתה ומגביר את שלמות האחזור. בניסיון שלנו, זה הניב רווח של 38% ב-recall עם עלייה מתונה ב-latency.
כיצד Multi-Query RAG מגביר את שלמות האחזור
אותו מידע יכול להיות מתואר באמצעות מונחים שונים. לדוגמה, במאגר ידע ארגוני, השאילתה 'איך להזמין חופשה' עשויה למצוא בקשות, 'הליך לקבלת חופשה שנתית' עשויה למצוא תקנות, ו'כללים למתן ימי חופשה' עשויה למצוא מדיניות משאבי אנוש. Multi-Query משלב את כל השלוש ומשיג הקשר מלא יותר, שהוא קריטי לתהליכים עסקיים. לפי מחקר RAG בפרקטיקה, שיפור ה-recall יכול להגיע ל-38%.
יישום עם LangChain
from langchain.retrievers.multi_query import MultiQueryRetriever
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Qdrant
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Qdrant.from_existing_collection(
embeddings=embeddings,
collection_name="knowledge_base",
url="http://localhost:6333",
)
retriever = MultiQueryRetriever.from_llm(
retriever=vectorstore.as_retriever(search_kwargs={"k": 5}),
llm=llm,
include_original=True,
)
# Использование
docs = retriever.invoke("каков порядок согласования крупной сделки")
# Внутри LangChain генерирует 3 перефразирования + оригинал,
# ищет по каждому и дедуплицирует результаты
זוהי הגרסה הבסיסית — אנו משתמשים בה לאב-טיפוס מהיר. בסביבת ייצור, לעיתים קרובות נדרש prompt מותאם אישית לפרטי הלקוח.
Multi-Query מותאם אישית עם שליטה ב-Prompt
ה-prompt ברירת המחדל של LangChain יכול להיות מוחלף ב-prompt מתמחה:
from langchain.prompts import PromptTemplate
from langchain_core.output_parsers import BaseOutputParser
class LineListOutputParser(BaseOutputParser):
"""Парсит список вопросов из ответа LLM"""
def parse(self, text: str) -> list[str]:
lines = text.strip().split("\n")
return [line.strip().lstrip("123456789.-) ") for line in lines if line.strip()]
MULTI_QUERY_PROMPT = PromptTemplate(
input_variables=["question"],
template="""Ты — AI-ассистент по поиску документов. Твоя задача — сгенерировать 5 различных вариантов следующего вопроса для улучшения поиска в векторной базе.
Правила:
- Используй синонимы и альтернативные формулировки
- Один вариант — более конкретный, один — более общий
- Сохраняй смысл оригинального вопроса
- Каждый вопрос с новой строки, без нумерации
Оригинальный вопрос: {question}
Варианты:"""
)
custom_retriever = MultiQueryRetriever(
retriever=vectorstore.as_retriever(search_kwargs={"k": 4}),
llm_chain=MULTI_QUERY_PROMPT | llm | LineListOutputParser(),
include_original=True,
)הניסיון שלנו מראה ש-prompt מותאם אישית מניב recall טוב יותר ב-5-10%, מכיוון שהוא מותאם לתחום הלקוח.
Multi-Query מקבילי עם הסרת כפילויות
כדי להפחית את ה-latency, אנו מריצים את החיפוש עבור כל הגרסאות במקביל:
import asyncio
from openai import AsyncOpenAI
async def multi_query_search(
original_query: str,
vectorstore,
n_variants: int = 4,
top_k_per_query: int = 5,
) -> list[str]:
"""Параллельный multi-query retrieval"""
async_client = AsyncOpenAI()
# Генерируем варианты запроса
response = await async_client.chat.completions.create(
model="gpt-4o-mini",
messages=[{
"role": "user",
"content": f"Сгенерируй {n_variants} перефразирования вопроса:\n{original_query}\nОдин вопрос на строку."
}],
temperature=0.5,
)
variants = response.choices[0].message.content.strip().split("\n")
all_queries = [original_query] + variants[:n_variants]
# Параллельный поиск
search_tasks = [
asyncio.to_thread(vectorstore.similarity_search, q, k=top_k_per_query)
for q in all_queries
]
results_per_query = await asyncio.gather(*search_tasks)
# Дедупликация по content
seen_texts = set()
unique_docs = []
for docs in results_per_query:
for doc in docs:
text_hash = hash(doc.page_content[:100])
if text_hash not in seen_texts:
seen_texts.add(text_hash)
unique_docs.append(doc)
return unique_docs
גישה זו שומרת על latency בטווח של 700-800 אלפיות השנייה גם עם 5 גרסאות שאילתה.
מהניסיון שלנו: מקרה של משרד עורכי דין
לאחרונה יישמנו Multi-Query RAG עבור לקוח בתחום המשפטי. מערך נתונים: מאגר ידע ארגוני (3200 מסמכים). קבוצת בדיקה — 200 שאילתות עם מסמכים רלוונטיים מסומנים.
| תצורה | Recall@10 | Precision@5 | Latency (ממוצע) |
|---|---|---|---|
| שאילתה בודדת, k=5 | 0.61 | 0.71 | 280ms |
| שאילתה בודדת, k=15 | 0.72 | 0.58 | 310ms |
| Multi-query (4 גרסאות), k=5 | 0.84 | 0.69 | 680ms |
| Multi-query + Reranker | 0.84 | 0.81 | 920ms |
Multi-query העלה את ה-recall מ-0.61 ל-0.84 (38%+) עם עלייה מתונה ב-latency (×2.4). לאחר הוספת reranker, ה-precision גם התאושש ל-0.81.
השוואה עם חלופות: HyDE (Hypothetical Document Embeddings) בבדיקה שלנו הראה Recall@10 = 0.71 אך דרש שלב נוסף של יצירת מסמך היפותטי. Multi-Query טוב ב-18% מ-HyDE ב-recall ופשוט יותר ליישום.
| שיטה | Recall@10 | Latency (ממוצע) | מורכבות יישום |
|---|---|---|---|
| שאילתה בודדת | 0.61 | 280 ms | נמוכה |
| HyDE | 0.71 | 450 ms | בינונית |
| Multi-Query | 0.84 | 680 ms | בינונית |
הטבלה מראה ש-Multi-Query מספק את ה-recall הטוב ביותר עם עלייה מקובלת ב-latency.
מדוע Multi-Query יעיל יותר מ-HyDE?
HyDE יוצר מסמך היפותטי אחד ומחפש על פיו, מה שמניב שיפור אך פחות מ-Multi-Query. הסיבה: מספר גרסאות שאילתה מכסות יותר וריאציות סמנטיות מאשר מסמך אחד. יתרה מכך, Multi-Query פשוט יותר ליישום — אין צורך בשלב נוסף של יצירת מסמך.
כיצד אנו מיישמים Multi-Query RAG: שלב אחר שלב
- ביקורת על מערכת ה-RAG הנוכחית ומערך הנתונים. ניתוח מבנה השאילתות והמסמכים.
- בחירת המודל ליצירת גרסאות (GPT-4o-mini, Claude Haiku, LLaMA 3). קביעת מספר הגרסאות (בדרך כלל 3-5).
- התאמת ה-prompt לתחום. בדיקה על מדגם מייצג.
- שילוב חיפוש מקבילי והסרת כפילויות. אופטימיזציה של latency.
- בדיקת A/B על השאילתות שלכם. השוואה עם המערכת הנוכחית.
- תיעוד והדרכת הצוות. מסירת קוד והוראות.
המחזור כולו אורך שבוע אחד. העלות מחושבת באופן אישי: טווח טיפוסי $5,000-$15,000 בהתאם למורכבות. זה מחזיר את עצמו תוך 2-3 חודשים דרך חיסכון בזמן למשתמשים. לפי הערכות לקוחות, חיסכון בזמן על חיפוש מידע יכול להיות משמעותי עבור חברה גדולה, ולעיתים קרובות עולה על $100,000 בשנה.
מתי ליישם Multi-Query ומתי לא?
Multi-Query הוא אופטימלי אם:
- שאילתות המשתמשים מגוונות ומכילות מילים נרדפות;
- מאגר הידע מכיל יותר מ-5000 מסמכים;
- latency של עד שנייה אחת מקובל.
אין צורך בכך כאשר:
- דרישות ה-latency נמוכות מ-200 אלפיות השנייה;
- המשתמשים מקפידים על טרמינולוגיה אחת;
- מערך הנתונים קטן (שאילתה בודדת כבר נותנת recall גבוה).
מה כלול בעבודת היישום שלנו
אנו מיישמים Multi-Query RAG במפתח מלא:
- ביקורת על מערכת ה-RAG הנוכחית ומערך הנתונים.
- בחירת המודל ליצירת גרסאות (GPT-4o-mini, Claude Haiku, LLaMA 3).
- התאמת ה-prompt לתחום.
- שילוב חיפוש מקבילי והסרת כפילויות.
- בדיקת A/B על השאילתות שלכם.
- תיעוד והדרכת צוות.
- מסירה: קוד מקור מלא, קבצי תצורה, מדריך פריסה, דוח תוצאות בדיקות, וגישה לצוות התמיכה שלנו למשך 30 יום לאחר היישום.
עם ניסיון של למעלה מ-5 שנים ב-AI/ML ויותר מ-50 יישומי RAG מוצלחים, אנו מבטיחים שיפור של לפחות 30% ב-recall או החזר כספי. הצוות שלנו מחזיק בהסמכות ב-OpenAI ו-LangChain.
דוגמה ל-prompt ליצירת גרסאות
Ты — AI-ассистент по поиску документов.
Сгенерируй 5 различных вариантов следующего вопроса для улучшения поиска в векторной базе.
Правила:
- Используй синонимы и альтернативные формулировки
- Один вариант — более конкретный, один — более общий
- Сохраняй смысл оригинального вопроса
- Каждый вопрос с новой строки, без нумерации
Оригинальный вопрос: {question}
Варианты: ציר זמן ועלות
ציר זמן משוער:
- יישום Multi-Query Retriever: 2-3 ימים;
- כוונון prompt ומספר גרסאות: 2-3 ימים;
- בדיקות על מערך נתונים: 2-3 ימים;
- סה"כ: שבוע אחד.
העלות מחושבת באופן אישי, אך בשל חיפוש מידע מואץ, המערכת מחזירה את עצמה תוך 2-3 חודשים. להערכה ראשונית של התרחיש שלכם, צרו קשר — ננתח את מערך הנתונים שלכם ונמליץ על התצורה האופטימלית. הזמינו יישום Multi-Query RAG וקבלו שיפור של עד 38% ב-recall תוך שבוע. קבלו ייעוץ עם מהנדס יישום.







