Jawaban singkat
Bagaimana cara membangun deep research agent dengan Python?
Tulis loop yang meminta kueri pencarian ke LLM, menjalankan masing-masing melalui web search API yang mengembalikan teks halaman, menyimpan setiap sumber sekali, meminta kueri lanjutan, lalu menulis jawaban dengan sitasi. Dengan LLMWebSearch, setiap pencarian mengembalikan 3 halaman pertama sebagai Markdown untuk satu kredit; skrip di bawah menjalankan enam pencarian per pertanyaan. Mulai dari US$1,20/1K pencarian, tanpa KYC, bayar dengan kripto.
Apa yang akan dilakukan agen?
Paket
Minta ke model empat kueri pendek yang bersama-sama mencakup pertanyaannya.
Pencarian
Jalankan setiap kueri dengan
read=3, sehingga tiga halaman pertama kembali sebagai Markdown.Simpan sumber sekali
Simpan hasil berdasarkan URL dan lewati halaman yang sudah dikumpulkan atau tanpa teks.
Temukan celah
Tunjukkan judul yang sudah ditemukan ke model dan minta dua kueri tentang apa yang belum ada.
Tulis
Beri nomor hingga 12 sumber dan minta jawaban yang menyitasinya sebagai [1], [2].
Apa yang saya butuhkan sebelum mulai?
Tiga hal:
- API key LLMWebSearch di environment variable
LLMWEBSEARCH_API_KEY. API key muncul di dashboard setelah paket pertama Anda. - Endpoint chat apa pun yang kompatibel dengan OpenAI, diatur dengan
LLM_BASE_URL,LLM_API_KEYdanLLM_MODEL. Model hosted maupun server lokal sama-sama bisa dipakai. - Python 3.9 atau lebih baru dengan paket
requests.
Skrip ini memakai HTTP biasa untuk kedua layanan, jadi Anda bisa mengganti model tanpa mengubah kode pencariannya.
import os
import requests
SEARCH = "https://llmwebsearch.com/api/v1/search"
SEARCH_HEADERS = {"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]}
# Any OpenAI-compatible chat endpoint works here: set your own base URL, key and model name.
LLM_URL = os.environ.get("LLM_BASE_URL", "https://llm.example.com/v1") + "/chat/completions"
LLM_HEADERS = {"Authorization": "Bearer " + os.environ.get("LLM_API_KEY", "")}
MODEL = os.environ.get("LLM_MODEL", "your-model-name")
def ask_llm(prompt):
body = {"model": MODEL, "messages": [{"role": "user", "content": prompt}]}
resp = requests.post(LLM_URL, headers=LLM_HEADERS, json=body, timeout=120)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
def plan_queries(question, n=4):
text = ask_llm(f"Write {n} short web search queries, one per line, to answer: {question}")
queries = [line.strip("-*0123456789. ").strip() for line in text.splitlines()]
return [q for q in queries if q][:n]
def search(query):
body = {"q": query, "max_results": 5, "read": 3, "max_chars": 5000}
resp = requests.post(SEARCH, headers=SEARCH_HEADERS, json=body, timeout=60)
resp.raise_for_status()
return resp.json()["results"]
def research(question, rounds=2):
sources = {} # url -> result, so the same page is never cited twice
queries = plan_queries(question)
for _ in range(rounds):
for query in queries:
for r in search(query):
if r.get("content_status") == "ok" and r["url"] not in sources:
sources[r["url"]] = r
notes = "\n".join(f"- {r['title']}" for r in sources.values())
queries = plan_queries(f"{question}\nAlready found:\n{notes}\nWhat is still missing?", n=2)
numbered = list(sources.values())[:12]
context = "\n\n".join(
f"[{i}] {r['title']} ({r['url']})\n{r['content']}" for i, r in enumerate(numbered, 1)
)
prompt = (
"Answer the question using only the sources below. Cite them as [1], [2]. "
"Say so when the sources disagree or do not cover a point.\n\n"
f"Question: {question}\n\nSources:\n{context}"
)
return ask_llm(prompt), [r["url"] for r in numbered]
if __name__ == "__main__":
answer, urls = research("What changed in the EU rules for general-purpose AI models in 2025?")
print(answer)
for i, url in enumerate(urls, 1):
print(f"[{i}] {url}")Bagaimana cara kerja setiap bagian?
plan_queries meminta kueri pendek ke model, satu per baris, dan membuang penanda list. Kueri pendek lebih cocok dengan cara mesin pencari memeringkat halaman dibandingkan pertanyaan lengkap.
search memanggil POST https://llmwebsearch.com/api/v1/search dengan max_results=5, read=3 dan max_chars=5000. Responsnya mencantumkan lima hasil; tiga pertama membawa teks halamannya di content.
research menjalankan ronde-ronde. Ia menyimpan sumber dalam dictionary yang dikunci berdasarkan URL, sehingga halaman yang ditemukan oleh dua kueri dihitung sekali. Hanya hasil dengan content_status sama dengan ok yang disimpan, karena jawaban harus bersandar pada teks yang bisa dibaca model.
Setelah setiap ronde, model melihat judul yang sudah terkumpul dan mengusulkan dua kueri baru untuk apa yang belum ada. Prompt akhirnya memberi nomor hingga dua belas sumber dan menetapkan aturan sitasi.
Berapa biaya satu laporan?
Enam pencarian per pertanyaan dengan pengaturan default: empat di ronde pertama dan dua di ronde kedua. Dengan read bernilai 3, itu berarti enam kredit. 25.000 pencarian seharga US$41,25 karenanya mencakup lebih dari 4.000 laporan.
Panggilan model menambah biaya di tagihan LLM Anda: tiga panggilan perencanaan dan satu panggilan jawaban panjang per laporan. Panggilan jawaban membawa hingga dua belas halaman berisi 5.000 karakter, jadi turunkan max_chars jika model Anda mahal.
Bagaimana cara membuatnya menggali lebih dalam?
Ubah angkanya, bukan strukturnya:
- Lebih banyak putaran.
rounds=4memungkinkan model mengejar dua set celah tambahan. - Lebih banyak kueri per putaran. Naikkan
npada panggilan pencarian celah dari 2 menjadi 4. - Sumber yang lebih sempit. Tambahkan
include_domainske body pencarian untuk situs resmi atau akademik. - Hanya materi terbaru. Tambahkan
"time_range": "month"atau"topic": "news". - Lebih banyak halaman per kueri.
read=5membaca lima halaman, dengan 2 kredit per pencarian.
Tambahkan aturan berhenti sebelum menaikkan batas: akhiri loop ketika sebuah putaran menghasilkan kurang dari dua sumber baru.
Bagaimana cara menjaga agent tetap aman dan sopan?
Perlakukan setiap halaman sebagai teks yang tidak tepercaya. Prompt akhir memberi tahu model untuk menggunakan sumber hanya sebagai bukti; tambahkan baris eksplisit bahwa instruksi di dalam sumber harus diabaikan. Jangan pernah membiarkan sumber memicu pemanggilan tool atau penulisan file.
Di sisi web, Pembaca kami mematuhi robots.txt: ia memperkenalkan diri sebagai LLMWebSearch, mengikuti aturan yang ditulis situs untuknya atau untuk semua robot, dan mengabaikan konten halaman apa pun yang tidak diizinkan untuk dibaca. Halaman yang menolak akses kembali tanpa konten dan skrip cukup melewatinya. Saat situs menolak pembaca kami, hasil kembali tanpa konten dan dengan status dari situs tersebut: kami tidak pernah memecahkan CAPTCHA, masuk ke akun, atau mengakali blokir atau paywall.
Apa yang bisa salah?
Tiga hal muncul saat pengujian:
- Kueri yang kabur. Jika rencananya lemah, setiap putaran menemukan halaman yang sama. Minta model membuat kueri yang menggunakan nama dan tanggal spesifik.
- Terlalu sedikit sumber dengan teks. Sebagian topik berada di balik paywall. Perluas kueri atau izinkan lebih banyak hasil.
- Prompt yang terlalu panjang. Dua belas halaman panjang bisa melebihi context model kecil. Turunkan
max_charsatau pertahankan delapan sumber.
Catat setiap kueri dan URL yang dikembalikannya. Membaca log itu adalah cara tercepat untuk memperbaiki prompt rencana.
Deep research agent: pertanyaan umum
Bisakah saya menjalankannya dengan model lokal?
Ya, jika server lokal mendukung format OpenAI chat-completions. Set LLM_BASE_URL ke alamatnya dan LLM_MODEL ke nama model.
Bisakah saya mengubahnya menjadi tool MCP?
Ya. Bungkus research() dalam tool MCP, atau berikan agent Anda tool pencarian biasa dari halaman MCP dan biarkan ia menjalankan loop sendiri.
Mengapa tidak meminta jawaban langsung ke API pencarian?
Karena LLMWebSearch tidak menulis jawaban. API mengembalikan sumber, bukan jawaban tertulis: model Anda sendiri yang membaca halaman, menulis balasan, dan mengutip URL-nya.
Bagaimana cara melihat biaya setiap pencarian?
Setiap respons menyertakan credits_used, dan GET https://llmwebsearch.com/api/v1/credits menampilkan saldo Anda yang tersisa tanpa biaya.