소스 검색

前面的异步改造失败,现在进行第二次尝试。

wangxq 1 개월 전
부모
커밋
4782e33fae
4개의 변경된 파일189개의 추가작업 그리고 179개의 파일을 삭제
  1. 109 45
      test/custom_react_agent/agent.py
  2. 69 120
      test/custom_react_agent/api.py
  3. 1 1
      test/custom_react_agent/config.py
  4. 10 13
      test/custom_react_agent/shell.py

+ 109 - 45
test/custom_react_agent/agent.py

@@ -11,7 +11,7 @@ from langchain_openai import ChatOpenAI
 from langchain_core.messages import HumanMessage, ToolMessage, BaseMessage, SystemMessage, AIMessage
 from langgraph.graph import StateGraph, END
 from langgraph.prebuilt import ToolNode
-from redis.asyncio import Redis
+import redis.asyncio as redis
 try:
     from langgraph.checkpoint.redis import AsyncRedisSaver
 except ImportError:
@@ -43,6 +43,7 @@ class CustomReactAgent:
         self.agent_executor = None
         self.checkpointer = None
         self._exit_stack = None
+        self.redis_client = None
 
     @classmethod
     async def create(cls):
@@ -55,7 +56,16 @@ class CustomReactAgent:
         """异步初始化所有组件。"""
         logger.info("🚀 开始初始化 CustomReactAgent...")
 
-        # 1. 初始化 LLM
+        # 1. 初始化异步Redis客户端
+        self.redis_client = redis.from_url(config.REDIS_URL, decode_responses=True)
+        try:
+            await self.redis_client.ping()
+            logger.info(f"   ✅ Redis连接成功: {config.REDIS_URL}")
+        except Exception as e:
+            logger.error(f"   ❌ Redis连接失败: {e}")
+            raise
+
+        # 2. 初始化 LLM
         self.llm = ChatOpenAI(
             api_key=config.QWEN_API_KEY,
             base_url=config.QWEN_BASE_URL,
@@ -72,12 +82,12 @@ class CustomReactAgent:
         )
         logger.info(f"   LLM 已初始化,模型: {config.QWEN_MODEL}")
 
-        # 2. 绑定工具
+        # 3. 绑定工具
         self.tools = sql_tools
         self.llm_with_tools = self.llm.bind_tools(self.tools)
         logger.info(f"   已绑定 {len(self.tools)} 个工具。")
 
-        # 3. 初始化 Redis Checkpointer
+        # 4. 初始化 Redis Checkpointer
         if config.REDIS_ENABLED and AsyncRedisSaver is not None:
             try:
                 self._exit_stack = AsyncExitStack()
@@ -93,7 +103,7 @@ class CustomReactAgent:
         else:
             logger.warning("   Redis 持久化功能已禁用。")
 
-        # 4. 构建 StateGraph
+        # 5. 构建 StateGraph
         self.agent_executor = self._create_graph()
         logger.info("   StateGraph 已构建并编译。")
         logger.info("✅ CustomReactAgent 初始化完成。")
@@ -105,23 +115,27 @@ class CustomReactAgent:
             self._exit_stack = None
             self.checkpointer = None
             logger.info("✅ RedisSaver 资源已通过 AsyncExitStack 释放。")
+        
+        if self.redis_client:
+            await self.redis_client.aclose()
+            logger.info("✅ Redis客户端已关闭。")
 
     def _create_graph(self):
         """定义并编译最终的、正确的 StateGraph 结构。"""
         builder = StateGraph(AgentState)
 
-        # 定义所有需要的节点
-        builder.add_node("agent", self._agent_node)
-        builder.add_node("prepare_tool_input", self._prepare_tool_input_node)
+        # 定义所有需要的节点 - 全部改为异步
+        builder.add_node("agent", self._async_agent_node)
+        builder.add_node("prepare_tool_input", self._async_prepare_tool_input_node)
         builder.add_node("tools", ToolNode(self.tools))
