Risposta rapida
Come aggiungo la ricerca web live a LangChain o LlamaIndex?
Avvolgi la chiamata /search di LLMWebSearch in un @tool di LangChain, in un retriever di LangChain o in un FunctionTool di LlamaIndex. Ognuno restituisce fonti ordinate con le prime 3 pagine in Markdown e i loro URL, pronti per le citazioni. Nessun nostro pacchetto da installare: il codice qui sotto è l'integrazione. Una ricerca costa 1 credito, a partire da 1,20 USD/1K ricerche da ottobre 2026. Senza KYC; paga in criptovalute.
A cosa serve ciascun wrapper
| Strumento LangChain | Un agente chiama search_web quando decide di aver bisogno del web |
|---|---|
| Retriever LangChain | Una catena recupera documenti freschi per ogni domanda, senza chiedere |
| FunctionTool LlamaIndex | La stessa funzione search_web, per gli agenti LlamaIndex |
| Pacchetti | langchain-core o llama-index-core, più requests |
| Chiave | LLMWEBSEARCH_API_KEY nell'environment |
| Fatturazione | Un credito paga una ricerca: fino a 20 risultati classificati, con le pagine dei primi 3 lette e restituite come Markdown. Leggere da 4 a 10 pagine costa 2 crediti. Una chiamata che termina in errore non costa nulla |
Come costruisco uno strumento di ricerca per un agente LangChain?
Decora una funzione con @tool da langchain_core.tools. LangChain legge il nome, gli argomenti tipizzati e la docstring per descrivere lo strumento al modello, quindi mantieni la docstring breve e precisa. La funzione restituisce una stringa con fonti numerate, che si adatta a ogni modello di chat e tiene gli URL accanto al testo.
Chiamala direttamente con .invoke() per testarla, poi passala nella lista tools del tuo agente.
import os
import requests
from langchain_core.tools import tool
API = "https://llmwebsearch.com/api/v1/search"
HEADERS = {"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]}
@tool
def search_web(query: str, read: int = 3) -> str:
"""Search the live web. Returns numbered sources with URLs; the first `read` include page text."""
body = {"q": query, "max_results": 5, "read": max(0, min(read, 10)), "max_chars": 5000}
resp = requests.post(API, headers=HEADERS, json=body, timeout=60)
resp.raise_for_status()
parts = []
for i, r in enumerate(resp.json()["results"], start=1):
text = r["content"] if r["content_status"] == "ok" else "(snippet only) " + r["snippet"]
parts.append(f"[{i}] {r['title']} - {r['url']}\n{text}")
return "\n\n".join(parts)
print(search_web.invoke({"query": "postgres connection pool size"})[:500])
Come uso il web live come retriever?
Crea una sottoclasse di BaseRetriever e restituisci un Document per ogni pagina letta. Il testo della pagina va in page_content; l'URL, il titolo, il rank e la data vanno in metadata, dove le catene che citano le fonti li cercano. I risultati senza contenuto vengono saltati, perché un retriever deve consegnare testo che il modello può citare.
Un retriever viene eseguito a ogni domanda. È giusto per una catena di question-answering su argomenti attuali, e uno spreco per una chat in cui la maggior parte dei turni non richiede ricerca; in quel caso usa lo strumento.
import os
from typing import List
import requests
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.documents import Document
from langchain_core.retrievers import BaseRetriever
class LLMWebSearchRetriever(BaseRetriever):
"""Live web search results as Documents, one per page that was read."""
max_results: int = 5
read: int = 3
max_chars: int = 6000
def _get_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun) -> List[Document]:
resp = requests.post(
"https://llmwebsearch.com/api/v1/search",
headers={"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]},
json={"q": query, "max_results": self.max_results, "read": self.read, "max_chars": self.max_chars},
timeout=60,
)
resp.raise_for_status()
docs = []
for r in resp.json()["results"]:
if r["content_status"] != "ok":
continue
meta = {"source": r["url"], "title": r["title"], "position": r["position"], "published_at": r.get("published_at")}
docs.append(Document(page_content=r["content"], metadata=meta))
return docs
retriever = LLMWebSearchRetriever(read=3)
for doc in retriever.invoke("pgbouncer transaction mode limitations"):
print(doc.metadata["position"], doc.metadata["source"], len(doc.page_content))
Come do la stessa ricerca a un agente LlamaIndex?
Scrivi una funzione semplice e avvolgila con FunctionTool.from_defaults da llama_index.core.tools. LlamaIndex costruisce il nome e la descrizione dello strumento dal nome, dalla firma e dalla docstring della funzione. Chiamando lo strumento si ottiene un ToolOutput il cui content è il testo che l'agente leggerà.
La funzione è la stessa di LangChain, così un team che usa entrambi i framework può condividerla.
import os
import requests
from llama_index.core.tools import FunctionTool
def search_web(query: str, read: int = 3) -> str:
"""Search the live web and return numbered sources; the first `read` include their page text."""
resp = requests.post(
"https://llmwebsearch.com/api/v1/search",
headers={"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]},
json={"q": query, "max_results": 5, "read": max(0, min(read, 10)), "max_chars": 5000},
timeout=60,
)
resp.raise_for_status()
parts = []
for i, r in enumerate(resp.json()["results"], start=1):
text = r["content"] if r["content_status"] == "ok" else "(snippet only) " + r["snippet"]
parts.append(f"[{i}] {r['title']} - {r['url']}\n{text}")
return "\n\n".join(parts)
search_tool = FunctionTool.from_defaults(fn=search_web)
print(search_tool.metadata.name)
print(search_tool.call(query="postgres connection pool size").content[:300])
Come tengo sotto controllo la context window?
Imposta max_chars nella chiamata API invece di tagliare dopo: viaggiano meno dati e il framework non conserva mai la versione lunga. 5.000 caratteri per pagina e 3 pagine fanno circa 4.000 token, una misura comoda per la maggior parte dei modelli.
Per le retrieval chain che combinano il web live con i tuoi documenti, assegna ai documenti web un peso inferiore o una sezione separata del prompt. Le pagine web cambiano e possono essere sbagliate; i tuoi documenti sono di solito il riferimento.
Posso memorizzare ciò che restituisce il retriever in un vector database?
Sì, per quanto ci riguarda. Non addestriamo alcun modello sulle tue query né su ciò che l'API restituisce, e non aggiungiamo regole nostre contro la conservazione dei risultati; restano validi i diritti dei siti di cui leggi le pagine. Memorizza l'URL e published_at con ogni chunk, così l'agente può citare la pagina e tu puoi eliminare i chunk che invecchiano.
La maggior parte dei team mette in cache i risultati web delle domande frequenti per un giorno o una settimana. Così si riducono le ricerche ripetute, e ogni ricerca evitata è un credito risparmiato.
LangChain e LlamaIndex: domande frequenti
Esiste un package langchain-llmwebsearch?
No. Non pubblichiamo nessun package. Il codice in questa pagina è abbastanza breve da essere tuo: copialo, cambia i valori predefiniti e tienilo nel tuo repository.
Posso rendere il tool async?
Sì. Sostituisci requests con httpx.AsyncClient e definisci la funzione con async def. Il decoratore @tool di LangChain decora direttamente le funzioni async; in LlamaIndex, passa la versione async a FunctionTool.from_defaults come async_fn.
Come leggo una pagina che l'agente ha trovato altrove?
Aggiungi un secondo tool che invia gli URL a https://llmwebsearch.com/api/v1/extract. Leggere pagine di cui hai già gli URL costa 1 credito per ogni gruppo iniziato di 3 pagine restituite con contenuto, fino a 20 URL per chiamata; gli URL rifiutati o falliti non vengono contati. La extract reference mostra la richiesta.