# Deep-Research-Agenten in Python mit einer Websuche-API bauen

> Deep-Research-Agent in etwa 60 Zeilen Python: Suchanfragen planen, mit Seiteninhalt suchen und eine belegte Antwort mit einem LLM schreiben.

- URL: https://llmwebsearch.com/de/guides/build-a-deep-research-agent/
- Zuletzt aktualisiert: 2026-10-22
- Herausgeber: LLMWebSearch (https://llmwebsearch.com)

Deep Research ist eine Schleife, kein Modell. Diese Anleitung baut eine, die Sie in wenigen Minuten lesen können: Sie plant Suchanfragen, sucht im Web inklusive Seitentext, behält jede Quelle nur einmal, sucht nach Lücken und schreibt eine Antwort mit nummerierten Belegen.

**Kurze Antwort — Wie baue ich einen Deep-Research-Agenten in Python?**

Schreiben Sie eine Schleife, die ein LLM nach Suchanfragen fragt, jede durch eine Websuche-API mit Seitentext laufen lässt, jede Quelle nur einmal behält, nach Folge-Suchanfragen fragt und dann eine belegte Antwort schreibt. Mit **LLMWebSearch** liefert jede Suchanfrage die ersten 3 Seiten als Markdown für einen Credit; das Skript unten führt sechs Suchanfragen pro Frage aus. Ab 1,20 $/1K Suchanfragen, ohne KYC, mit Krypto bezahlen.

## Was wird der Agent tun?

1. **Plan** — Das Modell nach vier kurzen Suchanfragen fragen, die zusammen die Frage abdecken.
2. **Suchen** — Jede Suchanfrage mit read=3 ausführen, sodass die ersten drei Seiten als Markdown zurückkommen.
3. **Quellen nur einmal behalten** — Ergebnisse nach URL speichern und Seiten überspringen, die bereits gesammelt wurden oder keinen Text haben.
4. **Lücken finden** — Dem Modell die bisher gefundenen Titel zeigen und nach zwei Suchanfragen zu dem fragen, was fehlt.
5. **Schreiben** — Bis zu 12 Quellen nummerieren und eine Antwort anfordern, die sie als [1], [2] zitiert.

## Was brauche ich, bevor ich anfange?

Drei Dinge:

- **Einen LLMWebSearch API-Schlüssel** in der Umgebungsvariable `LLMWEBSEARCH_API_KEY`. Schlüssel erscheinen im Dashboard nach Ihrem ersten Paket.
- **Einen beliebigen OpenAI-kompatiblen Chat-Endpoint**, gesetzt mit `LLM_BASE_URL`, `LLM_API_KEY` und `LLM_MODEL`. Gehostete Modelle und lokale Server funktionieren beide.
- **Python 3.9 oder neuer** mit dem `requests`-Paket.

Das Skript verwendet für beide Dienste einfaches HTTP, sodass Sie das Modell austauschen können, ohne den Suchcode zu ändern.

*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}")
```

## Wie funktioniert jeder Teil?

`plan_queries` fragt das Modell nach kurzen Suchanfragen, eine pro Zeile, und entfernt Listenmarker. Kurze Suchanfragen passen besser dazu, wie Suchmaschinen Seiten ranken, als ganze Fragen.

`search` ruft `POST https://llmwebsearch.com/api/v1/search` mit `max_results=5`, `read=3` und `max_chars=5000` auf. Die Antwort listet fünf Ergebnisse auf; die ersten drei enthalten ihren Seitentext in `content`.

`research` führt die Runden aus. Es behält Quellen in einem Dictionary, das nach URL verschlüsselt ist, sodass eine Seite, die von zwei Suchanfragen gefunden wird, nur einmal zählt. Nur Ergebnisse mit `content_status` gleich `ok` werden behalten, weil die Antwort auf Text beruhen muss, den das Modell lesen kann.

Nach jeder Runde sieht das Modell die bisher gesammelten Titel und schlägt zwei neue Suchanfragen für das Fehlende vor. Der letzte Prompt nummeriert bis zu zwölf Quellen und legt die Zitierregeln fest.

## Wie viel kostet ein Bericht?

Sechs Suchanfragen pro Frage mit den Standardeinstellungen: vier in der ersten Runde und zwei in der zweiten. Mit `read` auf 3 sind das sechs Credits. 25.000 Suchanfragen bei 41,25 $ deckt daher mehr als 4.000 Berichte ab.

Die Modellaufrufe kosten zusätzlich auf Ihrer LLM-Rechnung: drei Planungsaufrufe und ein langer Antwortaufruf pro Bericht. Der Antwortaufruf umfasst bis zu zwölf Seiten mit 5.000 Zeichen, senken Sie also `max_chars`, wenn Ihr Modell teuer ist.

