feat(memory): complete M.2-M.5 memory upgrade phases with tests
- M.2: ForgettingCurve, MemoryDecay, MemoryReinforcement (selective forgetting) - M.3: DailyDigestGenerator, ReminderScheduler, ProactiveInformer (proactive reminders) - M.4: MemoryExtractor with LLM-based memory extraction from conversations - M.5: MemoryRecallInjector with token budget control for prompt injection - All phases include comprehensive unit tests (109 tests passing) - Updated checklist.md to mark all tasks complete
This commit is contained in:
@@ -331,6 +331,40 @@ class AgentService:
|
||||
async with async_session() as session:
|
||||
await memory_service.try_auto_summarize(session, user_id, conversation_id)
|
||||
|
||||
# ———— M.4: 主动记忆提取 ————
|
||||
async def _extract_memories_background(self, user_id: str, conversation_id: str) -> None:
|
||||
"""Background task to extract memories from conversation after response."""
|
||||
from app.services.memory.memory_extractor import MemoryExtractor
|
||||
from sqlalchemy import select
|
||||
from app.models.conversation import Message
|
||||
|
||||
try:
|
||||
async with async_session() as db:
|
||||
# Load last 10 messages from conversation
|
||||
result = await db.execute(
|
||||
select(Message)
|
||||
.where(Message.conversation_id == conversation_id)
|
||||
.order_by(Message.created_at.desc())
|
||||
.limit(10)
|
||||
)
|
||||
messages = list(result.scalars().all())
|
||||
|
||||
if len(messages) < 2:
|
||||
return
|
||||
|
||||
extractor = MemoryExtractor()
|
||||
new_memories = await extractor.extract_from_conversation(
|
||||
db, user_id, conversation_id, messages
|
||||
)
|
||||
|
||||
if new_memories:
|
||||
await extractor.save_memories(db, user_id, conversation_id, new_memories)
|
||||
logger.info(
|
||||
f"[MemoryExtractor] Extracted {len(new_memories)} new memories from conversation {conversation_id}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception(f"[MemoryExtractor] Extraction failed: {e}")
|
||||
|
||||
def _build_progress_event(
|
||||
self,
|
||||
stage: str,
|
||||
@@ -543,6 +577,13 @@ class AgentService:
|
||||
self.db, user_id, conversation_id, message
|
||||
)
|
||||
|
||||
# M.5: Inject recall memories into context (before LLM call)
|
||||
from app.services.memory.recall_injector import MemoryRecallInjector
|
||||
|
||||
recall_ctx = await MemoryRecallInjector().build_context(self.db, user_id, message)
|
||||
if recall_ctx:
|
||||
memory_ctx = f"{memory_ctx}\n{recall_ctx}" if memory_ctx else recall_ctx
|
||||
|
||||
assistant_msg = Message(
|
||||
conversation_id=conversation_id,
|
||||
role="assistant",
|
||||
@@ -735,6 +776,8 @@ class AgentService:
|
||||
except Exception:
|
||||
logger.exception("save_assistant_message_failed")
|
||||
asyncio.create_task(self._try_auto_summarize_background(user_id, conversation_id))
|
||||
# M.4: Extract memories from conversation
|
||||
asyncio.create_task(self._extract_memories_background(user_id, conversation_id))
|
||||
|
||||
return conversation_id, assistant_msg.id, run_agent()
|
||||
|
||||
@@ -807,6 +850,13 @@ class AgentService:
|
||||
self.db, user_id, conversation_id, message
|
||||
)
|
||||
|
||||
# M.5: Inject recall memories into context (before LLM call)
|
||||
from app.services.memory.recall_injector import MemoryRecallInjector
|
||||
|
||||
recall_ctx = await MemoryRecallInjector().build_context(self.db, user_id, message)
|
||||
if recall_ctx:
|
||||
memory_ctx = f"{memory_ctx}\n{recall_ctx}" if memory_ctx else recall_ctx
|
||||
|
||||
set_current_user(user_id)
|
||||
try:
|
||||
graph = get_agent_graph()
|
||||
|
||||
Reference in New Issue
Block a user