Этап 7: долговременная память
- Факты о пользователе в SQLite, в системном промпте с номерами - Инструменты remember / update_memory / forget, автоматическое запоминание отключается - Цикл вызова инструментов со стримингом (до 5 кругов), проверка и приведение аргументов - Откат без инструментов для моделей, которые их не поддерживают, с одним предупреждением - Действия в журнале, вкладка «Память» в настройках - Фейковый OpenAI-совместимый сервер для тестов, тесты полного цикла через Assistant Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
542389c0f9
commit
84d8aaa848
@@ -0,0 +1,73 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from agr_assistent.core.memory import (
|
||||
SOURCE_AUTO,
|
||||
SOURCE_USER,
|
||||
MemoryStore,
|
||||
memory_prompt,
|
||||
memory_tools,
|
||||
)
|
||||
from agr_assistent.llm.tools import ToolRegistry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def store(tmp_path: Path) -> MemoryStore:
|
||||
memory = MemoryStore(tmp_path / "memory.sqlite3")
|
||||
yield memory
|
||||
memory.close()
|
||||
|
||||
|
||||
def test_facts_survive_reopening(tmp_path: Path) -> None:
|
||||
path = tmp_path / "memory.sqlite3"
|
||||
first = MemoryStore(path)
|
||||
fact = first.add(" Машина пользователя — Škoda Octavia ", SOURCE_USER)
|
||||
first.close()
|
||||
|
||||
second = MemoryStore(path)
|
||||
assert [(f.id, f.text, f.source) for f in second.facts()] == [
|
||||
(fact.id, "Машина пользователя — Škoda Octavia", SOURCE_USER)
|
||||
]
|
||||
second.close()
|
||||
|
||||
|
||||
def test_duplicates_update_and_delete(store: MemoryStore) -> None:
|
||||
fact = store.add("Зовут Лео", SOURCE_AUTO)
|
||||
|
||||
assert store.add("зовут лео", SOURCE_USER).id == fact.id
|
||||
assert store.update(fact.id, "Зовут Леонид").text == "Зовут Леонид"
|
||||
assert store.update(999, "нет такого") is None
|
||||
assert store.delete(fact.id).text == "Зовут Леонид"
|
||||
assert store.delete(fact.id) is None
|
||||
assert store.facts() == []
|
||||
|
||||
|
||||
def test_memory_tools(store: MemoryStore) -> None:
|
||||
tools = ToolRegistry(memory_tools(store))
|
||||
|
||||
saved = tools.execute("remember", '{"text": "Любит кофе", "requested_by_user": false}')
|
||||
assert saved.ok and saved.display == "Запомнил сам: Любит кофе"
|
||||
fact_id = store.facts()[0].id
|
||||
assert store.facts()[0].is_auto
|
||||
|
||||
assert tools.execute("update_memory", f'{{"id": {fact_id}, "text": "Любит чай"}}').ok
|
||||
assert store.facts()[0].text == "Любит чай"
|
||||
|
||||
missing = tools.execute("forget", '{"id": 12345}')
|
||||
assert not missing.ok and "12345" in missing.content
|
||||
assert tools.execute("forget", f'{{"id": "{fact_id}"}}').ok
|
||||
assert store.facts() == []
|
||||
assert not tools.execute("remember", '{"text": " "}').ok
|
||||
|
||||
|
||||
def test_prompt_lists_facts_and_respects_auto_save(store: MemoryStore) -> None:
|
||||
fact = store.add("Живёт в Казани", SOURCE_USER)
|
||||
|
||||
with_auto = memory_prompt(store.facts(), auto_save=True)
|
||||
without_auto = memory_prompt(store.facts(), auto_save=False)
|
||||
|
||||
assert f"[{fact.id}] Живёт в Казани" in with_auto
|
||||
assert "requested_by_user=false" in with_auto
|
||||
assert "requested_by_user=false" not in without_auto
|
||||
assert "пока пусто" in memory_prompt([], auto_save=True)
|
||||
Reference in New Issue
Block a user