Explorar el Código

测试了Chat模式的对轮对话功能,统一了缓存的创建机制,统一了guest用户机制,修复了相关的bug.

wangxq hace 1 semana
padre
commit
c64f630021

+ 32 - 1
agent/citu_agent.py

@@ -236,8 +236,13 @@ class CituLangGraphAgent:
             # 步骤3:生成摘要(可通过配置控制,仅在有数据时生成)
             if ENABLE_RESULT_SUMMARY and query_result.get('row_count', 0) > 0:
                 print(f"[DATABASE_AGENT] 步骤3:生成摘要")
+                
+                # 重要:提取原始问题用于摘要生成,避免历史记录循环嵌套
+                original_question = self._extract_original_question(question)
+                print(f"[DATABASE_AGENT] 原始问题: {original_question}")
+                
                 summary_result = generate_summary.invoke({
-                    "question": question,
+                    "question": original_question,  # 使用原始问题而不是enhanced_question
                     "query_result": query_result,
                     "sql": sql
                 })
@@ -539,6 +544,32 @@ class CituLangGraphAgent:
             routing_mode=QUESTION_ROUTING_MODE
         )
     
+    def _extract_original_question(self, question: str) -> str:
+        """
+        从enhanced_question中提取原始问题
+        
+        Args:
+            question: 可能包含上下文的问题
+            
+        Returns:
+            str: 原始问题
+        """
+        try:
+            # 检查是否为enhanced_question格式
+            if "\n[CONTEXT]\n" in question and "\n[CURRENT]\n" in question:
+                # 提取[CURRENT]标签后的内容
+                current_start = question.find("\n[CURRENT]\n")
+                if current_start != -1:
+                    original_question = question[current_start + len("\n[CURRENT]\n"):].strip()
+                    return original_question
+            
+            # 如果不是enhanced_question格式,直接返回原问题
+            return question.strip()
+            
+        except Exception as e:
+            print(f"[WARNING] 提取原始问题失败: {str(e)}")
+            return question.strip()
+
     def health_check(self) -> Dict[str, Any]:
         """健康检查"""
         try:

+ 1 - 2
agent/tools/sql_execution.py

@@ -127,8 +127,7 @@ def execute_sql(sql: str, max_rows: int = None) -> Dict[str, Any]:
                 "columns": columns,
                 "row_count": len(rows),
                 "total_row_count": total_rows,
-                "is_limited": total_rows > max_rows,
-                "sql": sql
+                "is_limited": total_rows > max_rows
             },
             "message": f"查询成功,共 {total_rows} 行数据"
         }

+ 5 - 9
app_config.py

@@ -167,15 +167,12 @@ QUESTION_ROUTING_MODE = "hybrid"
 # ==================== Redis对话管理配置 ====================
 
 # 对话上下文配置
-CONVERSATION_CONTEXT_COUNT = 5          # 传递给LLM的上下文消息条数
+CONVERSATION_CONTEXT_COUNT = 2          # 传递给LLM的上下文消息条数
 CONVERSATION_MAX_LENGTH = 20            # 单个对话最大消息数
 USER_MAX_CONVERSATIONS = 5              # 用户最大对话数
 
 # 用户管理配置
-DEFAULT_ANONYMOUS_USER_PREFIX = "guest" # 匿名用户前缀
-GUEST_USER_TTL = 7 * 24 * 3600         # guest用户数据保存7天
-MAX_GUEST_CONVERSATIONS = 3             # guest用户最多3个对话
-MAX_REGISTERED_CONVERSATIONS = 10       # 注册用户最多10个对话
+DEFAULT_ANONYMOUS_USER = "guest"        # 匿名用户统一ID
 
 # Redis配置
 REDIS_HOST = "localhost"
@@ -187,8 +184,7 @@ REDIS_PASSWORD = None
 ENABLE_CONVERSATION_CONTEXT = True      # 是否启用对话上下文
 ENABLE_QUESTION_ANSWER_CACHE = True     # 是否启用问答结果缓存
 
-# TTL配置(单位:秒)- 修正TTL逻辑
+# TTL配置(单位:秒)
 CONVERSATION_TTL = 7 * 24 * 3600        # 对话保存7天
-USER_CONVERSATIONS_TTL = 7 * 24 * 3600  # 用户对话列表保存7天(与对话TTL一致)
-QUESTION_ANSWER_TTL = 24 * 3600         # 问答结果缓存24小时
-GUEST_USER_TTL = 7 * 24 * 3600         # guest用户数据保存7天
+USER_CONVERSATIONS_TTL = 7 * 24 * 3600  # 用户对话列表保存7天(所有用户统一)
+QUESTION_ANSWER_TTL = 24 * 3600         # 问答结果缓存24小时

