빠른 답변
LangChain 또는 LlamaIndex에 라이브 웹 검색을 어떻게 추가하나요?
LLMWebSearch의 /search 호출을 LangChain @tool, LangChain retriever 또는 LlamaIndex FunctionTool으로 감싸세요. 각각 상위 3개 페이지를 Markdown과 URL로 포함한 순위 매겨진 소스를 반환하며, 인용할 준비가 되어 있습니다. 설치할 우리 패키지는 없습니다: 아래 코드가 바로 통합입니다. 검색 비용은 크레딧 1이며, 2026년 10월 기준 US$1.20/1K 검색부터입니다. KYC 없이, 암호화폐로 결제하세요.
각 래퍼의 용도
| LangChain 도구 | 에이전트가 웹이 필요하다고 판단할 때 search_web을 호출합니다 |
|---|---|
| LangChain retriever | 체인이 묻지 않고 모든 질문에 새 문서를 가져옵니다 |
| LlamaIndex FunctionTool | 동일한 search_web 함수를 LlamaIndex 에이전트용으로 사용합니다 |
| 패키지 | langchain-core 또는 llama-index-core, 그리고 requests |
| 키 | 환경 변수의 LLMWEBSEARCH_API_KEY |
| 과금 | 크레딧 1개로 검색 1회를 이용할 수 있습니다. 최대 20개의 순위 결과를 받고, 상위 3개 페이지의 본문을 읽어 Markdown으로 반환합니다. 4~10개 페이지를 읽으면 크레딧 2개가 됩니다. 오류로 끝난 호출에는 비용이 청구되지 않습니다 |
LangChain 에이전트용 검색 도구를 어떻게 만드나요?
langchain_core.tools의 @tool으로 함수를 데코레이터하세요. LangChain은 이름, 타입이 지정된 인수, docstring을 읽어 모델에 도구를 설명하므로 docstring을 짧고 정확하게 유지하세요. 함수는 번호가 매겨진 소스가 포함된 하나의 문자열을 반환하며, 이는 모든 채팅 모델에 적합하고 URL을 텍스트 옆에 유지합니다.
.invoke()로 직접 호출해 테스트한 다음, 에이전트의 tools 리스트에 전달하세요.
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])
라이브 웹을 retriever로 어떻게 사용하나요?
BaseRetriever을 서브클래스화하고 읽은 페이지당 하나의 Document을 반환하세요. 페이지 텍스트는 page_content에, URL, 제목, 순위, 날짜는 metadata에 넣으며, 출처를 인용하는 chain이 여기서 찾습니다. 콘텐츠가 없는 결과는 건너뜁니다. retriever는 모델이 인용할 수 있는 텍스트를 넘겨야 하기 때문입니다.
retriever는 모든 질문에서 실행됩니다. 이는 최신 주제에 대한 질문 답변 chain에는 맞지만, 대부분의 턴에 검색이 필요 없는 채팅에는 낭비입니다. 그런 경우 도구를 사용하세요.
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))
LlamaIndex 에이전트에 동일한 검색을 어떻게 제공하나요?
일반 함수를 작성하고 llama_index.core.tools의 FunctionTool.from_defaults으로 감싸세요. LlamaIndex는 함수의 이름, 시그니처, docstring으로 도구의 이름과 설명을 만듭니다. 도구를 호출하면 content이 에이전트가 읽을 텍스트인 ToolOutput를 반환합니다.
이 함수는 LangChain 것과 동일하므로, 두 프레임워크를 모두 사용하는 팀은 공유할 수 있습니다.
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])
컨텍스트 윈도우를 어떻게 관리해야 하나요?
나중에 잘라내기보다 API 호출에서 max_chars을 설정하세요. 전송되는 데이터가 줄고 프레임워크가 긴 버전을 들고 있지 않습니다. 페이지당 5,000자에 3페이지면 약 4,000 토큰으로 대부분의 모델에 여유가 있습니다.
실시간 웹과 자체 문서를 함께 쓰는 검색 체인에서는 웹 문서에 더 낮은 가중치를 주거나 프롬프트 섹션을 따로 두세요. 웹페이지는 변하고 틀릴 수 있지만, 자체 문서는 보통 기준 자료입니다.
retriever가 반환한 결과를 벡터 데이터베이스에 저장할 수 있나요?
저희 쪽에서는 문제없습니다. 저희는 귀하의 검색 쿼리나 API가 반환하는 내용으로 어떤 모델도 학습시키지 않으며, 결과 저장을 금지하는 자체 규칙도 두지 않습니다. 다만 귀하가 읽는 페이지의 사이트 권리는 여전히 적용됩니다. 모든 청크에 URL과 published_at을 함께 저장하면 에이전트가 페이지를 인용할 수 있고 오래된 청크는 버릴 수 있습니다.
대부분의 팀은 자주 묻는 질문의 웹 결과를 하루나 일주일 동안 캐시합니다. 반복 검색이 줄고, 피한 검색 하나하나가 아낀 크레딧입니다.
LangChain과 LlamaIndex: 자주 묻는 질문
langchain-llmwebsearch 패키지가 있나요?
아니요. 저희는 패키지를 배포하지 않습니다. 이 페이지의 코드는 직접 소유하기에 충분히 짧습니다. 복사하고 기본값을 바꿔 저장소에 두세요.
도구를 async로 만들 수 있나요?
네. requests을 httpx.AsyncClient로 바꾸고 함수를 async def로 정의하세요. LangChain의 @tool은 async 함수를 바로 데코레이트합니다. LlamaIndex에서는 async 버전을 FunctionTool.from_defaults에 async_fn로 넘기세요.
에이전트가 다른 곳에서 찾은 페이지는 어떻게 읽나요?
URL을 https://llmwebsearch.com/api/v1/extract로 보내는 두 번째 도구를 추가하세요. 이미 URL을 알고 있는 페이지를 읽는 경우, 콘텐츠와 함께 반환되는 3개 페이지 단위로 크레딧 1개가 청구되며, 호출당 최대 20개 URL까지 가능합니다. 거부되거나 실패한 URL은 집계되지 않습니다. extract 레퍼런스에 요청 예시가 있습니다.