Du benutzt ChatGPT jeden Tag. Text rein, Text raus, Produktivität simuliert. Aber auf jeder Tech-Konferenz 2026 fliegt dir ein Wort um die Ohren: Agents. Dein PM sagt 'wir brauchen einen Agent." Dein CTO sagt 'Agents sind die Zukunft." LinkedIn ertrinkt in Agent-Thinkpieces. Und du sitzt da und denkst: 'Ich weiß nicht mal, was das heißt."
Hier ist die Lücke. Ein Chatbot wartet, bis du tippst, und tippt zurück — wie WhatsApp mit einem schlauen Kumpel. Ein AI Agent ist was anderes. Ein Agent hat ein Ziel, wählt seine eigenen Tools, behandelt Fehler und arbeitet weiter, bis der Job erledigt ist — oder er entscheidet, dass es nicht geht. Niemand hält ihm die Hand. Der Unterschied: jemandem eine Frage stellen versus jemanden einstellen, der den Job macht.
Heute Abend schließt du diese Lücke. Du baust einen echten Agent — in Python, von Grund auf, ohne Frameworks — der das Web durchsucht, Informationen analysiert, Entscheidungen trifft und einen Research-Report auf die Festplatte schreibt. Bis zur Tagesschau verstehst du exakt das Pattern, das Claude Code, Codex, Devin und jedes andere Agent-Produkt antreibt, das 200 Dollar im Monat kostet. Das Pattern selbst: ungefähr 30 Zeilen.
Was wir bauen
Einen Research Agent, der:
- Ein Thema von dir entgegennimmt
- Das Web nach relevanten Informationen durchsucht
- Gefundenes liest und analysiert
- Eine strukturierte Zusammenfassung schreibt
- Das Ergebnis als Datei speichert
Das ist keine Spielzeug-Demo. Das ist die gleiche Architektur, die Production Agents verwenden — Tool Use, Reasoning Loops, Structured Output. Der einzige Unterschied zwischen diesem und einem 'Production Agent" ist Error Handling und Skalierung.
Schritt 1: Projekt aufsetzen (10 Minuten)
mkdir research-agent && cd research-agent
python3 -m venv venv
source venv/bin/activate
pip install anthropic httpx
Zwei Dependencies:
- anthropic — das Python SDK (Software Development Kit — eine fertige Bibliothek, um mit Claudes API zu reden)
- httpx — für Web-Requests aus Python
Du brauchst außerdem einen Anthropic API Key — im Grunde ein Passwort, mit dem dein Code mit Claude sprechen darf. Hol dir einen bei console.anthropic.com. Neue Accounts bekommen 5 Dollar Gratis-Credits, das reicht für hunderte Agent-Durchläufe.
export ANTHROPIC_API_KEY=sk-ant-...
touch agent.py
Schritt 2: Tools definieren (15 Minuten)
Ein Agent ohne Tools ist nur ein Chatbot. Tools sind Funktionen, die dem Agent erlauben, mit der echten Welt zu interagieren — das Web durchsuchen, Dateien lesen, APIs aufrufen (so schreien sich Programme übers Internet gegenseitig an — stell dir Maschine-zu-Maschine-SMS vor).
Wir geben unserem Agent zwei Tools:
# agent.py
import anthropic
import httpx
import json
import os
from datetime import datetime
client = anthropic.Anthropic()
MODEL = "claude-haiku-4.5"
# Tool-Definitionen — die sagen Claude, was verfügbar ist
tools = [
{
"name": "web_search",
"description": "Search the web for information on a topic. Returns results with titles, URLs, and snippets.",
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query"
}
},
"required": ["query"]
}
},
{
"name": "save_file",
"description": "Save text content to a file on disk.",
"input_schema": {
"type": "object",
"properties": {
"filename": {
"type": "string",
"description": "Name of the file to save"
},
"content": {
"type": "string",
"description": "Content to write to the file"
}
},
"required": ["filename", "content"]
}
}
]
Diese Definitionen funktionieren wie eine Speisekarte. Claude liest die Beschreibungen und entscheidet selbst, wann welches Tool zum Einsatz kommt — du hardcodest keine Reihenfolge. Der input_schema-Teil nutzt JSON Schema — ein Standardformat, um zu beschreiben, wie Daten aussehen sollen, damit Claude genau weiß, welche Parameter jedes Tool erwartet. Ja, du beschreibst dein Datenformat mit einem anderen Datenformat. Willkommen in der Programmierung.
Schritt 3: Tools implementieren (15 Minuten)
Definitionen sagen Claude, was existiert. Jetzt schreiben wir den Code, der tatsächlich läuft, wenn Claude ein Tool aufruft. Hier trifft die Theorie auf die Praxis — oder genauer: wo deine schönen Abstraktionen auf die hässliche Realität treffen, HTML zu parsen wie anno 2003:
def execute_tool(tool_name: str, tool_input: dict) -> str:
"""Execute a tool and return the result as a string."""
if tool_name == "web_search":
return do_web_search(tool_input["query"])
elif tool_name == "save_file":
return do_save_file(tool_input["filename"], tool_input["content"])
else:
return f"Error: Unknown tool '{tool_name}'"
def do_web_search(query: str) -> str:
"""Search using DuckDuckGo's HTML endpoint. No API key needed."""
try:
response = httpx.get(
"https://html.duckduckgo.com/html/",
params={"q": query},
headers={"User-Agent": "ResearchAgent/1.0"},
timeout=10.0,
)
response.raise_for_status()
text = response.text
results = []
parts = text.split('class="result__snippet"')
for part in parts[1:6]: # Grab up to 5 results
snippet_end = part.find("</a>")
if snippet_end > 0:
snippet = part[:snippet_end]
clean = snippet.replace("<b>", "").replace("</b>", "")
clean = clean.split(">")[-1] if ">" in clean else clean
if clean.strip():
results.append(clean.strip())
if results:
return "Search results:\n" + "\n".join(
f"- {r}" for r in results
)
return f"Search completed but no clear results for: {query}"
except Exception as e:
return f"Search error: {str(e)}"
def do_save_file(filename: str, content: str) -> str:
"""Save content to the output directory."""
os.makedirs("output", exist_ok=True)
filepath = os.path.join("output", filename)
try:
with open(filepath, "w") as f:
f.write(content)
return f"File saved successfully: {filepath}"
except Exception as e:
return f"Error saving file: {str(e)}"
Die Web-Suche nutzt DuckDuckGos HTML-Endpoint — kein API Key, keine Registrierung, keine Kosten. Das HTML-Parsing hält mit Klebeband und Optimismus zusammen (wir scrapen rohes Seiten-Markup statt einen sauberen Daten-Feed zu nutzen), aber es funktioniert. Für Production würdest du Brave Search API (2.000 Gratis-Abfragen/Monat) oder ein selbst gehostetes SearXNG einsetzen.
Schritt 4: Die Agent-Loop bauen (20 Minuten)
Das ist das Herzstück. Jedes Agent-Produkt mit hübscher Landing Page und 50-Millionen-Dollar-Bewertung läuft auf irgendeiner Version dieser 30 Zeilen:
def run_agent(topic: str, max_turns: int = 10) -> str:
"""Run the research agent on a topic."""
print(f"\n{'='*60}")
print(f"Research Agent — Topic: {topic}")
print(f"{'='*60}\n")
system_prompt = """You are a research agent. Your job is to research a topic
thoroughly and produce a well-structured summary.
Your process:
1. Search for information on the topic (multiple searches with different angles)
2. Analyze what you find
3. Write a comprehensive research summary
4. Save the summary to a file
Be thorough — do at least 3 different searches to cover the topic well.
Be critical — evaluate sources and note conflicting information.
When done, save the final summary as a markdown file.
Current date: """ + datetime.now().strftime("%Y-%m-%d")
messages = [
{
"role": "user",
"content": f"Research this topic and produce a detailed summary: {topic}"
}
]
# The agent loop
for turn in range(max_turns):
print(f"--- Turn {turn + 1} ---")
response = client.messages.create(
model=MODEL,
max_tokens=4096,
system=system_prompt,
tools=tools,
messages=messages,
)
print(f"Stop reason: {response.stop_reason}")
if response.stop_reason == "tool_use":
tool_results = []
for block in response.content:
if block.type == "tool_use":
tool_name = block.name
tool_input = block.input
tool_id = block.id
print(f" Tool: {tool_name}")
print(f" Input: {json.dumps(tool_input, indent=2)[:200]}")
result = execute_tool(tool_name, tool_input)
print(f" Result: {result[:200]}...")
tool_results.append({
"type": "tool_result",
"tool_use_id": tool_id,
"content": result,
})
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
elif response.stop_reason == "end_turn":
final_text = ""
for block in response.content:
if hasattr(block, "text"):
final_text += block.text
print(f"\nAgent completed in {turn + 1} turns.")
return final_text
return "Agent did not complete within turn limit."
Lass mich diese Loop aufschlüsseln, denn das ist der gesamte Zauber:
- Sende die Aufgabe an Claude zusammen mit den Tool-Definitionen
- Claude denkt nach und entscheidet, ob es ein Tool nutzen oder mit finalem Text antworten will
- Wenn tool_use — wir führen das Tool aus und senden das Ergebnis als neue Nachricht zurück
- Claude sieht das Ergebnis und entscheidet den nächsten Schritt
- Wiederholen bis Claude
end_turnsagt — also fertig ist
Die entscheidende Erkenntnis: Du hast nirgends hartcodiert 'erst suchen, dann analysieren, dann schreiben." Claude erarbeitet den Workflow selbst, basierend auf der Aufgabe. Das ist es, was einen Agent von einem Script unterscheidet. Ein Script folgt deinen Anweisungen. Ein Agent folgt seinen eigenen.
Das Feld stop_reason ist der Schlüssel. Claudes API gibt entweder "tool_use" zurück (ich will ein Tool aufrufen) oder "end_turn" (ich bin fertig). Deine Loop prüft einfach, welches es ist, und handelt entsprechend.
Schritt 5: Entry Point hinzufügen (5 Minuten)
Der langweilige Teil. Aber auch langweilige Teile müssen existieren, sonst läuft nichts — eine Lektion, die die Hälfte der AI-Demo-Repos auf GitHub immer noch nicht gelernt hat:
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
topic = " ".join(sys.argv[1:])
else:
topic = input("Enter research topic: ")
result = run_agent(topic)
print(f"\n{'='*60}")
print("Research complete. Check the output/ directory.")
print(f"{'='*60}")
Schritt 6: Starten (5 Minuten)
python agent.py "current state of MCP protocol adoption in 2026"
Schau ins Terminal. Du siehst, wie der Agent das Problem eigenständig durchdenkt:
============================================================
Research Agent — Topic: current state of MCP protocol adoption in 2026
============================================================
--- Turn 1 ---
Stop reason: tool_use
Tool: web_search
Input: {"query": "MCP model context protocol adoption 2026"}
Result: Search results: - The MCP ecosystem has grown...
--- Turn 2 ---
Stop reason: tool_use
Tool: web_search
Input: {"query": "MCP servers enterprise production 2026"}
Result: Search results: - Amazon Bedrock AgentCore...
--- Turn 3 ---
Stop reason: tool_use
Tool: web_search
Input: {"query": "MCP protocol limitations challenges 2026"}
Result: Search results: - Stateful sessions fight with...
--- Turn 4 ---
Stop reason: tool_use
Tool: save_file
Input: {"filename": "mcp-research-2026.md", "content": "# MCP Protocol..."}
Result: File saved successfully: output/mcp-research-2026.md
--- Turn 5 ---
Stop reason: end_turn
Agent completed in 5 turns.
Fünf Durchgänge. Drei Suchen, ein File Save, eine finale Zusammenfassung. Niemand hat ihm gesagt, aus verschiedenen Blickwinkeln zu suchen — das hat er selbst entschieden. Das ist Agency, kein Scripting.
Schritt 7: Schlauer machen (30 Minuten)
Der Basis-Agent funktioniert. Jetzt kommen drei Upgrades, die aus der Demo etwas machen, das du tatsächlich weiter nutzt.
Gedächtnis über Sessions hinweg
Gerade startet jeder Durchlauf bei Null. Geben wir dem Agent ein einfaches Gedächtnis — eine JSON-Datei (ein strukturiertes Textformat, das Programme leicht lesen und schreiben können), die speichert, was er vorher recherchiert hat:
from pathlib import Path
MEMORY_FILE = "memory.json"
def load_memory() -> list:
"""Load previous research topics and findings."""
if Path(MEMORY_FILE).exists():
with open(MEMORY_FILE) as f:
return json.load(f)
return []
def save_memory(topic: str, summary: str):
"""Save this research session to memory."""
memory = load_memory()
memory.append({
"date": datetime.now().isoformat(),
"topic": topic,
"summary": summary[:500],
})
memory = memory[-20:] # Keep last 20 entries
with open(MEMORY_FILE, "w") as f:
json.dump(memory, f, indent=2)
Das Gedächtnis in den System Prompt injizieren — den Anweisungstext, der Claudes Verhalten formt:
memory = load_memory()
if memory:
memory_context = "\n\nPrevious research sessions:\n"
for m in memory[-5:]:
memory_context += f"- [{m['date'][:10]}] {m['topic']}: {m['summary'][:100]}...\n"
system_prompt += memory_context
Jetzt weiß der Agent, was er vorher recherchiert hat. Er kann auf frühere Erkenntnisse verweisen, doppelte Suchen vermeiden und auf vorheriger Arbeit aufbauen.
Ein Denk-Tool
Das hier ist ein Trick. Füge ein Tool hinzu, das buchstäblich nichts tut:
tools.append({
"name": "think",
"description": "Use this tool to think through your approach before acting. Write out your reasoning and what you need to find out next.",
"input_schema": {
"type": "object",
"properties": {
"thought": {
"type": "string",
"description": "Your reasoning and plan"
}
},
"required": ["thought"]
}
})
Im Tool-Executor gibt es einfach eine Bestätigung zurück:
elif tool_name == "think":
print(f" Thinking: {tool_input['thought'][:300]}")
return "Thought recorded. Continue with your plan."
Warum ein Tool hinzufügen, das nichts tut? Weil es dem Agent einen strukturierten Raum gibt, nachzudenken, bevor er handelt. Ohne springt Claude direkt zu Tool Calls. Mit pausiert Claude, plant, und führt dann aus — mit merklich besseren Ergebnissen. Anthropic dokumentiert diese Technik in ihrem Tool Use Guide, und Production Agents verlassen sich darauf.
Fehlerbehandlung
def execute_tool_safe(tool_name: str, tool_input: dict) -> str:
"""Execute a tool with automatic retries."""
for attempt in range(3):
try:
result = execute_tool(tool_name, tool_input)
if "error" in result.lower() and attempt < 2:
print(f" Retry {attempt + 1}...")
continue
return result
except Exception as e:
if attempt < 2:
print(f" Error, retrying: {e}")
continue
return f"Tool failed after 3 attempts: {str(e)}"
Web-Requests scheitern. APIs gehen down. Timeouts passieren. Ein Production Agent versucht es nochmal, bevor er aufgibt. Drei Versuche mit Fallback sind die Baseline.
Die finale Struktur
research-agent/
├── agent.py # ~150 Zeilen Python
├── memory.json # Wird automatisch erstellt, speichert Session-Historie
├── output/ # Wird automatisch erstellt, speichert Research-Reports
│ └── *.md
└── requirements.txt # anthropic, httpx
Unter 200 Zeilen. Zwei Dependencies. Keine Frameworks.
Warum kein Framework?
Du fragst dich vielleicht: 'Warum nicht LangChain oder LlamaIndex?" (Beides sind populäre Python-Frameworks, die fertige Abstraktionen um LLM-Aufrufe legen.)
Weil die Agent-Loop oben 30 Zeilen hat. LangChain würde 15 Dependencies und drei Abstraktionsebenen für das gleiche Ergebnis mitbringen.
Nutze ein Framework wenn:
- Du 10+ Tools mit komplexer Routing-Logik brauchst
- Du Conversation Memory brauchst, das über tausende User skaliert
- Du mehrere Agents brauchst, die an derselben Aufgabe koordinieren
- Du simples Python entwachsen bist und die Architektur von jemand anderem brauchst
Überspring das Framework wenn:
- Du deinen ersten Agent baust
- Dein Agent 2–5 Tools hat
- Du jede Zeile verstehen willst, die läuft
- 'Es funktioniert und ich versteh's" besser ist als 'es funktioniert und ich vertraue der Abstraktion"
Stand März 2026 zeigt die Anthropic SDK-Dokumentation das gleiche Bare-Bones-Loop-Pattern, das wir gerade gebaut haben. Die offizielle Empfehlung lautet: ohne Framework starten.
Was du heute Abend gebaut hast
Bestandsaufnahme:
- Einen funktionierenden AI Agent — nimmt ein Ziel, verfolgt es autonom
- Tool Use — der Agent ruft externe Systeme auf (Websuche, Datei-I/O)
- Eine Reasoning Loop — Claude entscheidet die nächste Aktion basierend auf Ergebnissen, nicht nach einem hartcodierten Script
- Gedächtnis — der Agent erinnert sich an vergangene Sessions und baut darauf auf
- Fehlerbehandlung — Retries vor dem Aufgeben
- Output-Persistenz — Ergebnisse landen in echten Dateien auf deiner Festplatte
Das ist die gleiche Kernarchitektur wie Claude Code, Devin, OpenAI Codex und jedes andere Agent-Produkt. Die haben bessere Tools, mehr Error Handling und größere Context Windows — die Menge an Text, die das Model gleichzeitig 'sehen" kann, quasi sein Arbeitsgedächtnis. Aber die Loop ist identisch mit dem, was du gerade geschrieben hast.
Wie es weitergeht
Du verstehst jetzt das fundamentale Pattern. Alles andere ist Engineering on top:
- Mehr Tools — ein Taschenrechner, ein Web-Scraper, ein Datenbank-Connector, ein Code-Executor
- Besseres Gedächtnis — Vektordatenbanken (Systeme, die Text nach Bedeutung speichern, nicht nur nach Stichwörtern) für semantische Suche über vergangene Sessions
- Parallele Tool Calls — mehrere Suchen gleichzeitig statt nacheinander
- Multi-Agent-Systeme — ein zweiter Agent, der die Arbeit des ersten prüft, wie ein Code Review
- MCP-Integration — Model Context Protocol, ein Standard, um AI Agents mit externen Tools zu verbinden, wie USB aber für Datenquellen
Du lernst nicht das Framework von jemand anderem, das in sechs Monaten tot sein könnte. Du lernst das Pattern. Das gleiche Pattern, das 2024 funktioniert hat, 2026 funktioniert und 2028 funktionieren wird — weil die zugrundeliegende Mechanik (Model entscheidet → Tool führt aus → Ergebnis fließt zurück) ist, wie alle Agent-Systeme funktionieren, egal welchen Marketing-Namen sie tragen.
Die 'AI Agent"-Branche will dir einreden, Agents bauen erfordert einen Doktortitel und eine 100-Millionen-Dollar-Series-B. In Wahrheit braucht es das Verständnis einer einzigen Loop: Model aufrufen, prüfen ob es ein Tool will, Tool ausführen, Ergebnis zurücksenden, wiederholen. Das war's. Alles andere ist Engineering um diese Loop herum — und jetzt weißt du genug, um dieses Engineering selbst zu machen.





