# Bangun deep research agent dengan Python memakai web search API

> Bangun deep research agent dalam sekitar 60 baris Python: rencanakan kueri, cari konten, buang sumber duplikat, dan tulis jawaban bersitasi.

- URL: https://llmwebsearch.com/id/guides/build-a-deep-research-agent/
- Terakhir diperbarui: 2026-10-22
- Penerbit: LLMWebSearch (https://llmwebsearch.com)

Deep research adalah sebuah loop, bukan model. Panduan ini membangun satu loop yang bisa Anda baca dalam beberapa menit: ia merencanakan kueri, mencari di web dengan teks halaman sudah termasuk, menyimpan setiap sumber sekali, mencari celah, dan menulis jawaban dengan sitasi bernomor.

**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?

1. **Paket** — Minta ke model empat kueri pendek yang bersama-sama mencakup pertanyaannya.
2. **Pencarian** — Jalankan setiap kueri dengan read=3, sehingga tiga halaman pertama kembali sebagai Markdown.
3. **Simpan sumber sekali** — Simpan hasil berdasarkan URL dan lewati halaman yang sudah dikumpulkan atau tanpa teks.
4. **Temukan celah** — Tunjukkan judul yang sudah ditemukan ke model dan minta dua kueri tentang apa yang belum ada.
5. **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_KEY` dan `LLM_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.

*Python: deep_research.py*

```python
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=4` memungkinkan model mengejar dua set celah tambahan.
- **Lebih banyak kueri per putaran.** Naikkan `n` pada panggilan pencarian celah dari 2 menjadi 4.
- **Sumber yang lebih sempit.** Tambahkan `include_domains` ke body pencarian untuk situs resmi atau akademik.
- **Hanya materi terbaru.** Tambahkan `"time_range": "month"` atau `"topic": "news"`.
- **Lebih banyak halaman per kueri.** `read=5` membaca 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_chars` atau 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](https://llmwebsearch.com/id/docs/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.

---

LLMWebSearch: LLMWebSearch adalah API pencarian web untuk agen AI yang mengembalikan hasil berperingkat beserta konten halaman teratas dalam Markdown, dibayar dengan kripto tanpa unggah ID. Kredit prabayar mulai dari US$1,20/1K pencarian tidak pernah kedaluwarsa. Tanpa KYC — tanpa ID, tanpa selfie, tanpa dokumen. Bayar dengan kripto.
