Volver al blog

Cómo construir un pipeline RAG local con Scrapeless y Ollama

Ava Wilson
Ava Wilson

Expert in Web Scraping Technologies

30-Jul-2026

TL;DR:

  • Todo el bucle se ejecuta localmente, sin clave de API de proveedor de incrustación o de proveedor de LLM. Scrapeless es la única llamada de pago en este pipeline: obtiene las páginas fuente. La agrupación, incrustación, almacenamiento de vectores, recuperación y generación de respuestas se ejecutan en esta máquina.
  • La recuperación está comprobada, no asumida. Dos preguntas sobre dos personas diferentes raspadas devuelven cada una el fragmento biográfico correcto de 48 fragmentos que abarcan 5 autores, con las verdaderas distancias de similitud capturadas a continuación, no aseveradas.
  • El paso de generación es una llamada local en vivo, no una respuesta de plantilla. Un modelo de Ollama de 494 millones de parámetros solo lee el fragmento recuperado y responde correctamente, basado en el texto raspado minutos antes.
  • La agrupación y la incrustación se mantienen pequeñas a propósito. Cada biografía se convierte en fragmentos de 60 palabras superpuestos, de 4 a 14, transformados en vectores de 384 dimensiones con sentence-transformers en una llamada local por lotes, sin necesidad de GPU.
  • Este es la mitad descendente de otros dos pipelines de Scrapeless. Si ya tienes fragmentos limpios o un índice incrustado de OpenAI, salta a la Etapa 3 o a la Etapa 5 a continuación; las diferencias se indican en línea.
  • Gratis para comenzar. Crea tu clave de API de Scrapeless en el plan gratuito en app.scrapeless.com.

Pipeline a Simple Vista

La generación aumentada por recuperación, como se define en el documento RAG original, empareja un recuperador sobre un corpus externo con un generador que lee lo que encuentra el recuperador. La mayoría de las guías de ese concepto se detienen en una mitad. Esta construye ambas mitades, de principio a fin, contra páginas raspadas reales:

  1. Obtener — extraer HTML renderizado para un conjunto fijo de páginas de autores a través de la API Universal de Raspado de Scrapeless.
  2. Extraer y agrupar — analizar cada página en texto biográfico, dividido en ventanas de palabras superpuestas con procedencia.
  3. Incrustar — convertir cada fragmento en un vector localmente con sentence-transformers.
  4. Almacenar — persistir los vectores y sus metadatos de origen en una colección local de Chroma.
  5. Recuperar — incrustar una pregunta, pedir a Chroma los fragmentos más cercanos y verificar si volvió la persona correcta.
  6. Generar — pasar el fragmento recuperado a un modelo local de Ollama y obtener una respuesta fundamentada, sin ninguna llamada a una LLM en la nube.

Dos otras publicaciones de Scrapeless ya cubren partes de esto. La guía de ingestión de texto web obtiene, extrae y agrupa en corpus.jsonl, y luego se detiene a propósito: las incrustaciones son "descendentes, con cualquier pila que ya uses". La guía de pipeline de texto LLM va más allá: descubrir, extraer, agrupar e incrustar con OpenAI en Chroma. Pero se detiene en el registro incrustado: nada consulta el almacén o genera una respuesta, y necesita una OPENAI_API_KEY paga. Esta guía es la parte que ninguna de esas cubre: incrustaciones locales sin clave de proveedor de incrustación, una prueba en vivo de que la recuperación encuentra la fuente correcta, y un modelo local que realmente responde a la pregunta. (Para el concepto de RAG en sí, en lugar de una construcción, consulte Qué es la Generación Aumentada por Recuperación.)

Requisitos Previos

  • Python 3.10 o posterior.
  • Una clave de API de Scrapeless, exportada como SCRAPELESS_API_KEY: la etapa de obtención es la única que la necesita.
  • Ollama instalado y en funcionamiento, con un modelo pequeño descargado: ollama pull qwen2.5:0.5b.
  • pip install sentence-transformers chromadb beautifulsoup4 requests.

Instalar

sentence-transformers descarga PyTorch, por lo que la primera instalación lleva unos minutos y alrededor de un gigabyte de espacio en disco. Todo lo demás —carga de modelos, incrustaciones y consultas— se ejecuta sin conexión una vez que el modelo está en caché.

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

Etapa 1 — Obtener las Páginas Fuente

El corpus para este recorrido son cinco páginas de biografía de autores en quotes.toscrape.com, un sitio público construido para la práctica de scraping: Albert Einstein, Jane Austen, Marilyn Monroe, J.K. Rowling y Thomas A. Edison. Cinco personas diferentes con cinco lugares de nacimiento, fechas y carreras genuinamente diferentes es lo que hace que la verificación de recuperación de la Etapa 5 sea significativa: una pregunta sobre uno de ellos tiene una fuente correcta, no cinco plausibles.

Una solicitud POST por página a la API Universal Scraping devuelve el HTML renderizado en el campo data de la respuesta:

python Copy
# fetch.py -- extraer páginas de biografía de autores a través de la API Universal Scraping de Scrapeless
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")

Estas páginas se renderizan del lado del servidor sin JavaScript del lado del cliente construyendo la biografía, por lo que js_render permanece en False — el desbloqueador aún maneja la solicitud y devuelve la página de cualquier manera, pero no hay costo de renderización por el contenido que ya está en el HTML inicial. Una ejecución en vivo devuelve cinco conteos de bytes distintos, uno por autor:

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

Obtén tu clave API en el plan gratuito: app.scrapeless.com

Etapa 2 — Extraer y Fragmentar

Cada página lleva los mismos tres campos en clases fijas: author-title, author-born-date, author-born-location y author-description. Extrae esos campos con BeautifulSoup, encabeza la biografía con una oración de nacimiento explícita para que el hecho ancle un fragmento por sí solo, y luego divídelo en ventanas superpuestas de 60 palabras con un arrastre de 15 palabras:

python Copy
# chunk_corpus.py -- pages/*.html -> corpus.jsonl (fragmentos de ventana de palabras superpuestos con procedencia)
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']} nació {author['born_date']} en {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} fragmentos -> corpus.jsonl")

Una ejecución en vivo contra las cinco páginas extraídas produce 48 fragmentos, desigualmente distribuidos según lo que realmente dice cada biografía:

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

Sesenta palabras con un 25% de superposición es un entorno de corpus pequeño, deliberadamente más ajustado que la ventana de 220/40 palabras en la guía de ingestión vinculada arriba — estas biografías tienen unas pocas cientos de palabras cada una, no los artículos de miles de palabras que guían los fragmentos. Ajusta la ventana a la longitud de tu fuente, no a un valor predeterminado fijo.

Etapa 3 — Incrustar Localmente

Cada fragmento se convierte en un vector de 384 dimensiones con all-MiniLM-L6-v2, un modelo de incrustación de oraciones compacto lo suficientemente pequeño como para ejecutarse en CPU y evaluado junto a modelos más grandes en el conjunto de pruebas MTEB. El modelo se descarga una vez (aproximadamente 90 MB) y cada llamada de codificación posterior se ejecuta sin conexión:

python Copy
from sentence_transformers import SentenceTransformer

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

Etapa 4 — Almacenar en una Base de Datos Vectorial Local

El PersistentClient de Chroma escribe la colección en el disco, por lo que el índice sobrevive entre ejecuciones sin un servidor que lo administre. Cada vector mantiene su autor y la página de origen como metadatos, lo que hace que un fragmento recuperado sea rastreable hasta una página específica en lugar de una cadena de texto anónima:

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],
)

Una ejecución en vivo contra todos los 48 fragmentos confirma que la colección contiene todo lo que fue incrustado:

text Copy
incrustados 48 fragmentos, dim 384, recuento de colección 48

Etapa 5 — Recuperar el Fragmento Correcto

Este es el paso que la mayoría de los tutoriales de RAG local omiten: probar que la recuperación realmente devuelve la fuente correcta en lugar de simplemente devolver algo. Incrusta una pregunta de la misma manera que se incrustaron los fragmentos, y luego pide a Chroma los vecinos más cercanos por su distancia L2 predeterminada, donde un número más pequeño significa una coincidencia más cercana:

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]

Dos preguntas sobre dos personas diferentes, ejecutadas contra el mismo índice de 48 fragmentos, cada una devuelve a la persona correcta y la biografía de nadie más:

text Copy
P: ¿Dónde nació Albert Einstein?
  coincidencia principal autor: Albert Einstein | distancia: 0.6465
  fragmento: Albert Einstein nació el 14 de marzo de 1879 en Ulm, Alemania. En 1879, Albert Einstein nació en Ulm, Alemania. Completó su Ph.D. en la Universidad de Zúrich b...
P: ¿Dónde nació J.K. Rowling?
  coincidencia principal autor: J.K. Rowling | distancia: 0.4566
  fragmento: J.K. Rowling nació el 31 de julio de 1965 en Yate, South Gloucestershire, Inglaterra, Reino Unido. Ver también: Robert Galbraith Aunque escribe bajo el seudónimo...

Ninguna pregunta recupera un fragmento de Jane Austen, Marilyn Monroe o Thomas Edison: el espacio de incrustación separa cinco biografías no relacionadas lo suficiente como para que una pregunta factual sobre una persona no haga aparecer accidentalmente la otra.

Etapa 6 — Generar una Respuesta Fundada

El fragmento recuperado se convierte en el único contexto que recibe un modelo Ollama local. El mensaje solicita un único campo JSON — solo la respuesta — porque la procedencia ya vive en los metadatos de recuperación de la Etapa 5; pedirle a un modelo pequeño que también vuelva a declarar un campo de citación es pedirle que haga un trabajo que ya hizo la tienda de vectores de forma gratuita, y no es un campo extra confiable que pedirle a un modelo de 494M de parámetros:

python Copy
import json
import requests

prompt = (
    'Responde la pregunta en una corta oración usando SOLAMENTE el contexto a continuación. '
    'Responde SOLO con JSON {"answer": "..."}.\n\n'
    f"Contexto:\n{context}\n\nPregunta: {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"])

Una llamada en vivo, alimentada solo con el fragmento recuperado de Einstein, responde correctamente sin haber visto las otras cuatro biografías:

text Copy
fragmento recuperado autor (de los metadatos de la tienda de vectores): Albert Einstein
pregunta: ¿Dónde nació Albert Einstein y en qué año?
respuesta generada: Albert Einstein nació el 14 de marzo de 1879 en Ulm, Alemania.

Ese es el ciclo completo: se hace una pregunta, la tienda de vectores reduce 48 candidatos a un fragmento relevante, y un modelo que se ejecuta en localhost convierte ese fragmento en una oración — sin clave de OpenAI, sin navegador de scraping, nada sale de la máquina después de la recuperación inicial.

La Pipeline Completa

Cada etapa anterior se encadena en un solo script: obtener cinco páginas, dividirlas en fragmentos, incrustar y almacenar el resultado, ejecutar ambas verificaciones de recuperación, y luego generar la respuesta final. Nada de esto es falso o se ha ensamblado a partir de sesiones separadas: son las mismas seis etapas, ejecutadas una vez, de arriba hacia abajo:

python Copy
# full_pipeline.py -- obtener -> fragmentar -> incrustar -> almacenar -> recuperar -> generar, de principio a fin
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)

# Etapa 1: Obtener
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")

# Etapa 2: Extraer y fragmentar
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']} nació {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)} palabras -> {n_chunks} fragmentos")
print(f"{len(chunks)} fragmentos -> corpus.jsonl")

# Etapa 3 y 4: Incrustar y almacenar
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"incrustados {len(chunks)} fragmentos, dim {len(vectors[0])}, cuenta de la colección {collection.count()}")

# Etapa 5: Recuperar
for question in ["¿Dónde nació Albert Einstein?", "¿Dónde nació J.K. Rowling?"]:
    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"Q: {question}")
    print(f"  coincidencia principal autor: {top_author} | distancia: {top_distance:.4f}")
    print(f"  fragmento: {top_text[:160]}...")

# Etapa 6: Generar
question = "¿Dónde nació Albert Einstein, y en qué añ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 = (
    "Responde a la pregunta en una oración corta utilizando SOLAMENTE el contexto a continuación. "
    'Responde SOLAMENTE con JSON {"answer": "..."}.\n\n'
    f"Contexto:\n{context}\n\nPregunta: {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("autor del fragmento recuperado (de los metadatos del almacén de vectores):", retrieved_author)
print("pregunta:", question)
print("respuesta generada:", answer["answer"])

Una única ejecución de arriba hacia abajo reproduce cada número mostrado etapa por etapa: cinco conteos de bytes, 48 fragmentos, una colección de 384 dimensiones de 48 vectores, dos recuperaciones correctas y una respuesta fundamentada.

Abastecimiento Responsable

Las páginas de biografía describen personas reales, incluso en un sitio de práctica construido para scraping. Tres prácticas mantienen un corpus de recuperación defendible cuando apuntas este patrón a fuentes de producción: recopila solo páginas públicas y verifica los términos de servicio de cada sitio y las directivas de robots antes de agregar un dominio a la lista de búsqueda; mantén la URL de origen y el nombre del autor adjuntos a cada fragmento, como lo hacen los metadatos de este pipeline, para que una respuesta generada pueda rastrearse hasta su origen; y trata el volumen de solicitudes como lo hace este guía: cinco páginas, una vez, no un rastreo permanente contra un sitio que no ha aceptado uno.

Conclusión

