Этап 6: отдельные запросы вместо диалога
- Каждый запрос уходит в модель отдельно: системный промпт, текущие дата и время, запрос - Окно уточнений: цепочка обменов с паузами меньше follow_up_seconds (по умолчанию 120 с), не больше трёх последних - Окно чата стало журналом с временем и пометкой уточнений, «Очистить журнал» вместо «Новый диалог» - Настройка окна уточнений в окне настроек - Тесты контекста уточнений и сборки запроса Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
20a666e0d4
commit
542389c0f9
+67
-10
@@ -1,19 +1,76 @@
|
||||
from agr_assistent.core.assistant import build_messages
|
||||
from datetime import datetime
|
||||
|
||||
from agr_assistent.core.context import (
|
||||
MAX_FOLLOW_UP_EXCHANGES,
|
||||
FollowUpContext,
|
||||
build_messages,
|
||||
format_datetime,
|
||||
)
|
||||
|
||||
|
||||
def _history(count: int) -> list[dict[str, str]]:
|
||||
return [{"role": "user", "content": str(i)} for i in range(count)]
|
||||
class FakeClock:
|
||||
def __init__(self) -> None:
|
||||
self.now = 1000.0
|
||||
|
||||
def __call__(self) -> float:
|
||||
return self.now
|
||||
|
||||
|
||||
def test_build_messages_adds_system_prompt_and_trims_history() -> None:
|
||||
messages = build_messages(" be nice ", _history(5), max_history_messages=2)
|
||||
def test_build_messages_adds_date_context_and_request() -> None:
|
||||
context = [{"role": "user", "content": "погода?"}, {"role": "assistant", "content": "солнце"}]
|
||||
|
||||
messages = build_messages(" будь краток ", context, "а завтра?", datetime(2026, 9, 17, 4, 5))
|
||||
|
||||
assert messages == [
|
||||
{"role": "system", "content": "be nice"},
|
||||
{"role": "user", "content": "3"},
|
||||
{"role": "user", "content": "4"},
|
||||
{"role": "system", "content": "будь краток\n\nСейчас четверг, 17 сентября 2026 года, 04:05."},
|
||||
*context,
|
||||
{"role": "user", "content": "а завтра?"},
|
||||
]
|
||||
|
||||
|
||||
def test_build_messages_without_limit_or_prompt() -> None:
|
||||
assert build_messages("", _history(3), max_history_messages=0) == _history(3)
|
||||
def test_format_datetime() -> None:
|
||||
assert format_datetime(datetime(2026, 3, 1, 23, 59)) == "воскресенье, 1 марта 2026 года, 23:59"
|
||||
|
||||
|
||||
def test_follow_up_is_available_only_within_window() -> None:
|
||||
clock = FakeClock()
|
||||
context = FollowUpContext(window_seconds=120, clock=clock)
|
||||
|
||||
context.remember("погода?", "солнце")
|
||||
clock.now += 119
|
||||
assert [m["content"] for m in context.recent()] == ["погода?", "солнце"]
|
||||
|
||||
clock.now += 2
|
||||
assert context.recent() == []
|
||||
|
||||
|
||||
def test_follow_up_chain_extends_window_and_keeps_last_exchanges() -> None:
|
||||
clock = FakeClock()
|
||||
context = FollowUpContext(window_seconds=60, clock=clock)
|
||||
|
||||
for index in range(MAX_FOLLOW_UP_EXCHANGES + 2):
|
||||
context.remember(f"вопрос {index}", f"ответ {index}")
|
||||
clock.now += 50 # каждый следующий вопрос успевает в окно предыдущего
|
||||
|
||||
requests = [m["content"] for m in context.recent() if m["role"] == "user"]
|
||||
assert requests == [f"вопрос {i}" for i in range(2, MAX_FOLLOW_UP_EXCHANGES + 2)]
|
||||
|
||||
|
||||
def test_chain_restarts_after_pause() -> None:
|
||||
clock = FakeClock()
|
||||
context = FollowUpContext(window_seconds=60, clock=clock)
|
||||
|
||||
context.remember("старый", "ответ")
|
||||
clock.now += 61
|
||||
context.remember("новый", "ответ")
|
||||
|
||||
assert [m["content"] for m in context.recent()] == ["новый", "ответ"]
|
||||
|
||||
|
||||
def test_zero_window_disables_follow_ups_and_empty_replies_are_ignored() -> None:
|
||||
clock = FakeClock()
|
||||
assert FollowUpContext(window_seconds=0, clock=clock).recent() == []
|
||||
|
||||
context = FollowUpContext(window_seconds=60, clock=clock)
|
||||
context.remember("вопрос", " ")
|
||||
assert context.recent() == []
|
||||
|
||||
Reference in New Issue
Block a user