Files
2026-08-21 16:00:13 +08:00

101 lines
3.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""小周天每轮记忆候选 + 反思抽取测试。
高置信度(≥0.7)低敏感度记忆候选直接落地到 memories 表而非 MemoryCandidate。
"""
import pytest
from sqlalchemy import select
from app.application.reflection import (
drain_pending_reflections,
start_reflection_after_turn,
)
from app.db.session import AsyncSessionLocal
from app.models.memory import Memory
from app.repositories import candidates as candidates_repo
from app.services.characters import get_active_character_model
_REFLECTION_RESPONSE = (
'{"memories":[{"type":"preference","title":"喜欢简洁","content":"用户偏好简洁",'
'"importance":0.8,"sensitivity":"low","confidence":0.7}],'
'"reflection":"本轮应更主动确认用户意图。"}'
)
@pytest.fixture
def mock_reflection_chat(monkeypatch: pytest.MonkeyPatch) -> None:
async def fake_chat(*_args, **_kwargs):
return _REFLECTION_RESPONSE
monkeypatch.setattr("app.application.reflection._call_model", fake_chat)
@pytest.mark.asyncio
async def test_reflection_extracts_memory_and_writes_reflection(
mock_reflection_chat, # noqa: ARG001
) -> None:
async with AsyncSessionLocal() as session:
character = await get_active_character_model(session)
start_reflection_after_turn(character.id, "session_test", "你好", "你好,我是魂镜。")
await drain_pending_reflections()
async with AsyncSessionLocal() as session:
# 高置信度记忆直接落地到 memories 表(非 MemoryCandidate)
auto_mem = (
await session.execute(
select(Memory).where(
Memory.character_id == character.id,
Memory.type == "preference",
Memory.source == "reflection",
)
)
).scalar_one_or_none()
# 反思文本也落地为 memories 表
ref_mem = (
await session.execute(
select(Memory).where(
Memory.character_id == character.id,
Memory.type == "reflection",
)
)
).scalar_one_or_none()
assert auto_mem is not None
assert auto_mem.title == "喜欢简洁"
assert ref_mem is not None
assert ref_mem.source == "reflection"
assert ref_mem.status == "active" # Memory 无 status 字段,用 default
assert "确认用户意图" in ref_mem.content
@pytest.mark.asyncio
async def test_reflection_without_memories_only_writes_reflection(
monkeypatch: pytest.MonkeyPatch,
) -> None:
async def fake_chat(*_args, **_kwargs):
return '{"memories":[],"reflection":"只反思不抽记忆。"}'
monkeypatch.setattr("app.application.reflection._call_model", fake_chat)
async with AsyncSessionLocal() as session:
character = await get_active_character_model(session)
start_reflection_after_turn(character.id, "session_test", "hi", "你好。")
await drain_pending_reflections()
async with AsyncSessionLocal() as session:
cands = await candidates_repo.list_memory_candidates(session, character.id, source="cycle")
ref_mem = (
await session.execute(
select(Memory).where(
Memory.character_id == character.id,
Memory.type == "reflection",
)
)
).scalar_one_or_none()
# 无记忆候选(空 memories 数组),只有反思
assert len(cands) == 0
assert ref_mem is not None
assert "只反思" in ref_mem.content