## Wie mache ich es gründlicher?

Ändern Sie die Zahlen, nicht die Struktur:

- **Mehr Runden.** `rounds=4` lässt das Modell zwei weitere Sätze Lücken verfolgen.
- **Mehr Suchanfragen pro Runde.** Erhöhen Sie `n` im Aufruf zur Lückensuche von 2 auf 4.
- **Engere Quellen.** Fügen Sie `include_domains` zum Such-Body hinzu, um offizielle oder akademische Seiten zu finden.
- **Nur aktuelle Inhalte.** Fügen Sie `"time_range": "month"` oder `"topic": "news"` hinzu.
- **Mehr Seiten pro Suchanfrage.** `read=5` liest fünf Seiten, bei 2 Credits pro Suche.

Fügen Sie eine Stoppregel hinzu, bevor Sie die Limits erhöhen: Beenden Sie die Schleife, wenn eine Runde weniger als zwei neue Quellen bringt.

## Wie halte ich den Agenten sicher und höflich?

Behandeln Sie jede Seite als nicht vertrauenswürdigen Text. Der finale Prompt weist das Modell an, die Quellen nur als Belege zu verwenden; fügen Sie eine explizite Zeile hinzu, dass Anweisungen innerhalb von Quellen ignoriert werden müssen. Lassen Sie eine Quelle niemals einen Tool-Aufruf oder ein Schreiben in eine Datei auslösen.

Auf der Web-Seite: Unser Reader respektiert robots.txt: Er stellt sich als LLMWebSearch vor, befolgt die Regeln, die eine Website für ihn oder für alle Roboter schreibt, und lässt den Inhalt jeder Seite aus, die er nicht lesen darf. Seiten, die den Zugriff verweigern, kommen ohne Inhalt zurück, und das Skript überspringt sie einfach. Wenn eine Website unseren Reader abweist, kommt das Ergebnis ohne Inhalt und mit dem Status der Website zurück: Wir lösen niemals CAPTCHAs, melden uns nicht in Konten an und umgehen weder eine Sperre noch eine Paywall.

## Was kann schiefgehen?

Im Test zeigen sich drei Dinge:

- **Unpräzise Suchanfragen.** Wenn der Plan schwach ist, findet jede Runde dieselben Seiten. Bitten Sie das Modell um Suchanfragen mit konkreten Namen und Daten.
- **Zu wenige Quellen mit Text.** Manche Themen liegen hinter Paywalls. Erweitern Sie die Suchanfragen oder erlauben Sie mehr Ergebnisse.
- **Zu lange Prompts.** Zwölf lange Seiten können den Kontext eines kleinen Modells überschreiten. Senken Sie `max_chars` oder behalten Sie acht Quellen.

Protokollieren Sie jede Suchanfrage und die URLs, die sie zurückgegeben hat. Dieses Protokoll zu lesen ist der schnellste Weg, den Plan-Prompt zu verbessern.

## Deep-Research-Agent: häufige Fragen

### Kann ich das mit einem lokalen Modell ausführen?

Ja, wenn der lokale Server das OpenAI-Chat-Completions-Format spricht. Setzen Sie `LLM_BASE_URL` auf seine Adresse und `LLM_MODEL` auf den Modellnamen.

### Kann ich es in ein MCP-Tool umwandeln?

Ja. Packen Sie `research()` in ein MCP-Tool, oder geben Sie Ihrem Agenten das einfache Such-Tool von der [MCP-Seite](https://llmwebsearch.com/de/docs/mcp/) und lassen Sie ihn die Schleife selbst ausführen.

### Warum nicht die Such-API direkt nach einer Antwort fragen?

Weil LLMWebSearch keine Antworten schreibt. Die API liefert Quellen, keine geschriebene Antwort: Ihr eigenes Modell liest die Seiten, schreibt die Antwort und zitiert die URLs.

### Wie sehe ich, was jede Suche gekostet hat?

Jede Antwort enthält `credits_used`, und `GET https://llmwebsearch.com/api/v1/credits` zeigt Ihr verbleibendes Guthaben ohne Kosten.

---

LLMWebSearch: LLMWebSearch ist eine Websuch-API für KI-Agenten, die gerankte Ergebnisse mit dem Inhalt der Top-Seiten als Markdown liefert, bezahlt mit Krypto ohne Ausweis-Upload. Vorausbezahlte Credits ab 1,20 $/1K Suchanfragen verfallen nie. Ohne KYC — kein Ausweis, kein Selfie, keine Dokumente. Mit Krypto bezahlen.