-        builder.add_node("update_state_after_tool", self._update_state_after_tool_node)
-        builder.add_node("format_final_response", self._format_final_response_node)
+        builder.add_node("update_state_after_tool", self._async_update_state_after_tool_node)
+        builder.add_node("format_final_response", self._async_format_final_response_node)
 
         # 建立正确的边连接
         builder.set_entry_point("agent")
         builder.add_conditional_edges(
             "agent",
-            self._should_continue,
+            self._async_should_continue,
             {
                 "continue": "prepare_tool_input",
                 "end": "format_final_response"
@@ -134,30 +148,38 @@ class CustomReactAgent:
 
         return builder.compile(checkpointer=self.checkpointer)
 
-    def _should_continue(self, state: AgentState) -> str:
-        """判断是继续调用工具还是结束。"""
+    async def _async_should_continue(self, state: AgentState) -> str:
+        """异步判断是继续调用工具还是结束。"""
         last_message = state["messages"][-1]
         if hasattr(last_message, "tool_calls") and last_message.tool_calls:
             return "continue"
         return "end"
 
-    def _agent_node(self, state: AgentState) -> Dict[str, Any]:
-        """Agent 节点:只负责调用 LLM 并返回其输出。"""
-        logger.info(f"🧠 [Node] agent - Thread: {state['thread_id']}")
+    async def _async_agent_node(self, state: AgentState) -> Dict[str, Any]:
+        """异步Agent 节点:使用异步LLM调用。"""
+        logger.info(f"🧠 [Async Node] agent - Thread: {state['thread_id']}")
         
         messages_for_llm = list(state["messages"])
+        
+        # 🎯 添加数据库范围系统提示词(仅在对话开始时添加)
+        if len(state["messages"]) == 1 and isinstance(state["messages"][0], HumanMessage):
+            db_scope_prompt = self._get_database_scope_prompt()
+            if db_scope_prompt:
+                messages_for_llm.insert(0, SystemMessage(content=db_scope_prompt))
+                logger.info("   ✅ 已添加数据库范围判断提示词")
+        
         if state.get("suggested_next_step"):
-            instruction = f"提示:建议下一步使用工具 '{state['suggested_next_step']}'。"
+            instruction = f"Suggestion: Consider using the '{state['suggested_next_step']}' tool for the next step."
             messages_for_llm.append(SystemMessage(content=instruction))
 
         # 添加重试机制处理网络连接问题
-        import time
+        import asyncio
         max_retries = config.MAX_RETRIES
         for attempt in range(max_retries):
             try:
-                response = self.llm_with_tools.invoke(messages_for_llm)
-                logger.info(f"   LLM Response: {response.pretty_print()}")
-                # 只返回消息,不承担其他职责
+                # 使用异步调用
+                response = await self.llm_with_tools.ainvoke(messages_for_llm)
+                logger.info(f"   ✅ 异步LLM调用成功")
                 return {"messages": [response]}
                 
             except Exception as e:
@@ -172,14 +194,14 @@ class CustomReactAgent:
                     if attempt < max_retries - 1:
                         wait_time = config.RETRY_BASE_DELAY ** attempt  # 指数退避:2, 4, 8秒
                         logger.info(f"   🔄 网络错误,{wait_time}秒后重试...")
-                        time.sleep(wait_time)
+                        await asyncio.sleep(wait_time)
                         continue
                     else:
                         # 所有重试都失败了,返回一个降级的回答
                         logger.error(f"   ❌ 网络连接持续失败,返回降级回答")
                         
                         # 检查是否有SQL执行结果可以利用
-                        sql_data = self._extract_latest_sql_data(state["messages"])
+                        sql_data = await self._async_extract_latest_sql_data(state["messages"])
                         if sql_data:
                             fallback_content = "抱歉,由于网络连接问题,无法生成完整的文字总结。不过查询已成功执行,结果如下:\n\n" + sql_data
                         else:
@@ -232,11 +254,11 @@ class CustomReactAgent:
         
         logger.info(" ~" * 10 + " State Print End" + " ~" * 10)
 
-    def _prepare_tool_input_node(self, state: AgentState) -> Dict[str, Any]:
+    async def _async_prepare_tool_input_node(self, state: AgentState) -> Dict[str, Any]:
         """
-        信息组装节点:为需要上下文的工具注入历史消息。
+        异步信息组装节点:为需要上下文的工具注入历史消息。
         """
-        logger.info(f"🛠️ [Node] prepare_tool_input - Thread: {state['thread_id']}")
+        logger.info(f"🛠️ [Async Node] prepare_tool_input - Thread: {state['thread_id']}")
         
         # 🎯 打印 state 全部信息
         # self._print_state_info(state, "prepare_tool_input")
@@ -291,7 +313,7 @@ class CustomReactAgent:
         last_message.tool_calls = new_tool_calls
         return {"messages": [last_message]}
 
-    def _update_state_after_tool_node(self, state: AgentState) -> Dict[str, Any]:
+    async def _async_update_state_after_tool_node(self, state: AgentState) -> Dict[str, Any]:
         """在工具执行后,更新 suggested_next_step 并清理参数。"""
         logger.info(f"📝 [Node] update_state_after_tool - Thread: {state['thread_id']}")
         
@@ -335,16 +357,16 @@ class CustomReactAgent:
                         tool_call["args"]["history_messages"] = ""
                         logger.info(f"   已将 generate_sql 的 history_messages 设置为空字符串")
 
-    def _format_final_response_node(self, state: AgentState) -> Dict[str, Any]:
-        """最终输出格式化节点。"""
-        logger.info(f"🎨 [Node] format_final_response - Thread: {state['thread_id']}")
+    async def _async_format_final_response_node(self, state: AgentState) -> Dict[str, Any]:
+        """异步最终输出格式化节点。"""
+        logger.info(f"🎨 [Async Node] format_final_response - Thread: {state['thread_id']}")
         
         # 保持原有的消息格式化(用于shell.py兼容)
         last_message = state['messages'][-1]
         last_message.content = f"[Formatted Output]\n{last_message.content}"
         
         # 生成API格式的数据
-        api_data = self._generate_api_data(state)
+        api_data = await self._async_generate_api_data(state)
 
         # 打印api_data
         print("-"*20+"api_data_start"+"-"*20)
@@ -356,9 +378,9 @@ class CustomReactAgent:
             "api_data": api_data  # 新增:API格式数据
         }
 
-    def _generate_api_data(self, state: AgentState) -> Dict[str, Any]:
-        """生成API格式的数据结构"""
-        logger.info("📊 生成API格式数据...")
+    async def _async_generate_api_data(self, state: AgentState) -> Dict[str, Any]:
+        """异步生成API格式的数据结构"""
+        logger.info("📊 异步生成API格式数据...")
         
         # 提取基础响应内容
         last_message = state['messages'][-1]
@@ -374,20 +396,20 @@ class CustomReactAgent:
         }
         
         # 提取SQL和数据记录
