lct-07 и lct-19: звонящий на LLM, офлайн-таблица, предгенерация
Что можно сказать, решает слот-автомат, а не модель: в промпт попадают только раскрытые факты. Иначе LLM услужливо назовёт адрес, которого не спрашивали. Промпты лежат файлами — их правит тот, кто ведёт занятия. Отказ провайдера не роняет занятие: звонящий откатывается на заготовки. Молчащий звонящий хуже шаблонной фразы. Офлайн — таблица, а не локальная модель. Ветвление уже делает слот-автомат, поэтому таблица индексируется парой «событие × настроение» и выходит небольшой: для пожарного сценария 62 реплики, около 36 секунд генерации. Формулировки в ней от облачной модели, а задержка на занятии нулевая — локальная 3–4B дала бы формулировки хуже и за 2.5 секунды. Найдено на бесплатной модели OpenRouter: рассуждающие модели тратят сотни токенов на размышление и при малом бюджете возвращают пустой content с finish_reason=length. Пустой ответ теперь честный отказ, бюджет токенов поднят, рассуждение из ответа исключается. Живые запросы к LLM вынесены из общего прогона: они требуют сети, а рассуждающая модель отвечает десятками секунд.
This commit is contained in:
parent
34eb88270e
commit
2ceb26f2cd
19 changed files with 888 additions and 26 deletions
73
backend/scripts/llm_check.py
Normal file
73
backend/scripts/llm_check.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
"""make llm-check: один запрос к LLM, чтобы убедиться, что ключ и адрес рабочие.
|
||||
|
||||
Ключ берётся из backend/.env и никуда не печатается. Запускать там, где есть
|
||||
сеть до провайдера: из WSL под VPN российские адреса недоступны.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from app.config import get_settings # noqa: E402
|
||||
|
||||
|
||||
def main() -> int:
|
||||
settings = get_settings()
|
||||
if not settings.llm_api_key:
|
||||
print("в backend/.env нет LLM_API_KEY")
|
||||
return 1
|
||||
|
||||
print(f"адрес: {settings.llm_base_url}")
|
||||
print(f"модель: {settings.llm_model_caller}")
|
||||
|
||||
body = {
|
||||
"model": settings.llm_model_caller,
|
||||
"messages": [
|
||||
{"role": "system", "content": "Ты звонящий в службу 112, у тебя горит балкон. "
|
||||
"Ответь одной короткой фразой, в панике."},
|
||||
{"role": "user", "content": "Служба 112, что у вас случилось?"},
|
||||
],
|
||||
"max_tokens": 60,
|
||||
"temperature": 0.8,
|
||||
}
|
||||
|
||||
started = time.monotonic()
|
||||
try:
|
||||
response = httpx.post(
|
||||
f"{settings.llm_base_url}/chat/completions",
|
||||
headers={"Authorization": f"Bearer {settings.llm_api_key}"},
|
||||
json=body,
|
||||
timeout=60,
|
||||
)
|
||||
except httpx.HTTPError as exc:
|
||||
print(f"\nсеть: {type(exc).__name__} — до провайдера не достучались.")
|
||||
print("Проверь, что российские адреса идут мимо VPN.")
|
||||
return 2
|
||||
|
||||
elapsed = time.monotonic() - started
|
||||
print(f"\nHTTP {response.status_code}, {elapsed:.2f} с")
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print("ответ модели:", data["choices"][0]["message"]["content"].strip())
|
||||
usage = data.get("usage", {})
|
||||
print("токены:", usage.get("prompt_tokens"), "→", usage.get("completion_tokens"))
|
||||
print("\nКЛЮЧ РАБОТАЕТ")
|
||||
return 0
|
||||
|
||||
print("ответ сервера:", response.text[:400])
|
||||
if response.status_code in (401, 403):
|
||||
print("\nСеть в порядке, но ключ не принят: возможно, его нужно менять "
|
||||
"на временный токен — сверься с консолью Cloud.ru.")
|
||||
elif response.status_code == 404:
|
||||
print("\nКлюч принят, но такой модели нет: проверь LLM_MODEL_CALLER.")
|
||||
return 3
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Loading…
Reference in a new issue