+ 119 - 10
citu_app.py

@@ -20,7 +20,8 @@ from common.result import (  # 统一导入所有需要的响应函数
 )
 from app_config import (  # 添加Redis相关配置导入
     USER_MAX_CONVERSATIONS,
-    CONVERSATION_CONTEXT_COUNT
+    CONVERSATION_CONTEXT_COUNT,
+    DEFAULT_ANONYMOUS_USER
 )
 
 # 设置默认的最大返回行数
@@ -465,16 +466,38 @@ def ask_agent():
         # 3. 获取上下文(提前到缓存检查之前)
         context = redis_conversation_manager.get_context(conversation_id)
         
-        # 4. 检查缓存(修正:传入context以实现真正的上下文感知
+        # 4. 检查缓存(新逻辑:放宽使用条件,严控存储条件
         cached_answer = redis_conversation_manager.get_cached_answer(question, context)
         if cached_answer:
             print(f"[AGENT_API] 使用缓存答案")
             
+            # 确定缓存答案的助手回复内容(使用与非缓存相同的优先级逻辑)
+            cached_response_type = cached_answer.get("type", "UNKNOWN")
+            if cached_response_type == "DATABASE":
+                # DATABASE类型:按优先级选择内容
+                if cached_answer.get("response"):
+                    # 优先级1:错误或解释性回复(如SQL生成失败)
+                    assistant_response = cached_answer.get("response")
+                elif cached_answer.get("summary"):
+                    # 优先级2:查询成功的摘要
+                    assistant_response = cached_answer.get("summary")
+                elif cached_answer.get("query_result"):
+                    # 优先级3:构造简单描述
+                    query_result = cached_answer.get("query_result")
+                    row_count = query_result.get("row_count", 0)
+                    assistant_response = f"查询执行完成,共返回 {row_count} 条记录。"
+                else:
+                    # 异常情况
+                    assistant_response = "数据库查询已处理。"
+            else:
+                # CHAT类型:直接使用response
+                assistant_response = cached_answer.get("response", "")
+            
             # 更新对话历史
             redis_conversation_manager.save_message(conversation_id, "user", question)
             redis_conversation_manager.save_message(
                 conversation_id, "assistant", 
-                cached_answer.get("response", ""),
+                assistant_response,
                 metadata={"from_cache": True}
             )
             
@@ -487,7 +510,7 @@ def ask_agent():
             # 使用agent_success_response返回标准格式
             return jsonify(agent_success_response(
                 response_type=cached_answer.get("type", "UNKNOWN"),
-                response_text=cached_answer.get("response", ""),
+                response=cached_answer.get("response", ""),  # 修正:使用response而不是response_text
                 sql=cached_answer.get("sql"),
                 query_result=cached_answer.get("query_result"),
                 summary=cached_answer.get("summary"),
@@ -496,7 +519,7 @@ def ask_agent():
                 classification_info=cached_answer.get("classification_info", {}),
                 conversation_id=conversation_id,
                 user_id=user_id,
-                is_guest_user=user_id.startswith("guest"),
+                is_guest_user=(user_id == DEFAULT_ANONYMOUS_USER),
                 context_used=bool(context),
                 from_cache=True,
                 conversation_status=conversation_status["status"],
@@ -509,7 +532,7 @@ def ask_agent():
         
         # 6. 构建带上下文的问题
         if context:
-            enhanced_question = f"对话历史:\n{context}\n\n当前问题: {question}"
+            enhanced_question = f"\n[CONTEXT]\n{context}\n\n[CURRENT]\n{question}"
             print(f"[AGENT_API] 使用上下文,长度: {len(context)}字符")
         else:
             enhanced_question = question
@@ -540,7 +563,26 @@ def ask_agent():
             summary = agent_result.get("summary")
             execution_path = agent_result.get("execution_path", [])
             classification_info = agent_result.get("classification_info", {})
-            assistant_response = response_text  # 使用response_text作为助手回复
+            
+            # 确定助手回复内容的优先级
+            if response_type == "DATABASE":
+                # DATABASE类型:按优先级选择内容
+                if response_text:
+                    # 优先级1:错误或解释性回复(如SQL生成失败)
+                    assistant_response = response_text
+                elif summary:
+                    # 优先级2:查询成功的摘要
+                    assistant_response = summary
+                elif query_result:
+                    # 优先级3:构造简单描述
+                    row_count = query_result.get("row_count", 0)
+                    assistant_response = f"查询执行完成,共返回 {row_count} 条记录。"
+                else:
+                    # 异常情况
+                    assistant_response = "数据库查询已处理。"
+            else:
+                # CHAT类型:直接使用response
+                assistant_response = response_text
             
             # 保存助手回复
             redis_conversation_manager.save_message(
@@ -552,14 +594,14 @@ def ask_agent():
                 }
             )
             
-            # 缓存成功的答案(修正:缓存完整的agent_result
+            # 缓存成功的答案(新逻辑:只缓存无上下文的问答
             # 直接缓存agent_result,它已经包含所有需要的字段
             redis_conversation_manager.cache_answer(question, agent_result, context)
             
             # 使用agent_success_response的正确方式
             return jsonify(agent_success_response(
                 response_type=response_type,
-                response_text=response_text,
+                response=response_text,  # 修正:使用response而不是response_text
                 sql=sql,
                 query_result=query_result,
                 summary=summary,
@@ -568,7 +610,7 @@ def ask_agent():
                 classification_info=classification_info,
                 conversation_id=conversation_id,
                 user_id=user_id,
-                is_guest_user=user_id.startswith("guest"),
+                is_guest_user=(user_id == DEFAULT_ANONYMOUS_USER),
                 context_used=bool(context),
                 from_cache=False,
                 conversation_status=conversation_status["status"],
@@ -1548,6 +1590,73 @@ def conversation_cleanup():
             response_text="对话清理失败,请稍后重试"
         )), 500
 
+@app.flask_app.route('/api/v0/user/<user_id>/conversations/full', methods=['GET'])
+def get_user_conversations_with_messages(user_id: str):
+    """
+    获取用户的完整对话数据(包含所有消息)
+    一次性返回用户的所有对话和每个对话下的消息历史
+    
+    Args:
+        user_id: 用户ID(路径参数)
+        conversation_limit: 对话数量限制(查询参数,可选,不传则返回所有对话)
+        message_limit: 每个对话的消息数限制(查询参数,可选,不传则返回所有消息)
+    
+    Returns:
+        包含用户所有对话和消息的完整数据
+    """
+    try:
+        # 获取可选参数,不传递时使用None(返回所有记录)
+        conversation_limit = request.args.get('conversation_limit', type=int)
+        message_limit = request.args.get('message_limit', type=int)
+        
+        # 获取用户的对话列表
+        conversations = redis_conversation_manager.get_conversations(user_id, conversation_limit)
+        
+        # 为每个对话获取消息历史
+        full_conversations = []
+        total_messages = 0
+        
+        for conversation in conversations:
+            conversation_id = conversation['conversation_id']
+            
+            # 获取对话消息
+            messages = redis_conversation_manager.get_conversation_messages(
+                conversation_id, message_limit
+            )
+            
+            # 获取对话元数据
+            meta = redis_conversation_manager.get_conversation_meta(conversation_id)
+            
+            # 组合完整数据
+            full_conversation = {
+                **conversation,  # 基础对话信息
+                'meta': meta,    # 对话元数据
+                'messages': messages,  # 消息列表
+                'message_count': len(messages)
+            }
+            
+            full_conversations.append(full_conversation)
+            total_messages += len(messages)
+        
+        return jsonify(success_response(
+            response_text="获取用户完整对话数据成功",
+            data={
+                "user_id": user_id,
+                "conversations": full_conversations,
+                "total_conversations": len(full_conversations),
+                "total_messages": total_messages,
+                "conversation_limit_applied": conversation_limit,
+                "message_limit_applied": message_limit,
+                "query_time": datetime.now().isoformat()
+            }
+        ))
+        
+    except Exception as e:
+        print(f"[ERROR] 获取用户完整对话数据失败: {str(e)}")
+        return jsonify(internal_error_response(
+            response_text="获取用户对话数据失败,请稍后重试"
+        )), 500
+
 
 # 前端JavaScript示例 - 如何维持会话
 """

+ 31 - 57
common/redis_conversation_manager.py

@@ -10,8 +10,7 @@ from app_config import (
     CONVERSATION_CONTEXT_COUNT, CONVERSATION_MAX_LENGTH, USER_MAX_CONVERSATIONS,
     CONVERSATION_TTL, USER_CONVERSATIONS_TTL, QUESTION_ANSWER_TTL,
     ENABLE_CONVERSATION_CONTEXT, ENABLE_QUESTION_ANSWER_CACHE,
-    DEFAULT_ANONYMOUS_USER_PREFIX, MAX_GUEST_CONVERSATIONS, MAX_REGISTERED_CONVERSATIONS,
-    GUEST_USER_TTL
+    DEFAULT_ANONYMOUS_USER
 )
 
 class RedisConversationManager:
@@ -49,13 +48,13 @@ class RedisConversationManager:
                        session_id: Optional[str], request_ip: str,
                        login_user_id: Optional[str] = None) -> str:
         """
-        智能解析用户ID - 修正
+        智能解析用户ID - 统一
         
         Args:
             user_id_from_request: 请求参数中的user_id
-            session_id: 浏览器session_id
+            session_id: 浏览器session_id  
             request_ip: 请求IP地址
-            login_user_id: 从Flask session中获取的登录用户ID(在ask_agent中获取)
+            login_user_id: 从Flask session中获取的登录用户ID
         """
         
         # 1. 优先使用登录用户ID
@@ -68,18 +67,9 @@ class RedisConversationManager:
             print(f"[REDIS_CONV] 使用请求参数user_id: {user_id_from_request}")
             return user_id_from_request
         
-        # 3. 都没有则为匿名用户(guest)
-        if session_id:
-            guest_suffix = hashlib.md5(session_id.encode()).hexdigest()[:8]
-            guest_id = f"{DEFAULT_ANONYMOUS_USER_PREFIX}_{guest_suffix}"
-            print(f"[REDIS_CONV] 生成稳定guest用户: {guest_id}")
-            return guest_id
-        
-        # 4. 最后基于IP的临时guest ID
-        ip_suffix = hashlib.md5(request_ip.encode()).hexdigest()[:8]
-        temp_guest_id = f"{DEFAULT_ANONYMOUS_USER_PREFIX}_temp_{ip_suffix}"
-        print(f"[REDIS_CONV] 生成临时guest用户: {temp_guest_id}")
-        return temp_guest_id
+        # 3. 都没有则为匿名用户(统一为guest)
+        print(f"[REDIS_CONV] 使用匿名用户: {DEFAULT_ANONYMOUS_USER}")
+        return DEFAULT_ANONYMOUS_USER
     
     def resolve_conversation_id(self, user_id: str, conversation_id_input: Optional[str], 
                               continue_conversation: bool) -> tuple[str, dict]:
@@ -264,9 +254,9 @@ class RedisConversationManager:
                     content = msg_data.get("content", "")
                     
                     if role == "user":
-                        context_parts.append(f"用户: {content}")
+                        context_parts.append(f"User: {content}")
                     elif role == "assistant":
-                        context_parts.append(f"助手: {content}")
+                        context_parts.append(f"Assistant: {content}")
                         
                 except json.JSONDecodeError:
                     continue
@@ -349,16 +339,18 @@ class RedisConversationManager:
     # ==================== 智能缓存(修正版)====================
     
     def get_cached_answer(self, question: str, context: str = "") -> Optional[Dict]:
-        """检查问答缓存 - 真正上下文感知版"""
+        """检查问答缓存 - 放宽使用条件,无论是否有上下文都可以查找缓存"""
         if not self.is_available() or not ENABLE_QUESTION_ANSWER_CACHE:
             return None
         
+        # 移除上下文检查,允许任何情况下查找缓存
         try:
-            cache_key = self._get_cache_key(question, context)
-            cached_answer = self.redis_client.get(cache_key)  # 使用独立key而不是hash
+            cache_key = self._get_cache_key(question)
+            cached_answer = self.redis_client.get(cache_key)
             
             if cached_answer:
-                print(f"[REDIS_CONV] 缓存命中: {cache_key}")
+                context_info = "有上下文" if context else "无上下文"
+                print(f"[REDIS_CONV] 缓存命中: {cache_key} ({context_info})")
                 return json.loads(cached_answer)
             
             return None
@@ -368,19 +360,23 @@ class RedisConversationManager:
             return None
     
     def cache_answer(self, question: str, answer: Dict, context: str = ""):
-        """缓存问答结果 - 真正上下文感知版"""
+        """缓存问答结果 - 只缓存无上下文的问答"""
         if not self.is_available() or not ENABLE_QUESTION_ANSWER_CACHE:
             return
         
+        # 新增:如果有上下文,不缓存
+        if context:
+            print(f"[REDIS_CONV] 跳过缓存存储:存在上下文")
+            return
+        
         try:
-            cache_key = self._get_cache_key(question, context)
+            cache_key = self._get_cache_key(question)
             
-            # 添加缓存时间戳和上下文哈希
+            # 添加缓存时间戳
             answer_with_meta = {
                 **answer,
                 "cached_at": datetime.now().isoformat(),
-                "original_question": question,
-                "context_hash": hashlib.md5(context.encode()).hexdigest()[:8] if context else ""
+                "original_question": question
             }
             
             # 使用独立key,每个缓存项单独设置TTL
@@ -395,15 +391,9 @@ class RedisConversationManager:
         except Exception as e:
             print(f"[ERROR] 缓存答案失败: {str(e)}")
     
-    def _get_cache_key(self, question: str, context: str = "") -> str:
-        """生成真正包含上下文的缓存键"""
-        if context and ENABLE_CONVERSATION_CONTEXT:
-            # 使用上下文内容而不是conversation_id
-            cache_input = f"context:{context}\nquestion:{question}"
-        else:
-            cache_input = question
-        
-        normalized = cache_input.strip().lower()
+    def _get_cache_key(self, question: str) -> str:
+        """生成缓存键 - 简化版,只基于问题本身"""
+        normalized = question.strip().lower()
         question_hash = hashlib.md5(normalized.encode('utf-8')).hexdigest()[:16]
         return f"qa_cache:{question_hash}"
     
@@ -412,40 +402,24 @@ class RedisConversationManager:
     def _add_conversation_to_user(self, user_id: str, conversation_id: str):
         """添加对话到用户列表,按时间自动排序"""
         try:
-            # 获取用户类型配置
-            config = self._get_user_type_config(user_id)
-            
             # LPUSH添加到列表头部(最新的)
             self.redis_client.lpush(f"user:{user_id}:conversations", conversation_id)
             
-            # 根据用户类型限制数量
+            # 统一限制数量
             self.redis_client.ltrim(
                 f"user:{user_id}:conversations", 
-                0, config["max_conversations"] - 1
+                0, USER_MAX_CONVERSATIONS - 1
             )
             
-            # 设置TTL
+            # 统一设置TTL
             self.redis_client.expire(
                 f"user:{user_id}:conversations", 
-                config["ttl"]
+                USER_CONVERSATIONS_TTL
             )
             
         except Exception as e:
             print(f"[ERROR] 添加对话到用户列表失败: {str(e)}")
     
-    def _get_user_type_config(self, user_id: str) -> Dict:
-        """根据用户类型返回不同的配置 - 修正版"""
-        if user_id.startswith(DEFAULT_ANONYMOUS_USER_PREFIX):
-            return {
-                "max_conversations": MAX_GUEST_CONVERSATIONS,
-                "ttl": GUEST_USER_TTL  # 使用专门的guest TTL
-            }
-        else:
-            return {
-                "max_conversations": MAX_REGISTERED_CONVERSATIONS,
-                "ttl": USER_CONVERSATIONS_TTL
-            }
-    
     def _update_conversation_meta(self, conversation_id: str):
         """更新对话元信息"""
         try:

+ 484 - 0
docs/Redis对话管理API文档.md

@@ -0,0 +1,484 @@
+# Redis 对话管理 API 文档
+
+## 概述
+
+辞图智能数据问答平台提供了完整的基于 Redis 的对话管理功能,支持多用户、多会话的智能问答场景。本文档详细介绍了所有与 Redis 对话管理相关的 API 接口。
+
+## API 基础信息
+
+- **基础URL**: `http://localhost:8084`
+- **Content-Type**: `application/json`
+- **响应格式**: 标准化的JSON响应格式
+
+## API 列表
+
+### 1. 获取用户对话列表
+
+#### 接口信息
+- **URL**: `/api/v0/user/{user_id}/conversations`
+- **方法**: `GET`
+- **功能**: 获取指定用户的对话列表,按时间倒序排列
+
+#### 请求参数
+
+| 参数名 | 类型 | 位置 | 必填 | 说明 |
+|--------|------|------|------|------|
+| user_id | string | 路径参数 | 是 | 用户ID |
+| limit | integer | 查询参数 | 否 | 最大返回数量,默认为 USER_MAX_CONVERSATIONS |
+
+#### 请求示例
+```http
+GET /api/v0/user/john_doe/conversations?limit=10
+```
+
+#### 响应示例
+```json
+{
+    "success": true,
+    "code": 200,
+    "message": "获取用户对话列表成功",
+    "data": {
+        "user_id": "john_doe",
+        "conversations": [
+            {
+                "conversation_id": "conv_20241201_001",
+                "start_time": "2024-12-01T10:00:00",
+                "last_activity": "2024-12-01T10:30:00",
+                "message_count": 6
+            }
+        ],
+        "total_count": 1
+    }
+}
+```
+
+---
+
+### 2. 获取对话消息历史
+
+#### 接口信息
+- **URL**: `/api/v0/conversation/{conversation_id}/messages`
+- **方法**: `GET`
+- **功能**: 获取特定对话的所有消息历史
+
+#### 请求参数
+
+| 参数名 | 类型 | 位置 | 必填 | 说明 |
+|--------|------|------|------|------|
+| conversation_id | string | 路径参数 | 是 | 对话ID |
+| limit | integer | 查询参数 | 否 | 最大返回消息数量 |
+
+#### 请求示例
+```http
+GET /api/v0/conversation/conv_20241201_001/messages?limit=50
+```
+
+#### 响应示例
+```json
+{
+    "success": true,
+    "code": 200,
+    "message": "获取对话消息成功",
+    "data": {
+        "conversation_id": "conv_20241201_001",
+        "conversation_meta": {
+            "created_at": "2024-12-01T10:00:00",
+            "message_count": 6
+        },
+        "messages": [
+            {
+                "role": "user",
+                "content": "查询销售数据",
+                "timestamp": "2024-12-01T10:00:00",
+                "metadata": {}
+            },
+            {
+                "role": "assistant",
+                "content": "好的,我来帮您查询销售数据...",
+                "timestamp": "2024-12-01T10:00:05",
+                "metadata": {
+                    "type": "DATABASE",
+                    "sql": "SELECT * FROM sales"
+                }
+            }
+        ],
+        "message_count": 6
+    }
+}
+```
+
+---
+
+### 3. 获取对话上下文
+
+#### 接口信息
+- **URL**: `/api/v0/conversation/{conversation_id}/context`
+- **方法**: `GET`
+- **功能**: 获取对话上下文,格式化用于 LLM 处理
+
+#### 请求参数
+
+| 参数名 | 类型 | 位置 | 必填 | 说明 |
+|--------|------|------|------|------|
+| conversation_id | string | 路径参数 | 是 | 对话ID |
+| count | integer | 查询参数 | 否 | 上下文消息数量,默认为 CONVERSATION_CONTEXT_COUNT |
+
+#### 请求示例
+```http
+GET /api/v0/conversation/conv_20241201_001/context?count=5
+```
+
+#### 响应示例
+```json
+{
+    "success": true,
+    "code": 200,
+    "message": "获取对话上下文成功",
+    "data": {
+        "conversation_id": "conv_20241201_001",
+        "context": "User: 查询销售数据\nAssistant: 好的,我来帮您查询销售数据...\nUser: 能否按月份分组?",
+        "context_message_count": 5
+    }
+}
+```
+
+---
+
+### 4. 获取用户完整对话数据 🆕
+
+#### 接口信息
+- **URL**: `/api/v0/user/{user_id}/conversations/full`
+- **方法**: `GET`
+- **功能**: 一次性获取用户的所有对话和每个对话下的消息历史
+
+#### 请求参数
+
+| 参数名 | 类型 | 位置 | 必填 | 说明 |
+|--------|------|------|------|------|
+| user_id | string | 路径参数 | 是 | 用户ID |
+| conversation_limit | integer | 查询参数 | 否 | 对话数量限制,不传则返回所有对话 |
+| message_limit | integer | 查询参数 | 否 | 每个对话的消息数限制,不传则返回所有消息 |
+
+#### 请求示例
+```http
+# 获取所有对话和消息
+GET /api/v0/user/john_doe/conversations/full
+
+# 限制返回数据量
+GET /api/v0/user/john_doe/conversations/full?conversation_limit=50&message_limit=100
+
+# 只限制对话数量
+GET /api/v0/user/john_doe/conversations/full?conversation_limit=20
+```
+
+#### 响应示例
+```json
+{
+    "success": true,
+    "code": 200,
+    "message": "获取用户完整对话数据成功",
+    "data": {
+        "user_id": "john_doe",
+        "conversations": [
+            {
+                "conversation_id": "conv_20241201_001",
+                "start_time": "2024-12-01T10:00:00",
+                "last_activity": "2024-12-01T10:30:00",
+                "meta": {
+                    "message_count": 6,
+                    "created_at": "2024-12-01T10:00:00"
+                },
+                "messages": [
+                    {
+                        "role": "user",
+                        "content": "查询销售数据",
+                        "timestamp": "2024-12-01T10:00:00"
+                    },
+                    {
+                        "role": "assistant",
+                        "content": "好的,我来帮您查询销售数据...",
+                        "timestamp": "2024-12-01T10:00:05"
+                    }
+                ],
+                "message_count": 6
+            }
+        ],
+        "total_conversations": 1,
+        "total_messages": 6,
+        "conversation_limit_applied": null,
+        "message_limit_applied": null,
+        "query_time": "2024-12-01T15:30:00"
+    }
+}
+```
+
+---
+
+### 5. 获取对话系统统计信息
+
+#### 接口信息
+- **URL**: `/api/v0/conversation_stats`
+- **方法**: `GET`
+- **功能**: 获取 Redis 对话系统的统计信息
+
+#### 请求参数
+无
+
+#### 请求示例
+```http
+GET /api/v0/conversation_stats
+```
+
+#### 响应示例
+```json
+{
+    "success": true,
+    "code": 200,
+    "message": "获取统计信息成功",
+    "data": {
+        "total_users": 125,
+        "total_conversations": 1250,
+        "total_messages": 5000,
+        "active_users_today": 45,
+        "active_conversations_today": 200,
+        "cache_hit_rate": 0.75,
+        "redis_info": {
+            "connected": true,
+            "memory_usage": "120MB",
+            "keys_count": 2500
+        }
+    }
+}
+```
+
+---
+
+### 6. 清理过期对话
+
+#### 接口信息
+- **URL**: `/api/v0/conversation_cleanup`
+- **方法**: `POST`
+- **功能**: 手动清理 Redis 中的过期对话数据
+
+#### 请求参数
+无
+
+#### 请求示例
+```http
+POST /api/v0/conversation_cleanup
+```
+
+#### 响应示例
+```json
+{
+    "success": true,
+    "code": 200,
+    "message": "对话清理完成",
+    "data": {
+        "cleaned_conversations": 50,
+        "cleaned_messages": 200,
+        "cleanup_time": "2024-12-01T15:30:00"
+    }
+}
+```
+
+---
+
+### 7. 智能问答(支持对话上下文)
+
+#### 接口信息
+- **URL**: `/api/v0/ask_agent`
+- **方法**: `POST`
+- **功能**: 支持对话上下文的智能问答接口,集成 Redis 对话管理
+
+#### 请求参数
+
+| 参数名 | 类型 | 位置 | 必填 | 说明 |
+|--------|------|------|------|------|
+| question | string | 请求体 | 是 | 用户问题 |
+| session_id | string | 请求体 | 否 | 浏览器会话ID |
+| user_id | string | 请求体 | 否 | 用户ID |
+| conversation_id | string | 请求体 | 否 | 对话ID |
+| continue_conversation | boolean | 请求体 | 否 | 是否继续现有对话 |
+
+#### 请求示例
+```http
+POST /api/v0/ask_agent
+Content-Type: application/json
+
+{
+    "question": "查询最近一个月的销售数据",
+    "session_id": "session_12345",
+    "user_id": "john_doe",
+    "continue_conversation": true
+}
+```
+
+#### 响应示例
+```json
+{
+    "success": true,
+    "code": 200,
+    "message": "查询执行完成",
+    "data": {
+        "type": "DATABASE",
+        "response": "",
+        "sql": "SELECT * FROM sales WHERE date >= DATE_SUB(NOW(), INTERVAL 1 MONTH)",
+        "query_result": {
+            "rows": [...],
+            "columns": ["date", "amount", "product"],
+            "row_count": 150,
+            "total_row_count": 150,
+            "is_limited": false
+        },
+        "summary": "查询到最近一个月的销售数据共150条记录...",
+        "session_id": "session_12345",
+        "execution_path": ["classify_question", "generate_sql", "execute_sql", "generate_summary"],
+        "classification_info": {
+            "question_type": "DATABASE",
+            "confidence": 0.95,
+            "method": "rule_based_database_keywords"
+        },
+        "conversation_id": "conv_20241201_002",
+        "user_id": "john_doe",
+        "is_guest_user": false,
+        "context_used": true,
+        "from_cache": false,
+        "conversation_status": "continued",
+        "conversation_message": "继续现有对话"
+    }
+}
+```
+
+## Redis 对话管理特性
+
+### 1. 用户身份管理
+- 支持登录用户和访客用户
+- 基于 session_id 和 IP 地址的访客识别
+- 智能用户ID解析和会话管理
+
+### 2. 对话生命周期管理
+- 自动创建和管理对话
+- 支持对话继续和新建
+- 对话元数据维护和更新
+
+### 3. 上下文管理
+- 维护对话历史和上下文
+- 支持上下文感知的问答
+- 灵活的上下文长度控制
+
+### 4. 缓存机制
+- 智能答案缓存和检索
+- 只缓存无上下文的问答,避免上下文污染
+- 缓存命中率统计和性能优化
+
+### 5. 数据持久化
+- Redis 数据持久化存储
+- 自动过期和清理机制
+- 数据备份和恢复支持
+
+## 错误处理
+
+所有 API 都遵循统一的错误响应格式:
+
+```json
+{
+    "success": false,
+    "code": 500,
+    "message": "处理失败",
+    "data": {
+        "error": "具体错误信息",
+        "error_type": "error_type_code",
+        "can_retry": true,
+        "timestamp": "2024-12-01T15:30:00"
+    }
+}
+```
+
+### 常见错误码
+
+| 错误码 | 说明 |
+|--------|------|
+| 400 | 请求参数错误 |
+| 422 | 参数验证失败 |
+| 500 | 系统内部错误 |
+| 503 | 服务暂时不可用 |
+
+## 使用建议
+
+### 1. 性能优化
+- 合理使用分页参数控制返回数据量
+- 使用缓存机制减少重复查询
+- 定期清理过期对话数据
+
+### 2. 安全考虑
+- 验证用户权限和会话有效性
+- 避免暴露敏感的对话内容
+- 实施适当的访问频率限制
+
+### 3. 最佳实践
+- 使用 `/conversations/full` API 获取完整数据
+- 根据业务需求设置合理的上下文长度
+- 监控 Redis 内存使用情况
+
+## 配置参数
+
+相关配置参数在 `app_config.py` 中定义:
+
+```python
+# Redis 对话管理配置
+USER_MAX_CONVERSATIONS = 50        # 用户最大对话数
+CONVERSATION_CONTEXT_COUNT = 10    # 默认上下文消息数量
+REDIS_CONVERSATION_TTL = 86400 * 7 # 对话过期时间(7天)
+```
+
+## 缓存策略说明
+
+### 智能缓存机制
+
+系统采用**上下文感知的缓存策略**:
+
+#### 缓存规则
+- ✅ **缓存存储条件**: 只有**无上下文**的问答会被缓存(严控质量)
+- ✅ **缓存使用条件**: **无论是否有上下文**都可以查找缓存(提高利用率)
+
+#### 缓存逻辑
+```
+第1次问答(无上下文)→ 缓存存储 ✅
+第2次相同问题(有上下文)→ 缓存命中 ✅(优化后)
+第3次相同问题(新对话,无上下文)→ 缓存命中 ✅
+```
+
+#### 优势
+- 🎯 **缓存质量保障**: 严控存储条件,确保缓存都是明确的问答
+- ⚡ **性能全面提升**: 新用户和多轮对话都能享受缓存加速
+- 🧠 **平衡设计**: 存储严格,使用灵活,错误概率低
+- 🔄 **高利用率**: 最大化缓存的使用价值
+
+#### 配置控制
+```python
+# 在 app_config.py 中控制缓存行为
+ENABLE_QUESTION_ANSWER_CACHE = True  # 全局缓存开关
+QUESTION_ANSWER_TTL = 24 * 3600      # 缓存过期时间(24小时)
+```
+
+## 更新日志
+
+### v1.2.1 (2024-12-22)
+- 🚀 **平衡缓存策略**: 严控存储条件,放宽使用条件
+- ⚡ **性能提升**: 多轮对话中的重复问题也能使用缓存
+- 🎯 **智能平衡**: 既保证缓存质量,又最大化利用率
+
+### v1.2.0 (2024-12-22)
+- 🔧 **优化缓存策略**: 只缓存无上下文的问答,避免上下文污染
+- ✨ **简化缓存键**: 基于问题本身生成缓存键,提高缓存命中率
+- 🎯 **逻辑优化**: 解决同一对话中重复问题无法使用缓存的问题
+
+### v1.1.0 (2024-12-01)
+- 🆕 新增 `/api/v0/user/{user_id}/conversations/full` API
+- ✨ 支持一次性获取用户完整对话数据
+- 🔧 优化参数处理,支持不传递限制参数时返回所有记录
+
+### v1.0.0 (2024-11-01)
+- 🎉 首次发布 Redis 对话管理功能
+- 📝 完整的 API 文档和使用指南