citu_agent.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. # agent/citu_agent.py
  2. from typing import Dict, Any, Literal
  3. from langgraph.graph import StateGraph, END
  4. from langchain.agents import AgentExecutor, create_openai_tools_agent
  5. from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
  6. from langchain_core.messages import SystemMessage, HumanMessage
  7. from agent.state import AgentState
  8. from agent.classifier import QuestionClassifier
  9. from agent.tools import TOOLS, generate_sql, execute_sql, generate_summary, general_chat
  10. from agent.utils import get_compatible_llm
  11. from app_config import ENABLE_RESULT_SUMMARY
  12. class CituLangGraphAgent:
  13. """Citu LangGraph智能助手主类 - 使用@tool装饰器 + Agent工具调用"""
  14. def __init__(self):
  15. # 加载配置
  16. try:
  17. from agent.config import get_current_config, get_nested_config
  18. self.config = get_current_config()
  19. print("[CITU_AGENT] 加载Agent配置完成")
  20. except ImportError:
  21. self.config = {}
  22. print("[CITU_AGENT] 配置文件不可用,使用默认配置")
  23. self.classifier = QuestionClassifier()
  24. self.tools = TOOLS
  25. self.llm = get_compatible_llm()
  26. # 注意:现在使用直接工具调用模式,不再需要预创建Agent执行器
  27. print("[CITU_AGENT] 使用直接工具调用模式")
  28. # 不在构造时创建workflow,改为动态创建以支持路由模式参数
  29. # self.workflow = self._create_workflow()
  30. print("[CITU_AGENT] LangGraph Agent with Direct Tools初始化完成")
  31. def _create_workflow(self, routing_mode: str = None) -> StateGraph:
  32. """根据路由模式创建不同的工作流"""
  33. # 确定使用的路由模式
  34. if routing_mode:
  35. QUESTION_ROUTING_MODE = routing_mode
  36. print(f"[CITU_AGENT] 创建工作流,使用传入的路由模式: {QUESTION_ROUTING_MODE}")
  37. else:
  38. try:
  39. from app_config import QUESTION_ROUTING_MODE
  40. print(f"[CITU_AGENT] 创建工作流,使用配置文件路由模式: {QUESTION_ROUTING_MODE}")
  41. except ImportError:
  42. QUESTION_ROUTING_MODE = "hybrid"
  43. print(f"[CITU_AGENT] 配置导入失败,使用默认路由模式: {QUESTION_ROUTING_MODE}")
  44. workflow = StateGraph(AgentState)
  45. # 根据路由模式创建不同的工作流
  46. if QUESTION_ROUTING_MODE == "database_direct":
  47. # 直接数据库模式:跳过分类,直接进入数据库处理
  48. workflow.add_node("init_direct_database", self._init_direct_database_node)
  49. workflow.add_node("agent_database", self._agent_database_node)
  50. workflow.add_node("format_response", self._format_response_node)
  51. workflow.set_entry_point("init_direct_database")
  52. workflow.add_edge("init_direct_database", "agent_database")
  53. workflow.add_edge("agent_database", "format_response")
  54. workflow.add_edge("format_response", END)
  55. elif QUESTION_ROUTING_MODE == "chat_direct":
  56. # 直接聊天模式:跳过分类,直接进入聊天处理
  57. workflow.add_node("init_direct_chat", self._init_direct_chat_node)
  58. workflow.add_node("agent_chat", self._agent_chat_node)
  59. workflow.add_node("format_response", self._format_response_node)
  60. workflow.set_entry_point("init_direct_chat")
  61. workflow.add_edge("init_direct_chat", "agent_chat")
  62. workflow.add_edge("agent_chat", "format_response")
  63. workflow.add_edge("format_response", END)
  64. else:
  65. # 其他模式(hybrid, llm_only):使用原有的分类工作流
  66. workflow.add_node("classify_question", self._classify_question_node)
  67. workflow.add_node("agent_chat", self._agent_chat_node)
  68. workflow.add_node("agent_database", self._agent_database_node)
  69. workflow.add_node("format_response", self._format_response_node)
  70. workflow.set_entry_point("classify_question")
  71. # 添加条件边:分类后的路由
  72. workflow.add_conditional_edges(
  73. "classify_question",
  74. self._route_after_classification,
  75. {
  76. "DATABASE": "agent_database",
  77. "CHAT": "agent_chat"
  78. }
  79. )
  80. workflow.add_edge("agent_chat", "format_response")
  81. workflow.add_edge("agent_database", "format_response")
  82. workflow.add_edge("format_response", END)
  83. return workflow.compile()
  84. def _init_direct_database_node(self, state: AgentState) -> AgentState:
  85. """初始化直接数据库模式的状态"""
  86. try:
  87. # 从state中获取路由模式,而不是从配置文件读取
  88. routing_mode = state.get("routing_mode", "database_direct")
  89. # 设置直接数据库模式的分类状态
  90. state["question_type"] = "DATABASE"
  91. state["classification_confidence"] = 1.0
  92. state["classification_reason"] = "配置为直接数据库查询模式"
  93. state["classification_method"] = "direct_database"
  94. state["routing_mode"] = routing_mode
  95. state["current_step"] = "direct_database_init"
  96. state["execution_path"].append("init_direct_database")
  97. print(f"[DIRECT_DATABASE] 直接数据库模式初始化完成")
  98. return state
  99. except Exception as e:
  100. print(f"[ERROR] 直接数据库模式初始化异常: {str(e)}")
  101. state["error"] = f"直接数据库模式初始化失败: {str(e)}"
  102. state["error_code"] = 500
  103. state["execution_path"].append("init_direct_database_error")
  104. return state
  105. def _init_direct_chat_node(self, state: AgentState) -> AgentState:
  106. """初始化直接聊天模式的状态"""
  107. try:
  108. # 从state中获取路由模式,而不是从配置文件读取
  109. routing_mode = state.get("routing_mode", "chat_direct")
  110. # 设置直接聊天模式的分类状态
  111. state["question_type"] = "CHAT"
  112. state["classification_confidence"] = 1.0
  113. state["classification_reason"] = "配置为直接聊天模式"
  114. state["classification_method"] = "direct_chat"
  115. state["routing_mode"] = routing_mode
  116. state["current_step"] = "direct_chat_init"
  117. state["execution_path"].append("init_direct_chat")
  118. print(f"[DIRECT_CHAT] 直接聊天模式初始化完成")
  119. return state
  120. except Exception as e:
  121. print(f"[ERROR] 直接聊天模式初始化异常: {str(e)}")
  122. state["error"] = f"直接聊天模式初始化失败: {str(e)}"
  123. state["error_code"] = 500
  124. state["execution_path"].append("init_direct_chat_error")
  125. return state
  126. def _classify_question_node(self, state: AgentState) -> AgentState:
  127. """问题分类节点 - 支持渐进式分类策略"""
  128. try:
  129. # 从state中获取路由模式,而不是从配置文件读取
  130. routing_mode = state.get("routing_mode", "hybrid")
  131. print(f"[CLASSIFY_NODE] 开始分类问题: {state['question']}")
  132. # 获取上下文类型(如果有的话)
  133. context_type = state.get("context_type")
  134. if context_type:
  135. print(f"[CLASSIFY_NODE] 检测到上下文类型: {context_type}")
  136. # 使用渐进式分类策略,传递路由模式
  137. classification_result = self.classifier.classify(state["question"], context_type, routing_mode)
  138. # 更新状态
  139. state["question_type"] = classification_result.question_type
  140. state["classification_confidence"] = classification_result.confidence
  141. state["classification_reason"] = classification_result.reason
  142. state["classification_method"] = classification_result.method
  143. state["routing_mode"] = routing_mode
  144. state["current_step"] = "classified"
  145. state["execution_path"].append("classify")
  146. print(f"[CLASSIFY_NODE] 分类结果: {classification_result.question_type}, 置信度: {classification_result.confidence}")
  147. print(f"[CLASSIFY_NODE] 路由模式: {routing_mode}, 分类方法: {classification_result.method}")
  148. return state
  149. except Exception as e:
  150. print(f"[ERROR] 问题分类异常: {str(e)}")
  151. state["error"] = f"问题分类失败: {str(e)}"
  152. state["error_code"] = 500
  153. state["execution_path"].append("classify_error")
  154. return state
  155. def _agent_database_node(self, state: AgentState) -> AgentState:
  156. """数据库Agent节点 - 直接工具调用模式"""
  157. try:
  158. print(f"[DATABASE_AGENT] 开始处理数据库查询: {state['question']}")
  159. question = state["question"]
  160. # 步骤1:生成SQL
  161. print(f"[DATABASE_AGENT] 步骤1:生成SQL")
  162. sql_result = generate_sql.invoke({"question": question, "allow_llm_to_see_data": True})
  163. if not sql_result.get("success"):
  164. print(f"[DATABASE_AGENT] SQL生成失败: {sql_result.get('error')}")
  165. state["error"] = sql_result.get("error", "SQL生成失败")
  166. state["error_code"] = 500
  167. state["current_step"] = "database_error"
  168. state["execution_path"].append("agent_database_error")
  169. return state
  170. sql = sql_result.get("sql")
  171. state["sql"] = sql
  172. print(f"[DATABASE_AGENT] SQL生成成功: {sql}")
  173. # 步骤1.5:检查是否为解释性响应而非SQL
  174. error_type = sql_result.get("error_type")
  175. if error_type == "llm_explanation":
  176. # LLM返回了解释性文本,直接作为最终答案
  177. explanation = sql_result.get("error", "")
  178. state["chat_response"] = explanation + " 请尝试提问其它问题。"
  179. state["current_step"] = "database_completed"
  180. state["execution_path"].append("agent_database")
  181. print(f"[DATABASE_AGENT] 返回LLM解释性答案: {explanation}")
  182. return state
  183. # 额外验证:检查SQL格式(防止工具误判)
  184. from agent.utils import _is_valid_sql_format
  185. if not _is_valid_sql_format(sql):
  186. # 内容看起来不是SQL,当作解释性响应处理
  187. state["chat_response"] = sql + " 请尝试提问其它问题。"
  188. state["current_step"] = "database_completed"
  189. state["execution_path"].append("agent_database")
  190. print(f"[DATABASE_AGENT] 内容不是有效SQL,当作解释返回: {sql}")
  191. return state
  192. # 步骤2:执行SQL
  193. print(f"[DATABASE_AGENT] 步骤2:执行SQL")
  194. execute_result = execute_sql.invoke({"sql": sql})
  195. if not execute_result.get("success"):
  196. print(f"[DATABASE_AGENT] SQL执行失败: {execute_result.get('error')}")
  197. state["error"] = execute_result.get("error", "SQL执行失败")
  198. state["error_code"] = 500
  199. state["current_step"] = "database_error"
  200. state["execution_path"].append("agent_database_error")
  201. return state
  202. query_result = execute_result.get("data_result")
  203. state["query_result"] = query_result
  204. print(f"[DATABASE_AGENT] SQL执行成功,返回 {query_result.get('row_count', 0)} 行数据")
  205. # 步骤3:生成摘要(可通过配置控制,仅在有数据时生成)
  206. if ENABLE_RESULT_SUMMARY and query_result.get('row_count', 0) > 0:
  207. print(f"[DATABASE_AGENT] 步骤3:生成摘要")
  208. # 重要:提取原始问题用于摘要生成,避免历史记录循环嵌套
  209. original_question = self._extract_original_question(question)
  210. print(f"[DATABASE_AGENT] 原始问题: {original_question}")
  211. summary_result = generate_summary.invoke({
  212. "question": original_question, # 使用原始问题而不是enhanced_question
  213. "query_result": query_result,
  214. "sql": sql
  215. })
  216. if not summary_result.get("success"):
  217. print(f"[DATABASE_AGENT] 摘要生成失败: {summary_result.get('message')}")
  218. # 摘要生成失败不是致命错误,使用默认摘要
  219. state["summary"] = f"查询执行完成,共返回 {query_result.get('row_count', 0)} 条记录。"
  220. else:
  221. state["summary"] = summary_result.get("summary")
  222. print(f"[DATABASE_AGENT] 摘要生成成功")
  223. else:
  224. print(f"[DATABASE_AGENT] 跳过摘要生成(ENABLE_RESULT_SUMMARY={ENABLE_RESULT_SUMMARY},数据行数={query_result.get('row_count', 0)})")
  225. # 不生成摘要时,不设置summary字段,让格式化响应节点决定如何处理
  226. state["current_step"] = "database_completed"
  227. state["execution_path"].append("agent_database")
  228. print(f"[DATABASE_AGENT] 数据库查询完成")
  229. return state
  230. except Exception as e:
  231. print(f"[ERROR] 数据库Agent异常: {str(e)}")
  232. import traceback
  233. print(f"[ERROR] 详细错误信息: {traceback.format_exc()}")
  234. state["error"] = f"数据库查询失败: {str(e)}"
  235. state["error_code"] = 500
  236. state["current_step"] = "database_error"
  237. state["execution_path"].append("agent_database_error")
  238. return state
  239. def _agent_chat_node(self, state: AgentState) -> AgentState:
  240. """聊天Agent节点 - 直接工具调用模式"""
  241. try:
  242. print(f"[CHAT_AGENT] 开始处理聊天: {state['question']}")
  243. question = state["question"]
  244. # 构建上下文 - 仅使用真实的对话历史上下文
  245. # 注意:不要将分类原因传递给LLM,那是系统内部的路由信息
  246. enable_context_injection = self.config.get("chat_agent", {}).get("enable_context_injection", True)
  247. context = None
  248. if enable_context_injection:
  249. # TODO: 在这里可以添加真实的对话历史上下文
  250. # 例如从Redis或其他存储中获取最近的对话记录
  251. # context = get_conversation_history(state.get("session_id"))
  252. pass
  253. # 直接调用general_chat工具
  254. print(f"[CHAT_AGENT] 调用general_chat工具")
  255. chat_result = general_chat.invoke({
  256. "question": question,
  257. "context": context
  258. })
  259. if chat_result.get("success"):
  260. state["chat_response"] = chat_result.get("response", "")
  261. print(f"[CHAT_AGENT] 聊天处理成功")
  262. else:
  263. # 处理失败,使用备用响应
  264. state["chat_response"] = chat_result.get("response", "抱歉,我暂时无法处理您的问题。请稍后再试。")
  265. print(f"[CHAT_AGENT] 聊天处理失败,使用备用响应: {chat_result.get('error')}")
  266. state["current_step"] = "chat_completed"
  267. state["execution_path"].append("agent_chat")
  268. print(f"[CHAT_AGENT] 聊天处理完成")
  269. return state
  270. except Exception as e:
  271. print(f"[ERROR] 聊天Agent异常: {str(e)}")
  272. import traceback
  273. print(f"[ERROR] 详细错误信息: {traceback.format_exc()}")
  274. state["chat_response"] = "抱歉,我暂时无法处理您的问题。请稍后再试,或者尝试询问数据相关的问题。"
  275. state["current_step"] = "chat_error"
  276. state["execution_path"].append("agent_chat_error")
  277. return state
  278. def _format_response_node(self, state: AgentState) -> AgentState:
  279. """格式化最终响应节点"""
  280. try:
  281. print(f"[FORMAT_NODE] 开始格式化响应,问题类型: {state['question_type']}")
  282. state["current_step"] = "completed"
  283. state["execution_path"].append("format_response")
  284. # 根据问题类型和执行状态格式化响应
  285. if state.get("error"):
  286. # 有错误的情况
  287. state["final_response"] = {
  288. "success": False,
  289. "error": state["error"],
  290. "error_code": state.get("error_code", 500),
  291. "question_type": state["question_type"],
  292. "execution_path": state["execution_path"],
  293. "classification_info": {
  294. "confidence": state.get("classification_confidence", 0),
  295. "reason": state.get("classification_reason", ""),
  296. "method": state.get("classification_method", "")
  297. }
  298. }
  299. elif state["question_type"] == "DATABASE":
  300. # 数据库查询类型
  301. if state.get("chat_response"):
  302. # SQL生成失败的解释性响应(不受ENABLE_RESULT_SUMMARY配置影响)
  303. state["final_response"] = {
  304. "success": True,
  305. "response": state["chat_response"],
  306. "type": "DATABASE",
  307. "sql": state.get("sql"),
  308. "query_result": state.get("query_result"), # 获取query_result字段
  309. "execution_path": state["execution_path"],
  310. "classification_info": {
  311. "confidence": state["classification_confidence"],
  312. "reason": state["classification_reason"],
  313. "method": state["classification_method"]
  314. }
  315. }
  316. elif state.get("summary"):
  317. # 正常的数据库查询结果,有摘要的情况
  318. # 将summary的值同时赋给response字段(为将来移除summary字段做准备)
  319. state["final_response"] = {
  320. "success": True,
  321. "type": "DATABASE",
  322. "response": state["summary"], # 新增:将summary的值赋给response
  323. "sql": state.get("sql"),
  324. "query_result": state.get("query_result"), # 获取query_result字段
  325. "summary": state["summary"], # 暂时保留summary字段
  326. "execution_path": state["execution_path"],
  327. "classification_info": {
  328. "confidence": state["classification_confidence"],
  329. "reason": state["classification_reason"],
  330. "method": state["classification_method"]
  331. }
  332. }
  333. elif state.get("query_result"):
  334. # 有数据但没有摘要(摘要被配置禁用)
  335. query_result = state.get("query_result")
  336. row_count = query_result.get("row_count", 0)
  337. # 构建基本响应,不包含summary字段和response字段
  338. # 用户应该直接从query_result.columns和query_result.rows获取数据
  339. state["final_response"] = {
  340. "success": True,
  341. "type": "DATABASE",
  342. "sql": state.get("sql"),
  343. "query_result": query_result, # 获取query_result字段
  344. "execution_path": state["execution_path"],
  345. "classification_info": {
  346. "confidence": state["classification_confidence"],
  347. "reason": state["classification_reason"],
  348. "method": state["classification_method"]
  349. }
  350. }
  351. else:
  352. # 数据库查询失败,没有任何结果
  353. state["final_response"] = {
  354. "success": False,
  355. "error": state.get("error", "数据库查询未完成"),
  356. "type": "DATABASE",
  357. "sql": state.get("sql"),
  358. "execution_path": state["execution_path"]
  359. }
  360. else:
  361. # 聊天类型
  362. state["final_response"] = {
  363. "success": True,
  364. "response": state.get("chat_response", ""),
  365. "type": "CHAT",
  366. "execution_path": state["execution_path"],
  367. "classification_info": {
  368. "confidence": state["classification_confidence"],
  369. "reason": state["classification_reason"],
  370. "method": state["classification_method"]
  371. }
  372. }
  373. print(f"[FORMAT_NODE] 响应格式化完成")
  374. return state
  375. except Exception as e:
  376. print(f"[ERROR] 响应格式化异常: {str(e)}")
  377. state["final_response"] = {
  378. "success": False,
  379. "error": f"响应格式化异常: {str(e)}",
  380. "error_code": 500,
  381. "execution_path": state["execution_path"]
  382. }
  383. return state
  384. def _route_after_classification(self, state: AgentState) -> Literal["DATABASE", "CHAT"]:
  385. """
  386. 分类后的路由决策
  387. 完全信任QuestionClassifier的决策:
  388. - DATABASE类型 → 数据库Agent
  389. - CHAT和UNCERTAIN类型 → 聊天Agent
  390. 这样避免了双重决策的冲突,所有分类逻辑都集中在QuestionClassifier中
  391. """
  392. question_type = state["question_type"]
  393. confidence = state["classification_confidence"]
  394. print(f"[ROUTE] 分类路由: {question_type}, 置信度: {confidence} (完全信任分类器决策)")
  395. if question_type == "DATABASE":
  396. return "DATABASE"
  397. else:
  398. # 将 "CHAT" 和 "UNCERTAIN" 类型都路由到聊天流程
  399. # 聊天Agent可以处理不确定的情况,并在必要时引导用户提供更多信息
  400. return "CHAT"
  401. def process_question(self, question: str, session_id: str = None, context_type: str = None, routing_mode: str = None) -> Dict[str, Any]:
  402. """
  403. 统一的问题处理入口
  404. Args:
  405. question: 用户问题
  406. session_id: 会话ID
  407. context_type: 上下文类型 ("DATABASE" 或 "CHAT"),用于渐进式分类
  408. routing_mode: 路由模式,可选,用于覆盖配置文件设置
  409. Returns:
  410. Dict包含完整的处理结果
  411. """
  412. try:
  413. print(f"[CITU_AGENT] 开始处理问题: {question}")
  414. if context_type:
  415. print(f"[CITU_AGENT] 上下文类型: {context_type}")
  416. if routing_mode:
  417. print(f"[CITU_AGENT] 使用指定路由模式: {routing_mode}")
  418. # 动态创建workflow(基于路由模式)
  419. workflow = self._create_workflow(routing_mode)
  420. # 初始化状态
  421. initial_state = self._create_initial_state(question, session_id, context_type, routing_mode)
  422. # 执行工作流
  423. final_state = workflow.invoke(
  424. initial_state,
  425. config={
  426. "configurable": {"session_id": session_id}
  427. } if session_id else None
  428. )
  429. # 提取最终结果
  430. result = final_state["final_response"]
  431. print(f"[CITU_AGENT] 问题处理完成: {result.get('success', False)}")
  432. return result
  433. except Exception as e:
  434. print(f"[ERROR] Agent执行异常: {str(e)}")
  435. return {
  436. "success": False,
  437. "error": f"Agent系统异常: {str(e)}",
  438. "error_code": 500,
  439. "execution_path": ["error"]
  440. }
  441. def _create_initial_state(self, question: str, session_id: str = None, context_type: str = None, routing_mode: str = None) -> AgentState:
  442. """创建初始状态 - 支持渐进式分类"""
  443. # 确定使用的路由模式
  444. if routing_mode:
  445. effective_routing_mode = routing_mode
  446. else:
  447. try:
  448. from app_config import QUESTION_ROUTING_MODE
  449. effective_routing_mode = QUESTION_ROUTING_MODE
  450. except ImportError:
  451. effective_routing_mode = "hybrid"
  452. return AgentState(
  453. # 输入信息
  454. question=question,
  455. session_id=session_id,
  456. # 上下文信息
  457. context_type=context_type,
  458. # 分类结果 (初始值,会在分类节点或直接模式初始化节点中更新)
  459. question_type="UNCERTAIN",
  460. classification_confidence=0.0,
  461. classification_reason="",
  462. classification_method="",
  463. # 数据库查询流程状态
  464. sql=None,
  465. sql_generation_attempts=0,
  466. query_result=None,
  467. summary=None,
  468. # 聊天响应
  469. chat_response=None,
  470. # 最终输出
  471. final_response={},
  472. # 错误处理
  473. error=None,
  474. error_code=None,
  475. # 流程控制
  476. current_step="initialized",
  477. execution_path=["start"],
  478. retry_count=0,
  479. max_retries=3,
  480. # 调试信息
  481. debug_info={},
  482. # 路由模式
  483. routing_mode=effective_routing_mode
  484. )
  485. def _extract_original_question(self, question: str) -> str:
  486. """
  487. 从enhanced_question中提取原始问题
  488. Args:
  489. question: 可能包含上下文的问题
  490. Returns:
  491. str: 原始问题
  492. """
  493. try:
  494. # 检查是否为enhanced_question格式
  495. if "\n[CONTEXT]\n" in question and "\n[CURRENT]\n" in question:
  496. # 提取[CURRENT]标签后的内容
  497. current_start = question.find("\n[CURRENT]\n")
  498. if current_start != -1:
  499. original_question = question[current_start + len("\n[CURRENT]\n"):].strip()
  500. return original_question
  501. # 如果不是enhanced_question格式,直接返回原问题
  502. return question.strip()
  503. except Exception as e:
  504. print(f"[WARNING] 提取原始问题失败: {str(e)}")
  505. return question.strip()
  506. def health_check(self) -> Dict[str, Any]:
  507. """健康检查"""
  508. try:
  509. # 从配置获取健康检查参数
  510. from agent.config import get_nested_config
  511. test_question = get_nested_config(self.config, "health_check.test_question", "你好")
  512. enable_full_test = get_nested_config(self.config, "health_check.enable_full_test", True)
  513. if enable_full_test:
  514. # 完整流程测试
  515. test_result = self.process_question(test_question, "health_check")
  516. return {
  517. "status": "healthy" if test_result.get("success") else "degraded",
  518. "test_result": test_result.get("success", False),
  519. "workflow_compiled": True, # 动态创建,始终可用
  520. "tools_count": len(self.tools),
  521. "agent_reuse_enabled": False,
  522. "message": "Agent健康检查完成"
  523. }
  524. else:
  525. # 简单检查
  526. return {
  527. "status": "healthy",
  528. "test_result": True,
  529. "workflow_compiled": True, # 动态创建,始终可用
  530. "tools_count": len(self.tools),
  531. "agent_reuse_enabled": False,
  532. "message": "Agent简单健康检查完成"
  533. }
  534. except Exception as e:
  535. return {
  536. "status": "unhealthy",
  537. "error": str(e),
  538. "workflow_compiled": True, # 动态创建,始终可用
  539. "tools_count": len(self.tools) if hasattr(self, 'tools') else 0,
  540. "agent_reuse_enabled": False,
  541. "message": "Agent健康检查失败"
  542. }