-        sql_info = self._extract_sql_and_data(state['messages'])
+        sql_info = await self._async_extract_sql_and_data(state['messages'])
         if sql_info['sql']:
             api_data["sql"] = sql_info['sql']
         if sql_info['records']:
             api_data["records"] = sql_info['records']
         
         # 生成Agent元数据
-        api_data["react_agent_meta"] = self._collect_agent_metadata(state)
+        api_data["react_agent_meta"] = await self._async_collect_agent_metadata(state)
         
         logger.info(f"   API数据生成完成,包含字段: {list(api_data.keys())}")
         return api_data
 
-    def _extract_sql_and_data(self, messages: List[BaseMessage]) -> Dict[str, Any]:
-        """从消息历史中提取SQL和数据记录"""
+    async def _async_extract_sql_and_data(self, messages: List[BaseMessage]) -> Dict[str, Any]:
+        """异步从消息历史中提取SQL和数据记录"""
         result = {"sql": None, "records": None}
         
         # 查找最后一个HumanMessage之后的工具执行结果
@@ -438,7 +460,7 @@ class CustomReactAgent:
             
         return result
 
-    def _collect_agent_metadata(self, state: AgentState) -> Dict[str, Any]:
+    async def _async_collect_agent_metadata(self, state: AgentState) -> Dict[str, Any]:
         """收集Agent元数据"""
         messages = state['messages']
         
@@ -485,7 +507,7 @@ class CustomReactAgent:
             "agent_version": "custom_react_v1"
         }
 
-    def _extract_latest_sql_data(self, messages: List[BaseMessage]) -> Optional[str]:
+    async def _async_extract_latest_sql_data(self, messages: List[BaseMessage]) -> Optional[str]:
         """从消息历史中提取最近的run_sql执行结果,但仅限于当前对话轮次。"""
         logger.info("🔍 提取最新的SQL执行结果...")
         
@@ -552,7 +574,7 @@ class CustomReactAgent:
             answer = final_state["messages"][-1].content
             
             # 🎯 提取最近的 run_sql 执行结果(不修改messages)
-            sql_data = self._extract_latest_sql_data(final_state["messages"])
+            sql_data = await self._async_extract_latest_sql_data(final_state["messages"])
             
             logger.info(f"✅ 处理完成 - Final Answer: '{answer}'")
             
@@ -624,9 +646,8 @@ class CustomReactAgent:
             return []
         
         try:
-            # 创建Redis连接 - 使用与checkpointer相同的连接配置
-            from redis.asyncio import Redis
-            redis_client = Redis.from_url(config.REDIS_URL, decode_responses=True)
+            # 使用统一的异步Redis客户端
+            redis_client = self.redis_client
             
             # 1. 扫描匹配该用户的所有checkpoint keys
             # checkpointer的key格式通常是: checkpoint:thread_id:checkpoint_id
@@ -757,4 +778,47 @@ class CustomReactAgent:
                 return f"{year}-{month}-{day} {hour}:{minute}:{second}"
         except Exception:
             pass
-        return timestamp 
+        return timestamp 
+
+    def _get_database_scope_prompt(self) -> str:
+        """Get database scope prompt for intelligent query decision making"""
+        try:
+            import os
+            # Read agent/tools/db_query_decision_prompt.txt
+            project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+            db_scope_file = os.path.join(project_root, "agent", "tools", "db_query_decision_prompt.txt")
+            
+            with open(db_scope_file, 'r', encoding='utf-8') as f:
+                db_scope_content = f.read().strip()
+            
+            prompt = f"""You are an intelligent database query assistant. When deciding whether to use database query tools, please follow these rules:
+
+=== DATABASE BUSINESS SCOPE ===
+{db_scope_content}
+
+=== DECISION RULES ===
+1. If the question involves data within the above business scope (service areas, branches, revenue, traffic flow, etc.), use the generate_sql tool
+2. If the question is about general knowledge (like "when do lychees ripen?", weather, historical events, etc.), answer directly based on your knowledge WITHOUT using database tools
+3. When answering general knowledge questions, clearly indicate that this is based on general knowledge, not database data
+
+=== FALLBACK STRATEGY ===
+When generate_sql returns an error message or when queries return no results:
+1. First, check if the question is within the database scope described above
+2. For questions clearly OUTSIDE the database scope (world events, general knowledge, etc.):
+   - Provide the answer based on your knowledge immediately
+   - CLEARLY indicate this is based on general knowledge, not database data
+   - Use format: "Based on general knowledge (not database data): [answer]"
+3. For questions within database scope but queries return no results:
+   - If it's a reasonable question that might have a general answer, provide it
+   - Still indicate the source: "Based on general knowledge (database had no results): [answer]"
+4. For questions that definitely require specific database data:
+   - Acknowledge the limitation and suggest the data may not be available
+   - Do not attempt to guess or fabricate specific data
+
+Please intelligently choose whether to query the database based on the nature of the user's question."""
+            
+            return prompt
+            
+        except Exception as e:
+            logger.warning(f"⚠️ Unable to read database scope description file: {e}")
+            return ""

+ 69 - 120
test/custom_react_agent/api.py

@@ -10,6 +10,7 @@ from datetime import datetime
 from typing import Optional, Dict, Any
 
 from flask import Flask, request, jsonify
+import redis.asyncio as redis
 
 try:
     # 尝试相对导入(当作为模块导入时)
@@ -24,6 +25,7 @@ logger = logging.getLogger(__name__)
 
 # 全局Agent实例
 _agent_instance: Optional[CustomReactAgent] = None
+_redis_client: Optional[redis.Redis] = None
 
 def validate_request_data(data: Dict[str, Any]) -> Dict[str, Any]:
     """验证请求数据"""
@@ -51,23 +53,28 @@ def validate_request_data(data: Dict[str, Any]) -> Dict[str, Any]:
     }
 
 async def initialize_agent():
-    """初始化Agent"""
-    global _agent_instance
+    """异步初始化Agent"""
+    global _agent_instance, _redis_client
     
     if _agent_instance is None:
