🎯 Trình duyệt đám mây tùy chỉnh, chống phát hiện được hỗ trợ bởi Chromium tự phát triển, thiết kế dành cho trình thu thập dữ liệu webtác nhân AI. 👉Dùng thử ngay
Quay lại blog

Cách Xây Dựng Một Quy Trình RAG Địa Phương Với Scrapeless và Ollama

Ava Wilson
Ava Wilson

Expert in Web Scraping Technologies

30-Jul-2026

Tóm tắt:

  • Toàn bộ quy trình chạy cục bộ, không có khóa API của nhà cung cấp nhúng hoặc nhà cung cấp LLM. Scrapeless là cuộc gọi trả phí duy nhất trong quy trình này — thu thập các trang nguồn. Chia nhỏ, nhúng, lưu trữ vector, truy xuất và tạo câu trả lời đều chạy trên máy này.
  • Truy xuất được chứng minh, không phải giả định. Hai câu hỏi về hai người khác nhau được thu thập lại đúng tóm tắt tiểu sử của từng người trong số 48 tóm tắt trải dài trên 5 tác giả — với khoảng cách giống nhau thực tế được ghi lại bên dưới, không phải là khẳng định.
  • Bước tạo ra là một cuộc gọi cục bộ trực tiếp, không phải câu trả lời mẫu. Một mô hình Ollama 494 triệu tham số chỉ đọc tóm tắt đã được truy xuất và trả lời đúng, dựa trên văn bản được thu thập vài phút trước đó.
  • Chia nhỏ và nhúng giữ kích thước nhỏ có mục đích. Mỗi tiểu sử trở thành 4–14 tóm tắt chồng chéo 60 từ, được chuyển thành vector 384 chiều với sentence-transformers trong một cuộc gọi cục bộ được nhóm lại — không cần GPU.
  • Đây là nửa hạ lưu của hai quy trình Scrapeless khác. Nếu bạn đã có các tóm tắt sạch hoặc một chỉ mục nhúng OpenAI, hãy nhảy đến Giai đoạn 3 hoặc Giai đoạn 5 dưới đây — các sự khác biệt được chỉ ra trong nội dung.
  • Miễn phí để bắt đầu. Tạo khóa API Scrapeless của bạn theo gói miễn phí tại app.scrapeless.com.

Quy trình trong nháy mắt

Tạo ra gia tăng truy xuất, như được định nghĩa trong tài liệu RAG gốc, phối hợp một trình truy xuất trên một bộ dữ liệu bên ngoài với một trình sinh đọc những gì mà trình truy xuất tìm thấy. Hầu hết các hướng dẫn về ý tưởng đó dừng lại ở một nửa của nó. Cái này xây dựng cả hai nửa, từ đầu đến cuối, dựa trên các trang đã thu thập thực tế:

  1. Lấy dữ liệu — thu thập HTML đã được hiển thị cho một bộ trang tác giả cố định thông qua Scrapeless Universal Scraping API.
  2. Trích xuất và chia nhỏ — phân tích từng trang thành văn bản tiểu sử, chia thành các cửa sổ từ chồng chéo có nguồn gốc.
  3. Nhúng — chuyển đổi mỗi tóm tắt thành một vector cục bộ với sentence-transformers.
  4. Lưu trữ — duy trì các vector và siêu dữ liệu nguồn của chúng trong một bộ sưu tập Chroma cục bộ.
  5. Truy xuất — nhúng một câu hỏi, hỏi Chroma về các tóm tắt gần nhất, và kiểm tra xem liệu người đúng đã trở lại.
  6. Tạo ra — đưa tóm tắt đã được truy xuất cho một mô hình Ollama cục bộ và nhận được câu trả lời có cơ sở, mà không có cuộc gọi LLM nào trên đám mây.

Hai bài viết khác của Scrapeless đã đề cập đến những phần này. Hướng dẫn nhập văn bản web thu thập, trích xuất và chia nhỏ vào corpus.jsonl, sau đó dừng lại có chủ ý: các nhúng là "hạ lưu, với bất kỳ ngăn xếp nào bạn đã sử dụng." Hướng dẫn Pipeline văn bản LLM đi xa hơn: khám phá, trích xuất, chia nhỏ và nhúng với OpenAI vào Chroma. Nhưng nó dừng lại ở bản ghi nhúng — không có truy vấn nào với kho lưu trữ hoặc tạo ra câu trả lời — và nó cần một OPENAI_API_KEY trả phí. Hướng dẫn này là phần mà không có cái nào ở đó bao quát: nhúng cục bộ với khóa nhà cung cấp nhúng bằng không, một bằng chứng sống cho việc truy xuất tìm đúng nguồn, và một mô hình cục bộ thật sự trả lời câu hỏi. (Đối với khái niệm RAG bản thân nó hơn là một xây dựng, xem RAG là gì.)

Yêu cầu

  • Python 3.10 trở lên.
  • Một khóa API Scrapeless, được xuất ra dưới dạng SCRAPELESS_API_KEY — giai đoạn thu thập là giai đoạn duy nhất cần nó.
  • Ollama được cài đặt và chạy, với một mô hình nhỏ được tải xuống: ollama pull qwen2.5:0.5b.
  • pip install sentence-transformers chromadb beautifulsoup4 requests.

Cài đặt

sentence-transformers kéo theo PyTorch, vì vậy việc cài đặt đầu tiên mất một vài phút và khoảng một gigabyte dung lượng đĩa. Tất cả mọi thứ sau đó — tải mô hình, nhúng, và truy vấn — chạy ngoại tuyến một khi mô hình được lưu vào bộ nhớ cache.

bash Copy
pip install sentence-transformers chromadb beautifulsoup4 requests
ollama pull qwen2.5:0.5b
export SCRAPELESS_API_KEY="sk_your_scrapeless_key"

Giai đoạn 1 — Lấy Dữ Liệu Từ Các Trang Nguồn

Corpus cho buổi hướng dẫn này là năm trang tiểu sử tác giả trên quotes.toscrape.com, một trang web công cộng được xây dựng để thực hành việc thu thập thông tin: Albert Einstein, Jane Austen, Marilyn Monroe, J.K. Rowling và Thomas A. Edison. Năm người khác nhau với năm quê hương, ngày sinh và sự nghiệp thật sự khác biệt là điều khiến việc kiểm tra truy xuất ở Giai đoạn 5 có ý nghĩa - một câu hỏi về một trong số họ có một nguồn chính xác, không phải năm nguồn có thể.

Một POST cho mỗi trang đến Universal Scraping API sẽ trả lại HTML được hiển thị trong trường data của phản hồi:

python Copy
# fetch.py -- kéo các trang tiểu sử tác giả được hiển thị thông qua Scrapeless Universal Scraping API
import os
import pathlib

import requests

ENDPOINT = "https://api.scrapeless.com/api/v2/unlocker/request"
HEADERS = {
    "Content-Type": "application/json",
    "x-api-token": os.environ["SCRAPELESS_API_KEY"],
}
SOURCE_HOST = "https://quotes.toscrape.com"

AUTHORS = ["Albert-Einstein", "Jane-Austen", "Marilyn-Monroe", "J-K-Rowling", "Thomas-A-Edison"]

pathlib.Path("pages").mkdir(exist_ok=True)
for slug in AUTHORS:
    url = f"{SOURCE_HOST}/author/{slug}"
    resp = requests.post(
        ENDPOINT,
        headers=HEADERS,
        json={"actor": "unlocker.webunlocker", "input": {"url": url, "js_render": False, "redirect": True}},
        timeout=120,
    )
    resp.raise_for_status()
    html = resp.json()["data"]
    pathlib.Path("pages", f"{slug}.html").write_text(html, encoding="utf-8")
    print(f"{slug}: {len(html):,} bytes")

Các trang này được hiển thị từ phía máy chủ mà không có JavaScript phía khách hàng tạo tiểu sử, vì vậy js_render vẫn tại False - bộ mở khóa vẫn xử lý yêu cầu và trả lại trang theo bất kỳ cách nào, nhưng không có chi phí hiển thị cho nội dung đã có sẵn trong HTML ban đầu. Một lần chạy trực tiếp trả về năm kích thước byte khác nhau, mỗi tác giả một:

text Copy
Albert-Einstein: 5,329 bytes
Jane-Austen: 3,413 bytes
Marilyn-Monroe: 3,663 bytes
J-K-Rowling: 5,347 bytes
Thomas-A-Edison: 2,648 bytes

Nhận chìa khóa API của bạn trên gói miễn phí: app.scrapeless.com

Giai đoạn 2 - Trích xuất và Phân đoạn

Mỗi trang chứa ba trường giống nhau trong các lớp cố định: author-title, author-born-date, author-born-location, và author-description. Kéo chúng với BeautifulSoup, dẫn đầu tiểu sử với một câu sinh rõ ràng để thiết lập một khối riêng, sau đó phân tách thành các cửa sổ từ chồng chéo 60 từ với 15 từ chồng lên:

python Copy
# chunk_corpus.py -- pages/*.html -> corpus.jsonl (các khối từ cửa sổ chồng chéo với nguồn gốc)
import json
import pathlib
import re

from bs4 import BeautifulSoup

CHUNK_WORDS = 60
OVERLAP_WORDS = 15


def extract_author(html: str) -> dict:
    soup = BeautifulSoup(html, "html.parser")
    name = soup.find("h3", class_="author-title").get_text(strip=True)
    born_date = soup.find("span", class_="author-born-date").get_text(strip=True)
    born_loc = soup.find("span", class_="author-born-location").get_text(strip=True)
    desc = soup.find("div", class_="author-description").get_text(" ", strip=True)
    return {"name": name, "born_date": born_date, "born_location": born_loc,
            "description": re.sub(r"\s+", " ", desc)}


def chunk(words: list[str]):
    step = CHUNK_WORDS - OVERLAP_WORDS
    for start in range(0, max(len(words) - OVERLAP_WORDS, 1), step):
        yield start, " ".join(words[start:start + CHUNK_WORDS])


total = 0
with open("corpus.jsonl", "w", encoding="utf-8") as out:
    for page in sorted(pathlib.Path("pages").glob("*.html")):
        author = extract_author(page.read_text(encoding="utf-8"))
        lead = f"{author['name']} được sinh ra vào {author['born_date']} {author['born_location']}."
        words = f"{lead} {author['description']}".split()
        for start, body in chunk(words):
            out.write(json.dumps({"id": f"{page.stem}-{start}", "source": page.stem,
                                   "author": author["name"], "word_offset": start,
                                   "text": body}) + "\n")
            total += 1
print(f"{total} khối -> corpus.jsonl")

Một lần chạy trực tiếp trên năm trang đã thu thập được sẽ tạo ra 48 khối, không đồng đều theo cách mà mỗi tiểu sử thực sự nói:

text Copy
Albert Einstein: 615 từ -> 14 khối
J.K. Rowling: 644 từ -> 14 khối
Jane Austen: 327 từ -> 7 khối
Marilyn Monroe: 376 từ -> 9 khối
Thomas A. Edison: 195 từ -> 4 khối
48 khối -> corpus.jsonl

Sáu mươi từ với độ chồng chéo 25% là một cài đặt kho nhỏ, cố ý chặt chẽ hơn so với cửa sổ 220/40 từ trong hướng dẫn nhập liên kết ở trên - những tiểu sử này có vài trăm từ mỗi cái, không phải là những bài viết hàng nghìn từ mà khối hướng dẫn. Điều chỉnh cửa sổ theo chiều dài nguồn của bạn, không phải theo một mặc định cố định.

