Szybka odpowiedź
Jak dodać wyszukiwanie w żywej sieci do LangChain lub LlamaIndex?
Opakuj wywołanie /search LLMWebSearch w LangChain @tool, retriever LangChain lub LlamaIndex FunctionTool. Każde zwraca uszeregowane źródła z 3 najwyższymi stronami jako Markdown i ich URL-ami, gotowe do cytowania. Nie musisz instalować naszego pakietu: poniższy kod to cała integracja. Wyszukiwanie kosztuje 1 kredyt, od 1,20 USD/1K wyszukiwań według stanu na październik 2026. Bez KYC; płatność kryptowalutą.
Do czego służy każdy wrapper
| Narzędzie LangChain | Agent wywołuje search_web, gdy uzna, że potrzebuje sieci |
|---|---|
| Retriever LangChain | Łańcuch pobiera świeże dokumenty dla każdego pytania, bez pytania |
| FunctionTool LlamaIndex | Ta sama funkcja search_web, dla agentów LlamaIndex |
| Pakiety | langchain-core lub llama-index-core, plus requests |
| Klucz | LLMWEBSEARCH_API_KEY w środowisku |
| Rozliczanie | Jeden kredyt opłaca jedno wyszukiwanie: do 20 wyników z rankingiem, przy czym strony z pierwszych 3 są odczytywane i zwracane jako Markdown. Odczyt od 4 do 10 stron to 2 kredyty. Wywołanie zakończone błędem nic nie kosztuje |
Jak zbudować narzędzie wyszukiwania dla agenta LangChain?
Udekoruj funkcję za pomocą @tool z langchain_core.tools. LangChain odczytuje nazwę, typowane argumenty i docstring, aby opisać narzędzie modelowi, więc trzymaj docstring krótki i precyzyjny. Funkcja zwraca jeden ciąg z numerowanymi źródłami, co pasuje do każdego modelu czatu i trzyma URL-e obok tekstu.
Wywołaj ją bezpośrednio przez .invoke(), aby ją przetestować, a następnie przekaż ją na liście tools swojego agenta.
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])
Jak używać żywej sieci jako retrievera?
Utwórz podklasę BaseRetriever i zwracaj jeden Document na każdą odczytaną stronę. Tekst strony trafia do page_content; URL, tytuł, pozycja i data trafiają do metadata, gdzie szukają ich łańcuchy cytujące swoje źródła. Wyniki bez treści są pomijane, ponieważ retriever powinien przekazywać tekst, który model może zacytować.
Retriever uruchamia się przy każdym pytaniu. To dobre dla łańcucha odpowiadania na pytania o bieżące tematy, a marnotrawne dla czatu, w którym większość tur nie wymaga wyszukiwania; w takim przypadku użyj narzędzia.
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))
Jak dać to samo wyszukiwanie agentowi LlamaIndex?
Napisz zwykłą funkcję i opakuj ją za pomocą FunctionTool.from_defaults z llama_index.core.tools. LlamaIndex buduje nazwę i opis narzędzia z nazwy funkcji, jej sygnatury i docstringa. Wywołanie narzędzia zwraca ToolOutput, którego content to tekst, który odczyta agent.
Funkcja jest taka sama jak w LangChain, więc zespół używający obu frameworków może ją współdzielić.
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])
Jak utrzymać okno kontekstu pod kontrolą?
Ustaw max_chars w wywołaniu API, zamiast przycinać później: mniej danych podróżuje, a framework nigdy nie trzyma długiej wersji. 5 000 znaków na stronę i 3 strony to około 4 000 tokenów, komfortowo dla większości modeli.
W przypadku łańcuchów wyszukiwania, które łączą żywy web z Twoimi własnymi dokumentami, nadaj dokumentom webowym mniejszą wagę lub osobną sekcję promptu. Strony internetowe się zmieniają i mogą być błędne; Twoje własne dokumenty są zwykle punktem odniesienia.
Czy mogę przechować to, co zwraca retriever, w bazie wektorowej?
Tak, o ile to nas dotyczy. Nie trenujemy żadnego modelu na Twoich zapytaniach ani na tym, co zwraca API, i nie dodajemy własnej reguły przeciwko przechowywaniu wyników; nadal obowiązują prawa witryn, których strony czytasz. Przechowuj URL i published_at z każdym fragmentem, żeby agent mógł zacytować stronę, a Ty mogłeś usuwać fragmenty, które się starzeją.
Większość zespołów cachuje wyniki webowe częstych pytań na dzień lub tydzień. To ogranicza powtarzane wyszukiwania, a każde uniknięte wyszukiwanie to oszczędzony kredyt.
LangChain i LlamaIndex: częste pytania
Czy istnieje pakiet langchain-llmwebsearch?
Nie. Nie publikujemy żadnego pakietu. Kod na tej stronie jest wystarczająco krótki, by był Twój: skopiuj go, zmień wartości domyślne i trzymaj w swoim repozytorium.
Czy mogę uczynić narzędzie asynchronicznym?
Tak. Zastąp requests przez httpx.AsyncClient i zdefiniuj funkcję z async def. Dekorator @tool w LangChain obsługuje funkcje asynchroniczne bezpośrednio; w LlamaIndex przekaż wersję asynchroniczną do FunctionTool.from_defaults jako async_fn.
Jak odczytać stronę, którą agent znalazł gdzie indziej?
Dodaj drugie narzędzie, które wysyła URL-e do https://llmwebsearch.com/api/v1/extract. Odczyt stron, których adresy URL już masz, kosztuje 1 kredyt za każdą rozpoczętą grupę 3 stron zwróconych z treścią, do 20 adresów URL na wywołanie; adresy URL odrzucone lub nieudane nie są liczone. Dokumentacja extract pokazuje zapytanie.