Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -40,12 +40,12 @@ WISSEN_FILE = os.path.join(DATA_DIR, "wissen.json")
|
|
| 40 |
CHAT_FILE = os.path.join(DATA_DIR, "chat_history.json")
|
| 41 |
LOG_FILE = os.path.join(DATA_DIR, "ai_log.txt")
|
| 42 |
|
| 43 |
-
FALLBACK_NO_INFO = "Dazu habe ich
|
| 44 |
|
| 45 |
USE_QWEN_POLISH = True
|
| 46 |
|
| 47 |
DB_DIRECT_MATCH_THRESHOLD = 0.88
|
| 48 |
-
DB_FACT_MATCH_THRESHOLD = 0.
|
| 49 |
|
| 50 |
# =========================================================
|
| 51 |
# GLOBALE VARIABLEN
|
|
@@ -194,6 +194,16 @@ def looks_like_factual_question(text):
|
|
| 194 |
"welche", "welcher", "welches", "nenn", "nenne", "erklaer", "erklär"
|
| 195 |
))
|
| 196 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
# =========================================================
|
| 198 |
# KNOWLEDGE / DATENBANK
|
| 199 |
# =========================================================
|
|
@@ -593,8 +603,8 @@ def save_link_as_knowledge(url, thema="", kategorie="web"):
|
|
| 593 |
log_error("extract_webpage_text", e)
|
| 594 |
return False, f"❌ Link konnte nicht gelesen werden: {e}"
|
| 595 |
|
| 596 |
-
if not raw_text:
|
| 597 |
-
return False, "❌ Auf der Seite konnte kein Text gefunden werden."
|
| 598 |
|
| 599 |
summary = summarize_web_text(title, raw_text)
|
| 600 |
if not summary or len(summary.strip()) < 30:
|
|
@@ -669,7 +679,7 @@ def init_model_if_needed():
|
|
| 669 |
dtype = torch.float16 if device.type == "cuda" else torch.float32
|
| 670 |
model = AutoModelForCausalLM.from_pretrained(
|
| 671 |
MODEL_NAME,
|
| 672 |
-
|
| 673 |
low_cpu_mem_usage=True
|
| 674 |
)
|
| 675 |
model.to(device)
|
|
@@ -755,20 +765,20 @@ def compose_draft_from_facts(facts):
|
|
| 755 |
if not facts:
|
| 756 |
return ""
|
| 757 |
|
| 758 |
-
|
| 759 |
-
for item in facts:
|
| 760 |
-
|
| 761 |
-
|
| 762 |
-
|
|
|
|
|
|
|
|
|
|
| 763 |
|
| 764 |
-
if not
|
| 765 |
return ""
|
| 766 |
|
| 767 |
-
random.shuffle(
|
| 768 |
-
|
| 769 |
-
if take == 1:
|
| 770 |
-
return answers[0]
|
| 771 |
-
return " ".join(answers[:take])
|
| 772 |
|
| 773 |
def polish_with_model(user_message, draft, facts, history_context=""):
|
| 774 |
if not USE_QWEN_POLISH:
|
|
@@ -781,18 +791,20 @@ def polish_with_model(user_message, draft, facts, history_context=""):
|
|
| 781 |
for idx, item in enumerate(facts, 1):
|
| 782 |
fact_lines.append(
|
| 783 |
f"{idx}. Thema: {item.get('frage', '')}\n"
|
| 784 |
-
f"
|
| 785 |
)
|
| 786 |
-
fact_block = "\n".join(fact_lines)
|
| 787 |
|
| 788 |
messages = [
|
| 789 |
{
|
| 790 |
"role": "system",
|
| 791 |
"content": (
|
| 792 |
-
"Du bist
|
| 793 |
-
"
|
| 794 |
-
"
|
| 795 |
-
"
|
|
|
|
|
|
|
| 796 |
)
|
| 797 |
},
|
| 798 |
{
|
|
@@ -800,17 +812,23 @@ def polish_with_model(user_message, draft, facts, history_context=""):
|
|
| 800 |
"content": (
|
| 801 |
f"Frage: {user_message}\n\n"
|
| 802 |
f"Kontext: {history_context}\n\n"
|
| 803 |
-
f"
|
| 804 |
-
f"
|
| 805 |
-
"Aufgabe:
|
| 806 |
-
"
|
| 807 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 808 |
)
|
| 809 |
}
|
| 810 |
]
|
| 811 |
|
| 812 |
try:
|
| 813 |
-
out = model_generate(messages, max_new_tokens=
|
| 814 |
if not out:
|
| 815 |
return draft
|
| 816 |
return out.strip()
|
|
@@ -842,7 +860,7 @@ def general_chat_reply(user_message, history_context=""):
|
|
| 842 |
]
|
| 843 |
|
| 844 |
try:
|
| 845 |
-
out = model_generate(messages, max_new_tokens=140, temperature=0.
|
| 846 |
out = (out or "").strip()
|
| 847 |
return out if out else "Dazu habe ich gerade keine sichere Antwort."
|
| 848 |
except Exception as e:
|
|
@@ -852,27 +870,16 @@ def general_chat_reply(user_message, history_context=""):
|
|
| 852 |
def generate_reply(user_message, history_context=""):
|
| 853 |
query = f"{user_message} {history_context}".strip()
|
| 854 |
|
| 855 |
-
exact = exact_db_answer(user_message)
|
| 856 |
-
if exact:
|
| 857 |
-
facts = find_relevant_facts(query, max_items=6)
|
| 858 |
-
reply = polish_with_model(user_message, exact, facts, history_context)
|
| 859 |
-
return reply if reply else exact
|
| 860 |
-
|
| 861 |
-
fuzzy_direct = best_db_answer(user_message, threshold=DB_DIRECT_MATCH_THRESHOLD)
|
| 862 |
-
if fuzzy_direct:
|
| 863 |
-
facts = find_relevant_facts(query, max_items=6)
|
| 864 |
-
reply = polish_with_model(user_message, fuzzy_direct, facts, history_context)
|
| 865 |
-
return reply if reply else fuzzy_direct
|
| 866 |
-
|
| 867 |
facts = find_relevant_facts(query, max_items=6)
|
| 868 |
-
if facts:
|
| 869 |
-
draft = compose_draft_from_facts(facts)
|
| 870 |
-
if not draft:
|
| 871 |
-
return general_chat_reply(user_message, history_context)
|
| 872 |
|
| 873 |
-
|
| 874 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 875 |
|
|
|
|
| 876 |
return general_chat_reply(user_message, history_context)
|
| 877 |
|
| 878 |
# =========================================================
|
|
@@ -1020,7 +1027,7 @@ def erzeuge_gradio_app():
|
|
| 1020 |
)
|
| 1021 |
|
| 1022 |
gr.Markdown("# 🤖 Privates KI Kontrollzentrum")
|
| 1023 |
-
gr.Markdown("Die KI nutzt zuerst die Datenbank. Qwen
|
| 1024 |
|
| 1025 |
with gr.Tab("📊 Status"):
|
| 1026 |
status_text = gr.Textbox(label="Systembericht", lines=16, interactive=False)
|
|
|
|
| 40 |
CHAT_FILE = os.path.join(DATA_DIR, "chat_history.json")
|
| 41 |
LOG_FILE = os.path.join(DATA_DIR, "ai_log.txt")
|
| 42 |
|
| 43 |
+
FALLBACK_NO_INFO = "Dazu habe ich gerade keine sichere Antwort."
|
| 44 |
|
| 45 |
USE_QWEN_POLISH = True
|
| 46 |
|
| 47 |
DB_DIRECT_MATCH_THRESHOLD = 0.88
|
| 48 |
+
DB_FACT_MATCH_THRESHOLD = 0.62
|
| 49 |
|
| 50 |
# =========================================================
|
| 51 |
# GLOBALE VARIABLEN
|
|
|
|
| 194 |
"welche", "welcher", "welches", "nenn", "nenne", "erklaer", "erklär"
|
| 195 |
))
|
| 196 |
|
| 197 |
+
def compress_text(text, max_chars=220):
|
| 198 |
+
text = (text or "").strip()
|
| 199 |
+
if not text:
|
| 200 |
+
return ""
|
| 201 |
+
text = re.sub(r"\s+", " ", text)
|
| 202 |
+
if len(text) <= max_chars:
|
| 203 |
+
return text
|
| 204 |
+
cut = text[:max_chars].rsplit(" ", 1)[0].strip()
|
| 205 |
+
return cut + "..."
|
| 206 |
+
|
| 207 |
# =========================================================
|
| 208 |
# KNOWLEDGE / DATENBANK
|
| 209 |
# =========================================================
|
|
|
|
| 603 |
log_error("extract_webpage_text", e)
|
| 604 |
return False, f"❌ Link konnte nicht gelesen werden: {e}"
|
| 605 |
|
| 606 |
+
if not raw_text or len(raw_text) < 50:
|
| 607 |
+
return False, "❌ Auf der Seite konnte kein ausreichender Text gefunden werden."
|
| 608 |
|
| 609 |
summary = summarize_web_text(title, raw_text)
|
| 610 |
if not summary or len(summary.strip()) < 30:
|
|
|
|
| 679 |
dtype = torch.float16 if device.type == "cuda" else torch.float32
|
| 680 |
model = AutoModelForCausalLM.from_pretrained(
|
| 681 |
MODEL_NAME,
|
| 682 |
+
torch_dtype=dtype,
|
| 683 |
low_cpu_mem_usage=True
|
| 684 |
)
|
| 685 |
model.to(device)
|
|
|
|
| 765 |
if not facts:
|
| 766 |
return ""
|
| 767 |
|
| 768 |
+
pieces = []
|
| 769 |
+
for item in facts[:4]:
|
| 770 |
+
topic = (item.get("frage", "") or "").strip()
|
| 771 |
+
ans = compress_text(item.get("antwort", ""), 220)
|
| 772 |
+
if topic and ans:
|
| 773 |
+
pieces.append(f"{topic}: {ans}")
|
| 774 |
+
elif ans:
|
| 775 |
+
pieces.append(ans)
|
| 776 |
|
| 777 |
+
if not pieces:
|
| 778 |
return ""
|
| 779 |
|
| 780 |
+
random.shuffle(pieces)
|
| 781 |
+
return "\n".join(pieces)
|
|
|
|
|
|
|
|
|
|
| 782 |
|
| 783 |
def polish_with_model(user_message, draft, facts, history_context=""):
|
| 784 |
if not USE_QWEN_POLISH:
|
|
|
|
| 791 |
for idx, item in enumerate(facts, 1):
|
| 792 |
fact_lines.append(
|
| 793 |
f"{idx}. Thema: {item.get('frage', '')}\n"
|
| 794 |
+
f" Zusatzwissen: {compress_text(item.get('antwort', ''), 260)}"
|
| 795 |
)
|
| 796 |
+
fact_block = "\n".join(fact_lines) if fact_lines else "Keine zusätzlichen Fakten."
|
| 797 |
|
| 798 |
messages = [
|
| 799 |
{
|
| 800 |
"role": "system",
|
| 801 |
"content": (
|
| 802 |
+
"Du bist ein intelligenter KI-Assistent. "
|
| 803 |
+
"Beantworte die Frage hauptsächlich mit deinem eigenen Wissen. "
|
| 804 |
+
"Nutze die gespeicherten Fakten nur als zusätzliche Information, wenn sie passen. "
|
| 805 |
+
"Baue diese sinnvoll in deine Antwort ein, aber kopiere sie nicht. "
|
| 806 |
+
"Schreibe alles in eigenen Worten. "
|
| 807 |
+
"Die Antwort muss direkt zur Frage passen, natürlich klingen und hilfreich sein."
|
| 808 |
)
|
| 809 |
},
|
| 810 |
{
|
|
|
|
| 812 |
"content": (
|
| 813 |
f"Frage: {user_message}\n\n"
|
| 814 |
f"Kontext: {history_context}\n\n"
|
| 815 |
+
f"Zusätzliche Fakten (optional):\n{fact_block}\n\n"
|
| 816 |
+
f"Notizen / Ausgangspunkt:\n{draft if draft else 'Keine festen Vorgaben. Antworte frei, aber passend zur Frage.'}\n\n"
|
| 817 |
+
"Aufgabe:\n"
|
| 818 |
+
"- Beantworte die Frage vollständig\n"
|
| 819 |
+
"- Nutze dein eigenes Wissen als Hauptquelle\n"
|
| 820 |
+
"- Nutze die Fakten nur, wenn sie wirklich passen\n"
|
| 821 |
+
"- Ergänze Informationen sinnvoll\n"
|
| 822 |
+
"- Schreibe alles neu und verständlich\n"
|
| 823 |
+
"- Kein Copy-Paste\n"
|
| 824 |
+
"- Keine irrelevanten Infos\n"
|
| 825 |
+
"- Keine Rohdaten, sondern eine natürliche Antwort"
|
| 826 |
)
|
| 827 |
}
|
| 828 |
]
|
| 829 |
|
| 830 |
try:
|
| 831 |
+
out = model_generate(messages, max_new_tokens=160, temperature=0.65, do_sample=True)
|
| 832 |
if not out:
|
| 833 |
return draft
|
| 834 |
return out.strip()
|
|
|
|
| 860 |
]
|
| 861 |
|
| 862 |
try:
|
| 863 |
+
out = model_generate(messages, max_new_tokens=140, temperature=0.78, do_sample=True)
|
| 864 |
out = (out or "").strip()
|
| 865 |
return out if out else "Dazu habe ich gerade keine sichere Antwort."
|
| 866 |
except Exception as e:
|
|
|
|
| 870 |
def generate_reply(user_message, history_context=""):
|
| 871 |
query = f"{user_message} {history_context}".strip()
|
| 872 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 873 |
facts = find_relevant_facts(query, max_items=6)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 874 |
|
| 875 |
+
# Qwen bleibt Hauptdenker; DB ist nur Zusatzwissen.
|
| 876 |
+
draft = compose_draft_from_facts(facts)
|
| 877 |
+
|
| 878 |
+
reply = polish_with_model(user_message, draft, facts, history_context)
|
| 879 |
+
if reply:
|
| 880 |
+
return reply
|
| 881 |
|
| 882 |
+
# Wenn Modell nicht verfügbar oder leer antwortet, trotzdem nicht stumpf DB ausgeben
|
| 883 |
return general_chat_reply(user_message, history_context)
|
| 884 |
|
| 885 |
# =========================================================
|
|
|
|
| 1027 |
)
|
| 1028 |
|
| 1029 |
gr.Markdown("# 🤖 Privates KI Kontrollzentrum")
|
| 1030 |
+
gr.Markdown("Die KI nutzt zuerst die Datenbank. Qwen bleibt der Hauptdenker und ergänzt Fakten passend.")
|
| 1031 |
|
| 1032 |
with gr.Tab("📊 Status"):
|
| 1033 |
status_text = gr.Textbox(label="Systembericht", lines=16, interactive=False)
|