From 2a12be310d1441449a9ad06723c26581e93962ff Mon Sep 17 00:00:00 2001 From: Eternity <61316157+myhMARS@users.noreply.github.com> Date: Mon, 12 Jan 2026 12:16:04 +0800 Subject: [PATCH] Feature/memory work (#68) * feat(memory): add conversation title to conversation list response for frontend display * feat(memory): optimize conversation retrieval, enable working memory to return conversation question summaries * fix(memory): fix conversation re-generation logic * style(desc): improve description of get_conversation function --- .../controllers/memory_working_controller.py | 16 ++++-- api/app/models/conversation_model.py | 2 +- api/app/schemas/conversation_schema.py | 2 +- api/app/services/conversation_service.py | 53 ++++++++++++------- .../prompt/conversation_summary_system.jinja2 | 8 +-- 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/api/app/controllers/memory_working_controller.py b/api/app/controllers/memory_working_controller.py index b11b3685..dfd64044 100644 --- a/api/app/controllers/memory_working_controller.py +++ b/api/app/controllers/memory_working_controller.py @@ -2,14 +2,13 @@ import uuid from fastapi import APIRouter, Depends from sqlalchemy.orm import Session -from app.core.response_utils import success, fail from app.core.logging_config import get_api_logger +from app.core.response_utils import success from app.db import get_db from app.dependencies import get_current_user from app.models import User from app.schemas.response_schema import ApiResponse -from app.services import conversation_service from app.services.conversation_service import ConversationService api_logger = get_api_logger() @@ -54,8 +53,7 @@ def get_conversations( """ conversation_service = ConversationService(db) conversations = conversation_service.get_user_conversations( - group_id, - current_user.current_workspace_id + group_id ) return success(data=[ { @@ -88,9 +86,17 @@ def get_messages( - Logging can be added for audit and debugging. """ conversation_service = ConversationService(db) - messages = conversation_service.get_conversation_history( + messages_obj = conversation_service.get_messages( conversation_id, ) + messages = [ + { + "role": message.role, + "content": message.content, + "created_at": int(message.created_at.timestamp() * 1000), + } + for message in messages_obj + ] return success(data=messages, msg="get conversation history success") diff --git a/api/app/models/conversation_model.py b/api/app/models/conversation_model.py index 9e222610..4011247f 100644 --- a/api/app/models/conversation_model.py +++ b/api/app/models/conversation_model.py @@ -65,9 +65,9 @@ class ConversationDetail(Base): conversation_id = Column(UUID(as_uuid=True), ForeignKey("conversations.id")) theme = Column(String, comment="会话主题") - theme_intro = Column(String, comment="主题介绍") summary = Column(String, comment="会话摘要") takeaways = Column(JSON, comment="会话要点") + question = Column(JSON, comment="用户问题") info_score = Column(Integer, comment="会话信息量评分") diff --git a/api/app/schemas/conversation_schema.py b/api/app/schemas/conversation_schema.py index e119bb4a..6ec9b9b6 100644 --- a/api/app/schemas/conversation_schema.py +++ b/api/app/schemas/conversation_schema.py @@ -89,7 +89,7 @@ class ChatResponse(BaseModel): # ---------- Conversation Summary Schemas ---------- class ConversationOut(BaseModel): theme: str - theme_intro: str + question: list[str] summary: str takeaways: list[str] info_score: int diff --git a/api/app/services/conversation_service.py b/api/app/services/conversation_service.py index 113fe258..3695a222 100644 --- a/api/app/services/conversation_service.py +++ b/api/app/services/conversation_service.py @@ -118,25 +118,22 @@ class ConversationService: def get_user_conversations( self, - user_id: uuid.UUID, - workspace_id: uuid.UUID, + user_id: uuid.UUID ) -> list[Conversation]: """ - Retrieve recent conversations for a specific user within a workspace. + Retrieve recent conversations for a specific user This method delegates persistence logic to the repository layer and applies service-level defaults (e.g. recent conversation limit). Args: user_id (uuid.UUID): Unique identifier of the user. - workspace_id (uuid.UUID): Workspace scope for the query. Returns: list[Conversation]: A list of recent conversation entities. """ conversations = self.conversation_repo.get_conversation_by_user_id( user_id, - workspace_id, limit=10 ) return conversations @@ -465,11 +462,17 @@ class ConversationService: conversation = self.get_conversation( conversation_id=conversation_id, ) - if conversation_detail: + if not conversation: + raise BusinessException("Conversation not found", BizCode.INVALID_CONVERSATION) + is_stable = ( + conversation.updated_at + and datetime.now() - conversation.updated_at > timedelta(days=1) + ) + if conversation_detail and is_stable: logger.info(f"Conversation detail found in repository for conversation_id={conversation_id}") return ConversationOut( theme=conversation_detail.theme, - theme_intro=conversation_detail.theme_intro, + question=conversation_detail.question if conversation_detail.question else [], summary=conversation_detail.summary, takeaways=conversation_detail.takeaways, info_score=conversation_detail.info_score, @@ -526,6 +529,7 @@ class ConversationService: language=language, conversation=str(conversation_messages) ) + messages = [ (RoleType.SYSTEM, rendered_system_message), (RoleType.USER, rendered_user_message), @@ -547,28 +551,37 @@ class ConversationService: summary = result.get('summary', "") theme = result.get('theme', "") - theme_intro = result.get("theme_intro", "") + question = result.get("question") or [] takeaways = result.get("takeaways") or [] info_score = result.get("info_score", 50) - if datetime.now() - conversation.updated_at > timedelta(days=1): - logger.info(f"Updating conversation detail in DB for conversation_id={conversation_id}") - conversation_detail = ConversationDetail( - conversation_id=conversation.id, - summary=summary, - theme=theme, - theme_intro=theme_intro, - takeaways=takeaways, - info_score=info_score - ) - self.conversation_repo.add_conversation_detail(conversation_detail) + if not is_stable: + if not conversation_detail: + logger.info(f"Creating conversation detail in DB for conversation_id={conversation_id}") + conversation_detail = ConversationDetail( + conversation_id=conversation.id, + summary=summary, + theme=theme, + question=question, + takeaways=takeaways, + info_score=info_score + ) + self.conversation_repo.add_conversation_detail(conversation_detail) + else: + logger.info(f"Updating conversation detail in DB for conversation_id={conversation_id}") + conversation_detail.summary = summary + conversation_detail.theme = theme + conversation_detail.question = question + conversation_detail.takeaways = takeaways + conversation_detail.info_score = info_score self.db.commit() self.db.refresh(conversation_detail) + logger.info(f"Returning conversation summary for conversation_id={conversation_id}") conversation_out = ConversationOut( theme=theme, - theme_intro=theme_intro, + question=question, summary=summary, takeaways=takeaways, info_score=info_score diff --git a/api/app/services/prompt/conversation_summary_system.jinja2 b/api/app/services/prompt/conversation_summary_system.jinja2 index 256ae4bf..2947c237 100644 --- a/api/app/services/prompt/conversation_summary_system.jinja2 +++ b/api/app/services/prompt/conversation_summary_system.jinja2 @@ -33,18 +33,18 @@ You are a professional dialogue content summarizer, specializing in extracting c - Language: Please strictly output content in the language specified by the tag. - Structure: JSON object with five fields,: 1. `theme`: A concise phrase describing the conversation’s core topic (e.g., "inquiry about delivery time rules"); - 2. `theme_intro`: A brief explanation of the conversation’s core theme to clarify its specific scope and focus (e.g., "The conversation focuses on the user's inquiry about delivery time standards for regular and remote areas"); - 3. `summary`: A single sentence including "user request + AI response + interaction logic" (≤100 words); - 4. `takeaways`: A list of brief bullet-point takeaways summarizing the key points from the conversation (e.g., ["User clarified delivery time differences between regular and remote areas"]). + 2. `summary`: A single sentence including "user request + AI response + interaction logic" (≤150 words); + 3. `takeaways`: A list of brief bullet-point takeaways summarizing the key points from the conversation (e.g., ["User clarified delivery time differences between regular and remote areas"]). + 4. `question`: A list of brief declarative statements summarizing the pitfalls the user encountered during the current conversation.Return an empty list if none are present. 5. `info_score`: Numerical score (0–100) representing conversation information richness. - Language Style: Concise, objective, conversational (avoid overly formal terms). # Example JSON Output { "theme": string, - "theme_intro": string, "summary": string, "takeaways": array[string], + "question": array[string] "info_score": 85 } {% endraw %}