RAG: Vektor-Suche statt All-Items-Prompt, Kandidaten-System entfernt, Bot speichert direkt
This commit is contained in:
parent
659685228b
commit
a2c0279f99
2 changed files with 29 additions and 136 deletions
|
|
@ -48,7 +48,7 @@ PERMANENT (memory_type="permanent") — bleibt dauerhaft:
|
|||
- Wiederkehrende Regeln ("API-Kosten monatlich beobachten")
|
||||
|
||||
Im Zweifel: lieber temporary als permanent.
|
||||
IMMER memory_suggest aufrufen, auch wenn der User denselben Fakt wiederholt. Das System erkennt Duplikate automatisch und gibt den korrekten Status zurueck. Du sagst dann genau das was das Tool zurueckgibt.
|
||||
memory_suggest speichert DIREKT — kein Bestaetigen noetig. Das System erkennt Duplikate automatisch. Bei Duplikaten sag kurz "Weiss ich schon." und beantworte die Frage.
|
||||
NICHT speichern: Passwoerter, Tokens, Smalltalk, Hoeflichkeiten, reine Fragen.
|
||||
|
||||
SESSION-RUECKBLICK:
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ def _release_lock():
|
|||
except OSError:
|
||||
pass
|
||||
|
||||
from telegram import BotCommand, Update, ReplyKeyboardMarkup, KeyboardButton, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from telegram import BotCommand, Update, ReplyKeyboardMarkup, KeyboardButton
|
||||
from telegram.ext import (
|
||||
Application, CommandHandler, MessageHandler, CallbackQueryHandler, filters, ContextTypes,
|
||||
Application, CommandHandler, MessageHandler, filters, ContextTypes,
|
||||
)
|
||||
|
||||
BOT_COMMANDS = [
|
||||
|
|
@ -52,7 +52,7 @@ BOT_COMMANDS = [
|
|||
BotCommand("report", "Tagesbericht"),
|
||||
BotCommand("check", "Monitoring-Check"),
|
||||
BotCommand("feeds", "Feed-Status & Artikel heute"),
|
||||
BotCommand("memory", "Offene Memory-Kandidaten"),
|
||||
BotCommand("memory", "Gedaechtnis anzeigen"),
|
||||
BotCommand("start", "Hilfe anzeigen"),
|
||||
]
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ async def cmd_start(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
|
|||
"/report — Tagesbericht\n"
|
||||
"/check — Monitoring-Check\n"
|
||||
"/feeds — Feed-Status & Artikel\n"
|
||||
"/memory — Offene Memory-Kandidaten\n\n"
|
||||
"/memory — Gedaechtnis anzeigen\n\n"
|
||||
"Oder einfach eine Frage stellen!",
|
||||
reply_markup=KEYBOARD,
|
||||
)
|
||||
|
|
@ -313,75 +313,35 @@ def _find_matching_item(user_text: str, items: list[dict]) -> dict | None:
|
|||
return best
|
||||
|
||||
|
||||
def _memory_buttons(item_id: int) -> InlineKeyboardMarkup:
|
||||
return InlineKeyboardMarkup([[
|
||||
InlineKeyboardButton("🕒 Temporär", callback_data=f"mem_temp:{item_id}"),
|
||||
InlineKeyboardButton("📌 Dauerhaft", callback_data=f"mem_perm:{item_id}"),
|
||||
InlineKeyboardButton("❌ Verwerfen", callback_data=f"mem_delete:{item_id}"),
|
||||
]])
|
||||
|
||||
|
||||
def _format_candidate(item: dict) -> str:
|
||||
mtype = item.get("memory_type") or "?"
|
||||
type_icon = "🕒" if mtype == "temporary" else "📌" if mtype == "permanent" else "❓"
|
||||
line = f"{type_icon} [{item['scope']}/{item['kind']}] {mtype}\n{item['content']}"
|
||||
exp = item.get("expires_at")
|
||||
if exp:
|
||||
from datetime import datetime
|
||||
line += f"\n⏰ Ablauf: {datetime.fromtimestamp(exp).strftime('%d.%m.%Y %H:%M')}"
|
||||
return line
|
||||
|
||||
|
||||
async def cmd_memory(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
|
||||
if not _authorized(update):
|
||||
return
|
||||
candidates = memory_client.get_candidates()
|
||||
if not candidates:
|
||||
await update.message.reply_text("Keine offenen Kandidaten.")
|
||||
items = memory_client.get_active_memory()
|
||||
if not items:
|
||||
await update.message.reply_text("Kein Gedaechtnis vorhanden.")
|
||||
return
|
||||
for item in candidates:
|
||||
text = _format_candidate(item)
|
||||
await update.message.reply_text(text, reply_markup=_memory_buttons(item["id"]))
|
||||
permanent = [i for i in items if i.get("memory_type") != "temporary"]
|
||||
temporary = [i for i in items if i.get("memory_type") == "temporary"]
|
||||
lines = [f"🧠 Gedaechtnis: {len(items)} Eintraege\n"]
|
||||
if permanent:
|
||||
lines.append(f"📌 Dauerhaft ({len(permanent)}):")
|
||||
for i in permanent:
|
||||
lines.append(f" • {i['content'][:100]}")
|
||||
if temporary:
|
||||
lines.append(f"\n🕒 Temporaer ({len(temporary)}):")
|
||||
for i in temporary:
|
||||
exp = i.get("expires_at")
|
||||
exp_str = ""
|
||||
if exp:
|
||||
from datetime import datetime
|
||||
exp_str = f" (bis {datetime.fromtimestamp(exp).strftime('%d.%m.%Y')})"
|
||||
lines.append(f" • {i['content'][:100]}{exp_str}")
|
||||
text = "\n".join(lines)
|
||||
await update.message.reply_text(text[:4000], reply_markup=KEYBOARD)
|
||||
|
||||
|
||||
async def handle_memory_callback(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
|
||||
query = update.callback_query
|
||||
await query.answer()
|
||||
data = query.data or ""
|
||||
content_preview = query.message.text.split("\n", 2)[-1][:120] if query.message.text else ""
|
||||
|
||||
if data.startswith("mem_temp:"):
|
||||
item_id = int(data.split(":")[1])
|
||||
candidates = memory_client.get_candidates()
|
||||
item = next((c for c in candidates if c["id"] == item_id), None)
|
||||
exp = None
|
||||
if item:
|
||||
exp = item.get("expires_at") or memory_client.parse_expires_from_text(item.get("content", ""))
|
||||
if not exp:
|
||||
exp = memory_client.default_expires()
|
||||
ok = memory_client.activate_candidate(item_id, memory_type="temporary", expires_at=exp)
|
||||
if ok:
|
||||
from datetime import datetime
|
||||
exp_str = datetime.fromtimestamp(exp).strftime("%d.%m.%Y")
|
||||
await query.edit_message_text(f"🕒 Temporär gespeichert (bis {exp_str}): {content_preview}")
|
||||
else:
|
||||
await query.edit_message_text("Fehler beim Speichern.")
|
||||
|
||||
elif data.startswith("mem_perm:"):
|
||||
item_id = int(data.split(":")[1])
|
||||
ok = memory_client.activate_candidate(item_id, memory_type="permanent")
|
||||
if ok:
|
||||
await query.edit_message_text(f"📌 Dauerhaft gespeichert: {content_preview}")
|
||||
else:
|
||||
await query.edit_message_text("Fehler beim Speichern.")
|
||||
|
||||
elif data.startswith("mem_delete:"):
|
||||
item_id = int(data.split(":")[1])
|
||||
ok = memory_client.delete_candidate(item_id)
|
||||
if ok:
|
||||
await query.edit_message_text("🗑 Verworfen.")
|
||||
else:
|
||||
await query.edit_message_text("Fehler beim Loeschen.")
|
||||
|
||||
|
||||
async def handle_voice(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
|
||||
|
|
@ -455,8 +415,7 @@ async def handle_message(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
|
|||
|
||||
await update.message.reply_text("🤔 Denke nach...")
|
||||
try:
|
||||
context.last_suggest_result = {"type": None, "candidate_id": None}
|
||||
candidates_before = {c["id"] for c in memory_client.get_candidates()}
|
||||
context.last_suggest_result = {"type": None}
|
||||
handlers = context.get_tool_handlers(session_id=session_id)
|
||||
answer = llm.ask_with_tools(text, handlers, session_id=session_id)
|
||||
if session_id:
|
||||
|
|
@ -464,74 +423,9 @@ async def handle_message(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
|
|||
memory_client.log_message(session_id, "assistant", answer)
|
||||
|
||||
suggest = context.last_suggest_result
|
||||
log.info("suggest_result: type=%s candidate_id=%s", suggest.get("type"), suggest.get("candidate_id"))
|
||||
log.info("suggest_result: type=%s", suggest.get("type"))
|
||||
|
||||
sent = False
|
||||
|
||||
if suggest["type"] == "existing_candidate" and suggest["candidate_id"]:
|
||||
candidates = memory_client.get_candidates()
|
||||
item = next((c for c in candidates if c["id"] == suggest["candidate_id"]), None)
|
||||
if item:
|
||||
await update.message.reply_text(
|
||||
answer[:4000] + "\n\n" + _format_candidate(item),
|
||||
reply_markup=_memory_buttons(item["id"]),
|
||||
)
|
||||
sent = True
|
||||
|
||||
elif suggest["type"] in ("active_temporary", "active_permanent", "active_other"):
|
||||
from datetime import datetime as _dt
|
||||
active_items = memory_client.get_active_memory()
|
||||
matched = _find_matching_item(text, active_items)
|
||||
if matched:
|
||||
mtype = matched.get("memory_type", "")
|
||||
exp = matched.get("expires_at")
|
||||
if mtype == "temporary" and exp:
|
||||
status_msg = f"\n\n🕒 Schon temporär gespeichert bis {_dt.fromtimestamp(exp).strftime('%d.%m.%Y')}."
|
||||
elif mtype == "permanent":
|
||||
status_msg = "\n\n📌 Schon dauerhaft gespeichert."
|
||||
else:
|
||||
status_msg = "\n\n✅ Bereits gespeichert."
|
||||
await update.message.reply_text(answer[:3900] + status_msg, reply_markup=KEYBOARD)
|
||||
sent = True
|
||||
|
||||
elif suggest["type"] == "new_candidate":
|
||||
candidates_after = memory_client.get_candidates()
|
||||
new_candidates = [c for c in candidates_after if c["id"] not in candidates_before]
|
||||
if new_candidates:
|
||||
c = new_candidates[0]
|
||||
type_icon = "🕒" if c.get("memory_type") == "temporary" else "📌" if c.get("memory_type") == "permanent" else "📝"
|
||||
await update.message.reply_text(
|
||||
answer[:4000] + "\n\n" + type_icon + " " + c["content"],
|
||||
reply_markup=_memory_buttons(c["id"]),
|
||||
)
|
||||
sent = True
|
||||
|
||||
if not sent and suggest["type"] is None:
|
||||
from datetime import datetime as _dt
|
||||
matched_active = _find_matching_item(text, memory_client.get_active_memory())
|
||||
if matched_active:
|
||||
mtype = matched_active.get("memory_type", "")
|
||||
exp = matched_active.get("expires_at")
|
||||
if mtype == "temporary" and exp:
|
||||
status_msg = f"\n\n🕒 Schon temporär gespeichert bis {_dt.fromtimestamp(exp).strftime('%d.%m.%Y')}."
|
||||
elif mtype == "permanent":
|
||||
status_msg = "\n\n📌 Schon dauerhaft gespeichert."
|
||||
else:
|
||||
status_msg = "\n\n✅ Bereits gespeichert."
|
||||
await update.message.reply_text(answer[:3900] + status_msg, reply_markup=KEYBOARD)
|
||||
sent = True
|
||||
else:
|
||||
matched_cand = _find_matching_item(text, memory_client.get_candidates())
|
||||
if matched_cand:
|
||||
log.info("Fallback-Match: Kandidat ID=%s", matched_cand["id"])
|
||||
await update.message.reply_text(
|
||||
answer[:4000] + "\n\n" + _format_candidate(matched_cand),
|
||||
reply_markup=_memory_buttons(matched_cand["id"]),
|
||||
)
|
||||
sent = True
|
||||
|
||||
if not sent:
|
||||
await update.message.reply_text(answer[:4000], reply_markup=KEYBOARD)
|
||||
await update.message.reply_text(answer[:4000], reply_markup=KEYBOARD)
|
||||
except Exception as e:
|
||||
log.exception("Fehler bei Freitext")
|
||||
await update.message.reply_text(f"Fehler: {e}")
|
||||
|
|
@ -561,7 +455,6 @@ def main():
|
|||
app.add_handler(CommandHandler("check", cmd_check))
|
||||
app.add_handler(CommandHandler("feeds", cmd_feeds))
|
||||
app.add_handler(CommandHandler("memory", cmd_memory))
|
||||
app.add_handler(CallbackQueryHandler(handle_memory_callback, pattern=r"^mem_"))
|
||||
app.add_handler(MessageHandler(filters.VOICE, handle_voice))
|
||||
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue