Практычныя прытамулі: як застаўляць RAG (Retrieval-Augmented Generation) у вашай системе
Практычныя прыказкі: як застосаваць RAG (Retrieval-Augmented Generation) у вашых контрактах, перакальцаваннях і слотах для коду для команд, які выкарыстоўваюць гэты патэрн.
Існавайце гэты документ як перапрацоўаную версію ідэй з кнігі «Як реалізаваць RAG (Retrieval-Augmented Generation) у вашай веб-застосоўцы» для аператараў: чыткія этапы, аранжаваныя блакі коду і прыметкі з варыянтамі восстанавлення, якія застаюцца пасля перадачы задання. Этап «Апглед» найэфектывней працюе, калі яго розглядаць як мерыябельную паверхню. Запісаце адна ідеальная транскрыпцыю, адзін прыклад неудачы і прыметкі з варыянтамі анулювання перш чым расширваць масштаб задання. Храніце настройкі парадульна коду застосоўкі. Файлы сераўнавання, хранальнікі секрэтных дадзеных і флагі функцыйяў должны знаходзіцца ў аднам месцы, якое аператары можаць пераглядаць без неабходнасці чытання всей структуры.
Розумеўце архітектуру, якая стоіць за RAG
Ёнколі хочаце зразумець архітектуры, яка стоіць за практыкай «Understanding the Architecture Behind», неабходна ўзначэнне вхідных дадзенняў, адпаведальнага за кожны крок і крэтарыяў завершэння працы перад зменым коду. Аперацыйныя працавнікі павінны магчыма было перзапусціць крок з вядомага пункту контролю, не спрабоўваючы здагадвацца пра схованы стан. Неабходна аддакументаваць як шлях успеху, так і шлях вярнення да нормальнага стану. Перапрыбуткі, людзкія перакрыцця і обробка некоректных паведамленняў є часткай продукту, а не чымсь, што дадаецца пазней. Паказвайце тыя часткі тексту, якія фактычна лежаць у падставе адпаведнай адказы. Без ціх цитатаў аперацыйныя працавнікі не зможуць адразніць галюцинацыю ад прасоў у індэксаванні.
User Question
|
v
Generate Query Embedding
|
v
Metadata Filtering
|
v
Vector Similarity Search
|
v
Top-K Relevant Documents
|
v
Context Construction
|
v
LLM / Gemini
|
v
Grounded Response + Sources
Установка інфраструктуры Embedding
Для стадіі налагоджэння вбудоввання неабяцо пазначыць вхідныя даны, адпаведальнага за шаг і критэрыі завершэння пры перадзеяванні коду. Аператары должны магчымаць перзапуск шага з вядомай точкі контролю, не падозрываючы прыхованы стан. Лепш выбіраць маленькія, тэставаныя елементы замест большых скрыптов. Калі шаг не выйшаў, прычына неудачы должна вказываць на адну конкрэтную адпаведальнасць, а не на заплутаны процес. Наводзіце тыя часткі, якія фактычна лежалі в основе адпаведнай адказы. Без ціх цитатаў аператары не зможуць разлічыць галюцинацію ад працягу індэксавання.
const { PredictionServiceClient, helpers } =
require('@google-cloud/aiplatform');
const PROJECT_ID = process.env.PROJECT_ID;
const client = new PredictionServiceClient({
apiEndpoint: 'aiplatform.googleapis.com'
});
async function generateEmbedding(
text,
taskType = 'RETRIEVAL_DOCUMENT'
) {
const endpoint =
`projects/${PROJECT_ID}/locations/global/` +
`publishers/google/models/gemini-embedding-001`;
const instance = {
content: text,
task_type: taskType
};
const request = {
endpoint,
instances: [helpers.toValue(instance)]
};
const [response] = await client.predict(request);
return response.predictions[0].embeddings.values;
}
Чаму важна групаванне пад час стварэння эмбеддзінаў
Для стадії «Чаму важлівы пакетаванні» неабяцкова ўзначыць вхідныя даны, адпаведальнага за крок і крэтыры завершэння пры змяне коду. Аператары должны магчымаць перзапуск кроку з вядомай точкі контролю, не спрабоўваючы здагадвацца пра схованы стан. Спрацавляйце з гэтай стадіяй як з кантрактом межаў вхідных дадзеных і перакананых выходных рэзультатаў. Даць назвы артыфактам, узначыць перакананні ў успеху і адмовіцца ад беззвучнага частковага завершэння. Цітаваць тыя часткі, якія фактычна сталі падставай для адпаведнай адказы. Без цітатаў аператары не можуць розразліць галюцинацію ад прычыны, вызванай працэсам індексавання. Для стадії «Чаму важлівы пакетаванні» неабяцкова ўзначыць вхідныя даны, адпаведальнага за крок і крэтыры завершэння пры змяне коду. Аператары должны магчымаць перзапуск кроку з вядомай точкі контролю, не спрабоўваючы здагадвацца пра схованы стан. Зберагачыце настройкі параду ўнутры коду прыемленае. Файлы сераўіса, хранільнікі секрэтных дадзеных і флагі функций должны знаходзіцца ў аднам месцы, якое аператары можуць пераглядаць, не чытаючы весь код.
aph.
const EMBEDDING_CONFIG = {
maxSegmentsPerRequest: 100,
maxTokensPerRequest: 18000,
concurrency: 3,
tokenEstimateDivisor: 3
};
function estimateTokens(text) {
return Math.ceil(
text.length / EMBEDDING_CONFIG.tokenEstimateDivisor
);
}
function packIntoBatches(texts) {
const batches = [];
let currentBatch = [];
let currentTokens = 0;
for (const text of texts) {
const tokens = estimateTokens(text);
const exceedsCount =
currentBatch.length >=
EMBEDDING_CONFIG.maxSegmentsPerRequest;
const exceedsTokens =
currentTokens + tokens >
EMBEDDING_CONFIG.maxTokensPerRequest;
if (exceedsCount || exceedsTokens) {
if (currentBatch.length > 0) {
batches.push(currentBatch);
}
currentBatch = [text];
currentTokens = tokens;
} else {
currentBatch.push(text);
currentTokens += tokens;
}
}
if (currentBatch.length > 0) {
batches.push(currentBatch);
}
return batches;
}
Іспытанне Firebase Firestore для пошуку вектораў
Калі працуеце над этапам іспытання Firebase Firestore для пошуку вектораў, спачатку запісайце умовы: неабходныя даны, сигнал успеху і тое, што выканаецца у разы частковага невялікога успеху. Такі список контролю дапамагае заліцварыць пазнейшыя змены ў кодзе. Документавайце як шлях успеху, так і шлях вяселення. Перапрыбуткі, людзкі контроль і обработка некоректных паведамленняў є частью продукту, а не пазнейшай дапрацоўкі. Замеряйце рэкалі на фіксаваным наборы пытанняў прычым регулюванні запрошэнняў. Частае змены запрошэнняў рэдка калі вялікія паштучныя проблемы з пошукам.
const { Firestore } = require('@google-cloud/firestore');
const firestore = new Firestore();
async function storeDocumentWithEmbedding(
collectionPath,
docId,
text,
embedding,
metadata
) {
const docRef =
firestore.doc(`${collectionPath}/${docId}`);
await docRef.set({
text,
embedding,
...metadata,
createdAt: Firestore.FieldValue.serverTimestamp()
});
}
async function findSimilarDocuments(
collectionPath,
queryEmbedding,
limit = 5
) {
const collectionRef =
firestore.collection(collectionPath);
const vectorQuery = collectionRef.findNearest({
vectorField: 'embedding',
queryVector: queryEmbedding,
limit,
distanceMeasure: 'DOT_PRODUCT'
});
const snapshot = await vectorQuery.get();
return snapshot.docs.map(doc => ({
id: doc.id,
data: doc.data()
}));
}
Спалучэнне фільтрацыі метадаў з семантычным пошукам
Калі працюеце над этапам «Адыянае фільтраўванне метадаў», спачатку запісайце умовы: неабходныя даны, сігнал успеху і тое, што выходзіць па частым неудачам. Такі список контроля дапамагае заліцьваты змяны ў кодзе пазнейша. Валіце маленькія, тэставаныя елементы замест большых скрыптаў. Калі якісь крок не выйшоў, неудача должна вказваць на адну конкрэтную адпаведальнасць, а не на заплутаны процес. Перад налаштаваннем запитоў пераканайцеся, як што працуе з фіксаваным наборам запитанняў. Частае змена запитоў рэдка калі вярнайце слабкую эфектыўнасць пошуку.
async function retrieveContext(
queryText,
selectedTopics = [],
limit = 5
) {
const queryEmbedding =
await generateEmbedding(
queryText,
'RETRIEVAL_QUERY'
);
let collectionRef =
firestore.collection('knowledge_base');
if (selectedTopics.length > 0) {
collectionRef = collectionRef.where(
'topics',
'array-contains-any',
selectedTopics
);
}
const vectorQuery =
collectionRef.findNearest({
vectorField: 'embedding',
queryVector: queryEmbedding,
limit,
distanceMeasure: 'DOT_PRODUCT'
});
const snapshot = await vectorQuery.get();
return snapshot.docs.map(doc => {
const data = doc.data();
return {
text: data.text,
source: data.source,
metadata: data.metadata || {}
};
});
}
Ператварэнне знайдзеных дакументаў у контекст модэлю
Калі працюеце над стадзіяй «Ператварэнне атрыманых дакументаў», спачатку запісайце контракт: неабходныя вхідныя даны, сігнал успеху і тое, што выходзіць у разе частковага невыпання. Такі список пераконвае ў тым, што пазнейшыя змены коду будуць чыстымі. Спрэчвайце гэтую стадзію як контракт межа вхіднымі данымі і пераверанымі выходамі. Дайце назву артыфактам, задаце перакананні успеху і не падзволяйце частковаму завершэнню без паведамлення. Зберагаце у кэшы стабільныя інструкцыі системы і схемы інструментаў. Перадача ідэнтычных прамуслов ёсць частым выклікам зношэння ресурсаў. Калі працюеце над стадзіяй «Ператварэнне атрыманых дакументаў», спачатку запісайце контракт: неабходныя вхідныя даны, сігнал успеху і тое, што выходзіць у разе частковага невыпання. Такі список пераконвае ў тым, што пазнейшыя змены коду будуць чыстымі. Зберагаце настройкі за межамі коду прыемлівача. Файлы сераўнавання, хранільнікі секрэтных дадзеных і флагі функцыйяў должны знаходзіцца ў аднам месцы, куды аператары можаць адбавіць аудыт без неабходнасці чытання всей структуры.
async function generateRAGResponse(
userQuery,
contextDocuments
) {
const contextSection =
contextDocuments
.map((doc, index) => {
return `
### Reference ${index + 1}
${doc.text}
Source: ${doc.metadata?.source || 'Unknown'}
`;
})
.join('\n');
const prompt = `
You are an AI assistant with access
to a knowledge base.
Use the provided context to answer
the user's question accurately.
CONTEXT:
${contextSection}
USER QUESTION:
${userQuery}
INSTRUCTIONS:
1. Answer using the provided context.
2. If the context is insufficient, say so clearly.
3. Cite the references used.
4. Do not invent information.
ANSWER:
`;
const result =
await genAI.models.generateContent({
model: 'gemini-2.5-flash-lite',
contents: prompt
});
return result.text.trim();
}
Оптымізацыя RAG за дапамою кэшавання і логікі практыкаў
Этап оптымізацыі RAG за дапамою кэшавання працуе наяўней, калі яго розглядаюць як мерыябельную структуру. Перш чым расширваць масштаб, зафіксавайце адна ідеальная транскрыпцыя, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану. Запісуйце адночасна шлях успеху і шлях вярнэння. Практыкі, адзначэнне людзя і обробка некоректных паведамленняў є частью продукту, а не етапамі пазнейшай доработкі. Раздзеліце правілы часткавання інфармацыі ад правілаў яе выявлення. Змена аднаго з іх не должна прымусіваць перапісванне другога, калі змянююцыся паказнікі якосці.
const embeddingCache = new Map();
async function getCachedEmbedding(text, taskType) {
const cacheKey = `${taskType}:${text}`;
if (embeddingCache.has(cacheKey)) {
return embeddingCache.get(cacheKey);
}
const embedding =
await generateEmbedding(text, taskType);
embeddingCache.set(cacheKey, embedding);
return embedding;
}
async function retryWithBackoff(
fn,
maxRetries = 3
) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (
error.code === 429 ||
error.message.includes('rate limit')
) {
const delay =
Math.pow(2, attempt) * 1000;
await new Promise(resolve =>
setTimeout(resolve, delay)
);
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}
Выявленне разных типаў контэксту
Процес выявлення разных типаў стадій працы найэфектывнейшы, калі яго розглядаць як вимерную плошчу. Зафіксавайце адну «золатую» транскрыпцыю, адин прыклад неудачы і запіс про відкатанне раней, чым расширваце масштабы. Валідзіце невялікія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выходзіць, прычына неудачы павінна вказываць на адную адпаведальнасць, а не на заплутаны ланцюг задач. Раздзеліце правілы часткавання інфармацыі ад правіл выявлення. Змена ў одных не павінна прымусваць перапісванне іншых, калі змянююцца показнікі якосці.
async function retrieveMultiContext(
queryText,
options = {}
) {
const {
includeDefinitions = true,
includeExamples = true,
includeHistorical = false,
topics = []
} = options;
const queryEmbedding =
await generateEmbedding(
queryText,
'RETRIEVAL_QUERY'
);
const contextPromises = [];
if (includeDefinitions) {
contextPromises.push(
findSimilarDocuments(
'definitions',
queryEmbedding,
3
).then(documents => ({
type: 'definitions',
documents
}))
);
}
if (includeExamples) {
contextPromises.push(
findSimilarDocuments(
'examples',
queryEmbedding,
5
).then(documents => ({
type: 'examples',
documents
}))
);
}
const contexts =
await Promise.all(contextPromises);
return contexts;
}
Кантроль RAG замест спэкуляцый пра якосць
Этап «Монітарынг RAG заместо» працуе наяўней, калі яго спрыяваць як мерыемую паверхню. Зафіксавайце адна ідеальная транскрыпцыя, адзін прыклад неудачы і запіс про вярнэнне да пачатковага стану пры розшырэнні масштаба. Спрыявайце гэты этап як кантракт между вхіднымі дадзеннямі і паўнастацэннымі выходамі. Дайце назвы артыфактам, задаць критэрыя успеху і не падзеўляйцеся частым, непূরным выкананнем задач. Раздзеліце політыку часткавага абрабатвання дадзенняў і політыку ўзяць дадзеныя. Змена адной з іх не должна прымусіваць перапісванне другой, калі зменяюцыся паказнікі якосці. Этап «Монітарынг RAG заместо» працуе наяўней, калі яго спрыяваць як мерыемую паверхню. Зафіксавайце адна ідеальная транскрыпцыя, адзін прыклад неудачы і запіс про вярнэнне да пачатковага стану пры розшырэнні масштаба. Зберагайце настройкі праза код аплікацыі. Файлы сяродавысці, хранільнікі секрэтных дадзенняў і флагі функций должны знаходзіцца ў аднам месцы, якое аператары можаць пераглядаць без неабяжнага чытання всіх дадзеных структураў.
class RAGMetrics {
constructor() {
this.metrics = {
totalQueries: 0,
averageLatency: 0,
retrievalAccuracy: [],
errors: []
};
}
logQuery(
query,
contextCount,
latency,
sources
) {
this.metrics.totalQueries++;
const previousLatency =
this.metrics.averageLatency *
(this.metrics.totalQueries - 1);
this.metrics.averageLatency =
(previousLatency + latency) /
this.metrics.totalQueries;
console.log({
query: query.substring(0, 100),
contextCount,
latency,
sourceCount: sources.length,
timestamp: new Date().toISOString()
});
}
logRetrievalAccuracy(
retrievedDocs,
relevantDocs
) {
const retrievedIds =
new Set(retrievedDocs.map(d => d.id));
const relevantIds =
new Set(relevantDocs.map(d => d.id));
const intersection =
new Set(
[...retrievedIds]
.filter(id => relevantIds.has(id))
);
const precision =
intersection.size / retrievedIds.size;
const recall =
intersection.size / relevantIds.size;
this.metrics.retrievalAccuracy.push({
precision,
recall
});
}
}
Практычны пайплайн RAG у працоўнай средзе
Для стадіі The Real Production RAG неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крэтыры завершэння пры змяне коду. Аперацыйныя працавнікі должны магчыма ўвайсці крок з вядомага пункту контролю, не спрабоўваючы здагадвацца пра схованы стан. Неабяжна задокументаваць як шлях успеху, так і шлях вярнення да нормы. Перапрыбуткі, людзкія перакрыцця і обробка некоректных паведамленняў є часткай продукту, а не чымсь, што дадаецца пазней. Паказваць часткі тексту, якія фактычна лежаць у падставе адпаведнай адказы. Без ціх цитатаў аперацыйныя працавнікі не зможуць адразніць галюцинацыю ад прасоў у індэксаванні.
DOCUMENT INGESTION
|
v
Clean + Split Documents
|
v
Generate Embeddings
|
v
Firestore + Metadata Storage
|
|
USER QUERY --------+
|
v
Query Embedding
|
v
Authorization + Filters
|
v
Vector Similarity Search
|
v
Relevant Context
|
v
Prompt Construction
|
v
Gemini / LLM
|
v
Answer + Sources + Metrics
На шым вы бы зосерадзіліся як старшы інжынер
У стадії «На штах вы хочаце сфармаваць увагу» неабяжна практычна апрацаваць інпуты, адміністратара крока та крэтыяры выходу пры перадзеіснавленні коду. Аператары должны магчымаць перзапуск крока з вядомай точкі контролю, не падозрываючы схованы стан. Лепш выбіраць маленькія, тэставаныя елементы замест большых скрыптов. Калі крок не выйшаў, прычына нехаспекі должна вказваць на адну конкрэтную адпаведальнасць, а не на заплутаны ланцужок задач. Прыкладзіце тыя часткі тексту, якія фактычна лежалі в основе адпаведнай адказы. Без цых цітатаў аператары не зможаць разлічыць галюцинацію ад працягу індэксавання.
Заключэнне: Стварэнне системы RAG, гатовай да выкарыстання
Для заключэння: пад час ствароўкі етапу, гатовага да выкарыстання ў прымэтнай средзе, пярш чым зменіць код, неабходна визначыць вхідныя даны, адпаведальнага за выкананне крока і критэрыя завершэння. Аператары должны магчымае перзапускать крок з вядомай точкі контролю, не спрабоўваючы здагадвацца пра схованы стан. Спрыяйце цэму етапу як даговору межа вхіднымі данымі і перакананымі выходнымі рэзультатамі. Даўце назвы артыфактам, визначыце критэрыя успеху і адмовіцеся ад беззвучнага частковага завершэння. Цітуйце тыя часткі, якія фактычна лежалі в основе адпаведнай адказы. Без цітатаў аператары не можуць разлічыць галюцинацію ад прасоў у індэксаванні. Для заключэння: пад час ствароўкі етапу, гатовага да выкарыстання ў прымэтнай средзе, пярш чым зменіць код, неабходна визначыць вхідныя даны, адпаведальнага за выкананне крока і критэрыя завершэння. Аператары должны магчымае перзапускать крок з вядомай точкі контролю, не спрабоўваючы здагадвацца пра схованы стан. Зберагачыце настройкі параду ад коду прыемленае. Файлы среды, хранільнікі секрэтных данных і флагі функций должны знаходзіцца ў аднам месцы, якое аператары можуць пераглядаць.
Не чытаючы весь граф.Чэк-ліст для аператыўнай роботы
Калі працуеце над чэк-лістам для аператыўнай роботы, спачатку запісуйце умовы контракту: неабходныя данні, сігнал успеху і тое, што выходзіць на падчасныя неудачы. Такі чэк-ліст дапамагае заставаць пазнейшыя змены коду чыстымі.
Запісвайце час выконання і кост токенаў або запытаў па боку функцыйнаых рэзультатаў. Відразлівасць костаў з самага пачатку запобегае неспакойным рахункам, калі працэс пераходзіць з дэмаверсіі ў спяльныя сераўы.
Замерьце рэткі адзыв на фіксаваны набор запытаў прычыну налаштавання підказак. Частае змены підказак рэдка калі-небудзь выправляюць слабую эфектыўнасць адзыву.
Заморозьце «золаты» набор даных прычыну змены підказак або модэляў. Змена як самай системы, так і критэрыяў магчымае сховаць регрэсіі.
Дадзіце тест на працэс, які пераглядае критычны шлях у CI з фіксамі, а не з рэальнымі платнымі API, калі толькі дозволяе бюджет.
Лепшыя маленькія, тэставаныя елементы чым велікія скрыпты. Калі якісь крок не выйшае, адказчыкам трэба быць чыстаю прычынай, а не заплутаным ланцугам задач.
Перш чым пераходзіць да наступнага крока, заморозьце версіі, зафіксавайце ідеальны прымер дзейна для критичнага маршруту та паказвайце способы вярнення да пачатковага стану. У спільных средах неабходны ліміты частоты запуска, пераканальнія перагляды та чыстае апамяроўванне адпаведальнага чалавека за зміны секрэтных даных. Лепшая надзеяна надзейнасць чым хітрыя едыноразовыя дэманстрацыі.
Запіс для 9607363b4f86: не кладзіце ключы прадаўцаў у репазітарый, задаце верхнюю межу токенаў на кожную сесію та зберагачыце прымеры дзейна рядом з фіксатрамі для ацэнкі, каб пазнейшыя замены моделяў заставаліся порównанымі.