feat: schedule_hours-Backstop, zentrale Datum/Uhrzeit, Filler-Timing
1) schedule_hours-Nudge: search_backstop erkennt jetzt auch Oeffnungszeiten,
"hat X geoeffnet", Notdienst und Fahrplaene/naechste Abfahrt (de/en/nl/fr) und
erzwingt dafuer die Suche. +6 Offline-Tests.
2) Zentrale Datum-/Uhrzeit-Auskunft (app/core/clock.py, now_context()): jedes
Modul kann das aktuelle Datum + Uhrzeit (lokale TZ, Wochentag) erfragen.
In die System-Prompts aller antwortenden LLMs injiziert (ToolCallingLLM,
OpenRouter, lokal) -> relative Zeitangaben ("heute", "morgen", "in 3 Stunden",
"naechsten Montag") werden aufgeloest. Live: "Welcher Wochentag/Datum?" ->
korrekt OHNE Web-Suche.
3) Filler-Timing: Mindestabstand 10 s zwischen Opening und Geduldssaetzen
(FILLER_PATIENCE_INTERVAL 4->10). Sobald die Antwort vorliegt, wird ein
anstehender Filler/Geduldssatz NICHT mehr ausgegeben (answer_started-Gate).
Tests: 318 gruen. Doc §5.4/§5.7/§8.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
48a79da12f
commit
b70d6f364a
9 changed files with 80 additions and 19 deletions
|
|
@ -2,6 +2,7 @@ from collections.abc import AsyncIterator
|
|||
|
||||
import httpx
|
||||
|
||||
from app.core import clock
|
||||
from app.providers.llm.base import (
|
||||
LLMProvider,
|
||||
lang_instruction,
|
||||
|
|
@ -41,6 +42,7 @@ class LocalOpenAICompatibleLLM(LLMProvider):
|
|||
system_parts: list[str] = []
|
||||
if self.system_prompt:
|
||||
system_parts.append(self.system_prompt)
|
||||
system_parts.append(clock.now_context()) # aktuelles Datum/Uhrzeit (relative Angaben auflösen)
|
||||
rest: list[dict] = []
|
||||
for msg in history or []:
|
||||
if msg.get("role") == "system":
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from collections.abc import AsyncIterator
|
|||
|
||||
import httpx
|
||||
|
||||
from app.core import clock
|
||||
from app.providers.llm.base import (
|
||||
LLMProvider,
|
||||
lang_instruction,
|
||||
|
|
@ -100,10 +101,10 @@ class OpenRouterLLMProvider(LLMProvider):
|
|||
if not text or not text.strip():
|
||||
raise ValueError("LLM input text is empty")
|
||||
|
||||
system_content = SYSTEM_PROMPT
|
||||
system_content = f"{SYSTEM_PROMPT}\n\n{clock.now_context()}"
|
||||
instr = lang_instruction(language)
|
||||
if instr:
|
||||
system_content = f"{SYSTEM_PROMPT}\n\n{instr}"
|
||||
system_content = f"{system_content}\n\n{instr}"
|
||||
|
||||
messages = [{"role": "system", "content": system_content}]
|
||||
if history:
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ import asyncio
|
|||
import json
|
||||
import logging
|
||||
from collections.abc import AsyncIterator
|
||||
from datetime import date
|
||||
|
||||
import httpx
|
||||
|
||||
from app.core import clock
|
||||
from app.metrics import metrics
|
||||
from app.pipeline.search_backstop import should_force_search
|
||||
from app.providers.llm.base import LLMProvider, lang_instruction, with_lang_reminder
|
||||
|
|
@ -75,7 +75,7 @@ WEB_SEARCH_TOOL = {
|
|||
# zu früh als zu spät) — das verschiebt im Zweifel Richtung „suchen", die sichere
|
||||
# Seite (höherer Recall). Im Eval mit genau dieser Formulierung validiert.
|
||||
_TRIGGER_AND_PRIORITY = (
|
||||
"Your knowledge was last updated around {cutoff}. Today is {today}. "
|
||||
"Your knowledge was last updated around {cutoff}. "
|
||||
"When a question could require information newer than {cutoff} that you "
|
||||
"cannot reason out yourself, call web_search before answering. Never call "
|
||||
"web_search merely to find the current time or today's date — you already "
|
||||
|
|
@ -86,8 +86,7 @@ _TRIGGER_AND_PRIORITY = (
|
|||
|
||||
|
||||
def _system_prompt(cutoff: str, language: str | None) -> str:
|
||||
today = date.today().isoformat()
|
||||
parts = [PERSONA_PROMPT, _TRIGGER_AND_PRIORITY.format(cutoff=cutoff, today=today)]
|
||||
parts = [PERSONA_PROMPT, clock.now_context(), _TRIGGER_AND_PRIORITY.format(cutoff=cutoff)]
|
||||
instr = lang_instruction(language)
|
||||
if instr:
|
||||
parts.append(instr)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue