クイック回答
LangChainやLlamaIndexにライブWeb検索を追加するには?
LLMWebSearchの/search呼び出しを、LangChain @tool、LangChain retriever、またはLlamaIndex FunctionToolでラップします。それぞれがランク付けされたソースを返し、上位3ページをMarkdownとそのURLで返すので、引用にすぐ使えます。インストールする当社のパッケージはありません: 以下のコードがその統合です。検索は1クレジットで、2026年10月時点で$1.20/1K検索から。KYC不要; 暗号資産で支払い。
各ラッパーの用途
| LangChainツール | エージェントがWebが必要と判断したときに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は短く正確にしてください。関数は番号付きソースを含む1つの文字列を返すので、あらゆるチャットモデルに適しており、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])
ライブWebをretrieverとして使うには?
BaseRetrieverをサブクラス化し、読み取ったページごとに1つのDocumentを返します。ページテキストはpage_contentに入れます; URL、タイトル、ランク、日付はmetadataに入れます。引用元を示すチェーンはそこでそれらを探します。コンテンツのない結果はスキップされます。retrieverはモデルが引用できるテキストを渡すべきだからです。
retrieverは質問ごとに実行されます。これは時事トピックに関する質問応答チェーンには適していますが、ほとんどのターンで検索が不要なチャットには無駄です。その場合はツールを使用してください。
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からツールの名前と説明を構築します。ツールを呼び出すとToolOutputが返され、そのcontentがエージェントの読むテキストになります。
関数は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を設定してください。転送されるデータが減り、フレームワークが長いバージョンを保持することもありません。1ページあたり5,000文字、3ページで約4,000トークンとなり、ほとんどのモデルにとって余裕のある量です。
ライブWebと自分のドキュメントを組み合わせる検索チェーンでは、Webドキュメントの重みを下げるか、別のプロンプトセクションに分けてください。Webページは変化し、誤っていることもあります。自分のドキュメントが通常は参照元です。
レトリーバーが返した結果をベクトルデータベースに保存できますか?
はい、当社としては問題ないと考えています。当社はあなたの検索クエリやAPIが返す内容でモデルを学習させることはなく、結果の保存を禁じる独自のルールも設けていません。ただし、あなたが読むページのサイトの権利は引き続き適用されます。各チャンクと一緒にURLとpublished_atを保存すれば、エージェントがページを引用でき、古くなったチャンクを削除できます。
多くのチームは、頻出する質問のWeb結果を1日または1週間キャッシュしています。これにより繰り返しの検索が減り、回避した検索1回ごとにクレジットを1つ節約できます。
LangChainとLlamaIndex: よくある質問
langchain-llmwebsearchパッケージはありますか?
いいえ。当社はパッケージを公開していません。このページのコードは自分で管理できるほど短いものです。コピーしてデフォルト値を変更し、リポジトリに保存してください。
ツールを非同期にできますか?
はい。requestsをhttpx.AsyncClientに置き換え、関数をasync defで定義してください。LangChainの@toolは非同期関数を直接デコレートします。LlamaIndexでは、非同期バージョンをasync_fnとしてFunctionTool.from_defaultsに渡してください。
エージェントが別の場所で見つけたページを読むにはどうすればよいですか?
URLをhttps://llmwebsearch.com/api/v1/extractにポストする2つ目のツールを追加してください。URLをすでに持っているページを読む場合は、コンテンツ付きで返される3ページごとの開始グループにつき1クレジットで、1回の呼び出しにつき最大20 URLです。拒否されたURLや失敗したURLはカウントされません。extractリファレンスにリクエスト例があります。