|
@@ -105,15 +105,281 @@ def _update_expansion_status(requestId: str, status: int):
|
|
|
@asynccontextmanager
|
|
|
async def lifespan(app: FastAPI):
|
|
|
"""应用生命周期管理"""
|
|
|
- # 启动时初始化
|
|
|
+ # 启动时执行
|
|
|
+ logger.info("🚀 启动 Knowledge Agent 服务...")
|
|
|
+
|
|
|
+ # 初始化全局工具
|
|
|
global identify_tool
|
|
|
identify_tool = IdentifyTool()
|
|
|
- logger.info("Agent 服务启动完成")
|
|
|
+
|
|
|
+ # 启动后恢复中断的流程
|
|
|
+ await restore_interrupted_processes()
|
|
|
|
|
|
yield
|
|
|
|
|
|
- # 关闭时清理
|
|
|
- logger.info("Agent 服务正在关闭")
|
|
|
+ # 关闭时执行
|
|
|
+ logger.info("🛑 关闭 Knowledge Agent 服务...")
|
|
|
+
|
|
|
+async def restore_interrupted_processes():
|
|
|
+ """
|
|
|
+ 启动后恢复中断的流程
|
|
|
+ 1. 找到knowledge_request表中parsing_status=1的request_id,去请求 /parse/async
|
|
|
+ 2. 找到knowledge_request表中extraction_status=1的request_id和query,去请求 /extract
|
|
|
+ 3. 找到knowledge_request表中expansion_status=1的request_id和query,去请求 /expand
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ logger.info("🔄 开始恢复中断的流程...")
|
|
|
+
|
|
|
+ # 等待服务完全启动
|
|
|
+ await asyncio.sleep(3)
|
|
|
+
|
|
|
+ # 1. 恢复解析中断的流程
|
|
|
+ await restore_parsing_processes()
|
|
|
+
|
|
|
+ # 2. 恢复提取中断的流程
|
|
|
+ await restore_extraction_processes()
|
|
|
+
|
|
|
+ # 3. 恢复扩展中断的流程
|
|
|
+ await restore_expansion_processes()
|
|
|
+
|
|
|
+ logger.info("✅ 流程恢复完成")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"❌ 流程恢复失败: {e}")
|
|
|
+
|
|
|
+async def restore_parsing_processes():
|
|
|
+ """恢复解析中断的流程"""
|
|
|
+ try:
|
|
|
+ from utils.mysql_db import MysqlHelper
|
|
|
+
|
|
|
+ # 查询parsing_status=1的请求
|
|
|
+ sql = "SELECT request_id FROM knowledge_request WHERE parsing_status = 1"
|
|
|
+ rows = MysqlHelper.get_values(sql)
|
|
|
+
|
|
|
+ if not rows:
|
|
|
+ logger.info("📋 没有发现中断的解析流程")
|
|
|
+ return
|
|
|
+
|
|
|
+ logger.info(f"🔄 发现 {len(rows)} 个中断的解析流程,开始恢复...")
|
|
|
+
|
|
|
+ for row in rows:
|
|
|
+ request_id = row[0]
|
|
|
+ try:
|
|
|
+ # 调用 /parse/async 接口,带重试机制
|
|
|
+ await call_parse_async_with_retry(request_id)
|
|
|
+ logger.info(f"✅ 恢复解析流程成功: request_id={request_id}")
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"❌ 恢复解析流程失败: request_id={request_id}, error={e}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"❌ 恢复解析流程时发生错误: {e}")
|
|
|
+
|
|
|
+async def restore_extraction_processes():
|
|
|
+ """恢复提取中断的流程"""
|
|
|
+ try:
|
|
|
+ from utils.mysql_db import MysqlHelper
|
|
|
+
|
|
|
+ # 查询extraction_status=1的请求和query
|
|
|
+ sql = "SELECT request_id, query FROM knowledge_request WHERE extraction_status = 1"
|
|
|
+ rows = MysqlHelper.get_values(sql)
|
|
|
+
|
|
|
+ if not rows:
|
|
|
+ logger.info("📋 没有发现中断的提取流程")
|
|
|
+ return
|
|
|
+
|
|
|
+ logger.info(f"🔄 发现 {len(rows)} 个中断的提取流程,开始恢复...")
|
|
|
+
|
|
|
+ for row in rows:
|
|
|
+ request_id = row[0]
|
|
|
+ query = row[1] if len(row) > 1 else ""
|
|
|
+ try:
|
|
|
+ # 调用 /extract 接口,带重试机制
|
|
|
+ await call_extract_with_retry(request_id, query)
|
|
|
+ logger.info(f"✅ 恢复提取流程成功: request_id={request_id}")
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"❌ 恢复提取流程失败: request_id={request_id}, error={e}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"❌ 恢复提取流程时发生错误: {e}")
|
|
|
+
|
|
|
+async def restore_expansion_processes():
|
|
|
+ """恢复扩展中断的流程"""
|
|
|
+ try:
|
|
|
+ from utils.mysql_db import MysqlHelper
|
|
|
+
|
|
|
+ # 查询expansion_status=1的请求和query
|
|
|
+ sql = "SELECT request_id, query FROM knowledge_request WHERE expansion_status = 1"
|
|
|
+ rows = MysqlHelper.get_values(sql)
|
|
|
+
|
|
|
+ if not rows:
|
|
|
+ logger.info("📋 没有发现中断的扩展流程")
|
|
|
+ return
|
|
|
+
|
|
|
+ logger.info(f"🔄 发现 {len(rows)} 个中断的扩展流程,开始恢复...")
|
|
|
+
|
|
|
+ for row in rows:
|
|
|
+ request_id = row[0]
|
|
|
+ query = row[1] if len(row) > 1 else ""
|
|
|
+ try:
|
|
|
+ # 调用 /expand 接口,带重试机制
|
|
|
+ await call_expand_with_retry(request_id, query)
|
|
|
+ logger.info(f"✅ 恢复扩展流程成功: request_id={request_id}")
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"❌ 恢复扩展流程失败: request_id={request_id}, error={e}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"❌ 恢复扩展流程时发生错误: {e}")
|
|
|
+
|
|
|
+async def call_parse_async_with_retry(request_id: str, max_retries: int = 3):
|
|
|
+ """调用 /parse/async 接口,带重试机制"""
|
|
|
+ for attempt in range(max_retries):
|
|
|
+ try:
|
|
|
+ import httpx
|
|
|
+
|
|
|
+ # 创建异步HTTP客户端
|
|
|
+ async with httpx.AsyncClient(timeout=30.0) as client:
|
|
|
+ response = await client.post(
|
|
|
+ "http://localhost:8080/parse/async",
|
|
|
+ json={"requestId": request_id}
|
|
|
+ )
|
|
|
+
|
|
|
+ if response.status_code == 200:
|
|
|
+ result = response.json()
|
|
|
+ logger.info(f"调用 /parse/async 成功: request_id={request_id}, result={result}")
|
|
|
+ return
|
|
|
+ else:
|
|
|
+ logger.warning(f"调用 /parse/async 失败: request_id={request_id}, status_code={response.status_code}, attempt={attempt+1}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning(f"调用 /parse/async 异常: request_id={request_id}, error={e}, attempt={attempt+1}")
|
|
|
+
|
|
|
+ # 如果不是最后一次尝试,等待后重试
|
|
|
+ if attempt < max_retries - 1:
|
|
|
+ await asyncio.sleep(2 ** attempt) # 指数退避
|
|
|
+
|
|
|
+ logger.error(f"调用 /parse/async 最终失败: request_id={request_id}, 已重试{max_retries}次")
|
|
|
+
|
|
|
+async def call_extract_with_retry(request_id: str, query: str, max_retries: int = 3):
|
|
|
+ """调用 /extract 接口,带重试机制"""
|
|
|
+ for attempt in range(max_retries):
|
|
|
+ try:
|
|
|
+ import httpx
|
|
|
+
|
|
|
+ # 创建异步HTTP客户端
|
|
|
+ async with httpx.AsyncClient(timeout=30.0) as client:
|
|
|
+ response = await client.post(
|
|
|
+ "http://localhost:8080/extract",
|
|
|
+ json={"requestId": request_id, "query": query}
|
|
|
+ )
|
|
|
+
|
|
|
+ if response.status_code == 200:
|
|
|
+ result = response.json()
|
|
|
+ logger.info(f"调用 /extract 成功: request_id={request_id}, result={result}")
|
|
|
+ return
|
|
|
+ else:
|
|
|
+ logger.warning(f"调用 /extract 失败: request_id={request_id}, status_code={response.status_code}, attempt={attempt+1}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning(f"调用 /extract 异常: request_id={request_id}, error={e}, attempt={attempt+1}")
|
|
|
+
|
|
|
+ # 如果不是最后一次尝试,等待后重试
|
|
|
+ if attempt < max_retries - 1:
|
|
|
+ await asyncio.sleep(2 ** attempt) # 指数退避
|
|
|
+
|
|
|
+ logger.error(f"调用 /extract 最终失败: request_id={request_id}, 已重试{max_retries}次")
|
|
|
+
|
|
|
+async def call_expand_with_retry(request_id: str, query: str, max_retries: int = 3):
|
|
|
+ """调用 /expand 接口,带重试机制"""
|
|
|
+ for attempt in range(max_retries):
|
|
|
+ try:
|
|
|
+ import httpx
|
|
|
+
|
|
|
+ # 创建异步HTTP客户端
|
|
|
+ async with httpx.AsyncClient(timeout=30.0) as client:
|
|
|
+ response = await client.post(
|
|
|
+ "http://localhost:8080/expand",
|
|
|
+ json={"requestId": request_id, "query": query}
|
|
|
+ )
|
|
|
+
|
|
|
+ if response.status_code == 200:
|
|
|
+ result = response.json()
|
|
|
+ logger.info(f"调用 /expand 成功: request_id={request_id}, result={result}")
|
|
|
+ return
|
|
|
+ else:
|
|
|
+ logger.warning(f"调用 /expand 失败: request_id={request_id}, status_code={response.status_code}, attempt={attempt+1}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning(f"调用 /expand 异常: request_id={request_id}, error={e}, attempt={attempt+1}")
|
|
|
+
|
|
|
+ # 如果不是最后一次尝试,等待后重试
|
|
|
+ if attempt < max_retries - 1:
|
|
|
+ await asyncio.sleep(2 ** attempt) # 指数退避
|
|
|
+
|
|
|
+ logger.error(f"调用 /expand 最终失败: request_id={request_id}, 已重试{max_retries}次")
|
|
|
+
|
|
|
+async def call_parse_async(request_id: str):
|
|
|
+ """调用 /parse/async 接口"""
|
|
|
+ try:
|
|
|
+ import httpx
|
|
|
+ import asyncio
|
|
|
+
|
|
|
+ # 创建异步HTTP客户端
|
|
|
+ async with httpx.AsyncClient(timeout=30.0) as client:
|
|
|
+ response = await client.post(
|
|
|
+ "http://localhost:8080/parse/async",
|
|
|
+ json={"requestId": request_id}
|
|
|
+ )
|
|
|
+
|
|
|
+ if response.status_code == 200:
|
|
|
+ result = response.json()
|
|
|
+ logger.info(f"调用 /parse/async 成功: request_id={request_id}, result={result}")
|
|
|
+ else:
|
|
|
+ logger.error(f"调用 /parse/async 失败: request_id={request_id}, status_code={response.status_code}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"调用 /parse/async 异常: request_id={request_id}, error={e}")
|
|
|
+
|
|
|
+async def call_extract(request_id: str, query: str):
|
|
|
+ """调用 /extract 接口"""
|
|
|
+ try:
|
|
|
+ import httpx
|
|
|
+
|
|
|
+ # 创建异步HTTP客户端
|
|
|
+ async with httpx.AsyncClient(timeout=30.0) as client:
|
|
|
+ response = await client.post(
|
|
|
+ "http://localhost:8080/extract",
|
|
|
+ json={"requestId": request_id, "query": query}
|
|
|
+ )
|
|
|
+
|
|
|
+ if response.status_code == 200:
|
|
|
+ result = response.json()
|
|
|
+ logger.info(f"调用 /extract 成功: request_id={request_id}, result={result}")
|
|
|
+ else:
|
|
|
+ logger.error(f"调用 /extract 失败: request_id={request_id}, status_code={response.status_code}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"调用 /extract 异常: request_id={request_id}, error={e}")
|
|
|
+
|
|
|
+async def call_expand(request_id: str, query: str):
|
|
|
+ """调用 /expand 接口"""
|
|
|
+ try:
|
|
|
+ import httpx
|
|
|
+
|
|
|
+ # 创建异步HTTP客户端
|
|
|
+ async with httpx.AsyncClient(timeout=30.0) as client:
|
|
|
+ response = await client.post(
|
|
|
+ "http://localhost:8080/expand",
|
|
|
+ json={"requestId": request_id, "query": query}
|
|
|
+ )
|
|
|
+
|
|
|
+ if response.status_code == 200:
|
|
|
+ result = response.json()
|
|
|
+ logger.info(f"调用 /expand 成功: request_id={request_id}, result={result}")
|
|
|
+ else:
|
|
|
+ logger.error(f"调用 /expand 失败: request_id={request_id}, status_code={response.status_code}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"调用 /expand 异常: request_id={request_id}, error={e}")
|
|
|
|
|
|
# 创建 FastAPI 应用
|
|
|
app = FastAPI(
|