"""Web-Suche Tool via SearXNG (CT 121).""" import requests SEARXNG_URLS = [ "http://10.10.10.121:8080", "http://100.74.196.29:8080", ] TOOLS = [ { "type": "function", "function": { "name": "web_search", "description": "Schnelle Web-Suche (3-10s). STANDARDTOOL fuer Preise, News, Fakten. WICHTIG: Nur kurze Keywords als query (2-5 Woerter), KEINE ganzen Saetze. Mehrfach mit verschiedenen Keywords aufrufen.", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "Kurze Keyword-Suchanfrage (2-5 Woerter, KEINE ganzen Saetze). Beispiel: goldpreis euro unze heute" }, "max_results": { "type": "integer", "description": "Anzahl Ergebnisse (1-8)", "default": 5 } }, "required": ["query"] }, }, }, ] SYSTEM_PROMPT_EXTRA = """WEB-SUCHE: Du hast das Tool web_search fuer schnelle Web-Recherche (3-10s). SUCH-QUERIES MUESSEN kurze Keywords sein (2-5 Woerter), KEINE natuerlichen Saetze! Gut: "goldpreis euro unze heute" Gut: "DDR5 RAM 32GB preis 2026" Schlecht: "Was kostet aktuell eine Feinunze Gold in Euro?" Nutze web_search bei aktuellen Fakten, Preisen, News, Vergleichen. Mache 2-3 Suchen mit verschiedenen Keywords fuer bessere Ergebnisse. Nutze deep_research NUR wenn User explizit danach fragt.""" def _search_once(base_url: str, query: str): r = requests.get( f"{base_url}/search", params={"q": query, "format": "json", "language": "de"}, timeout=10, ) r.raise_for_status() return r.json() def handle_web_search(query: str, max_results: int = 5, **kw): if not query or not query.strip(): return "web_search: query fehlt." max_results = max(1, min(int(max_results or 5), 8)) _pq = query.lower() _is_price = any(w in _pq for w in ["preis", "price", "kurs", "kostet", "gold", "silber", "unze", "ounce"]) if _is_price: max_results = max(max_results, 5) last_err = None data = None used_url = None for base in SEARXNG_URLS: try: data = _search_once(base, query.strip()) used_url = base break except Exception as e: last_err = e if data is None: return f"web_search nicht erreichbar: {last_err}" results = data.get("results", [])[:max_results] if not results: return f"Keine Treffer fuer: {query}" lines = [ f"Web-Suche: {query}", # SearXNG URL nicht als Quelle zeigen "", ] for idx, item in enumerate(results, 1): title = (item.get("title") or "(ohne Titel)").strip() url = (item.get("url") or "").strip() snippet = (item.get("content") or "").strip().replace("\n", " ") if len(snippet) > 220: snippet = snippet[:220] + "..." lines.append(f"{idx}. {title}") if snippet: lines.append(f" {snippet}") lines.append(f" {url}") if _is_price: lines.append("") lines.append("ACHTUNG EINHEITEN: Goldpreis-Quellen mischen oft Gramm/Unze/Kilo!") lines.append("1 troy ounce = 31,103 g. goldpreis.de zeigt oft EUR/Gramm, NICHT EUR/Unze.") lines.append("Rechne ggf. um: Preis/Gramm x 31,103 = Preis/Unze.") return "\n".join(lines) HANDLERS = { "web_search": handle_web_search, }