RAG (पुनर्प्राप्ति-संवर्धित जनरेशन)
अपने एजेंट को ज्ञान आधार दें, ताकि वे आपके अपने डेटा का उपयोग करके प्रश्नों के उत्तर दे सकें।
RAG क्या है?
RAG आपके एजेंट को किसी प्रश्न का उत्तर देने से पहले अपने दस्तावेज़ों से प्रासंगिक जानकारी देखने की सुविधा देता है। केवल उस जानकारी पर निर्भर रहने के बजाय जिस पर LLM को प्रशिक्षित किया गया था, एजेंट आपके डेटा में खोज करता है और परिणामों को संदर्भ के रूप में उपयोग करता है।
प्रवाह इस प्रकार है:
- अंतर्ग्रहण — दस्तावेज़ों को खंडों में विभाजित किया जाता है, वेक्टर एम्बेडिंग में बदला जाता है, और संग्रहीत किया जाता है
- क्वेरी — किसी प्रश्न का एम्बेडिंग बनाया जाता है और समानता के आधार पर संग्रहीत खंडों से मिलान किया जाता है
- जनरेशन — सबसे प्रासंगिक खंडों को LLM के पास उसके उत्तर के संदर्भ के रूप में भेजा जाता है
इसका अर्थ है कि आपका एजेंट उत्पाद दस्तावेज़ों, कंपनी नीतियों, कोडबेस या आपके द्वारा दिए गए किसी भी पाठ के बारे में प्रश्नों के उत्तर दे सकता है।
मुख्य विशेषताएँ:
- 📄 किसी भी पाठ का अंतर्ग्रहण — उत्पाद दस्तावेज़, markdown, कोड, नीतियाँ
- 🔍 सिमेंटिक खोज — केवल कीवर्ड के आधार पर नहीं, बल्कि अर्थ के आधार पर प्रासंगिक सामग्री खोजें
- 🤖 एजेंटिक पुनर्प्राप्ति — एजेंट
RagToolके माध्यम से तय करता है कि कब खोज करनी है- 🔌 प्लग-योग्य बैकएंड — कोड बदले बिना एम्बेडिंग प्रदाताओं और वेक्टर स्टोर को बदलें
इंस्टॉलेशन
[dependencies]
# Core only (in-memory store, all chunkers, no external deps)
adk-rag = "2.1.0"
# With Gemini embeddings (recommended for getting started)
adk-rag = { version = "2.1.0", features = ["gemini"] }
चरण 1: पाइपलाइन बनाएँ
एक RagPipeline चार घटकों को एक साथ जोड़ता है: एक chunker, एक embedding provider, एक vector store, और एक वैकल्पिक reranker।
use std::collections::HashMap;
use std::sync::Arc;
use adk_rag::{
Document, FixedSizeChunker, InMemoryVectorStore,
RagConfig, RagPipeline, EmbeddingProvider,
};
// Mock embedder for demos — no API key needed.
// In production, use GeminiEmbeddingProvider or OpenAIEmbeddingProvider.
struct MockEmbedder;
#[async_trait::async_trait]
impl EmbeddingProvider for MockEmbedder {
async fn embed(&self, text: &str) -> adk_rag::Result<Vec<f32>> {
let hash = text.bytes().fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64));
let mut v = vec![0.0f32; 64];
for (i, x) in v.iter_mut().enumerate() {
*x = ((hash.wrapping_add(i as u64)) as f32).sin();
}
let norm: f32 = v.iter().map(|x| x * x).sum::<f32>().sqrt();
if norm > 0.0 { v.iter_mut().for_each(|x| *x /= norm); }
Ok(v)
}
fn dimensions(&self) -> usize { 64 }
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let pipeline = RagPipeline::builder()
.config(RagConfig::builder()
.chunk_size(256)
.chunk_overlap(50)
.top_k(3)
.build()?)
.embedding_provider(Arc::new(MockEmbedder))
.vector_store(Arc::new(InMemoryVectorStore::new()))
.chunker(Arc::new(FixedSizeChunker::new(256, 50)))
.build()?;
// Create a collection and ingest a document
pipeline.create_collection("docs").await?;
pipeline.ingest("docs", &Document {
id: "intro".into(),
text: "Rust is a systems programming language focused on safety and speed.".into(),
metadata: HashMap::from([("topic".into(), "rust".into())]),
source_uri: None,
}).await?;
// Query
let results = pipeline.query("docs", "safe programming").await?;
for r in &results {
println!("[{:.3}] {}", r.score, r.chunk.text);
}
Ok(())
}
यह कैसे काम करता है:
FixedSizeChunkerदस्तावेज़ को 50-अक्षर के ओवरलैप के साथ 256-अक्षर के खंडों में विभाजित करता हैMockEmbedderप्रत्येक खंड को 64-आयामी वेक्टर में बदलता हैInMemoryVectorStoreवेक्टरों को संग्रहीत करता है और cosine similarity के आधार पर खोज करता हैquery()प्रश्न का एम्बेडिंग बनाता है, निकटतम खंड खोजता है, और उन्हें स्कोर के आधार पर क्रमित करके लौटाता है
चरण 2: किसी एजेंट में RAG जोड़ें
RAG की वास्तविक शक्ति तब सामने आती है जब कोई agent इसका उपयोग tool के रूप में करता है। RagTool pipeline को adk_core::Tool के रूप में लपेटता है — agent जब भी जानकारी की आवश्यकता होती है, rag_search को call करता है।
जब आप Gemini-आधारित agents के साथ RagTool का उपयोग करते हैं, तो ADK tool result को स्वचालित रूप से Gemini-संगत function response में सामान्यीकृत करता है। यह महत्वपूर्ण है क्योंकि rag_search स्वाभाविक रूप से chunks की सूची लौटाता है, जबकि Gemini wire पर functionResponse.response के एक JSON object होने की अपेक्षा करता है।
use std::sync::Arc;
use adk_agent::LlmAgentBuilder;
use adk_model::gemini::GeminiModel;
use adk_rag::{
Document, GeminiEmbeddingProvider, InMemoryVectorStore,
RagConfig, RagPipeline, RagTool, RecursiveChunker,
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let api_key = std::env::var("GOOGLE_API_KEY")?;
// Build pipeline with real embeddings
let pipeline = Arc::new(
RagPipeline::builder()
.config(RagConfig::builder().chunk_size(300).chunk_overlap(50).top_k(3).build()?)
.embedding_provider(Arc::new(GeminiEmbeddingProvider::new(&api_key)?))
.vector_store(Arc::new(InMemoryVectorStore::new()))
.chunker(Arc::new(RecursiveChunker::new(300, 50)))
.build()?,
);
// Ingest documents
pipeline.create_collection("kb").await?;
pipeline.ingest("kb", &Document {
id: "returns".into(),
text: "Our return policy allows returns within 30 days with a receipt.".into(),
metadata: Default::default(),
source_uri: None,
}).await?;
// Wrap pipeline as a tool and attach to an agent
let agent = LlmAgentBuilder::new("support")
.instruction("Answer questions using the rag_search tool. Cite your sources.")
.model(Arc::new(GeminiModel::new(&api_key, "gemini-3.7-flash")?))
.tool(Arc::new(RagTool::new(pipeline, "kb")))
.build()?;
// The agent now calls rag_search automatically when it needs knowledge base info
adk_cli::console::run_console(Arc::new(agent), "app".into(), "user1".into()).await?;
Ok(())
}
जब कोई उपयोगकर्ता पूछता है, "आपकी return policy क्या है?", तो agent:
- तय करता है कि उसे knowledge base में खोज करनी है
{"query": "return policy"}के साथrag_searchको call करता है- scores के साथ प्रासंगिक chunks प्राप्त करता है
- प्राकृतिक उत्तर तैयार करने के लिए chunks को context के रूप में उपयोग करता है
चरण 3: Chunking रणनीति चुनें
आप documents को जिस तरह विभाजित करते हैं, उसका retrieval quality पर प्रभाव पड़ता है। adk-rag तीन chunkers प्रदान करता है:
| Chunker | इसके लिए सर्वोत्तम | यह कैसे विभाजित करता है |
|---|---|---|
FixedSizeChunker | सामान्य पाठ, लॉग | ओवरलैप के साथ प्रत्येक N वर्ण |
RecursiveChunker | लेख, दस्तावेज़, कोड टिप्पणियाँ | अनुच्छेद → वाक्य → शब्द |
MarkdownChunker | Markdown फ़ाइलें, README | शीर्षकों के अनुसार, अनुभाग पदानुक्रम बनाए रखते हुए |
use adk_rag::{FixedSizeChunker, RecursiveChunker, MarkdownChunker};
// Fixed: simple, predictable chunks
let chunker = FixedSizeChunker::new(512, 100);
// Recursive: respects natural text boundaries
let chunker = RecursiveChunker::new(512, 100);
// Markdown: each section becomes a chunk with header_path metadata
let chunker = MarkdownChunker::new(512, 100);
RecursiveChunker सबसे अच्छा डिफ़ॉल्ट विकल्प है — यह पहले अनुच्छेद के विराम, फिर वाक्य की सीमाओं और अंत में शब्दों की सीमाओं को आज़माता है, जिससे निश्चित आकार में विभाजन की तुलना में अधिक स्वाभाविक खंड बनते हैं।
MarkdownChunker प्रत्येक खंड में एक header_path मेटाडेटा फ़ील्ड जोड़ता है (जैसे "Getting Started > Installation"), जिससे agent को विशिष्ट अनुभागों का संदर्भ देने में सहायता मिलती है।
कॉन्फ़िगरेशन
use adk_rag::RagConfig;
let config = RagConfig::builder()
.chunk_size(256) // max characters per chunk (default: 512)
.chunk_overlap(50) // overlap between chunks (default: 100)
.top_k(5) // results to return (default: 10)
.similarity_threshold(0.5) // minimum score to include (default: 0.0)
.build()?;
| पैरामीटर | यह क्या नियंत्रित करता है | मार्गदर्शन |
|---|---|---|
chunk_size | प्रत्येक खंड में अधिकतम वर्ण | अधिकांश उपयोग के मामलों के लिए 200–500। छोटा = अधिक सटीक, बड़ा = अधिक संदर्भ |
chunk_overlap | आसन्न खंडों के बीच साझा वर्ण | chunk_size का 10–20% सीमाओं पर जानकारी खोने से बचाता है |
top_k | लौटाए गए परिणामों की संख्या | अधिक परिणाम = LLM के लिए अधिक संदर्भ, लेकिन अधिक टोकन उपयोग |
similarity_threshold | शामिल करने के लिए न्यूनतम स्कोर | 0.0 सब कुछ लौटाता है; 0.3–0.7 कमजोर मिलानों को फ़िल्टर करता है |
एम्बेडिंग प्रदाता
| प्रदाता | फ़ीचर फ़्लैग | मॉडल | आवश्यकताएँ |
|---|---|---|---|
GeminiEmbeddingProvider | gemini | gemini-embedding-2 | GOOGLE_API_KEY |
OpenAIEmbeddingProvider | openai | text-embedding-3-small | OPENAI_API_KEY |
// Gemini
use adk_rag::GeminiEmbeddingProvider;
let embedder = GeminiEmbeddingProvider::new(&api_key)?;
// OpenAI
use adk_rag::OpenAIEmbeddingProvider;
let embedder = OpenAIEmbeddingProvider::new(&api_key, "text-embedding-3-small");
आप किसी भी कस्टम एम्बेडिंग सेवा के लिए EmbeddingProvider को भी कार्यान्वित कर सकते हैं।
वेक्टर स्टोर बैकएंड્સ
| बैकएंड | सुविधा फ़्लैग | इनके लिए सर्वोत्तम |
|---|---|---|
InMemoryVectorStore | (डिफ़ॉल्ट) | विकास, परीक्षण, छोटे डेटासेट |
QdrantVectorStore | qdrant | समर्पित वेक्टर डेटाबेस के साथ प्रोडक्शन |
LanceDBVectorStore | lancedb | एम्बेडेड वेक्टर DB (सर्वर की आवश्यकता नहीं) |
PgVectorStore | pgvector | जब आप पहले से PostgreSQL का उपयोग करते हैं |
// In-memory (no setup needed)
use adk_rag::InMemoryVectorStore;
let store = InMemoryVectorStore::new();
// Qdrant (requires running Qdrant server)
use adk_rag::QdrantVectorStore;
let store = QdrantVectorStore::new("http://localhost:6334").await?;
// pgvector (requires PostgreSQL with pgvector extension)
use adk_rag::PgVectorStore;
let store = PgVectorStore::new("postgres://user:pass@localhost/db").await?;
कस्टम रीरैंकर
डिफ़ॉल्ट NoOpReranker परिणामों को बिना किसी बदलाव के आगे भेजता है। सटीकता सुधारने के लिए एक कस्टम रीरैंकर लिखें:
use adk_rag::{Reranker, SearchResult};
struct KeywordBoostReranker { boost: f32 }
#[async_trait::async_trait]
impl Reranker for KeywordBoostReranker {
async fn rerank(
&self,
query: &str,
mut results: Vec<SearchResult>,
) -> adk_rag::Result<Vec<SearchResult>> {
let keywords: Vec<String> = query.split_whitespace()
.filter(|w| w.len() > 3)
.map(|w| w.to_lowercase())
.collect();
for r in &mut results {
let text = r.chunk.text.to_lowercase();
let hits = keywords.iter().filter(|kw| text.contains(kw.as_str())).count();
r.score += hits as f32 * self.boost;
}
results.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal));
Ok(results)
}
}
// Add to pipeline
let pipeline = RagPipeline::builder()
.config(config)
.embedding_provider(embedder)
.vector_store(store)
.chunker(chunker)
.reranker(Arc::new(KeywordBoostReranker { boost: 0.1 }))
.build()?;
एकाधिक संग्रह
अलग-अलग ज्ञान डोमेन के लिए अलग-अलग संग्रह का उपयोग करें। एजेंट विशिष्ट संग्रहों में खोज कर सकता है या आप कई RagTool इंस्टेंस बना सकते हैं:
// Create collections for different content types
pipeline.create_collection("docs").await?;
pipeline.create_collection("faq").await?;
pipeline.create_collection("changelog").await?;
// Ingest into each
pipeline.ingest("docs", &setup_doc).await?;
pipeline.ingest("faq", &faq_doc).await?;
pipeline.ingest("changelog", &release_doc).await?;
// One tool per collection — the agent picks which to search
let docs_tool = RagTool::new(pipeline.clone(), "docs");
let faq_tool = RagTool::new(pipeline.clone(), "faq");
let agent = LlmAgentBuilder::new("support")
.instruction("Search 'docs' for how-to questions, 'faq' for common questions.")
.model(Arc::new(model))
.tool(Arc::new(docs_tool))
.tool(Arc::new(faq_tool))
.build()?;
कॉल के समय "collection": "faq" को टूल में पास करके एजेंट संग्रह को ओवरराइड भी कर सकता है।
फीचर फ़्लैग
केवल अपनी आवश्यक निर्भरताएँ ही शामिल करें:
| सुविधा | सक्षम करता है | अतिरिक्त निर्भरता |
|---|---|---|
| (डिफ़ॉल्ट) | मुख्य traits, InMemoryVectorStore, सभी chunkers | कोई नहीं |
gemini | GeminiEmbeddingProvider | adk-gemini |
openai | OpenAIEmbeddingProvider | reqwest |
qdrant | QdrantVectorStore | qdrant-client |
lancedb | LanceDBVectorStore | lancedb, arrow |
pgvector | PgVectorStore | sqlx |
full | उपरोक्त सभी | सभी |
# Just core
adk-rag = "2.1.0"
# With Gemini embeddings
adk-rag = { version = "2.1.0", features = ["gemini"] }
# Everything
adk-rag = { version = "2.1.0", features = ["full"] }
नोट:
lancedbसुविधा के लिएprotocइंस्टॉल होना आवश्यक है।brew install protobuf(macOS) याapt install protobuf-compiler(Ubuntu) का उपयोग करके इंस्टॉल करें।
आर्किटेक्चर
Ingestion
Documents ──→ [Chunker] ──→ [EmbeddingProvider] ──→ [VectorStore]
Query
Question ──→ [EmbeddingProvider] ──→ [VectorStore search] ──→ [Reranker] ──→ Results
│
▼
Agent uses as context
RagPipeline दोनों प्रवाहों का समन्वय करता है। RagTool पाइपलाइन को adk_core::Tool के रूप में रैप करता है, ताकि एजेंट आवश्यकता पड़ने पर इसे कॉल कर सकें।
उदाहरण चलाएँ
cargo adk new rag_agent --template rag
cd rag_agent
cargo run
सर्वोत्तम प्रथाएँ
| अभ्यास | क्यों |
|---|---|
डिफ़ॉल्ट के रूप में RecursiveChunker का उपयोग करें | स्वाभाविक खंड सीमाएँ उत्पन्न करता है |
| खंडों को 200–500 अक्षरों का रखें | सटीकता और संदर्भ के बीच संतुलन बनाता है |
| उत्पादन में वास्तविक एम्बेडिंग का उपयोग करें | मॉक एम्बेडर केवल परीक्षण के लिए हैं |
similarity_threshold > 0 सेट करें | अप्रासंगिक शोर को फ़िल्टर करता है |
| डोमेन के अनुसार अलग-अलग संग्रह रखें | सटीकता में सुधार करता है और एजेंटों को लक्षित खोज करने देता है |
केवल डेवलपमेंट के लिए InMemoryVectorStore का उपयोग करें | उत्पादन के लिए Qdrant/pgvector पर स्विच करें |
संबंधित
- फ़ंक्शन टूल्स - कस्टम टूल्स बनाना
- LlmAgent - एजेंट्स में टूल्स जोड़ना
- मेमोरी - दीर्घकालिक मेमोरी (RAG से अलग)
पिछला: ← UI टूल्स | अगला: सेशंस →