Cinco páginas adentro, una oración fundamentada afuera, y cada etapa después de la recuperación inicial se ejecuta en esta máquina. La mitad de recuperación es la parte que vale la pena confiar antes de la mitad de generación: 48 fragmentos, dos preguntas, dos autores correctos, con las distancias para probarlo; y solo entonces el modelo local puede responder. Intercambia las páginas de origen, el modelo de incrustación o el modelo de Ollama, y la forma se mantiene; el pipeline no cambia cuando el contenido lo hace.

¿Listo para Construir tu Pipeline RAG?

Únete a la comunidad que construye pipelines de datos en Scrapeless: Discord · Telegram.

Regístrate en app.scrapeless.com para obtener créditos de prueba gratuitos, y apunta la Etapa 1 a las páginas que necesita tu propio corpus de recuperación. La documentación para desarrolladores cubre la forma de la solicitud unlocker.webunlocker, y los planes actuales se encuentran en la página de precios.

Preguntas Frecuentes

Q: ¿Alguna parte de este pipeline necesita una clave API en la nube además de Scrapeless?

No. Scrapeless es la única llamada de pago, y solo se necesita para la búsqueda de la Etapa 1. La incrustación se ejecuta localmente a través de sentence-transformers, el almacén de vectores es un archivo de Chroma en disco, y la generación se ejecuta a través de un modelo local de Ollama; nada más requiere una clave de proveedor.

Q: ¿En qué se diferencia esto de las otras publicaciones relacionadas con RAG de Scrapeless?

Cobertura, no solapamiento. La guía de ingestión busca, extrae y fragmenta, luego se detiene deliberadamente antes de las incrustaciones. La guía del pipeline de texto LLM incrusta con OpenAI y almacena en Chroma, luego se detiene antes de consultar o generar. Esta guía es la mitad local, sin clave de incrustación, que completa el ciclo: recuperar, luego generar.

Q: ¿Necesito una GPU?

No. all-MiniLM-L6-v2 y qwen2.5:0.5b se ejecutaron ambos en CPU para esta guía. Una GPU acelera un modelo local más grande, pero ni la etapa de incrustación ni un pequeño modelo de generación requieren una para construir y probar el pipeline.

Q: ¿Cómo elijo un tamaño de fragmento?

Ajústalo a cuánto dice realmente una sola fuente. Las biografías de esta guía tienen unas pocas centenas de palabras cada una, por lo que 60 palabras con un solapamiento de 15 palabras mantienen un fragmento a aproximadamente una idea. Un artículo de varios miles de palabras requiere una ventana más amplia: la guía de ingestión anterior utiliza 220 palabras con 40 de solapamiento precisamente por esa razón.

Q: ¿Qué pasa si la recuperación devuelve el fragmento equivocado?

Normalmente significa que el corpus tiene contenido casi duplicado que el modelo de incrustación no puede separar, o que la pregunta está formulada muy alejada de cómo está redactado el texto de origen. Ampliar el solapamiento de fragmentos, incrustar con un modelo más grande, o agregar un segundo pase de reordenación sobre los principales candidatos ayuda; las cinco biografías distintas de esta guía son deliberadamente fáciles de distinguir, lo que hace posible mostrar un resultado de recuperación limpio en lugar de solo afirmar.

Q: ¿Puedo intercambiar un modelo local más grande para la generación?

Sí, la forma de la solicitud no cambia, solo la cadena model lo hace. qwen2.5:0.5b es lo suficientemente rápido como para demostrar el ciclo en CPU; un modelo de Ollama de 3B o más grande responde más confiablemente en contextos desordenados si el hardware tiene memoria de sobra.
Q: ¿Es legal extraer información de páginas de biografía y generar respuestas a partir de ellas?

La extracción de páginas de acceso público está generalmente permitida, pero los términos del sitio y la jurisdicción son importantes. Solo recopila páginas públicas, revisa los términos de servicio del sitio objetivo y las directivas de robots primero, mantén el volumen limitado y maneja cualquier dato personal en el corpus de acuerdo con las leyes de privacidad que te correspondan.

En Scrapeless, solo accedemos a datos disponibles públicamente y cumplimos estrictamente con las leyes, regulaciones y políticas de privacidad del sitio web aplicables. El contenido de este blog es sólo para fines de demostración y no implica ninguna actividad ilegal o infractora. No ofrecemos garantías y renunciamos a toda responsabilidad por el uso de la información de este blog o enlaces de terceros. Antes de realizar cualquier actividad de scraping, consulte a su asesor legal y revise los términos de servicio del sitio web de destino u obtenga los permisos necesarios.

Artículos más populares

Catalogar