Giai đoạn 3 - Nhúng Tại Chỗ

Mỗi đoạn trở thành một vector 384 chiều với all-MiniLM-L6-v2, một mô hình nhúng câu gọn nhẹ đủ nhỏ để chạy trên CPU và được so sánh với các mô hình lớn hơn trên bộ kiểm tra MTEB. Mô hình chỉ cần tải xuống một lần (khoảng 90 MB) và mỗi lần gọi encode sau đó chạy offline:

python Copy
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("all-MiniLM-L6-v2")
vectors = model.encode(texts, show_progress_bar=False).tolist()

Giai đoạn 4 — Lưu trữ trong cơ sở dữ liệu vector cục bộ

PersistentClient của Chroma ghi bộ sưu tập vào đĩa, do đó chỉ mục vẫn tồn tại giữa các lần chạy mà không cần một máy chủ quản lý. Mỗi vector giữ thông tin về tác giả và trang nguồn như metadata, điều này giúp truy xuất một đoạn trở lại một trang cụ thể thay vì là một chuỗi văn bản vô danh:

python Copy
import chromadb

client = chromadb.PersistentClient(path=".chroma")
collection = client.create_collection("author_bios")
collection.add(
    ids=[c["id"] for c in chunks],
    documents=[c["text"] for c in chunks],
    embeddings=vectors,
    metadatas=[{"author": c["author"], "source": c["source"], "word_offset": c["word_offset"]}
               for c in chunks],
)

Một lần chạy thực tế trên tất cả 48 đoạn xác nhận rằng bộ sưu tập giữ mọi thứ đã được nhúng:

text Copy
đã nhúng 48 đoạn, chiều 384, số lượng bộ sưu tập 48

Giai đoạn 5 — Truy xuất đoạn đúng

Đây là bước mà hầu hết các hướng dẫn RAG cục bộ bỏ qua: chứng minh rằng việc truy xuất thực sự trả về nguồn chính xác thay vì chỉ trả về một cái gì đó. Nhúng một câu hỏi theo cách tương tự như các đoạn đã được nhúng, sau đó yêu cầu Chroma tìm các hàng xóm gần nhất theo khoảng cách L2 mặc định của nó, nơi một số nhỏ hơn có nghĩa là khớp gần hơn:

python Copy
query_vector = model.encode([question]).tolist()
result = collection.query(query_embeddings=query_vector, n_results=2)
top_author = result["metadatas"][0][0]["author"]
top_distance = result["distances"][0][0]

Hai câu hỏi về hai người khác nhau, chạy trên cùng một chỉ mục 48 đoạn, mỗi câu hỏi kéo về người đúng và không ai khác:

text Copy
H: Albert Einstein sinh ra ở đâu?
  tác giả khớp hàng đầu: Albert Einstein | khoảng cách: 0.6465
  đoạn: Albert Einstein sinh ngày 14 tháng 3 năm 1879 tại Ulm, Đức. Năm 1879, Albert Einstein sinh ra ở Ulm, Đức. Ông hoàn thành bằng Tiến sĩ tại Đại học Zurich b...
H: J.K. Rowling sinh ra ở đâu?
  tác giả khớp hàng đầu: J.K. Rowling | khoảng cách: 0.4566
  đoạn: J.K. Rowling sinh ngày 31 tháng 7 năm 1965 tại Yate, South Gloucestershire, Anh, Vương quốc Anh. Xem thêm: Robert Galbraith Mặc dù cô viết dưới bút danh...

Không câu hỏi nào truy xuất một đoạn từ Jane Austen, Marilyn Monroe, hay Thomas Edison — không gian nhúng tách biệt năm tiểu sử không liên quan đủ tốt để một câu hỏi thực tế về một người không vô tình làm nổi bật tiểu sử của người khác.

Giai đoạn 6 — Tạo một câu trả lời có căn cứ

