Pada halaman ini
Retrieval-Augmented Generation (RAG) hanya sebaik data yang anda berikannya. Kebanyakan tutorial RAG menggunakan koleksi dokumen statik -- PDF atau fail markdown yang berada dalam folder. Sistem RAG pengeluaran memerlukan data web secara langsung: dokumentasi yang dikemas kini setiap minggu, harga pesaing yang berubah setiap bulan, kertas penyelidikan yang diterbitkan setiap hari.
Panduan ini membimbing melalui pembinaan saluran paip RAG yang lengkap yang menggunakan CrawlForge untuk crawl dan mengekstrak kandungan web, kemudian menyalurkannya ke dalam pangkalan data vektor untuk retrieval-augmented generation. Setiap langkah termasuk kod TypeScript yang berfungsi.
Kandungan
- Apakah itu RAG dan Kenapa Menggunakan Data Web?
- Seni Bina Saluran Paip RAG
- Langkah 1: Crawl Laman Web Sasaran
- Langkah 2: Ekstrak dan Bersihkan Kandungan
- Langkah 3: Chunk Teks untuk Embedding
- Langkah 4: Jana Embedding
- Langkah 5: Simpan dalam Pangkalan Data Vektor
- Langkah 6: Pertanyaan dan Dapatkan Semula
- Menyatukan Semuanya
- Tip Pengoptimuman Prestasi
- Soalan Lazim
Apakah itu RAG dan Kenapa Menggunakan Data Web?
Retrieval-Augmented Generation ialah teknik di mana respons LLM diakarkan dalam dokumen relevan yang diambil daripada pangkalan pengetahuan luaran. Daripada bergantung semata-mata pada data latihan (yang mempunyai had pengetahuan), sistem RAG mengambil konteks semasa yang relevan sebelum menjana jawapan.
Kenapa data web menjadikan RAG lebih baik:
- Kesegaran -- kandungan web dikemas kini dalam masa nyata; data latihan tidak
- Keluasan -- web meliputi setiap topik, industri, dan niche
- Kekhususan -- scrape dengan tepat halaman yang relevan dengan domain anda
- Kewibawaan -- tarik daripada dokumentasi rasmi, kertas penyelidikan, dan sumber yang dipercayai
Kes penggunaan RAG biasa dengan data web:
- Bot sokongan pelanggan yang berakar dalam dokumentasi secara langsung
- Sistem perisikan persaingan yang menjejaki perubahan pesaing
- Pembantu penyelidikan yang menarik daripada pangkalan data akademik dan berita
- Pangkalan pengetahuan dalaman yang diperkaya dengan data industri luaran
Seni Bina Saluran Paip RAG
Saluran paip RAG data web mempunyai enam peringkat:
Crawl --> Extract --> Chunk --> Embed --> Store --> Retrieve
| | | | | |
URLs Clean Text Vectors Vector Context
text segments DB + LLM
| Peringkat | Alat | Tujuan |
|---|---|---|
| Crawl | CrawlForge crawl_deep / batch_scrape | Temui dan ambil halaman |
| Extract | CrawlForge extract_content | Bersihkan HTML menjadi teks boleh dibaca |
| Chunk | Logik tersuai | Pisahkan teks menjadi segmen bersaiz embedding |
| Embed | OpenAI / Cohere / model setempat | Tukar teks kepada perwakilan vektor |
| Store | Pinecone / Weaviate / Qdrant | Indeks vektor untuk carian persamaan |
| Retrieve | Pertanyaan DB vektor + LLM | Cari chunk relevan, jana jawapan |
Langkah 1: Crawl Laman Web Sasaran
Pertama, temui dan ambil semua halaman relevan daripada domain sasaran anda. Alat crawl_deep CrawlForge mengendalikan penomboran, penemuan pautan, dan pengambilan selari.
import { CrawlForge } from '@crawlforge/sdk';
const cf = new CrawlForge({ apiKey: process.env.CRAWLFORGE_API_KEY });
// Crawl a documentation site up to 3 levels deep
const crawlResult = await cf.crawlDeep({
url: 'https://docs.example.com',
max_depth: 3,
max_pages: 200,
include_patterns: ['/docs/', '/guides/', '/api/'],
exclude_patterns: ['/changelog', '/blog'],
extract_content: true, // Get clean text during crawl
concurrency: 10
});
console.log(`Crawled ${crawlResult.pages.length} pages`);
// Crawled 147 pagesUntuk scraping senarai URL yang diketahui (seperti sitemap), gunakan batch_scrape sebagai gantinya:
// Scrape a specific list of URLs in parallel
const batchResult = await cf.batchScrape({
urls: [
'https://docs.example.com/auth',
'https://docs.example.com/billing',
'https://docs.example.com/webhooks',
'https://docs.example.com/rate-limits',
// ... up to 50 URLs per batch
],
formats: ['text'],
maxConcurrency: 10
});Kos credit: crawl_deep berharga 5 credits setiap penggunaan. batch_scrape berharga 5 credits setiap kelompok (sehingga 50 URL). Untuk 200 halaman, satu panggilan crawl_deep tunggal lebih kos efektif berbanding berbilang panggilan batch_scrape.
Langkah 2: Ekstrak dan Bersihkan Kandungan
HTML mentah mengandungi navigasi, iklan, pengaki, dan boilerplate yang akan mencemarkan embedding anda. Alat extract_content CrawlForge menggunakan algoritma kebolehbacaan untuk mengasingkan kandungan utama.
Jika anda menggunakan extract_content: true semasa langkah crawl, kandungan anda sudah bersih. Untuk halaman individu:
// Extract clean content from a single page
const page = await cf.extractContent({
url: 'https://docs.example.com/authentication'
});
console.log(page.content);
// Returns: "Authentication
All API requests require..."
// No nav bars, no footers, no cookie bannersTip pasca pemprosesan:
- Buang kandungan pendua (banyak laman mengulangi pengepala/pengaki dalam teks yang diekstrak)
- Lucutkan pautan navigasi dalaman ("Next: Billing" / "Previous: Setup")
- Normalkan ruang putih dan buang baris kosong
- Kekalkan pengepala -- ia menyediakan struktur untuk chunking
Langkah 3: Chunk Teks untuk Embedding
Model embedding mempunyai had token (biasanya 512-8,192 token). Dokumen panjang mesti dipisahkan menjadi chunk yang lebih kecil yang memelihara makna semantik.
interface TextChunk {
content: string;
metadata: {
source: string;
title: string;
heading: string;
chunkIndex: number;
};
}
function chunkByHeading(
text: string,
source: string,
title: string,
maxChunkSize: number = 1000 // characters
): TextChunk[] {
const chunks: TextChunk[] = [];
// Split on markdown headings (## or ###)
const sections = text.split(/(?=^#{2,3}\s)/m);
let chunkIndex = 0;
for (const section of sections) {
// Extract heading from section
const headingMatch = section.match(/^#{2,3}\s+(.+)/);
const heading = headingMatch ? headingMatch[1].trim() : title;
const content = section.trim();
if (content.length <= maxChunkSize) {
chunks.push({
content,
metadata: { source, title, heading, chunkIndex: chunkIndex++ }
});
} else {
// Split large sections by paragraph
const paragraphs = content.split('\n\n');
let currentChunk = '';
for (const para of paragraphs) {
if ((currentChunk + para).length > maxChunkSize && currentChunk) {
chunks.push({
content: currentChunk.trim(),
metadata: { source, title, heading, chunkIndex: chunkIndex++ }
});
currentChunk = para;
} else {
currentChunk += (currentChunk ? '\n\n' : '') + para;
}
}
if (currentChunk.trim()) {
chunks.push({
content: currentChunk.trim(),
metadata: { source, title, heading, chunkIndex: chunkIndex++ }
});
}
}
}
return chunks;
}
// Usage with crawled pages
const allChunks: TextChunk[] = [];
for (const page of crawlResult.pages) {
const pageChunks = chunkByHeading(
page.content,
page.url,
page.title || 'Untitled',
1000
);
allChunks.push(...pageChunks);
}
console.log(`Created ${allChunks.length} chunks from ${crawlResult.pages.length} pages`);
// Created 892 chunks from 147 pagesStrategi chunking dibandingkan:
| Strategi | Kelebihan | Kekurangan | Sesuai Untuk |
|---|---|---|---|
| Saiz tetap | Mudah, boleh diramal | Memutuskan di tengah ayat | Kandungan umum |
| Berasaskan pengepala | Memelihara struktur | Saiz chunk tidak sekata | Dokumentasi |
| Berasaskan ayat | Sempadan semula jadi | Mungkin terlalu kecil | Data perbualan |
| Rekursif | Saiz seimbang + struktur | Lebih kompleks | Sistem pengeluaran |
Langkah 4: Jana Embedding
Tukar setiap chunk teks menjadi perwakilan vektor menggunakan model embedding.
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
interface EmbeddedChunk extends TextChunk {
embedding: number[];
}
async function embedChunks(
chunks: TextChunk[],
batchSize: number = 100
): Promise<EmbeddedChunk[]> {
const embeddedChunks: EmbeddedChunk[] = [];
for (let i = 0; i < chunks.length; i += batchSize) {
const batch = chunks.slice(i, i + batchSize);
const texts = batch.map(c => c.content);
const response = await openai.embeddings.create({
model: 'text-embedding-3-small', // 1536 dimensions, $0.02/1M tokens
input: texts
});
for (let j = 0; j < batch.length; j++) {
embeddedChunks.push({
...batch[j],
embedding: response.data[j].embedding
});
}
console.log(`Embedded ${Math.min(i + batchSize, chunks.length)}/${chunks.length} chunks`);
}
return embeddedChunks;
}
const embeddedChunks = await embedChunks(allChunks);Pilihan model embedding:
| Model | Dimensi | Kos | Kualiti |
|---|---|---|---|
| OpenAI text-embedding-3-small | 1,536 | $0.02/1M tokens | Baik |
| OpenAI text-embedding-3-large | 3,072 | $0.13/1M tokens | Lebih Baik |
| Cohere embed-english-v3.0 | 1,024 | $0.10/1M tokens | Baik |
| Setempat (all-MiniLM-L6-v2) | 384 | Percuma | Memadai |
Langkah 5: Simpan dalam Pangkalan Data Vektor
Indeks chunk yang telah di-embed dalam pangkalan data vektor untuk carian persamaan yang pantas.
Contoh Pinecone
import { Pinecone } from '@pinecone-database/pinecone';
const pinecone = new Pinecone({ apiKey: process.env.PINECONE_API_KEY });
const index = pinecone.Index('rag-web-data');
// Upsert chunks in batches of 100
async function storeChunks(chunks: EmbeddedChunk[]) {
const batchSize = 100;
for (let i = 0; i < chunks.length; i += batchSize) {
const batch = chunks.slice(i, i + batchSize);
const vectors = batch.map((chunk, j) => ({
id: `${chunk.metadata.source}-${chunk.metadata.chunkIndex}`,
values: chunk.embedding,
metadata: {
content: chunk.content,
source: chunk.metadata.source,
title: chunk.metadata.title,
heading: chunk.metadata.heading
}
}));
await index.upsert(vectors);
console.log(`Stored ${Math.min(i + batchSize, chunks.length)}/${chunks.length} vectors`);
}
}
await storeChunks(embeddedChunks);Contoh Weaviate
import weaviate from 'weaviate-ts-client';
const client = weaviate.client({
scheme: 'https',
host: process.env.WEAVIATE_HOST || 'localhost:8080'
});
// Create collection schema
await client.schema.classCreator().withClass({
class: 'WebDocument',
vectorizer: 'none', // We provide our own vectors
properties: [
{ name: 'content', dataType: ['text'] },
{ name: 'source', dataType: ['string'] },
{ name: 'title', dataType: ['string'] },
{ name: 'heading', dataType: ['string'] }
]
}).do();
// Store chunks
for (const chunk of embeddedChunks) {
await client.data.creator()
.withClassName('WebDocument')
.withProperties({
content: chunk.content,
source: chunk.metadata.source,
title: chunk.metadata.title,
heading: chunk.metadata.heading
})
.withVector(chunk.embedding)
.do();
}Langkah 6: Pertanyaan dan Dapatkan Semula
Sekarang tanyakan pangkalan data vektor dengan soalan pengguna, dapatkan semula chunk yang relevan, dan hantarnya kepada LLM sebagai konteks.
async function ragQuery(question: string): Promise<string> {
// 1. Embed the question
const questionEmbedding = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: question
});
// 2. Query Pinecone for the 5 most relevant chunks
const queryResult = await index.query({
vector: questionEmbedding.data[0].embedding,
topK: 5,
includeMetadata: true
});
// 3. Build context from retrieved chunks
const context = queryResult.matches
.map(match => {
const meta = match.metadata as Record<string, string>;
return `Source: ${meta.source}\nSection: ${meta.heading}\n\n${meta.content}`;
})
.join('\n\n---\n\n');
// 4. Generate answer with context
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: `Answer the user's question based on the provided context.
Cite sources when possible. If the context does not contain enough
information, say so.`
},
{
role: 'user',
content: `Context:\n${context}\n\nQuestion: ${question}`
}
]
});
return completion.choices[0].message.content || 'No answer generated';
}
// Example usage
const answer = await ragQuery('How does authentication work?');
console.log(answer);Menyatukan Semuanya
Inilah saluran paip lengkap dalam satu fungsi orkestrasi tunggal:
async function buildRAGPipeline(config: {
targetUrl: string;
maxPages: number;
indexName: string;
}) {
const cf = new CrawlForge({ apiKey: process.env.CRAWLFORGE_API_KEY });
// 1. Crawl
console.log('Crawling...');
const crawled = await cf.crawlDeep({
url: config.targetUrl,
max_depth: 3,
max_pages: config.maxPages,
extract_content: true,
concurrency: 10
});
console.log(`Crawled ${crawled.pages.length} pages`);
// 2. Chunk
console.log('Chunking...');
const chunks = crawled.pages.flatMap(page =>
chunkByHeading(page.content, page.url, page.title || 'Untitled')
);
console.log(`Created ${chunks.length} chunks`);
// 3. Embed
console.log('Embedding...');
const embedded = await embedChunks(chunks);
// 4. Store
console.log('Storing...');
await storeChunks(embedded);
console.log('RAG pipeline complete. Ready for queries.');
}
// Run it
await buildRAGPipeline({
targetUrl: 'https://docs.example.com',
maxPages: 200,
indexName: 'rag-web-data'
});Jumlah kos credit CrawlForge untuk 200 halaman: 5 credits (satu panggilan crawl_deep tunggal dengan extract_content: true).
Tip Pengoptimuman Prestasi
- Kelompokkan embedding -- embed 100 chunk setiap panggilan API dan bukan satu demi satu (10x lebih pantas, kos sama)
- Gunakan chunking berasaskan pengepala untuk dokumentasi, berasaskan ayat untuk artikel berita
- Tetapkan pertindihan yang sesuai -- pertindihan 10-15% antara chunk menghalang kehilangan konteks di sempadan
- Tapis semasa crawl -- gunakan
include_patternsdanexclude_patternsuntuk mengelakkan crawling halaman yang tidak relevan - Caching secara agresif -- simpan kandungan yang di-crawl secara setempat supaya anda hanya crawl semula apabila kandungan berubah
- Pantau kesegaran -- gunakan penjejakan perubahan CrawlForge untuk mengesan bila halaman sumber dikemas kini, kemudian crawl semula dan embed semula hanya kandungan yang berubah
Soalan Lazim
Berapa banyak credits kos untuk membina saluran paip RAG dengan CrawlForge?
Satu panggilan crawl_deep tunggal berharga 5 credits dan boleh crawl sehingga 1,000 halaman. Untuk laman dokumentasi 200 halaman, jumlah kos CrawlForge ialah 5 credits. Free tier (1,000 credits) membolehkan anda membina 200 saluran paip RAG sebelum membayar apa-apa. Lihat butiran harga.
Pangkalan data vektor manakah yang patut saya gunakan untuk RAG?
Pinecone adalah yang paling mudah untuk dimulakan (terurus sepenuhnya, tiada infrastruktur). Weaviate menawarkan lebih fleksibiliti dengan carian hibrid (vektor + kata kunci). Qdrant ialah pilihan hos sendiri yang terbaik. ChromaDB berfungsi dengan baik untuk pembuatan prototaip dan pembangunan setempat.
Berapa kerap saya patut crawl semula dan kemas kini data RAG saya?
Ia bergantung pada berapa kerap kandungan sumber anda berubah. Laman dokumentasi: mingguan. Berita dan penyelidikan: harian. Katalog produk: setiap jam. Gunakan penjejakan perubahan CrawlForge untuk mengesan kemas kini dan hanya proses semula halaman yang berubah.
Bolehkah saya menggunakan CrawlForge dengan LangChain atau LlamaIndex?
Ya. CrawlForge berintegrasi dengan kedua-dua rangka kerja. Gunakan SDK untuk mengambil kandungan, kemudian hantarnya kepada pemuat dokumen LangChain atau penyambung data LlamaIndex. Lihat panduan integrasi LangChain kami untuk contoh.
Bina saluran paip RAG pertama anda dalam masa kurang daripada 10 minit. Mula percuma dengan 1,000 credits dan crawl laman pertama anda hari ini.
Cuba sendiri — tiada pendaftaran diperlukan
Jalankan mana-mana daripada 27 alat scraping dan pengekstrakan CrawlForge dalam playground, kemudian mula secara percuma dengan 1,000 credits.
1,000 credits percuma • Isi semula setiap bulan • Tiada kad kredit diperlukan
Tag
Tentang Penulis
Kekal dikemas kini dengan pandangan terkini
Dapatkan tutorial, kemas kini produk dan petua web scraping terus ke peti masuk anda.
Tiada spam. Berhenti melanggan bila-bila masa.