-        logger.info("🚀 正在初始化 Custom React Agent...")
+        logger.info("🚀 正在异步初始化 Custom React Agent...")
         try:
             # 设置环境变量(checkpointer内部需要)
             os.environ['REDIS_URL'] = 'redis://localhost:6379'
             
+            # 初始化共享的Redis客户端
+            _redis_client = redis.from_url('redis://localhost:6379', decode_responses=True)
+            await _redis_client.ping()
+            logger.info("✅ Redis客户端连接成功")
+            
             _agent_instance = await CustomReactAgent.create()
-            logger.info("✅ Agent 初始化完成")
+            logger.info("✅ Agent 异步初始化完成")
         except Exception as e:
-            logger.error(f"❌ Agent 初始化失败: {e}")
+            logger.error(f"❌ Agent 异步初始化失败: {e}")
             raise
 
 async def ensure_agent_ready():
-    """确保Agent实例可用"""
+    """异步确保Agent实例可用"""
     global _agent_instance
     
     if _agent_instance is None:
@@ -85,74 +92,29 @@ async def ensure_agent_ready():
         await initialize_agent()
         return True
 
-def run_async_safely(async_func, *args, **kwargs):
-    """安全地运行异步函数,处理事件循环问题"""
-    try:
-        # 检查是否已有事件循环
-        loop = asyncio.get_event_loop()
-        if loop.is_running():
-            # 如果事件循环在运行,创建新的事件循环
-            new_loop = asyncio.new_event_loop()
-            asyncio.set_event_loop(new_loop)
-            try:
-                return new_loop.run_until_complete(async_func(*args, **kwargs))
-            finally:
-                new_loop.close()
-        else:
-            # 如果事件循环没有运行,直接使用
-            return loop.run_until_complete(async_func(*args, **kwargs))
-    except RuntimeError:
-        # 如果没有事件循环,创建新的
-        loop = asyncio.new_event_loop()
-        asyncio.set_event_loop(loop)
-        try:
-            return loop.run_until_complete(async_func(*args, **kwargs))
-        finally:
-            loop.close()
-
-def ensure_agent_ready_sync():
-    """同步版本的ensure_agent_ready,用于Flask路由"""
-    global _agent_instance
-    
-    if _agent_instance is None:
-        try:
-            # 使用新的事件循环初始化
-            loop = asyncio.new_event_loop()
-            asyncio.set_event_loop(loop)
-            try:
-                loop.run_until_complete(initialize_agent())
-            finally:
-                loop.close()
-        except Exception as e:
-            logger.error(f"初始化Agent失败: {e}")
-            return False
-    
-    return _agent_instance is not None
+# 删除复杂的事件循环管理函数 - 不再需要
 
 async def cleanup_agent():
-    """清理Agent资源"""
-    global _agent_instance
+    """异步清理Agent资源"""
+    global _agent_instance, _redis_client
     
     if _agent_instance:
         await _agent_instance.close()
-        logger.info("✅ Agent 资源已清理")
+        logger.info("✅ Agent 资源已异步清理")
         _agent_instance = None
+    
+    if _redis_client:
+        await _redis_client.aclose()
+        logger.info("✅ Redis客户端已异步关闭")
+        _redis_client = None
 
 # 创建Flask应用
 app = Flask(__name__)
 
-# 注册清理函数
+# 简化的退出处理
 def cleanup_on_exit():
     """程序退出时的清理函数"""
-    try:
-        loop = asyncio.new_event_loop()
-        asyncio.set_event_loop(loop)
-        try:
-            loop.run_until_complete(cleanup_agent())
-        finally:
-            loop.close()
-    except Exception as e:
-        logger.error(f"清理资源时发生错误: {e}")
+    logger.info("程序退出,资源清理将在异步上下文中进行")
 
 atexit.register(cleanup_on_exit)
 
@@ -176,27 +138,18 @@ def health_check():
         return jsonify({"status": "unhealthy", "error": str(e)}), 500
 
 @app.route("/api/chat", methods=["POST"])
-def chat_endpoint():
-    """智能问答接口"""
+async def chat_endpoint():
+    """异步智能问答接口"""
     global _agent_instance
     
     # 确保Agent已初始化
-    if not _agent_instance:
-        try:
-            # 尝试初始化Agent(使用新的事件循环)
-            loop = asyncio.new_event_loop()
-            asyncio.set_event_loop(loop)
-            try:
-                loop.run_until_complete(initialize_agent())
-            finally:
-                loop.close()
-        except Exception as e:
-            return jsonify({
-                "code": 503,
-                "message": "服务未就绪",
-                "success": False,
-                "error": "Agent 初始化失败"
-            }), 503
+    if not await ensure_agent_ready():
+        return jsonify({
+            "code": 503,
+            "message": "服务未就绪",
+            "success": False,
+            "error": "Agent 初始化失败"
+        }), 503
     
     try:
         # 获取请求数据
@@ -214,18 +167,12 @@ def chat_endpoint():
         
         logger.info(f"📨 收到请求 - User: {validated_data['user_id']}, Question: {validated_data['question'][:50]}...")
         
-        # 调用Agent处理(使用新的事件循环)
-        try:
-            loop = asyncio.new_event_loop()
-            asyncio.set_event_loop(loop)
-            
-            agent_result = loop.run_until_complete(_agent_instance.chat(
-                message=validated_data['question'],
-                user_id=validated_data['user_id'],
-                thread_id=validated_data['thread_id']
-            ))
-        finally:
-            loop.close()
+        # 直接调用异步方法,不需要事件循环包装
+        agent_result = await _agent_instance.chat(
+            message=validated_data['question'],
+            user_id=validated_data['user_id'],
+            thread_id=validated_data['thread_id']
+        )
         
         if not agent_result.get("success", False):
             # Agent处理失败
@@ -240,7 +187,7 @@ def chat_endpoint():
                 "data": {
                     "react_agent_meta": {
                         "thread_id": agent_result.get("thread_id"),
-                        "agent_version": "custom_react_v1",
+                        "agent_version": "custom_react_v1_async",
                         "execution_path": ["error"]
                     },
                     "timestamp": datetime.now().isoformat()
@@ -286,8 +233,8 @@ def chat_endpoint():
         }), 500
 
 @app.route('/api/v0/react/users/<user_id>/conversations', methods=['GET'])
-def get_user_conversations(user_id: str):
-    """获取用户的聊天记录列表"""
+async def get_user_conversations(user_id: str):
+    """异步获取用户的聊天记录列表"""
     global _agent_instance
     
     try:
@@ -297,18 +244,18 @@ def get_user_conversations(user_id: str):
         # 限制limit的范围
         limit = max(1, min(limit, 50))  # 限制在1-50之间
         
-        logger.info(f"📋 获取用户 {user_id} 的对话列表,限制 {limit} 条")
+        logger.info(f"📋 异步获取用户 {user_id} 的对话列表,限制 {limit} 条")
         
         # 确保Agent可用
-        if not ensure_agent_ready_sync():
+        if not await ensure_agent_ready():
             return jsonify({
                 "success": False,
                 "error": "Agent 未就绪",
                 "timestamp": datetime.now().isoformat()
             }), 503
         
-        # 获取对话列表
-        conversations = run_async_safely(_agent_instance.get_user_recent_conversations, user_id, limit)
+        # 直接调用异步方法
+        conversations = await _agent_instance.get_user_recent_conversations(user_id, limit)
         
         return jsonify({
             "success": True,
@@ -322,7 +269,7 @@ def get_user_conversations(user_id: str):
         }), 200
         
     except Exception as e:
-        logger.error(f"❌ 获取用户 {user_id} 对话列表失败: {e}")
+        logger.error(f"❌ 异步获取用户 {user_id} 对话列表失败: {e}")
         return jsonify({
             "success": False,
             "error": str(e),
@@ -330,8 +277,8 @@ def get_user_conversations(user_id: str):
         }), 500
 
 @app.route('/api/v0/react/users/<user_id>/conversations/<thread_id>', methods=['GET'])
-def get_user_conversation_detail(user_id: str, thread_id: str):
-    """获取特定对话的详细历史"""
+async def get_user_conversation_detail(user_id: str, thread_id: str):
+    """异步获取特定对话的详细历史"""
     global _agent_instance
     
     try:
@@ -343,19 +290,19 @@ def get_user_conversation_detail(user_id: str, thread_id: str):
                 "timestamp": datetime.now().isoformat()
             }), 400
         
-        logger.info(f"📖 获取用户 {user_id} 的对话 {thread_id} 详情")
+        logger.info(f"📖 异步获取用户 {user_id} 的对话 {thread_id} 详情")
         
         # 确保Agent可用
-        if not ensure_agent_ready_sync():
+        if not await ensure_agent_ready():
             return jsonify({
                 "success": False,
                 "error": "Agent 未就绪",
                 "timestamp": datetime.now().isoformat()
             }), 503
         
-        # 获取对话历史
-        history = run_async_safely(_agent_instance.get_conversation_history, thread_id)
-        logger.info(f"✅ 成功获取对话历史,消息数量: {len(history)}")
+        # 直接调用异步方法
+        history = await _agent_instance.get_conversation_history(thread_id)
+        logger.info(f"✅ 异步成功获取对话历史,消息数量: {len(history)}")
         
         if not history:
             return jsonify({
@@ -377,7 +324,7 @@ def get_user_conversation_detail(user_id: str, thread_id: str):
         
     except Exception as e:
         import traceback
-        logger.error(f"❌ 获取对话 {thread_id} 详情失败: {e}")
+        logger.error(f"❌ 异步获取对话 {thread_id} 详情失败: {e}")
         logger.error(f"❌ 详细错误信息: {traceback.format_exc()}")
         return jsonify({
             "success": False,
@@ -902,17 +849,19 @@ def get_conversation_summary_api(thread_id: str):
 
 # 为了支持独立运行
 if __name__ == "__main__":
-    # 在启动前初始化Agent
-    try:
-        loop = asyncio.new_event_loop()
-        asyncio.set_event_loop(loop)
-        try:
-            loop.run_until_complete(initialize_agent())
-        finally:
-            loop.close()
-        logger.info("✅ API 服务启动成功")
-    except Exception as e:
-        logger.error(f"❌ API 服务启动失败: {e}")
+    # 使用Flask 3.x原生异步支持启动
+    logger.info("🚀 使用Flask内置异步支持启动...")
+    
+    # 信号处理
+    import signal
+    
+    def signal_handler(signum, frame):
+        logger.info("🛑 收到关闭信号,开始清理...")
+        print("正在关闭服务...")
+        exit(0)
+    
+    signal.signal(signal.SIGINT, signal_handler)
+    signal.signal(signal.SIGTERM, signal_handler)
     
     # 启动Flask应用
-    app.run(host="0.0.0.0", port=8000, debug=False) 
+    app.run(host="0.0.0.0", port=8000, debug=False, threaded=True) 

+ 1 - 1
test/custom_react_agent/config.py

@@ -23,7 +23,7 @@ LOG_LEVEL = logging.INFO
 LOG_FORMAT = '%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'
 
 # --- Agent 配置 ---
-DEFAULT_USER_ID = "default-user"
+DEFAULT_USER_ID = "guest"
 
 # --- 网络重试配置 ---
 MAX_RETRIES = 3                    # 最大重试次数

+ 10 - 13
test/custom_react_agent/shell.py

@@ -1,26 +1,23 @@
 """
 重构后的 CustomReactAgent 的交互式命令行客户端
 """
+# from __future__ import annotations
+
 import asyncio
 import logging
 import sys
 import os
 import json
 
-# 动态地将项目根目录添加到 sys.path,以支持跨模块导入
-# 这使得脚本更加健壮,无论从哪里执行
-PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
-sys.path.insert(0, PROJECT_ROOT)
+# 将当前目录和项目根目录添加到 sys.path
+CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
+PROJECT_ROOT = os.path.abspath(os.path.join(CURRENT_DIR, '..', '..'))
+sys.path.insert(0, CURRENT_DIR)  # 当前目录优先
+sys.path.insert(1, PROJECT_ROOT)  # 项目根目录
 
-# 从新模块导入 Agent 和配置
-try:
-    # 相对导入(当作为模块导入时)
-    from .agent import CustomReactAgent
-    from . import config
-except ImportError:
-    # 绝对导入(当直接运行时)
-    from test.custom_react_agent.agent import CustomReactAgent
-    from test.custom_react_agent import config
+# 导入 Agent 和配置(简化版本)
+from agent import CustomReactAgent
+import config
 
 # 配置日志
 logging.basicConfig(level=config.LOG_LEVEL, format=config.LOG_FORMAT)