feat(bot): Qwen3 30B-A3B lokal (Ollama/RTX3090), web_search bevorzugt, Date-Injection, Thinking-Mode Fix
This commit is contained in:
parent
6e5a4c9529
commit
981118f940
3 changed files with 44 additions and 34 deletions
|
|
@ -4,6 +4,7 @@ Neue Datenquelle = eine Datei in tools/ anlegen. Fertig.
|
|||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import requests
|
||||
import os
|
||||
import sys
|
||||
|
|
@ -12,11 +13,22 @@ sys.path.insert(0, os.path.dirname(__file__))
|
|||
from core import config
|
||||
import tool_loader
|
||||
|
||||
MODEL = "openai/gpt-4o-mini"
|
||||
log = logging.getLogger('llm')
|
||||
|
||||
OLLAMA_BASE = "http://100.84.255.83:11434"
|
||||
OPENROUTER_BASE = "https://openrouter.ai/api/v1"
|
||||
|
||||
MODEL = "qwen3:30b-a3b"
|
||||
VISION_MODEL = "openai/gpt-4o"
|
||||
MAX_TOOL_ROUNDS = 3
|
||||
|
||||
SYSTEM_PROMPT = """Du bist der Hausmeister-Bot fuer ein Homelab. Deutsch, kurz, direkt, operativ.
|
||||
import datetime as _dt
|
||||
_TODAY = _dt.date.today()
|
||||
_3M_AGO = (_TODAY - _dt.timedelta(days=90))
|
||||
_DATE_LINE = f'Heutiges Datum: {_TODAY.strftime("%d. %B %Y")}. Wir sind im Jahr {_TODAY.year}. Letzte 3 Monate = {_3M_AGO.strftime("%B %Y")} bis {_TODAY.strftime("%B %Y")}.'
|
||||
|
||||
SYSTEM_PROMPT = _DATE_LINE + """
|
||||
Du bist der Hausmeister-Bot fuer ein Homelab. Deutsch, kurz, direkt, operativ.
|
||||
|
||||
STIL:
|
||||
- So wenig Worte wie moeglich, solange nichts Wichtiges fehlt.
|
||||
|
|
@ -150,7 +162,8 @@ PREISRECHERCHE (PFLICHT):
|
|||
Wenn der User nach Preisen, Kosten oder Preisentwicklung fragt:
|
||||
- Nutze IMMER Tools statt Allgemeinwissen.
|
||||
- Fuer schnelle Preisabfrage: web_search.
|
||||
- Fuer Preisentwicklung ueber Wochen/Monate: deep_research.
|
||||
- Mache 2-3 gezielte web_search Aufrufe mit verschiedenen Suchbegriffen.
|
||||
- deep_research NUR wenn User explizit 'deep research' oder 'tiefenrecherche' sagt.
|
||||
- Gib konkrete Zahlen aus (EUR), nicht nur Tendenzen.
|
||||
- Nenne 3-5 Quellen-Links.
|
||||
- Wenn keine belastbaren Zahlen gefunden werden: klar sagen "keine belastbaren Preisdaten gefunden".
|
||||
|
|
@ -173,9 +186,13 @@ def _get_api_key() -> str:
|
|||
|
||||
|
||||
def _call_openrouter(messages: list, api_key: str, use_tools: bool = True,
|
||||
model: str = None, max_tokens: int = 600) -> dict:
|
||||
model: str = None, max_tokens: int = 4000) -> dict:
|
||||
chosen = model or MODEL
|
||||
use_ollama = (chosen == MODEL)
|
||||
log.info("LLM-Call: model=%s ollama=%s max_tokens=%d", chosen, use_ollama, max_tokens)
|
||||
|
||||
payload = {
|
||||
"model": model or MODEL,
|
||||
"model": chosen,
|
||||
"messages": messages,
|
||||
"max_tokens": max_tokens,
|
||||
}
|
||||
|
|
@ -183,12 +200,16 @@ def _call_openrouter(messages: list, api_key: str, use_tools: bool = True,
|
|||
payload["tools"] = TOOLS
|
||||
payload["tool_choice"] = "auto"
|
||||
|
||||
r = requests.post(
|
||||
"https://openrouter.ai/api/v1/chat/completions",
|
||||
headers={"Authorization": f"Bearer {api_key}"},
|
||||
json=payload,
|
||||
timeout=90,
|
||||
)
|
||||
if use_ollama:
|
||||
url = f"{OLLAMA_BASE}/v1/chat/completions"
|
||||
headers = {"Content-Type": "application/json"}
|
||||
timeout = 180
|
||||
else:
|
||||
url = f"{OPENROUTER_BASE}/chat/completions"
|
||||
headers = {"Authorization": f"Bearer {api_key}"}
|
||||
timeout = 90
|
||||
|
||||
r = requests.post(url, headers=headers, json=payload, timeout=timeout)
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
|
|
@ -275,7 +296,10 @@ def ask_with_tools(question: str, tool_handlers: dict, session_id: str = None) -
|
|||
|
||||
tool_calls = msg.get("tool_calls")
|
||||
if not tool_calls:
|
||||
return msg.get("content", "Keine Antwort vom LLM.")
|
||||
content = msg.get("content") or ""
|
||||
if not content and msg.get("reasoning"):
|
||||
content = msg.get("reasoning", "")
|
||||
return content or "Keine Antwort vom LLM."
|
||||
|
||||
messages.append(msg)
|
||||
|
||||
|
|
@ -286,6 +310,7 @@ def ask_with_tools(question: str, tool_handlers: dict, session_id: str = None) -
|
|||
except (json.JSONDecodeError, KeyError):
|
||||
fn_args = {}
|
||||
|
||||
log.info("Tool-Call: %s args=%s", fn_name, str(fn_args)[:200])
|
||||
handler = tool_handlers.get(fn_name)
|
||||
if handler:
|
||||
try:
|
||||
|
|
@ -366,7 +391,10 @@ def ask_with_image(image_base64: str, caption: str, tool_handlers: dict, session
|
|||
|
||||
tool_calls = msg.get("tool_calls")
|
||||
if not tool_calls:
|
||||
return msg.get("content", "Keine Antwort vom LLM.")
|
||||
content = msg.get("content") or ""
|
||||
if not content and msg.get("reasoning"):
|
||||
content = msg.get("reasoning", "")
|
||||
return content or "Keine Antwort vom LLM."
|
||||
|
||||
messages.append(msg)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ MAX_WAIT = 600
|
|||
|
||||
SYSTEM_PROMPT_EXTRA = """DEEP RESEARCH:
|
||||
Du hast Zugriff auf deep_research — eine KI-gestuetzte Tiefenrecherche die 20-30 Quellen durchsucht.
|
||||
Nutze es wenn der User explizit "recherchiere", "finde heraus", "vergleiche" sagt oder eine komplexe Frage hat.
|
||||
Nutze es NUR wenn der User explizit "deep research" oder "tiefenrecherche" sagt. Fuer alles andere: web_search.
|
||||
NICHT fuer einfache Fakten oder Homelab-Fragen.
|
||||
WICHTIG: deep_research dauert 2-5 Minuten. Das ist normal. Warte auf das Ergebnis.
|
||||
Das Ergebnis ist ein ausfuehrlicher Report. Fasse ihn fuer Telegram zusammen (max ~3000 Zeichen).
|
||||
|
|
@ -26,25 +26,7 @@ QUALITAET BEI PREISFRAGEN:
|
|||
- Zeige Zeitraum, Preis damals/heute, Delta in % und Quellen.
|
||||
- Wenn keine belastbaren Daten vorhanden sind, sage es explizit."""
|
||||
|
||||
TOOLS = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "deep_research",
|
||||
"description": "Startet eine tiefe Web-Recherche zu einem Thema. Durchsucht 20-30 Quellen und erstellt einen ausfuehrlichen Report. Dauert 2-5 Minuten — das ist normal, warte auf das Ergebnis.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "Die Recherche-Frage, moeglichst spezifisch formuliert."
|
||||
}
|
||||
},
|
||||
"required": ["query"]
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
TOOLS = [] # removed from auto-discovery; use HANDLERS directly
|
||||
|
||||
|
||||
def _create_thread():
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ TOOLS = [
|
|||
"type": "function",
|
||||
"function": {
|
||||
"name": "web_search",
|
||||
"description": "Schnelle Web-Suche (3-10 Sekunden) ueber SearXNG. Nutze fuer aktuelle Fakten, Preise, News und einfache Web-Fragen.",
|
||||
"description": "Schnelle Web-Suche (3-10 Sekunden). STANDARDTOOL fuer alle Fragen zu Preisen, Recherchen, News, Fakten, Vergleichen. Immer zuerst web_search nutzen, mehrfach mit verschiedenen Suchbegriffen.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue