fix: 修复人物画像混入聊天摘要与机器人输出事实的问题
This commit is contained in:
@@ -337,30 +337,11 @@ async def test_text_to_stream_triggers_real_chat_summary_writeback(
|
||||
else None
|
||||
),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
memory_flow_service_module.global_config.memory,
|
||||
"chat_summary_writeback_enabled",
|
||||
True,
|
||||
raising=False,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
memory_flow_service_module.global_config.memory,
|
||||
"chat_summary_writeback_message_threshold",
|
||||
2,
|
||||
raising=False,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
memory_flow_service_module.global_config.memory,
|
||||
"chat_summary_writeback_context_length",
|
||||
10,
|
||||
raising=False,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
memory_flow_service_module.global_config.memory,
|
||||
"person_fact_writeback_enabled",
|
||||
False,
|
||||
raising=False,
|
||||
)
|
||||
integration_config = memory_flow_service_module.global_config.a_memorix.integration
|
||||
monkeypatch.setattr(integration_config, "chat_summary_writeback_enabled", True, raising=False)
|
||||
monkeypatch.setattr(integration_config, "chat_summary_writeback_message_threshold", 2, raising=False)
|
||||
monkeypatch.setattr(integration_config, "chat_summary_writeback_context_length", 10, raising=False)
|
||||
monkeypatch.setattr(integration_config, "person_fact_writeback_enabled", False, raising=False)
|
||||
|
||||
await kernel.initialize()
|
||||
|
||||
|
||||
@@ -5,6 +5,14 @@ import pytest
|
||||
from src.services import memory_flow_service as memory_flow_module
|
||||
|
||||
|
||||
def _fake_global_config(**integration_values):
|
||||
return SimpleNamespace(
|
||||
a_memorix=SimpleNamespace(
|
||||
integration=SimpleNamespace(**integration_values),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_person_fact_parse_fact_list_deduplicates_and_filters_short_items():
|
||||
raw = '["他喜欢猫", "他喜欢猫", "好", "", "他会弹吉他"]'
|
||||
|
||||
@@ -38,6 +46,43 @@ def test_person_fact_resolve_target_person_for_private_chat(monkeypatch):
|
||||
assert person.person_id == "qq:123"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_person_fact_writeback_skips_bot_only_fact_without_user_evidence(monkeypatch):
|
||||
stored_facts: list[tuple[str, str, str]] = []
|
||||
|
||||
class FakePerson:
|
||||
person_id = "person-1"
|
||||
person_name = "测试用户"
|
||||
nickname = "测试用户"
|
||||
is_known = True
|
||||
|
||||
service = memory_flow_module.PersonFactWritebackService.__new__(memory_flow_module.PersonFactWritebackService)
|
||||
service._resolve_target_person = lambda message: FakePerson()
|
||||
|
||||
async def fake_extract_facts(person, reply_text, user_evidence_text):
|
||||
del person, reply_text, user_evidence_text
|
||||
return ["测试用户喜欢辣椒"]
|
||||
|
||||
async def fake_store_person_memory_from_answer(person_name: str, memory_content: str, chat_id: str, **kwargs):
|
||||
del kwargs
|
||||
stored_facts.append((person_name, memory_content, chat_id))
|
||||
|
||||
service._extract_facts = fake_extract_facts
|
||||
monkeypatch.setattr(memory_flow_module, "store_person_memory_from_answer", fake_store_person_memory_from_answer)
|
||||
monkeypatch.setattr(memory_flow_module, "find_messages", lambda **kwargs: [])
|
||||
|
||||
message = SimpleNamespace(
|
||||
processed_plain_text="我记得你喜欢辣椒。",
|
||||
session_id="session-1",
|
||||
reply_to="",
|
||||
session=SimpleNamespace(platform="qq", user_id="bot-1", group_id=""),
|
||||
)
|
||||
|
||||
await service._handle_message(message)
|
||||
|
||||
assert stored_facts == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_chat_summary_writeback_service_triggers_when_threshold_reached(monkeypatch):
|
||||
events: list[tuple[str, object]] = []
|
||||
@@ -45,12 +90,10 @@ async def test_chat_summary_writeback_service_triggers_when_threshold_reached(mo
|
||||
monkeypatch.setattr(
|
||||
memory_flow_module,
|
||||
"global_config",
|
||||
SimpleNamespace(
|
||||
memory=SimpleNamespace(
|
||||
chat_summary_writeback_enabled=True,
|
||||
chat_summary_writeback_message_threshold=3,
|
||||
chat_summary_writeback_context_length=7,
|
||||
)
|
||||
_fake_global_config(
|
||||
chat_summary_writeback_enabled=True,
|
||||
chat_summary_writeback_message_threshold=3,
|
||||
chat_summary_writeback_context_length=7,
|
||||
),
|
||||
)
|
||||
monkeypatch.setattr(memory_flow_module, "count_messages", lambda **kwargs: 5)
|
||||
@@ -94,12 +137,10 @@ async def test_chat_summary_writeback_service_skips_when_threshold_not_reached(m
|
||||
monkeypatch.setattr(
|
||||
memory_flow_module,
|
||||
"global_config",
|
||||
SimpleNamespace(
|
||||
memory=SimpleNamespace(
|
||||
chat_summary_writeback_enabled=True,
|
||||
chat_summary_writeback_message_threshold=6,
|
||||
chat_summary_writeback_context_length=9,
|
||||
)
|
||||
_fake_global_config(
|
||||
chat_summary_writeback_enabled=True,
|
||||
chat_summary_writeback_message_threshold=6,
|
||||
chat_summary_writeback_context_length=9,
|
||||
),
|
||||
)
|
||||
monkeypatch.setattr(memory_flow_module, "count_messages", lambda **kwargs: 5)
|
||||
@@ -135,12 +176,10 @@ async def test_chat_summary_writeback_service_restores_previous_trigger_count(mo
|
||||
monkeypatch.setattr(
|
||||
memory_flow_module,
|
||||
"global_config",
|
||||
SimpleNamespace(
|
||||
memory=SimpleNamespace(
|
||||
chat_summary_writeback_enabled=True,
|
||||
chat_summary_writeback_message_threshold=3,
|
||||
chat_summary_writeback_context_length=7,
|
||||
)
|
||||
_fake_global_config(
|
||||
chat_summary_writeback_enabled=True,
|
||||
chat_summary_writeback_message_threshold=3,
|
||||
chat_summary_writeback_context_length=7,
|
||||
),
|
||||
)
|
||||
monkeypatch.setattr(memory_flow_module, "count_messages", lambda **kwargs: 8)
|
||||
@@ -178,12 +217,10 @@ async def test_chat_summary_writeback_service_falls_back_to_current_count_for_le
|
||||
monkeypatch.setattr(
|
||||
memory_flow_module,
|
||||
"global_config",
|
||||
SimpleNamespace(
|
||||
memory=SimpleNamespace(
|
||||
chat_summary_writeback_enabled=True,
|
||||
chat_summary_writeback_message_threshold=3,
|
||||
chat_summary_writeback_context_length=7,
|
||||
)
|
||||
_fake_global_config(
|
||||
chat_summary_writeback_enabled=True,
|
||||
chat_summary_writeback_message_threshold=3,
|
||||
chat_summary_writeback_context_length=7,
|
||||
),
|
||||
)
|
||||
monkeypatch.setattr(memory_flow_module, "count_messages", lambda **kwargs: 5)
|
||||
|
||||
115
pytests/A_memorix_test/test_person_profile_service.py
Normal file
115
pytests/A_memorix_test/test_person_profile_service.py
Normal file
@@ -0,0 +1,115 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from src.A_memorix.core.utils.person_profile_service import PersonProfileService
|
||||
|
||||
|
||||
class FakeMetadataStore:
|
||||
def __init__(self) -> None:
|
||||
self.snapshots: list[dict] = []
|
||||
|
||||
@staticmethod
|
||||
def get_latest_person_profile_snapshot(person_id: str):
|
||||
del person_id
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_relations(**kwargs):
|
||||
del kwargs
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def get_paragraphs_by_source(source: str):
|
||||
if source == "person_fact:person-1":
|
||||
return [
|
||||
{
|
||||
"hash": "person-fact-1",
|
||||
"content": "测试用户喜欢猫。",
|
||||
"source": source,
|
||||
"metadata": {"source_type": "person_fact"},
|
||||
"created_at": 2.0,
|
||||
"updated_at": 2.0,
|
||||
}
|
||||
]
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def get_paragraph(hash_value: str):
|
||||
if hash_value == "chat-summary-1":
|
||||
return {
|
||||
"hash": hash_value,
|
||||
"content": "机器人建议测试用户以后叫星灯。",
|
||||
"source": "chat_summary:session-1",
|
||||
"metadata": {"source_type": "chat_summary"},
|
||||
"word_count": 1,
|
||||
}
|
||||
if hash_value == "person-fact-1":
|
||||
return {
|
||||
"hash": hash_value,
|
||||
"content": "测试用户喜欢猫。",
|
||||
"source": "person_fact:person-1",
|
||||
"metadata": {"source_type": "person_fact"},
|
||||
"word_count": 1,
|
||||
}
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_paragraph_stale_relation_marks_batch(paragraph_hashes):
|
||||
del paragraph_hashes
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def get_relation_status_batch(relation_hashes):
|
||||
del relation_hashes
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def get_person_profile_override(person_id: str):
|
||||
del person_id
|
||||
return None
|
||||
|
||||
def upsert_person_profile_snapshot(self, **kwargs):
|
||||
self.snapshots.append(kwargs)
|
||||
return {
|
||||
"person_id": kwargs["person_id"],
|
||||
"profile_text": kwargs["profile_text"],
|
||||
"aliases": kwargs["aliases"],
|
||||
"relation_edges": kwargs["relation_edges"],
|
||||
"vector_evidence": kwargs["vector_evidence"],
|
||||
"evidence_ids": kwargs["evidence_ids"],
|
||||
"updated_at": 1.0,
|
||||
"expires_at": kwargs["expires_at"],
|
||||
"source_note": kwargs["source_note"],
|
||||
}
|
||||
|
||||
|
||||
class FakeRetriever:
|
||||
async def retrieve(self, query: str, top_k: int):
|
||||
del query, top_k
|
||||
return [
|
||||
SimpleNamespace(
|
||||
hash_value="chat-summary-1",
|
||||
result_type="paragraph",
|
||||
score=0.95,
|
||||
content="机器人建议测试用户以后叫星灯。",
|
||||
metadata={"source_type": "chat_summary"},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_person_profile_keeps_chat_summary_as_recent_interaction_not_stable_profile():
|
||||
metadata_store = FakeMetadataStore()
|
||||
service = PersonProfileService(metadata_store=metadata_store, retriever=FakeRetriever())
|
||||
service.get_person_aliases = lambda person_id: (["测试用户"], "测试用户", [])
|
||||
|
||||
payload = await service.query_person_profile(person_id="person-1", top_k=6, force_refresh=True)
|
||||
|
||||
assert payload["success"] is True
|
||||
profile_text = payload["profile_text"]
|
||||
stable_section = profile_text.split("近期相关互动:", 1)[0]
|
||||
assert "测试用户喜欢猫" in stable_section
|
||||
assert "星灯" not in stable_section
|
||||
assert "近期相关互动:" in profile_text
|
||||
assert "星灯" in profile_text
|
||||
Reference in New Issue
Block a user