Đoạn được truy xuất trở thành bối cảnh duy nhất mà một mô hình Ollama cục bộ nhận được. Lời nhắc yêu cầu một trường JSON duy nhất — chỉ cần câu trả lời — vì nguồn gốc đã sống trong metadata truy xuất từ Giai đoạn 5; yêu cầu một mô hình nhỏ cũng diễn đạt trường trích dẫn là yêu cầu nó làm một công việc mà kho vector đã thực hiện miễn phí, và đó không phải là một trường bổ sung đáng tin cậy để yêu cầu từ mô hình 494M thông số:

python Copy
import json
import requests

prompt = (
    'Trả lời câu hỏi trong một câu ngắn gọn chỉ sử dụng bối cảnh dưới đây. '
    'Trả lời chỉ với JSON {"answer": "..."}.\n\n'
    f"Bối cảnh:\n{context}\n\nCâu hỏi: {question}"
)
resp = requests.post(
    "http://localhost:11434/api/generate",
    json={"model": "qwen2.5:0.5b", "prompt": prompt, "stream": False,
          "format": "json", "options": {"temperature": 0, "num_predict": 40}},
    timeout=600,
)
answer = json.loads(resp.json()["response"])

Một cuộc gọi thực tế, chỉ cung cấp đoạn Einstein đã truy xuất, trả lời đúng mà không bao giờ thấy bốn tiểu sử khác:

text Copy
tác giả đoạn truy xuất (từ metadata kho vector): Albert Einstein
câu hỏi: Albert Einstein sinh ra ở đâu, và năm nào?
câu trả lời tạo ra: Albert Einstein sinh ra ngày 14 tháng 3 năm 1879 tại Ulm, Đức.

Đó là vòng lặp hoàn chỉnh: một câu hỏi được đưa vào, kho vector thu hẹp 48 ứng viên xuống còn một đoạn liên quan, và một mô hình chạy trên localhost biến đoạn đó thành một câu — không cần khóa OpenAI, không cần Trình duyệt Scraping, không có gì rời khỏi máy sau lần lấy ban đầu.

Dòng ống hoàn chỉnh

Mỗi giai đoạn ở trên được xâu chuỗi thành một script: tải xuống năm trang, chia nhỏ chúng, nhúng và lưu trữ kết quả, chạy cả hai kiểm tra truy xuất, sau đó tạo ra câu trả lời cuối cùng. Không có gì ở đây là giả tạo hoặc lắp ráp từ các phiên tách biệt — đó là sáu giai đoạn giống nhau, được thực hiện một lần, từ trên xuống dưới:

python Copy
# full_pipeline.py -- lấy -> phân đoạn -> nhúng -> lưu trữ -> truy xuất -> tạo ra, từ đầu đến cuối
import json
import logging
import os
import pathlib
import re

os.environ.setdefault("HF_HOME", "/usr/local/share/hf-cache")
os.environ.setdefault("HF_HUB_OFFLINE", "1")

import chromadb
import requests
from bs4 import BeautifulSoup
from sentence_transformers import SentenceTransformer

logging.getLogger("chromadb.telemetry.product.posthog").setLevel(logging.CRITICAL)

# Giai đoạn 1: Lấy
ENDPOINT = "https://api.scrapeless.com/api/v2/unlocker/request"
HEADERS = {"Content-Type": "application/json", "x-api-token": os.environ["SCRAPELESS_API_KEY"]}
SOURCE_HOST = "https://quotes.toscrape.com"
AUTHORS = ["Albert-Einstein", "Jane-Austen", "Marilyn-Monroe", "J-K-Rowling", "Thomas-A-Edison"]

pathlib.Path("pages").mkdir(exist_ok=True)
for slug in AUTHORS:
    url = f"{SOURCE_HOST}/author/{slug}"
    resp = requests.post(
        ENDPOINT, headers=HEADERS,
        json={"actor": "unlocker.webunlocker", "input": {"url": url, "js_render": False, "redirect": True}},
        timeout=120,
    )
    resp.raise_for_status()
    html = resp.json()["data"]
    pathlib.Path("pages", f"{slug}.html").write_text(html, encoding="utf-8")
    print(f"{slug}: {len(html):,} bytes")

# Giai đoạn 2: Trích xuất và phân đoạn
CHUNK_WORDS, OVERLAP_WORDS = 60, 15


def extract_author(html: str) -> dict:
    soup = BeautifulSoup(html, "html.parser")
    name = soup.find("h3", class_="author-title").get_text(strip=True)
    born_date = soup.find("span", class_="author-born-date").get_text(strip=True)
    born_loc = soup.find("span", class_="author-born-location").get_text(strip=True)
    desc = soup.find("div", class_="author-description").get_text(" ", strip=True)
    return {"name": name, "born_date": born_date, "born_location": born_loc,
            "description": re.sub(r"\s+", " ", desc)}


def chunk_words(words: list[str]):
    step = CHUNK_WORDS - OVERLAP_WORDS
    for start in range(0, max(len(words) - OVERLAP_WORDS, 1), step):
        yield start, " ".join(words[start:start + CHUNK_WORDS])


chunks = []
with open("corpus.jsonl", "w", encoding="utf-8") as out:
    for page in sorted(pathlib.Path("pages").glob("*.html")):
        author = extract_author(page.read_text(encoding="utf-8"))
        lead = f"{author['name']} sinh ra vào {author['born_date']} {author['born_location']}."
        words = f"{lead} {author['description']}".split()
        n_chunks = 0
        for start, body in chunk_words(words):
            rec = {"id": f"{page.stem}-{start}", "source": page.stem, "author": author["name"],
                   "word_offset": start, "text": body}
            out.write(json.dumps(rec) + "\n")
            chunks.append(rec)
            n_chunks += 1
        print(f"{author['name']}: {len(words)} từ -> {n_chunks} phân đoạn")
print(f"{len(chunks)} phân đoạn -> corpus.jsonl")

# Giai đoạn 3 & 4: Nhúng và lưu trữ
model = SentenceTransformer("all-MiniLM-L6-v2")
client = chromadb.PersistentClient(path=".chroma", settings=chromadb.Settings(anonymized_telemetry=False))
if "author_bios" in [c.name for c in client.list_collections()]:
    client.delete_collection("author_bios")
collection = client.create_collection("author_bios")

texts = [c["text"] for c in chunks]
vectors = model.encode(texts, show_progress_bar=False).tolist()
collection.add(
    ids=[c["id"] for c in chunks],
    documents=texts,
    embeddings=vectors,
    metadatas=[{"author": c["author"], "source": c["source"], "word_offset": c["word_offset"]} for c in chunks],
)
print(f"đã nhúng {len(chunks)} phân đoạn, kích thước {len(vectors[0])}, số lượng bộ sưu tập {collection.count()}")

# Giai đoạn 5: Truy xuất
for question in ["Albert Einstein sinh ra ở đâu?", "J.K. Rowling sinh ra ở đâu?"]:
    query_vector = model.encode([question]).tolist()
    result = collection.query(query_embeddings=query_vector, n_results=2)
    top_author = result["metadatas"][0][0]["author"]
    top_distance = result["distances"][0][0]
    top_text = result["documents"][0][0]
    print(f"C: {question}")
    print(f"  tác giả phù hợp hàng đầu: {top_author} | khoảng cách: {top_distance:.4f}")
    print(f"  phân đoạn: {top_text[:160]}...")

# Giai đoạn 6: Tạo ra
question = "Albert Einstein sinh ra ở đâu, và vào năm nào?"
query_vector = model.encode([question]).tolist()
result = collection.query(query_embeddings=query_vector, n_results=1)
context = result["documents"][0][0]
retrieved_author = result["metadatas"][0][0]["author"]

prompt = (
    "Trả lời câu hỏi trong một câu ngắn sử dụng CHỈ các thông tin dưới đây. "
    'Trả lời với CHỈ JSON {"answer": "..."}.\n\n'
    f"Ngữ cảnh:\n{context}\n\nCâu hỏi: {question}"
)
resp = requests.post(
    "http://localhost:11434/api/generate",
    json={"model": "qwen2.5:0.5b", "prompt": prompt, "stream": False,
          "format": "json", "options": {"temperature": 0, "num_predict": 40}},
    timeout=600,
)
resp.raise_for_status()
answer = json.loads(resp.json()["response"])
```python
print("tác giả đã lấy (từ siêu dữ liệu của cửa hàng vector):", retrieved_author)
print("câu hỏi:", question)
print("câu trả lời được tạo ra:", answer["answer"])

Một lần chạy từ trên xuống dưới sao chép lại mọi con số được hiển thị từng bước một ở trên: năm số byte, 48 khối, một bộ sưu tập 384 chiều của 48 vector, hai lần truy xuất đúng, và một câu trả lời có cơ sở.

Nguồn Tài Nguyên Trách Nhiệm

Các trang tiểu sử mô tả những người có thật, ngay cả trên một trang thực hành được xây dựng cho việc thu thập thông tin. Ba thực tiễn giữ cho một tập hợp truy xuất có thể được bảo vệ khi bạn chỉ định mẫu này vào các nguồn sản xuất: chỉ thu thập các trang công khai và kiểm tra điều khoản dịch vụ của từng trang web và chỉ dẫn cho robot trước khi thêm một miền vào danh sách lấy; giữ URL nguồn và tên tác giả gắn liền với mỗi khối, giống như cách siêu dữ liệu của quy trình này làm, để một câu trả lời được tạo ra có thể được truy xuất về nguồn gốc của nó; và xử lý khối lượng yêu cầu theo cách mà hướng dẫn này làm — năm trang, một lần, không phải là một việc quét đứng chống lại một trang đã không đồng ý với điều đó.

Kết Luận

Năm trang vào, một câu có cơ sở ra, và mỗi giai đoạn sau lần lấy dữ liệu ban đầu đều chạy trên máy này. Nửa phần truy xuất là phần đáng tin cậy trước nửa phần sinh ra: 48 khối, hai câu hỏi, hai tác giả đúng, với các khoảng cách để chứng minh điều đó — và chỉ sau đó mô hình địa phương mới được phép trả lời. Hoán đổi các trang nguồn, mô hình nhúng, hoặc mô hình Ollama, và hình dạng vẫn giữ nguyên; quy trình không thay đổi khi nội dung thay đổi.

Sẵn Sàng Xây Dựng Quy Trình RAG Của Bạn?

Tham gia cộng đồng xây dựng các quy trình dữ liệu trên Scrapeless: Discord · Telegram.

Đăng ký tại app.scrapeless.com để nhận tín dụng dùng thử miễn phí, và chỉ định Giai Đoạn 1 vào các trang mà tập hợp truy xuất của bạn cần. Tài liệu dành cho nhà phát triển đề cập đến hình dạng yêu cầu unlocker.webunlocker, và các kế hoạch hiện tại có trên trang giá cả.

Câu Hỏi Thường Gặp

Q: Có phần nào của quy trình này cần một khóa API đám mây ngoài Scrapeless không?

Không. Scrapeless là cuộc gọi trả phí duy nhất, và nó chỉ cần cho lần lấy dữ liệu của Giai Đoạn 1. Việc nhúng diễn ra cục bộ qua sentence-transformers, cửa hàng vector là một tệp Chroma trên đĩa, và việc sinh ra diễn ra qua một mô hình Ollama cục bộ — không có gì khác yêu cầu một khóa nhà cung cấp.

Q: Sự khác biệt này với các bài đăng liên quan đến RAG khác của Scrapeless là gì?

Phạm vi, không phải sự chồng chéo. Hướng dẫn tiếp nhận thu thập, trích xuất, và phân khối, sau đó dừng lại cố ý trước khi nhúng. Hướng dẫn pipeline LLM-text nhúng với OpenAI và lưu trữ trong Chroma, sau đó dừng lại trước khi truy vấn hoặc sinh ra. Hướng dẫn này là phần cục bộ, không cần khóa nhúng, làm hoàn tất vòng lặp: truy xuất, sau đó sinh ra.

Q: Tôi có cần một GPU không?

Không. all-MiniLM-L6-v2qwen2.5:0.5b đều chạy trên CPU cho hướng dẫn này. Một GPU tăng tốc mô hình cục bộ lớn hơn, nhưng cả bước nhúng và mô hình sinh nhỏ không yêu cầu phải có một để xây dựng và kiểm tra quy trình.

Q: Làm thế nào tôi chọn kích thước khối?

Khớp nó với cách mà một nguồn duy nhất thực sự nói. Các tiểu sử trong hướng dẫn này chạy vài trăm từ mỗi cái, vì vậy 60 từ với một độ chồng chéo 15 từ giữ cho một khối gần bằng một ý tưởng. Một bài báo nhiều ngàn từ yêu cầu một cửa sổ rộng hơn — hướng dẫn tiếp nhận phía trên sử dụng 220 từ với 40 từ chồng chéo vì lý do đó.

Q: Thì sao nếu việc truy xuất trả về khối sai?

Thông thường có nghĩa là tập hợp có nội dung gần giống mà mô hình nhúng không thể phân tách, hoặc câu hỏi được diễn đạt rất khác so với cách mà văn bản nguồn được diễn đạt. Mở rộng sự chồng chéo của các khối, nhúng với một mô hình lớn hơn, hoặc thêm một lần sắp xếp lại thứ hai trên những ứng viên hàng đầu đều giúp đỡ; năm tiểu sử riêng biệt trong hướng dẫn này là cố ý dễ phân biệt, đó là điều làm cho một kết quả truy xuất sạch có thể hiển thị thay vì chỉ khẳng định.

Q: Tôi có thể hoán đổi một mô hình cục bộ lớn hơn cho việc sinh ra không?

Có — hình dạng yêu cầu không thay đổi, chỉ có chuỗi model thay đổi. qwen2.5:0.5b đủ nhanh để chứng minh vòng lặp trên CPU; một mô hình Ollama 3B hoặc lớn hơn trả lời đáng tin cậy hơn trong ngữ cảnh hỗn loạn hơn nếu phần cứng có đủ bộ nhớ để dự trữ.
H: Việc thu thập thông tin từ các trang tiểu sử và tạo ra câu trả lời từ đó có hợp pháp không?

Việc thu thập thông tin từ các trang công khai là khá được phép, nhưng các điều khoản của trang web và quyền tài phán đều có ý nghĩa quan trọng. Chỉ thu thập các trang công khai, kiểm tra điều khoản dịch vụ và chỉ dẫn của robot của trang mục tiêu trước, giữ cho khối lượng thu thập có giới hạn và xử lý bất kỳ dữ liệu cá nhân nào trong tập hợp theo các luật bảo mật áp dụng cho bạn.

Tại Scrapless, chúng tôi chỉ truy cập dữ liệu có sẵn công khai trong khi tuân thủ nghiêm ngặt các luật, quy định và chính sách bảo mật trang web hiện hành. Nội dung trong blog này chỉ nhằm mục đích trình diễn và không liên quan đến bất kỳ hoạt động bất hợp pháp hoặc vi phạm nào. Chúng tôi không đảm bảo và từ chối mọi trách nhiệm đối với việc sử dụng thông tin từ blog này hoặc các liên kết của bên thứ ba. Trước khi tham gia vào bất kỳ hoạt động cạo nào, hãy tham khảo ý kiến ​​cố vấn pháp lý của bạn và xem xét các điều khoản dịch vụ của trang web mục tiêu hoặc có được các quyền cần thiết.

Bài viết phổ biến nhất

Danh mục