From e14e3ee7a500ee9efeebfd52d7d971091a44f588 Mon Sep 17 00:00:00 2001 From: Jeason <1710884619@qq.com> Date: Fri, 20 Mar 2026 16:50:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=A7=E6=94=B9=EF=BC=8C=E6=9C=AA=E9=AA=8C?= =?UTF-8?q?=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 12 +- .env.example | 23 + data/tsp_assistant.db | Bin 192512 -> 196608 bytes init_database.py | 36 +- requirements.txt | 5 +- scripts/migrate_embeddings.py | 81 ++++ src/agent/__init__.py | 20 +- src/agent/action_executor.py | 255 ---------- src/agent/agent_assistant_core.py | 268 ----------- src/agent/agent_core.py | 313 ------------- src/agent/agent_message_handler.py | 243 ---------- src/agent/agent_sample_actions.py | 405 ---------------- src/agent/auto_monitor.py | 4 +- src/agent/executor.py | 589 ------------------------ src/agent/goal_manager.py | 573 ----------------------- src/agent/intelligent_agent.py | 371 --------------- src/agent/planner.py | 409 ---------------- src/agent/react_agent.py | 345 ++++++++++++++ src/agent/reasoning_engine.py | 479 ------------------- src/agent/tool_manager.py | 435 ----------------- src/agent_assistant.py | 50 +- src/agent_assistant_new.py | 322 ------------- src/config/unified_config.py | 27 ++ src/core/embedding_client.py | 152 ++++++ src/core/models.py | 39 ++ src/core/query_optimizer.py | 1 + src/core/vector_store.py | 164 +++++++ src/dialogue/conversation_history.py | 133 +++++- src/dialogue/realtime_chat.py | 109 ++--- src/knowledge_base/knowledge_manager.py | 164 ++++++- src/web/blueprints/agent.py | 39 +- src/web/blueprints/conversations.py | 65 +++ src/web/blueprints/monitoring.py | 37 +- src/web/static/js/dashboard.js | 38 ++ src/web/templates/dashboard.html | 18 + tsp_assistant.db | Bin 65536 -> 73728 bytes 36 files changed, 1419 insertions(+), 4805 deletions(-) create mode 100644 scripts/migrate_embeddings.py delete mode 100644 src/agent/action_executor.py delete mode 100644 src/agent/agent_assistant_core.py delete mode 100644 src/agent/agent_core.py delete mode 100644 src/agent/agent_message_handler.py delete mode 100644 src/agent/agent_sample_actions.py delete mode 100644 src/agent/executor.py delete mode 100644 src/agent/goal_manager.py delete mode 100644 src/agent/intelligent_agent.py delete mode 100644 src/agent/planner.py create mode 100644 src/agent/react_agent.py delete mode 100644 src/agent/reasoning_engine.py delete mode 100644 src/agent/tool_manager.py delete mode 100644 src/agent_assistant_new.py create mode 100644 src/core/embedding_client.py create mode 100644 src/core/vector_store.py diff --git a/.env b/.env index 376ec89..be2b728 100644 --- a/.env +++ b/.env @@ -48,7 +48,7 @@ LLM_API_KEY=sk-Gce85QLROESeOWf3icd2mQnYHOrmMYojwVPQ0AubMjGQ5ZE2 LLM_BASE_URL=https://gemini.jeason.online/v1 # The specific model to use, e.g., "qwen-plus-latest", "gpt-3.5-turbo", "claude-3-sonnet-20240229" -LLM_MODEL=gemini-2.5-flash +LLM_MODEL=mimo-v2-flash # The temperature for the model's responses (0.0 to 2.0). LLM_TEMPERATURE=0.7 @@ -123,3 +123,13 @@ REDIS_DEFAULT_TTL=3600 # Enable Redis cache (set to False to disable caching) REDIS_ENABLED=True + + +# ============================================================================ +# EMBEDDING CONFIGURATION (知识库向量检索 - 本地模型) +# ============================================================================ +# 暂时禁用,等有合适的 embedding API 或服务器资源时再启用 +EMBEDDING_ENABLED=False +EMBEDDING_MODEL=BAAI/bge-small-zh-v1.5 +EMBEDDING_DIMENSION=512 +EMBEDDING_SIMILARITY_THRESHOLD=0.5 diff --git a/.env.example b/.env.example index 858e4b5..72fcf0e 100644 --- a/.env.example +++ b/.env.example @@ -124,3 +124,26 @@ REDIS_DEFAULT_TTL=3600 # Enable Redis cache (set to False to disable caching) REDIS_ENABLED=True + + +# ============================================================================ +# EMBEDDING CONFIGURATION (知识库向量检索 - 本地模型) +# ============================================================================ +# 启用 Embedding 语义检索(禁用则降级为关键词匹配) +EMBEDDING_ENABLED=True + +# 本地 embedding 模型名称(首次运行自动从 HuggingFace 下载) +# 推荐模型: +# BAAI/bge-small-zh-v1.5 (~95MB, 512维, 中文效果好, 内存占用~150MB) +# BAAI/bge-base-zh-v1.5 (~400MB, 768维, 中文效果更好) +# shibing624/text2vec-base-chinese (~400MB, 768维, 中文专优) +EMBEDDING_MODEL=BAAI/bge-small-zh-v1.5 + +# 向量维度(需与模型匹配) +EMBEDDING_DIMENSION=512 + +# 语义搜索相似度阈值(0.0-1.0,越高越严格) +EMBEDDING_SIMILARITY_THRESHOLD=0.5 + +# Embedding 缓存过期时间(秒),默认 1 天 +EMBEDDING_CACHE_TTL=86400 diff --git a/data/tsp_assistant.db b/data/tsp_assistant.db index bda581e31911e5559ff104684cb78e09d52e2297..53c8b025719ce5f87a7eafd1ec068bec0f11ac5f 100644 GIT binary patch delta 12256 zcmeI2d2kd}8o;}ICYd9XOn^WL5Hf^YVCegpMUycG6=9(e+&@qQLJBAaZ4#s`x9AYa z1>_h;7zp7AlE9)Qz(^+KAi9*bYt`#h{2`}ItYW@eIL2>g{) z6353SqVG8^?~~_`k423;wRY(LbFQbK##m3s&*&qlC+}OdHR@X&DG0bLV+MBKmGmd< zn&;2op*-rU{uFJD?2iR1>6!mBhGL#YpQA141xtb@b)xyE`M>7x&DYF+^H=77nLoxF z(4$DF!*SUCPPP~GU>KT>8fn95WL2?{p)d_y&yQXX~Cz`h6EQt%OYmEubKxvO% zgT9Qqs~>e}E?$OVyVov$4)b`lXbL(L z?Yr9LnlCgjU^lQ*Yz%rForko6CL}R40eNuh#I#(*U`KpKrvf`W{f8aCs#AFzHm={0 zw<&+qi(6h%U2a>mW%I_(>o?`E+qiX;Tv{TZJ?QQ1^1pW4UsV?9Dhs}G&}QKoD>FIqhp$}k*rnVcvhgqDSb$37@0(DCQEa;AX@sEYNT|R^X!L72#i3B8L^paq%;KC zQZeVr5;zUAojf>Gjg;cMPUO`s5jsqDV3Ao-mn6vsI9imEjnc zB*pRfhe_E;+R8HoL?SU2kkasnK00!qAtuK&0#AtJBBm(~Pq>ZED6|+CSC40(Ki^L*25a1%VOA+#yqqlm>s5u`%_@&>#C2BG+V+(l2(?&S$KOU z^f1*(>8@9r{V)ldqj=F6i>XFRLt5QNxk_lB5cpV@5eebOw+!fPddIGAdr6xywDk#>vQNW#icIL^~z z+;B71NU3j!6lM|x!Lg$Do-m_Ci`yiaCu|!Cf)q9Pi>V+vfq<0>mLT1W&1Z~xDohqc z%Zuo3(_A-P8{U4HuyLQCd7u5tWMLp37EO4k87a_A8w1UhQP5jyOyw8T=^@|XS7BVuKu_JU7$5maP{*F zl+qe$xFS}fOSDD?uC}j4pPPz=017Dfg0{8 zceDuAntw#=-J{~ZF}iPl5uco<6&y^#sGQ}w_7%C7750Z0=URFnIe)p4VeI}v-o|y# zbkhXw)`I*kp`4cUX4?G1jfF4dTb{Bn&w0$gd=@OOXV0}54F!d33bz)hZint}$}e!* zjpLk5;yCBxTN9l28>vp|gH%oa=5@}`e#vz1{n@1NVLAVKD=S_w$EPlt>8vr2pVB)b zNT?O$5x0FD4f@o(U;5v>nn9%K{^ZsiO+0FT7BN2y@>yY)c4VN5NKQIJq@o#!-EQx} zD|5`laUzf4I0{zMJi+zck{gS?o%;i)UiY^<0-cS4%cU^>{B@db`Og)+xc2${!U(0a zHiol83?msX;t6k?M{rhw<#~aRdK7xX@q%DftkrxKF<*6$TK$pPK3S7v7+{!+aVH`j zNYa(WO4FKzbR(j_qT3svigai{(tL*f1O1P~QJYZA^d#y~B~kY=iaEu{#ydaH)%Rrs zIUB>kN`=O0t~+Jdo9VlV#mL(&KpT_Mw_B32567XIp1;q)<`_^*3y$T@L8p6GufmRL z(Wxz~F>R4igDgYh7GVpKWe&%~($0ePWCS5f$Jlj<4#|M==S0icSA1G-=)B&sLoN7V zQLWtC8mub{H12?FNVdrhWq!v|x#^(qz$N>#WxnzQfgP{;-R*MI313}hpzCm;ey`72 z>Fuohx~N!gZwoXvNlMox<^DtMa_9bt-(T<83%`*|8ony3^OZaNwY$O^|ElN+bPYGb z2IQt%e^b4`rW9@l>n{e6oPb~Xjy3q}_V^Aq%QcO@x{C0igGCj=`b!G9vDkOC&gVMr zbMFs4Qg)_yP|7dM5BORtO;=td@8GK|Z+)#@7w5gAQA~?Fak^ z9MGpieDg%0t14Jq?Q^xr`%VX%VFEiM?2)tKgcTdOSttJf-L)&~eVLQFdMFQbozGpdOu^)UDLL!F}a&^Cj1}dUWDbAmrY;Dype& z_O>_3=Z*xL_rry^b&v0CL-5SW>m9X5BZTT;S*7p5xv=&?<4VkiTXIcvSW}>TfBP{b z<+MDWA{mWg$%p#c9kv6xrYel{wpaS<&VZ)mQg>L)pac*}w*iF=%XhvLx(yqXBDr#h zxBX4|T&e$!vw`MXZ)+P280<{mT@S)mi!hV{pRJ_88A!cyS=aue{QY&+tC<_uW!fyl zTuY|7HNT(`q8oh7R3tIewele>#W_8luU%7^zaG}f%43+Py!#mBrCl}YSek2L z7M7|3^|)Tjz#evW9Yam=l)zJ*;QC-GmXUnoZA06$OjfU+Jd0$k#b{Cj1!laCB))6B z4#DtiV@k=B8SuW$*=4rtN*?U?L}etC^gYfO($Hj>dDwLn^+YMgm1RKf8koP+kvmNT z<{Yw3C*Bn=`Cc#bUEFhbcBsT8g2EYmFc~WDfAF&ZWLww}yj}IKpY-V1AxL#8HcNUY zyI#L!=@-9Q*DJ;WV(>10vJ%nC()%J>z0TO`ZeS6g)*>gdbI*P!h6vba^k;{(<51sU0+9Ov~D$ZI` z3BDU>I;VtBaHfzyhHqZ)I1KKnX#H42xa;tsK?cD?HPBm_3V?3Ggq#c%7ij8&IfUm4 zolw@CZl{hIV7R+(gF0Fl(~*0p-eZSjB_#?BF$_|I6_#~^Dh!_1{*d+ifA6~!yHH#% z6KTsu%$4Q|NvB3iRjTq>KV_qlQq=&J$B|Oi?IqUOWg}svR5gGN-C?dRs?0D_syY=I zDOH6JLk!i1MoLw8munj-RgILYhVOtZ$W|$jkvyDeQ8w**(}t)$JKT0pOI~7<#0PGxpAFZTqBg|5 zje;r}L*fi8&hA6r)AOQXCd=b+LSj}Q^PZj;4KpboQl;X|*vz{+JsnMt(G@{!FvzWzfamkitQi?*k zt&{`Q2?~{q;9?7$K(Wv<JGsqZ&m=?3jLBba#&iXlPA@!nFgGRJwFXf{LOu~i^2lZKDfxs+ zKDJ0|DG69|2|x~>q%>59F|kWNB4gwtxj^0{#_(ly!{_T~qE41jZ&Mr40e5x2LP>wM z<0t>8>#*J*%)=W0LOxdeBG#mGr}kXa12qoI*Qmax)xu_lHUY+R?BR zoXg8qDq}fPBINHcB(mwsU5{6_)b!p;jejf;Ct)*PUX#PLRCY7O#mRHLR&`VmrjN<0>Y5}GH3!u+6%?;mDohuN7{u4y11 zxV|)ZfuhtF6BP>th2T)UbC_%UGS)Q}Z}RfzLtMuQckY}p%`DA%IB$p>zbJ&d;}-{n zc^}si;#x-ew&r+ekoWYk(fL@{C^ysuSt%K z3W3pB@A*UkzOR|@J{P~x7po7kk)SXY7K4qrAqjqB;DM{2^ZLX{eGzz$pP!8N`eV#& zY_^~4{hA+WTGh9isFnP>=OHaIzq*$R+G1BaUsT-!+VO@_&eJGFN4QI^@jjO@GsZ@} zoaZ`NR>kZlD$y%GFvv#RS6VT2mW_O|7fhfcG+ljBe*k=-{@7R~VdtA$SM7C)%8Q`# z)?uh~(bdOwc5!s)qU$0zIL<~JdH+D7k({@ki*zl`wLzPOhA2A|5$Lmt7O)X7JcwJf z5g{1lJFbcLsaE(a9=P1lS!fnJ(<01|^K?5K?E_7|+XLqXWhgAvcR{!4y!o^`A=JY6 zTmzT?_!g{Og1Z0f7OY%?{lS0t7Qld2Px$1x^VS;4BHAa;RNsJ6Nr8s6oj3g+;y zHzcpoWEP$mNOE*2ilo!}?{$|@R9mm9Q0pY+%DV(s{3yR8`^D$$W!6;h#7tm^6NfX` zYt!_Kr-c1oCcYmO5mX8cXhEi6@HjTdRW_zWa< z8WVaqtU5LA%^ne_ot3hf%giNZW{auFW-hgw!&?p#m|&`MrmZttG*+q)qepL*-^&; diff --git a/init_database.py b/init_database.py index 7982fb2..4dee5f6 100644 --- a/init_database.py +++ b/init_database.py @@ -20,7 +20,7 @@ from src.utils.helpers import setup_logging from src.core.database import db_manager from src.core.models import ( Base, WorkOrder, KnowledgeEntry, Conversation, Analytics, Alert, VehicleData, - WorkOrderSuggestion, WorkOrderProcessHistory, User + WorkOrderSuggestion, WorkOrderProcessHistory, User, ChatSession ) class DatabaseInitializer: @@ -197,7 +197,8 @@ class DatabaseInitializer: self._migrate_workorder_dispatch_fields, self._migrate_workorder_process_history_table, self._migrate_analytics_enhancements, - self._migrate_system_optimization_fields + self._migrate_system_optimization_fields, + self._migrate_chat_sessions_table, ] success_count = 0 @@ -445,6 +446,37 @@ class DatabaseInitializer: return success + def _migrate_chat_sessions_table(self) -> bool: + """迁移:创建 chat_sessions 表并为 conversations 添加 session_id 字段""" + print(" 检查会话管理表...") + + try: + inspector = inspect(db_manager.engine) + + # 1. 创建 chat_sessions 表 + if 'chat_sessions' not in inspector.get_table_names(): + print(" 创建 chat_sessions 表...") + ChatSession.__table__.create(db_manager.engine, checkfirst=True) + print(" chat_sessions 表创建成功") + else: + print(" chat_sessions 表已存在") + + # 2. 为 conversations 表添加 session_id 字段 + if not self._column_exists('conversations', 'session_id'): + print(" 添加 conversations.session_id 字段...") + self._add_table_columns('conversations', [ + ('session_id', 'VARCHAR(100)') + ]) + print(" session_id 字段添加成功") + else: + print(" conversations.session_id 字段已存在") + + return True + + except Exception as e: + print(f" 会话管理表迁移失败: {e}") + return False + def _add_table_columns(self, table_name: str, fields: List[tuple]) -> bool: """为表添加字段""" try: diff --git a/requirements.txt b/requirements.txt index 9f2739f..d126998 100644 --- a/requirements.txt +++ b/requirements.txt @@ -68,4 +68,7 @@ pydantic==2.9.2 marshmallow==3.23.3 # 飞书官方 SDK(事件订阅 2.0 - 长连接模式) -lark-oapi==1.3.5 \ No newline at end of file +lark-oapi==1.3.5 + +# 本地 Embedding 模型(可选,EMBEDDING_ENABLED=True 时需要) +# pip install sentence-transformers torch \ No newline at end of file diff --git a/scripts/migrate_embeddings.py b/scripts/migrate_embeddings.py new file mode 100644 index 0000000..27114e5 --- /dev/null +++ b/scripts/migrate_embeddings.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +批量为已有知识库条目生成 Embedding 向量(本地模型) +运行方式: python scripts/migrate_embeddings.py + +首次运行会自动下载模型(~95MB),之后走本地缓存 +""" + +import sys +import os +import json +import logging + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from src.config.unified_config import get_config +from src.core.database import db_manager +from src.core.models import KnowledgeEntry +from src.core.embedding_client import EmbeddingClient +from src.core.vector_store import vector_store + +logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") +logger = logging.getLogger(__name__) + + +def migrate(): + config = get_config() + if not config.embedding.enabled: + logger.warning("Embedding 功能未启用,请在 .env 中设置 EMBEDDING_ENABLED=True") + return + + client = EmbeddingClient() + + # 测试模型加载 + logger.info("正在加载本地 embedding 模型(首次运行需下载)...") + if not client.test_connection(): + logger.error("Embedding 模型加载失败,请检查 sentence-transformers 是否安装") + return + logger.info("模型加载成功") + + # 获取所有需要生成 embedding 的条目 + with db_manager.get_session() as session: + entries = session.query(KnowledgeEntry).filter( + KnowledgeEntry.is_active == True + ).all() + + # 筛选出没有 embedding 的条目 + to_process = [] + for entry in entries: + if not entry.vector_embedding or entry.vector_embedding.strip() == '': + to_process.append(entry) + + logger.info(f"共 {len(entries)} 条活跃知识,{len(to_process)} 条需要生成 embedding") + + if not to_process: + logger.info("所有条目已有 embedding,无需迁移") + return + + # 批量生成 + texts = [e.question + " " + e.answer for e in to_process] + logger.info(f"开始批量生成 embedding...") + vectors = client.embed_batch(texts) + + success_count = 0 + for i, entry in enumerate(to_process): + vec = vectors[i] + if vec: + entry.vector_embedding = json.dumps(vec) + success_count += 1 + + session.commit() + logger.info(f"Embedding 生成完成: 成功 {success_count}/{len(to_process)}") + + # 重建向量索引 + vector_store.load_from_db() + logger.info(f"向量索引重建完成: {vector_store.size} 条") + + +if __name__ == "__main__": + migrate() diff --git a/src/agent/__init__.py b/src/agent/__init__.py index ffc5378..e99e4fd 100644 --- a/src/agent/__init__.py +++ b/src/agent/__init__.py @@ -1,22 +1,8 @@ - # -*- coding: utf-8 -*- """ -Agent模块初始化文件 +Agent模块 """ -from .agent_core import AgentCore, AgentState -from .planner import TaskPlanner -from .executor import TaskExecutor -from .tool_manager import ToolManager -from .reasoning_engine import ReasoningEngine -from .goal_manager import GoalManager +from .react_agent import ReactAgent -__all__ = [ - 'AgentCore', - 'AgentState', - 'TaskPlanner', - 'TaskExecutor', - 'ToolManager', - 'ReasoningEngine', - 'GoalManager' -] +__all__ = ['ReactAgent'] diff --git a/src/agent/action_executor.py b/src/agent/action_executor.py deleted file mode 100644 index abdea48..0000000 --- a/src/agent/action_executor.py +++ /dev/null @@ -1,255 +0,0 @@ - -# -*- coding: utf-8 -*- -""" -Agent动作执行器 - 执行具体的Agent动作 -""" - -import logging -import asyncio -from typing import Dict, Any, List, Optional -from datetime import datetime -import json - -from .intelligent_agent import AgentAction, ActionType, AlertContext, KnowledgeContext - -logger = logging.getLogger(__name__) - -class ActionExecutor: - """动作执行器""" - - def __init__(self, tsp_assistant=None): - self.tsp_assistant = tsp_assistant - self.execution_history = [] - self.action_handlers = { - ActionType.ALERT_RESPONSE: self._handle_alert_response, - ActionType.KNOWLEDGE_UPDATE: self._handle_knowledge_update, - ActionType.WORKORDER_CREATE: self._handle_workorder_create, - ActionType.SYSTEM_OPTIMIZE: self._handle_system_optimize, - ActionType.USER_NOTIFY: self._handle_user_notify - } - - async def execute_action(self, action: AgentAction) -> Dict[str, Any]: - """执行动作""" - try: - logger.info(f"开始执行动作: {action.action_type.value}") - start_time = datetime.now() - - # 获取处理器 - handler = self.action_handlers.get(action.action_type) - if not handler: - return {"success": False, "error": f"未找到动作处理器: {action.action_type}"} - - # 执行动作 - result = await handler(action) - - # 记录执行历史 - execution_record = { - "action_id": f"{action.action_type.value}_{datetime.now().timestamp()}", - "action_type": action.action_type.value, - "description": action.description, - "priority": action.priority, - "confidence": action.confidence, - "start_time": start_time.isoformat(), - "end_time": datetime.now().isoformat(), - "success": result.get("success", False), - "result": result - } - self.execution_history.append(execution_record) - - logger.info(f"动作执行完成: {action.action_type.value}, 结果: {result.get('success', False)}") - return result - - except Exception as e: - logger.error(f"执行动作失败: {e}") - return {"success": False, "error": str(e)} - - async def _handle_alert_response(self, action: AgentAction) -> Dict[str, Any]: - """处理预警响应""" - try: - alert_id = action.parameters.get("alert_id") - service = action.parameters.get("service") - - # 根据动作描述执行具体操作 - if "重启" in action.description: - return await self._restart_service(service) - elif "检查" in action.description: - return await self._check_system_status(alert_id) - elif "通知" in action.description: - return await self._notify_alert(alert_id, action.description) - else: - return await self._generic_alert_response(action) - - except Exception as e: - logger.error(f"处理预警响应失败: {e}") - return {"success": False, "error": str(e)} - - async def _handle_knowledge_update(self, action: AgentAction) -> Dict[str, Any]: - """处理知识库更新""" - try: - question = action.parameters.get("question") - enhanced_answer = action.parameters.get("enhanced_answer") - - if enhanced_answer: - # 更新知识库条目 - if self.tsp_assistant: - # 这里调用TSP助手的知识库更新方法 - result = await self._update_knowledge_entry(question, enhanced_answer) - return result - else: - return {"success": True, "message": "知识库条目已标记更新"} - else: - # 标记低置信度条目 - return await self._mark_low_confidence_knowledge(question) - - except Exception as e: - logger.error(f"处理知识库更新失败: {e}") - return {"success": False, "error": str(e)} - - async def _handle_workorder_create(self, action: AgentAction) -> Dict[str, Any]: - """处理工单创建""" - try: - title = action.parameters.get("title", "Agent自动创建工单") - description = action.description - category = action.parameters.get("category", "系统问题") - priority = action.parameters.get("priority", "medium") - - if self.tsp_assistant: - # 调用TSP助手创建工单 - workorder = self.tsp_assistant.create_work_order( - title=title, - description=description, - category=category, - priority=priority - ) - return {"success": True, "workorder": workorder} - else: - return {"success": True, "message": "工单创建请求已记录"} - - except Exception as e: - logger.error(f"处理工单创建失败: {e}") - return {"success": False, "error": str(e)} - - async def _handle_system_optimize(self, action: AgentAction) -> Dict[str, Any]: - """处理系统优化""" - try: - optimization_type = action.parameters.get("type", "general") - - if optimization_type == "performance": - return await self._optimize_performance(action) - elif optimization_type == "memory": - return await self._optimize_memory(action) - elif optimization_type == "database": - return await self._optimize_database(action) - else: - return await self._general_optimization(action) - - except Exception as e: - logger.error(f"处理系统优化失败: {e}") - return {"success": False, "error": str(e)} - - async def _handle_user_notify(self, action: AgentAction) -> Dict[str, Any]: - """处理用户通知""" - try: - message = action.description - user_id = action.parameters.get("user_id", "admin") - notification_type = action.parameters.get("type", "info") - - # 这里实现具体的通知逻辑 - # 可以是邮件、短信、系统通知等 - return await self._send_notification(user_id, message, notification_type) - - except Exception as e: - logger.error(f"处理用户通知失败: {e}") - return {"success": False, "error": str(e)} - - # 具体实现方法 - async def _restart_service(self, service: str) -> Dict[str, Any]: - """重启服务""" - logger.info(f"重启服务: {service}") - # 这里实现具体的服务重启逻辑 - await asyncio.sleep(2) # 模拟重启时间 - return {"success": True, "message": f"服务 {service} 已重启"} - - async def _check_system_status(self, alert_id: str) -> Dict[str, Any]: - """检查系统状态""" - logger.info(f"检查系统状态: {alert_id}") - # 这里实现具体的系统检查逻辑 - await asyncio.sleep(1) - return {"success": True, "status": "正常", "alert_id": alert_id} - - async def _notify_alert(self, alert_id: str, message: str) -> Dict[str, Any]: - """通知预警""" - logger.info(f"通知预警: {alert_id} - {message}") - # 这里实现具体的通知逻辑 - return {"success": True, "message": "预警通知已发送"} - - async def _generic_alert_response(self, action: AgentAction) -> Dict[str, Any]: - """通用预警响应""" - logger.info(f"执行通用预警响应: {action.description}") - return {"success": True, "message": "预警响应已执行"} - - async def _update_knowledge_entry(self, question: str, enhanced_answer: str) -> Dict[str, Any]: - """更新知识库条目""" - logger.info(f"更新知识库条目: {question}") - # 这里实现具体的知识库更新逻辑 - return {"success": True, "message": "知识库条目已更新"} - - async def _mark_low_confidence_knowledge(self, question: str) -> Dict[str, Any]: - """标记低置信度知识""" - logger.info(f"标记低置信度知识: {question}") - # 这里实现具体的标记逻辑 - return {"success": True, "message": "低置信度知识已标记"} - - async def _optimize_performance(self, action: AgentAction) -> Dict[str, Any]: - """性能优化""" - logger.info("执行性能优化") - # 这里实现具体的性能优化逻辑 - return {"success": True, "message": "性能优化已执行"} - - async def _optimize_memory(self, action: AgentAction) -> Dict[str, Any]: - """内存优化""" - logger.info("执行内存优化") - # 这里实现具体的内存优化逻辑 - return {"success": True, "message": "内存优化已执行"} - - async def _optimize_database(self, action: AgentAction) -> Dict[str, Any]: - """数据库优化""" - logger.info("执行数据库优化") - # 这里实现具体的数据库优化逻辑 - return {"success": True, "message": "数据库优化已执行"} - - async def _general_optimization(self, action: AgentAction) -> Dict[str, Any]: - """通用优化""" - logger.info(f"执行通用优化: {action.description}") - return {"success": True, "message": "系统优化已执行"} - - async def _send_notification(self, user_id: str, message: str, notification_type: str) -> Dict[str, Any]: - """发送通知""" - logger.info(f"发送通知给 {user_id}: {message}") - # 这里实现具体的通知发送逻辑 - return {"success": True, "message": "通知已发送"} - - def get_execution_history(self, limit: int = 100) -> List[Dict[str, Any]]: - """获取执行历史""" - return self.execution_history[-limit:] - - def get_action_statistics(self) -> Dict[str, Any]: - """获取动作统计""" - total_actions = len(self.execution_history) - successful_actions = sum(1 for record in self.execution_history if record["success"]) - - action_types = {} - for record in self.execution_history: - action_type = record["action_type"] - if action_type not in action_types: - action_types[action_type] = {"total": 0, "successful": 0} - action_types[action_type]["total"] += 1 - if record["success"]: - action_types[action_type]["successful"] += 1 - - return { - "total_actions": total_actions, - "successful_actions": successful_actions, - "success_rate": successful_actions / total_actions if total_actions > 0 else 0, - "action_types": action_types - } diff --git a/src/agent/agent_assistant_core.py b/src/agent/agent_assistant_core.py deleted file mode 100644 index b91b785..0000000 --- a/src/agent/agent_assistant_core.py +++ /dev/null @@ -1,268 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TSP Agent助手核心模块 -包含Agent助手的核心功能和基础类 -""" - -import logging -import asyncio -from typing import Dict, Any, List, Optional -from datetime import datetime -import json - -from src.main import TSPAssistant -from src.agent import AgentCore, AgentState -from src.agent.auto_monitor import AutoMonitorService -from src.agent.intelligent_agent import IntelligentAgent, AlertContext, KnowledgeContext -from src.agent.llm_client import LLMManager, LLMConfig -from src.agent.action_executor import ActionExecutor - -logger = logging.getLogger(__name__) - -class TSPAgentAssistantCore(TSPAssistant): - """TSP Agent助手核心 - 基础功能""" - - def __init__(self, llm_config: Optional[LLMConfig] = None): - # 初始化基础TSP助手 - super().__init__() - - # 初始化Agent核心 - self.agent_core = AgentCore() - - # 初始化自动监控服务 - self.auto_monitor = AutoMonitorService(self) - - # 初始化LLM客户端 - self._init_llm_manager(llm_config) - - # 初始化智能Agent - self.intelligent_agent = IntelligentAgent( - llm_client=self.llm_manager - ) - - # 初始化动作执行器 - self.action_executor = ActionExecutor(self) - - # Agent状态 - self.agent_state = AgentState.IDLE - self.is_agent_mode = True - self.proactive_monitoring_enabled = False - - # 执行历史 - self.execution_history = [] - self.max_history_size = 1000 - - logger.info("TSP Agent助手核心初始化完成") - - def _init_llm_manager(self, llm_config: Optional[LLMConfig] = None): - """初始化LLM管理器""" - if llm_config: - self.llm_manager = LLMManager(llm_config) - else: - # 从统一配置管理器获取LLM配置 - try: - from src.config.unified_config import get_config - unified_llm = get_config().llm - # 将统一配置的LLMConfig转换为agent需要的LLMConfig - agent_llm_config = LLMConfig( - provider=unified_llm.provider, - api_key=unified_llm.api_key, - base_url=unified_llm.base_url, - model=unified_llm.model, - temperature=unified_llm.temperature, - max_tokens=unified_llm.max_tokens - ) - self.llm_manager = LLMManager(agent_llm_config) - except Exception as e: - logger.warning(f"无法从统一配置加载LLM配置,使用config/llm_config.py: {e}") - try: - from config.llm_config import DEFAULT_CONFIG - self.llm_manager = LLMManager(DEFAULT_CONFIG) - except ImportError: - # 最后的fallback - default_config = LLMConfig( - provider="qwen", - api_key="", - base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", - model="qwen-turbo", - temperature=0.7, - max_tokens=2000 - ) - self.llm_manager = LLMManager(default_config) - - def get_agent_status(self) -> Dict[str, Any]: - """获取Agent状态""" - return { - "agent_state": self.agent_state.value, - "is_agent_mode": self.is_agent_mode, - "proactive_monitoring": self.proactive_monitoring_enabled, - "execution_count": len(self.execution_history), - "llm_status": self.llm_manager.get_status(), - "agent_core_status": self.agent_core.get_status(), - "last_activity": self.execution_history[-1]["timestamp"] if self.execution_history else None - } - - def toggle_agent_mode(self, enabled: bool) -> bool: - """切换Agent模式""" - try: - self.is_agent_mode = enabled - if enabled: - self.agent_state = AgentState.IDLE - logger.info("Agent模式已启用") - else: - self.agent_state = AgentState.DISABLED - logger.info("Agent模式已禁用") - return True - except Exception as e: - logger.error(f"切换Agent模式失败: {e}") - return False - - def start_proactive_monitoring(self) -> bool: - """启动主动监控""" - try: - if not self.proactive_monitoring_enabled: - self.proactive_monitoring_enabled = True - self.auto_monitor.start_monitoring() - logger.info("主动监控已启动") - return True - return False - except Exception as e: - logger.error(f"启动主动监控失败: {e}") - return False - - def stop_proactive_monitoring(self) -> bool: - """停止主动监控""" - try: - if self.proactive_monitoring_enabled: - self.proactive_monitoring_enabled = False - self.auto_monitor.stop_monitoring() - logger.info("主动监控已停止") - return True - return False - except Exception as e: - logger.error(f"停止主动监控失败: {e}") - return False - - def run_proactive_monitoring(self) -> Dict[str, Any]: - """运行主动监控检查""" - try: - if not self.proactive_monitoring_enabled: - return {"success": False, "message": "主动监控未启用"} - - # 获取系统状态 - system_health = self.get_system_health() - - # 检查预警 - alerts = self.check_alerts() - - # 检查工单状态 - workorders_status = self._check_workorders_status() - - # 运行智能分析 - analysis = self.intelligent_agent.analyze_system_state( - system_health=system_health, - alerts=alerts, - workorders=workorders_status - ) - - # 执行建议的动作 - actions_taken = [] - if analysis.get("recommended_actions"): - for action in analysis["recommended_actions"]: - result = self.action_executor.execute_action(action) - actions_taken.append(result) - - return { - "success": True, - "analysis": analysis, - "actions_taken": actions_taken, - "timestamp": datetime.now().isoformat() - } - except Exception as e: - logger.error(f"主动监控检查失败: {e}") - return {"success": False, "error": str(e)} - - def _check_workorders_status(self) -> Dict[str, Any]: - """检查工单状态""" - try: - from src.core.database import db_manager - from src.core.models import WorkOrder - - with db_manager.get_session() as session: - total_workorders = session.query(WorkOrder).count() - open_workorders = session.query(WorkOrder).filter(WorkOrder.status == 'open').count() - resolved_workorders = session.query(WorkOrder).filter(WorkOrder.status == 'resolved').count() - - return { - "total": total_workorders, - "open": open_workorders, - "resolved": resolved_workorders, - "resolution_rate": resolved_workorders / total_workorders if total_workorders > 0 else 0 - } - except Exception as e: - logger.error(f"检查工单状态失败: {e}") - return {"error": str(e)} - - def run_intelligent_analysis(self) -> Dict[str, Any]: - """运行智能分析""" - try: - # 获取系统数据 - system_health = self.get_system_health() - alerts = self.check_alerts() - workorders = self._check_workorders_status() - - # 创建分析上下文 - context = { - "system_health": system_health, - "alerts": alerts, - "workorders": workorders, - "timestamp": datetime.now().isoformat() - } - - # 运行智能分析 - analysis = self.intelligent_agent.comprehensive_analysis(context) - - # 记录分析结果 - self._record_execution("intelligent_analysis", analysis) - - return analysis - except Exception as e: - logger.error(f"智能分析失败: {e}") - return {"error": str(e)} - - def _record_execution(self, action_type: str, result: Any): - """记录执行历史""" - execution_record = { - "timestamp": datetime.now().isoformat(), - "action_type": action_type, - "result": result, - "agent_state": self.agent_state.value - } - - self.execution_history.append(execution_record) - - # 保持历史记录大小限制 - if len(self.execution_history) > self.max_history_size: - self.execution_history = self.execution_history[-self.max_history_size:] - - def get_action_history(self, limit: int = 50) -> List[Dict[str, Any]]: - """获取动作执行历史""" - return self.execution_history[-limit:] if self.execution_history else [] - - def clear_execution_history(self) -> Dict[str, Any]: - """清空执行历史""" - try: - self.execution_history.clear() - logger.info("执行历史已清空") - return {"success": True, "message": "执行历史已清空"} - except Exception as e: - logger.error(f"清空执行历史失败: {e}") - return {"success": False, "error": str(e)} - - def get_llm_usage_stats(self) -> Dict[str, Any]: - """获取LLM使用统计""" - try: - return self.llm_manager.get_usage_stats() - except Exception as e: - logger.error(f"获取LLM使用统计失败: {e}") - return {"error": str(e)} diff --git a/src/agent/agent_core.py b/src/agent/agent_core.py deleted file mode 100644 index 3d5a1b8..0000000 --- a/src/agent/agent_core.py +++ /dev/null @@ -1,313 +0,0 @@ - -# -*- coding: utf-8 -*- -""" -Agent核心模块 -实现智能体的核心逻辑和决策机制 -""" - -import logging -import asyncio -from typing import Dict, List, Any, Optional, Callable -from datetime import datetime -from enum import Enum -import json - -from ..core.database import db_manager -from ..core.llm_client import QwenClient -from .planner import TaskPlanner -from .executor import TaskExecutor -from .tool_manager import ToolManager -from .reasoning_engine import ReasoningEngine -from .goal_manager import GoalManager - -logger = logging.getLogger(__name__) - -class AgentState(Enum): - """Agent状态枚举""" - IDLE = "idle" - THINKING = "thinking" - PLANNING = "planning" - EXECUTING = "executing" - LEARNING = "learning" - PROCESSING = "processing" - ERROR = "error" - -class AgentCore: - """Agent核心类""" - - def __init__(self): - self.state = AgentState.IDLE - self.llm_client = QwenClient() - self.planner = TaskPlanner() - self.executor = TaskExecutor() - self.tool_manager = ToolManager() - self.reasoning_engine = ReasoningEngine() - self.goal_manager = GoalManager() - - # Agent记忆和上下文 - self.memory = {} - self.current_goal = None - self.active_tasks = [] - self.execution_history = [] - - # 配置参数 - self.max_iterations = 10 - self.confidence_threshold = 0.7 - - logger.info("Agent核心初始化完成") - - async def process_request(self, request: Dict[str, Any]) -> Dict[str, Any]: - """处理用户请求的主入口""" - try: - self.state = AgentState.THINKING - - # 1. 理解用户意图 - intent = await self._understand_intent(request) - - # 2. 设定目标 - goal = await self._set_goal(intent, request) - - # 3. 制定计划 - plan = await self._create_plan(goal) - - # 4. 执行计划 - result = await self._execute_plan(plan) - - # 5. 学习和反思 - await self._learn_from_execution(result) - - self.state = AgentState.IDLE - return result - - except Exception as e: - logger.error(f"处理请求失败: {e}") - self.state = AgentState.ERROR - return {"error": f"处理失败: {str(e)}"} - - async def _understand_intent(self, request: Dict[str, Any]) -> Dict[str, Any]: - """理解用户意图""" - user_message = request.get("message", "") - context = request.get("context", {}) - - # 使用推理引擎分析意图 - intent_analysis = await self.reasoning_engine.analyze_intent( - message=user_message, - context=context, - history=self.execution_history[-5:] # 最近5次执行历史 - ) - - return intent_analysis - - async def _set_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]: - """设定目标""" - goal = await self.goal_manager.create_goal( - intent=intent, - request=request, - current_state=self.state - ) - - self.current_goal = goal - return goal - - async def _create_plan(self, goal: Dict[str, Any]) -> List[Dict[str, Any]]: - """制定执行计划""" - self.state = AgentState.PLANNING - - plan = await self.planner.create_plan( - goal=goal, - available_tools=self.tool_manager.get_available_tools(), - constraints=self._get_constraints() - ) - - return plan - - async def _execute_plan(self, plan: List[Dict[str, Any]]) -> Dict[str, Any]: - """执行计划""" - self.state = AgentState.EXECUTING - - execution_result = await self.executor.execute_plan( - plan=plan, - tool_manager=self.tool_manager, - context=self.memory - ) - - # 记录执行历史 - self.execution_history.append({ - "timestamp": datetime.now().isoformat(), - "plan": plan, - "result": execution_result - }) - - return execution_result - - async def _learn_from_execution(self, result: Dict[str, Any]): - """从执行结果中学习""" - self.state = AgentState.LEARNING - - # 分析执行效果 - learning_insights = await self.reasoning_engine.extract_insights( - execution_result=result, - goal=self.current_goal - ) - - # 更新记忆 - self._update_memory(learning_insights) - - # 更新工具使用统计 - self.tool_manager.update_usage_stats(result.get("tool_usage", [])) - - def _get_constraints(self) -> Dict[str, Any]: - """获取执行约束""" - return { - "max_iterations": self.max_iterations, - "confidence_threshold": self.confidence_threshold, - "timeout": 300, # 5分钟超时 - "memory_limit": 1000 # 内存限制 - } - - def _update_memory(self, insights: Dict[str, Any]): - """更新Agent记忆""" - timestamp = datetime.now().isoformat() - - # 更新成功模式 - if insights.get("success_patterns"): - if "success_patterns" not in self.memory: - self.memory["success_patterns"] = [] - self.memory["success_patterns"].extend(insights["success_patterns"]) - - # 更新失败模式 - if insights.get("failure_patterns"): - if "failure_patterns" not in self.memory: - self.memory["failure_patterns"] = [] - self.memory["failure_patterns"].extend(insights["failure_patterns"]) - - # 更新知识 - if insights.get("new_knowledge"): - if "knowledge" not in self.memory: - self.memory["knowledge"] = [] - self.memory["knowledge"].extend(insights["new_knowledge"]) - - # 限制记忆大小 - for key in self.memory: - if isinstance(self.memory[key], list) and len(self.memory[key]) > 100: - self.memory[key] = self.memory[key][-100:] - - async def proactive_action(self) -> Optional[Dict[str, Any]]: - """主动行动 - Agent主动发起的行为""" - try: - # 检查是否有需要主动处理的任务 - proactive_tasks = await self._identify_proactive_tasks() - - if proactive_tasks: - # 选择最重要的任务 - priority_task = max(proactive_tasks, key=lambda x: x.get("priority", 0)) - - # 执行主动任务 - result = await self.process_request(priority_task) - return result - - return None - - except Exception as e: - logger.error(f"主动行动失败: {e}") - return None - - async def _identify_proactive_tasks(self) -> List[Dict[str, Any]]: - """识别需要主动处理的任务""" - tasks = [] - - # 检查预警系统 - alerts = await self._check_alerts() - if alerts: - tasks.extend([{ - "type": "alert_response", - "message": f"处理预警: {alert['message']}", - "priority": self._calculate_alert_priority(alert), - "context": {"alert": alert} - } for alert in alerts]) - - # 检查知识库更新需求 - knowledge_gaps = await self._identify_knowledge_gaps() - if knowledge_gaps: - tasks.append({ - "type": "knowledge_update", - "message": "更新知识库", - "priority": 0.6, - "context": {"gaps": knowledge_gaps} - }) - - # 检查系统健康状态 - health_issues = await self._check_system_health() - if health_issues: - tasks.append({ - "type": "system_maintenance", - "message": "系统维护", - "priority": 0.8, - "context": {"issues": health_issues} - }) - - return tasks - - async def _check_alerts(self) -> List[Dict[str, Any]]: - """检查预警""" - # 这里可以调用现有的预警系统 - from ..analytics.alert_system import AlertSystem - alert_system = AlertSystem() - return alert_system.get_active_alerts() - - def _calculate_alert_priority(self, alert: Dict[str, Any]) -> float: - """计算预警优先级""" - severity_map = { - "low": 0.3, - "medium": 0.6, - "high": 0.8, - "critical": 1.0 - } - return severity_map.get(alert.get("severity", "medium"), 0.5) - - async def _identify_knowledge_gaps(self) -> List[Dict[str, Any]]: - """识别知识库缺口""" - # 分析未解决的问题,识别知识缺口 - gaps = [] - - # 这里可以实现具体的知识缺口识别逻辑 - # 例如:分析低置信度的回复、未解决的问题等 - - return gaps - - async def _check_system_health(self) -> List[Dict[str, Any]]: - """检查系统健康状态""" - issues = [] - - # 检查各个组件的健康状态 - if not self.llm_client.test_connection(): - issues.append({"component": "llm_client", "issue": "连接失败"}) - - # 检查内存使用 - import psutil - memory_percent = psutil.virtual_memory().percent - if memory_percent > 80: - issues.append({"component": "memory", "issue": f"内存使用率过高: {memory_percent}%"}) - - return issues - - def get_status(self) -> Dict[str, Any]: - """获取Agent状态""" - return { - "state": self.state.value, - "current_goal": self.current_goal, - "active_tasks": len(self.active_tasks), - "execution_history_count": len(self.execution_history), - "memory_size": len(str(self.memory)), - "available_tools": len(self.tool_manager.get_available_tools()), - "timestamp": datetime.now().isoformat() - } - - def reset(self): - """重置Agent状态""" - self.state = AgentState.IDLE - self.current_goal = None - self.active_tasks = [] - self.execution_history = [] - self.memory = {} - logger.info("Agent状态已重置") diff --git a/src/agent/agent_message_handler.py b/src/agent/agent_message_handler.py deleted file mode 100644 index b027d37..0000000 --- a/src/agent/agent_message_handler.py +++ /dev/null @@ -1,243 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TSP Agent消息处理模块 -处理Agent的消息处理和对话功能 -""" - -import logging -import asyncio -from typing import Dict, Any, List, Optional -from datetime import datetime - -from .agent_assistant_core import TSPAgentAssistantCore -from .intelligent_agent import IntelligentAgent - -logger = logging.getLogger(__name__) - -class AgentMessageHandler: - """Agent消息处理器""" - - def __init__(self, agent_core: TSPAgentAssistantCore): - self.agent_core = agent_core - self.intelligent_agent = agent_core.intelligent_agent - self.action_executor = agent_core.action_executor - - async def process_message_agent(self, message: str, user_id: str = "admin", - work_order_id: Optional[int] = None, - enable_proactive: bool = True) -> Dict[str, Any]: - """使用Agent处理消息""" - try: - # 更新Agent状态 - self.agent_core.agent_state = self.agent_core.agent_core.AgentState.PROCESSING - - # 创建对话上下文 - context = { - "message": message, - "user_id": user_id, - "work_order_id": work_order_id, - "timestamp": datetime.now().isoformat(), - "enable_proactive": enable_proactive - } - - # 使用智能Agent处理消息 - agent_response = await self.intelligent_agent.process_message(context) - - # 执行建议的动作 - actions_taken = [] - if agent_response.get("recommended_actions"): - for action in agent_response["recommended_actions"]: - action_result = self.action_executor.execute_action(action) - actions_taken.append(action_result) - - # 生成响应 - response = { - "response": agent_response.get("response", "Agent已处理您的请求"), - "actions": actions_taken, - "status": "completed", - "confidence": agent_response.get("confidence", 0.8), - "context": context - } - - # 记录执行历史 - self.agent_core._record_execution("message_processing", response) - - # 更新Agent状态 - self.agent_core.agent_state = self.agent_core.agent_core.AgentState.IDLE - - return response - - except Exception as e: - logger.error(f"Agent消息处理失败: {e}") - self.agent_core.agent_state = self.agent_core.agent_core.AgentState.ERROR - - return { - "response": f"处理消息时发生错误: {str(e)}", - "actions": [], - "status": "error", - "error": str(e) - } - - async def process_conversation_agent(self, conversation_data: Dict[str, Any]) -> Dict[str, Any]: - """使用Agent处理对话""" - try: - # 提取对话信息 - user_message = conversation_data.get("message", "") - user_id = conversation_data.get("user_id", "anonymous") - session_id = conversation_data.get("session_id") - - # 创建对话上下文 - context = { - "message": user_message, - "user_id": user_id, - "session_id": session_id, - "conversation_history": conversation_data.get("history", []), - "timestamp": datetime.now().isoformat() - } - - # 使用智能Agent处理对话 - agent_response = await self.intelligent_agent.process_conversation(context) - - # 执行建议的动作 - actions_taken = [] - if agent_response.get("recommended_actions"): - for action in agent_response["recommended_actions"]: - action_result = self.action_executor.execute_action(action) - actions_taken.append(action_result) - - # 生成响应 - response = { - "response": agent_response.get("response", "Agent已处理您的对话"), - "actions": actions_taken, - "status": "completed", - "confidence": agent_response.get("confidence", 0.8), - "context": context, - "session_id": session_id - } - - # 记录执行历史 - self.agent_core._record_execution("conversation_processing", response) - - return response - - except Exception as e: - logger.error(f"Agent对话处理失败: {e}") - return { - "response": f"处理对话时发生错误: {str(e)}", - "actions": [], - "status": "error", - "error": str(e) - } - - async def process_workorder_agent(self, workorder_data: Dict[str, Any]) -> Dict[str, Any]: - """使用Agent处理工单""" - try: - # 提取工单信息 - workorder_id = workorder_data.get("workorder_id") - action_type = workorder_data.get("action_type", "analyze") - - # 创建工单上下文 - context = { - "workorder_id": workorder_id, - "action_type": action_type, - "workorder_data": workorder_data, - "timestamp": datetime.now().isoformat() - } - - # 使用智能Agent处理工单 - agent_response = await self.intelligent_agent.process_workorder(context) - - # 执行建议的动作 - actions_taken = [] - if agent_response.get("recommended_actions"): - for action in agent_response["recommended_actions"]: - action_result = self.action_executor.execute_action(action) - actions_taken.append(action_result) - - # 生成响应 - response = { - "response": agent_response.get("response", "Agent已处理工单"), - "actions": actions_taken, - "status": "completed", - "confidence": agent_response.get("confidence", 0.8), - "context": context - } - - # 记录执行历史 - self.agent_core._record_execution("workorder_processing", response) - - return response - - except Exception as e: - logger.error(f"Agent工单处理失败: {e}") - return { - "response": f"处理工单时发生错误: {str(e)}", - "actions": [], - "status": "error", - "error": str(e) - } - - async def process_alert_agent(self, alert_data: Dict[str, Any]) -> Dict[str, Any]: - """使用Agent处理预警""" - try: - # 创建预警上下文 - context = { - "alert_data": alert_data, - "timestamp": datetime.now().isoformat() - } - - # 使用智能Agent处理预警 - agent_response = await self.intelligent_agent.process_alert(context) - - # 执行建议的动作 - actions_taken = [] - if agent_response.get("recommended_actions"): - for action in agent_response["recommended_actions"]: - action_result = self.action_executor.execute_action(action) - actions_taken.append(action_result) - - # 生成响应 - response = { - "response": agent_response.get("response", "Agent已处理预警"), - "actions": actions_taken, - "status": "completed", - "confidence": agent_response.get("confidence", 0.8), - "context": context - } - - # 记录执行历史 - self.agent_core._record_execution("alert_processing", response) - - return response - - except Exception as e: - logger.error(f"Agent预警处理失败: {e}") - return { - "response": f"处理预警时发生错误: {str(e)}", - "actions": [], - "status": "error", - "error": str(e) - } - - def get_conversation_suggestions(self, context: Dict[str, Any]) -> List[str]: - """获取对话建议""" - try: - return self.intelligent_agent.get_conversation_suggestions(context) - except Exception as e: - logger.error(f"获取对话建议失败: {e}") - return [] - - def get_workorder_suggestions(self, workorder_data: Dict[str, Any]) -> List[str]: - """获取工单建议""" - try: - return self.intelligent_agent.get_workorder_suggestions(workorder_data) - except Exception as e: - logger.error(f"获取工单建议失败: {e}") - return [] - - def get_alert_suggestions(self, alert_data: Dict[str, Any]) -> List[str]: - """获取预警建议""" - try: - return self.intelligent_agent.get_alert_suggestions(alert_data) - except Exception as e: - logger.error(f"获取预警建议失败: {e}") - return [] diff --git a/src/agent/agent_sample_actions.py b/src/agent/agent_sample_actions.py deleted file mode 100644 index c492898..0000000 --- a/src/agent/agent_sample_actions.py +++ /dev/null @@ -1,405 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TSP Agent示例动作模块 -包含Agent的示例动作和测试功能 -""" - -import logging -import asyncio -from typing import Dict, Any, List -from datetime import datetime, timedelta - -from .agent_assistant_core import TSPAgentAssistantCore - -logger = logging.getLogger(__name__) - -class AgentSampleActions: - """Agent示例动作处理器""" - - def __init__(self, agent_core: TSPAgentAssistantCore): - self.agent_core = agent_core - - async def trigger_sample_actions(self) -> Dict[str, Any]: - """触发示例动作""" - try: - logger.info("开始执行示例动作") - - # 执行多个示例动作 - actions_results = [] - - # 1. 系统健康检查 - health_result = await self._sample_health_check() - actions_results.append(health_result) - - # 2. 预警分析 - alert_result = await self._sample_alert_analysis() - actions_results.append(alert_result) - - # 3. 工单处理 - workorder_result = await self._sample_workorder_processing() - actions_results.append(workorder_result) - - # 4. 知识库更新 - knowledge_result = await self._sample_knowledge_update() - actions_results.append(knowledge_result) - - # 5. 性能优化 - optimization_result = await self._sample_performance_optimization() - actions_results.append(optimization_result) - - # 记录执行历史 - self.agent_core._record_execution("sample_actions", { - "actions_count": len(actions_results), - "results": actions_results - }) - - return { - "success": True, - "message": f"成功执行 {len(actions_results)} 个示例动作", - "actions_results": actions_results, - "timestamp": datetime.now().isoformat() - } - - except Exception as e: - logger.error(f"执行示例动作失败: {e}") - return { - "success": False, - "error": str(e), - "timestamp": datetime.now().isoformat() - } - - async def _sample_health_check(self) -> Dict[str, Any]: - """示例:系统健康检查""" - try: - # 获取系统健康状态 - health_data = self.agent_core.get_system_health() - - # 模拟健康检查逻辑 - health_score = health_data.get("health_score", 0) - - if health_score > 80: - status = "excellent" - message = "系统运行状态良好" - elif health_score > 60: - status = "good" - message = "系统运行状态正常" - elif health_score > 40: - status = "fair" - message = "系统运行状态一般,建议关注" - else: - status = "poor" - message = "系统运行状态较差,需要优化" - - return { - "action_type": "health_check", - "status": status, - "message": message, - "health_score": health_score, - "timestamp": datetime.now().isoformat() - } - - except Exception as e: - logger.error(f"健康检查失败: {e}") - return { - "action_type": "health_check", - "status": "error", - "error": str(e) - } - - async def _sample_alert_analysis(self) -> Dict[str, Any]: - """示例:预警分析""" - try: - # 获取预警数据 - alerts = self.agent_core.check_alerts() - - # 分析预警 - alert_count = len(alerts) - critical_alerts = [a for a in alerts if a.get("level") == "critical"] - warning_alerts = [a for a in alerts if a.get("level") == "warning"] - - # 生成分析结果 - if alert_count == 0: - status = "no_alerts" - message = "当前无活跃预警" - elif len(critical_alerts) > 0: - status = "critical" - message = f"发现 {len(critical_alerts)} 个严重预警,需要立即处理" - elif len(warning_alerts) > 0: - status = "warning" - message = f"发现 {len(warning_alerts)} 个警告预警,建议关注" - else: - status = "info" - message = f"发现 {alert_count} 个信息预警" - - return { - "action_type": "alert_analysis", - "status": status, - "message": message, - "alert_count": alert_count, - "critical_count": len(critical_alerts), - "warning_count": len(warning_alerts), - "timestamp": datetime.now().isoformat() - } - - except Exception as e: - logger.error(f"预警分析失败: {e}") - return { - "action_type": "alert_analysis", - "status": "error", - "error": str(e) - } - - async def _sample_workorder_processing(self) -> Dict[str, Any]: - """示例:工单处理""" - try: - # 获取工单状态 - workorders_status = self.agent_core._check_workorders_status() - - total = workorders_status.get("total", 0) - open_count = workorders_status.get("open", 0) - resolved_count = workorders_status.get("resolved", 0) - resolution_rate = workorders_status.get("resolution_rate", 0) - - # 分析工单状态 - if total == 0: - status = "no_workorders" - message = "当前无工单" - elif open_count > 10: - status = "high_backlog" - message = f"工单积压严重,有 {open_count} 个待处理工单" - elif resolution_rate > 0.8: - status = "good_resolution" - message = f"工单处理效率良好,解决率 {resolution_rate:.1%}" - else: - status = "normal" - message = f"工单处理状态正常,待处理 {open_count} 个" - - return { - "action_type": "workorder_processing", - "status": status, - "message": message, - "total_workorders": total, - "open_workorders": open_count, - "resolved_workorders": resolved_count, - "resolution_rate": resolution_rate, - "timestamp": datetime.now().isoformat() - } - - except Exception as e: - logger.error(f"工单处理分析失败: {e}") - return { - "action_type": "workorder_processing", - "status": "error", - "error": str(e) - } - - async def _sample_knowledge_update(self) -> Dict[str, Any]: - """示例:知识库更新""" - try: - from src.core.database import db_manager - from src.core.models import KnowledgeEntry - - with db_manager.get_session() as session: - # 获取知识库统计 - total_knowledge = session.query(KnowledgeEntry).count() - verified_knowledge = session.query(KnowledgeEntry).filter( - KnowledgeEntry.is_verified == True - ).count() - unverified_knowledge = total_knowledge - verified_knowledge - - # 分析知识库状态 - if total_knowledge == 0: - status = "empty" - message = "知识库为空,建议添加知识条目" - elif unverified_knowledge > 0: - status = "needs_verification" - message = f"有 {unverified_knowledge} 个知识条目需要验证" - else: - status = "up_to_date" - message = "知识库状态良好,所有条目已验证" - - return { - "action_type": "knowledge_update", - "status": status, - "message": message, - "total_knowledge": total_knowledge, - "verified_knowledge": verified_knowledge, - "unverified_knowledge": unverified_knowledge, - "timestamp": datetime.now().isoformat() - } - - except Exception as e: - logger.error(f"知识库更新分析失败: {e}") - return { - "action_type": "knowledge_update", - "status": "error", - "error": str(e) - } - - async def _sample_performance_optimization(self) -> Dict[str, Any]: - """示例:性能优化""" - try: - # 获取系统性能数据 - system_health = self.agent_core.get_system_health() - - # 分析性能指标 - cpu_usage = system_health.get("cpu_usage", 0) - memory_usage = system_health.get("memory_usage", 0) - disk_usage = system_health.get("disk_usage", 0) - - # 生成优化建议 - optimization_suggestions = [] - - if cpu_usage > 80: - optimization_suggestions.append("CPU使用率过高,建议优化计算密集型任务") - if memory_usage > 80: - optimization_suggestions.append("内存使用率过高,建议清理缓存或增加内存") - if disk_usage > 90: - optimization_suggestions.append("磁盘空间不足,建议清理日志文件或扩容") - - if not optimization_suggestions: - status = "optimal" - message = "系统性能良好,无需优化" - else: - status = "needs_optimization" - message = f"发现 {len(optimization_suggestions)} 个性能优化点" - - return { - "action_type": "performance_optimization", - "status": status, - "message": message, - "cpu_usage": cpu_usage, - "memory_usage": memory_usage, - "disk_usage": disk_usage, - "optimization_suggestions": optimization_suggestions, - "timestamp": datetime.now().isoformat() - } - - except Exception as e: - logger.error(f"性能优化分析失败: {e}") - return { - "action_type": "performance_optimization", - "status": "error", - "error": str(e) - } - - async def run_performance_test(self) -> Dict[str, Any]: - """运行性能测试""" - try: - start_time = datetime.now() - - # 执行多个测试 - test_results = [] - - # 1. 响应时间测试 - response_time = await self._test_response_time() - test_results.append(response_time) - - # 2. 并发处理测试 - concurrency_test = await self._test_concurrency() - test_results.append(concurrency_test) - - # 3. 内存使用测试 - memory_test = await self._test_memory_usage() - test_results.append(memory_test) - - end_time = datetime.now() - total_time = (end_time - start_time).total_seconds() - - return { - "success": True, - "message": "性能测试完成", - "total_time": total_time, - "test_results": test_results, - "timestamp": datetime.now().isoformat() - } - - except Exception as e: - logger.error(f"性能测试失败: {e}") - return { - "success": False, - "error": str(e), - "timestamp": datetime.now().isoformat() - } - - async def _test_response_time(self) -> Dict[str, Any]: - """测试响应时间""" - start_time = datetime.now() - - # 模拟处理任务 - await asyncio.sleep(0.1) - - end_time = datetime.now() - response_time = (end_time - start_time).total_seconds() - - return { - "test_type": "response_time", - "response_time": response_time, - "status": "good" if response_time < 0.5 else "slow" - } - - async def _test_concurrency(self) -> Dict[str, Any]: - """测试并发处理""" - try: - # 创建多个并发任务 - tasks = [] - for i in range(5): - task = asyncio.create_task(self._simulate_task(i)) - tasks.append(task) - - # 等待所有任务完成 - results = await asyncio.gather(*tasks) - - return { - "test_type": "concurrency", - "concurrent_tasks": len(tasks), - "successful_tasks": len([r for r in results if r.get("success")]), - "status": "good" - } - - except Exception as e: - return { - "test_type": "concurrency", - "status": "error", - "error": str(e) - } - - async def _simulate_task(self, task_id: int) -> Dict[str, Any]: - """模拟任务""" - try: - await asyncio.sleep(0.05) # 模拟处理时间 - return { - "task_id": task_id, - "success": True, - "result": f"Task {task_id} completed" - } - except Exception as e: - return { - "task_id": task_id, - "success": False, - "error": str(e) - } - - async def _test_memory_usage(self) -> Dict[str, Any]: - """测试内存使用""" - try: - import psutil - - # 获取当前内存使用情况 - memory_info = psutil.virtual_memory() - - return { - "test_type": "memory_usage", - "total_memory": memory_info.total, - "available_memory": memory_info.available, - "used_memory": memory_info.used, - "memory_percentage": memory_info.percent, - "status": "good" if memory_info.percent < 80 else "high" - } - - except Exception as e: - return { - "test_type": "memory_usage", - "status": "error", - "error": str(e) - } diff --git a/src/agent/auto_monitor.py b/src/agent/auto_monitor.py index 4bc1fd7..e3f2796 100644 --- a/src/agent/auto_monitor.py +++ b/src/agent/auto_monitor.py @@ -5,7 +5,7 @@ 实现Agent的主动调用功能 """ -import asyncio跳过系统检查,直接启动服务... +import asyncio import logging import threading import time @@ -22,7 +22,7 @@ class AutoMonitorService: self.agent_assistant = agent_assistant self.is_running = False self.monitor_thread = None - self.check_interval = 300 # 5分钟检查一次 + self.check_interval = 900 # 5分钟检查一次 self.last_check_time = None self.monitoring_stats = { "total_checks": 0, diff --git a/src/agent/executor.py b/src/agent/executor.py deleted file mode 100644 index 9560ebe..0000000 --- a/src/agent/executor.py +++ /dev/null @@ -1,589 +0,0 @@ - -# -*- coding: utf-8 -*- -""" -任务执行器 -负责执行计划中的具体任务 -""" - -import logging -import asyncio -from typing import Dict, List, Any, Optional -from datetime import datetime -import json - -logger = logging.getLogger(__name__) - -class TaskExecutor: - """任务执行器""" - - def __init__(self): - self.execution_strategies = { - "sequential": self._execute_sequential, - "parallel": self._execute_parallel, - "conditional": self._execute_conditional, - "iterative": self._execute_iterative - } - self.active_executions = {} - - async def execute_plan( - self, - plan: List[Dict[str, Any]], - tool_manager: Any, - context: Dict[str, Any] - ) -> Dict[str, Any]: - """执行计划""" - try: - execution_id = f"exec_{datetime.now().strftime('%Y%m%d_%H%M%S')}" - self.active_executions[execution_id] = { - "start_time": datetime.now(), - "status": "running", - "plan": plan - } - - # 根据计划类型选择执行策略 - execution_strategy = self._determine_execution_strategy(plan) - - # 执行计划 - result = await self.execution_strategies[execution_strategy]( - plan=plan, - tool_manager=tool_manager, - context=context, - execution_id=execution_id - ) - - # 更新执行状态 - self.active_executions[execution_id]["status"] = "completed" - self.active_executions[execution_id]["end_time"] = datetime.now() - self.active_executions[execution_id]["result"] = result - - logger.info(f"计划执行完成: {execution_id}") - return result - - except Exception as e: - logger.error(f"执行计划失败: {e}") - if execution_id in self.active_executions: - self.active_executions[execution_id]["status"] = "failed" - self.active_executions[execution_id]["error"] = str(e) - - return { - "success": False, - "error": str(e), - "execution_id": execution_id - } - - def _determine_execution_strategy(self, plan: List[Dict[str, Any]]) -> str: - """确定执行策略""" - if not plan: - return "sequential" - - # 检查计划类型 - plan_types = [task.get("type") for task in plan] - - if "parallel_group" in plan_types: - return "parallel" - elif "condition" in plan_types or "branch" in plan_types: - return "conditional" - elif "iteration_control" in plan_types: - return "iterative" - else: - return "sequential" - - async def _execute_sequential( - self, - plan: List[Dict[str, Any]], - tool_manager: Any, - context: Dict[str, Any], - execution_id: str - ) -> Dict[str, Any]: - """顺序执行计划""" - results = [] - execution_log = [] - - for i, task in enumerate(plan): - try: - logger.info(f"执行任务 {i+1}/{len(plan)}: {task.get('id', 'unknown')}") - - # 检查任务依赖 - if not await self._check_dependencies(task, results): - logger.warning(f"任务 {task.get('id')} 的依赖未满足,跳过执行") - continue - - # 执行任务 - task_result = await self._execute_single_task(task, tool_manager, context) - - results.append({ - "task_id": task.get("id"), - "result": task_result, - "timestamp": datetime.now().isoformat() - }) - - execution_log.append({ - "task_id": task.get("id"), - "status": "completed", - "duration": task_result.get("duration", 0) - }) - - # 检查是否满足成功条件 - if not self._check_success_criteria(task, task_result): - logger.warning(f"任务 {task.get('id')} 未满足成功条件") - - except Exception as e: - logger.error(f"执行任务 {task.get('id')} 失败: {e}") - execution_log.append({ - "task_id": task.get("id"), - "status": "failed", - "error": str(e) - }) - - # 根据任务重要性决定是否继续 - if task.get("critical", False): - return { - "success": False, - "error": f"关键任务失败: {task.get('id')}", - "results": results, - "execution_log": execution_log - } - - return { - "success": True, - "results": results, - "execution_log": execution_log, - "execution_id": execution_id - } - - async def _execute_parallel( - self, - plan: List[Dict[str, Any]], - tool_manager: Any, - context: Dict[str, Any], - execution_id: str - ) -> Dict[str, Any]: - """并行执行计划""" - results = [] - execution_log = [] - - # 将计划分组 - parallel_groups = self._group_tasks_for_parallel_execution(plan) - - for group in parallel_groups: - if group["execution_mode"] == "parallel": - # 并行执行组内任务 - group_results = await self._execute_tasks_parallel( - group["tasks"], tool_manager, context - ) - results.extend(group_results) - else: - # 顺序执行组内任务 - for task in group["tasks"]: - task_result = await self._execute_single_task(task, tool_manager, context) - results.append({ - "task_id": task.get("id"), - "result": task_result, - "timestamp": datetime.now().isoformat() - }) - - return { - "success": True, - "results": results, - "execution_log": execution_log, - "execution_id": execution_id - } - - async def _execute_conditional( - self, - plan: List[Dict[str, Any]], - tool_manager: Any, - context: Dict[str, Any], - execution_id: str - ) -> Dict[str, Any]: - """条件执行计划""" - results = [] - execution_log = [] - - # 找到条件检查任务 - condition_task = None - branch_tasks = [] - - for task in plan: - if task.get("type") == "condition": - condition_task = task - elif task.get("type") == "branch": - branch_tasks.append(task) - - if not condition_task: - logger.error("条件计划中缺少条件检查任务") - return {"success": False, "error": "缺少条件检查任务"} - - # 执行条件检查 - condition_result = await self._execute_single_task(condition_task, tool_manager, context) - results.append({ - "task_id": condition_task.get("id"), - "result": condition_result, - "timestamp": datetime.now().isoformat() - }) - - # 根据条件结果选择分支 - selected_branch = self._select_branch(condition_result, branch_tasks) - - if selected_branch: - # 执行选中的分支 - branch_result = await self._execute_sequential( - selected_branch.get("tasks", []), - tool_manager, - context, - execution_id - ) - results.extend(branch_result.get("results", [])) - execution_log.extend(branch_result.get("execution_log", [])) - - return { - "success": True, - "results": results, - "execution_log": execution_log, - "execution_id": execution_id, - "selected_branch": selected_branch.get("id") if selected_branch else None - } - - async def _execute_iterative( - self, - plan: List[Dict[str, Any]], - tool_manager: Any, - context: Dict[str, Any], - execution_id: str - ) -> Dict[str, Any]: - """迭代执行计划""" - # 找到迭代控制任务 - iteration_task = None - for task in plan: - if task.get("type") == "iteration_control": - iteration_task = task - break - - if not iteration_task: - logger.error("迭代计划中缺少迭代控制任务") - return {"success": False, "error": "缺少迭代控制任务"} - - max_iterations = iteration_task.get("max_iterations", 10) - convergence_criteria = iteration_task.get("convergence_criteria", {}) - tasks = iteration_task.get("tasks", []) - - results = [] - execution_log = [] - iteration_count = 0 - - while iteration_count < max_iterations: - iteration_count += 1 - logger.info(f"执行第 {iteration_count} 次迭代") - - # 执行迭代任务 - iteration_result = await self._execute_sequential( - tasks, tool_manager, context, f"{execution_id}_iter_{iteration_count}" - ) - - results.append({ - "iteration": iteration_count, - "result": iteration_result, - "timestamp": datetime.now().isoformat() - }) - - # 检查收敛条件 - if self._check_convergence(iteration_result, convergence_criteria): - logger.info(f"迭代在第 {iteration_count} 次收敛") - break - - return { - "success": True, - "results": results, - "execution_log": execution_log, - "execution_id": execution_id, - "iterations": iteration_count, - "converged": iteration_count < max_iterations - } - - async def _execute_single_task( - self, - task: Dict[str, Any], - tool_manager: Any, - context: Dict[str, Any] - ) -> Dict[str, Any]: - """执行单个任务""" - start_time = datetime.now() - - try: - task_id = task.get("id", "unknown") - task_type = task.get("type", "action") - tool_name = task.get("tool", "") - parameters = task.get("parameters", {}) - - logger.info(f"执行任务: {task_id}, 类型: {task_type}, 工具: {tool_name}") - - # 根据任务类型执行 - if task_type == "action": - result = await self._execute_action_task(task, tool_manager, context) - elif task_type == "condition": - result = await self._execute_condition_task(task, tool_manager, context) - elif task_type == "control": - result = await self._execute_control_task(task, tool_manager, context) - else: - result = await self._execute_general_task(task, tool_manager, context) - - duration = (datetime.now() - start_time).total_seconds() - result["duration"] = duration - - logger.info(f"任务 {task_id} 执行完成,耗时: {duration:.2f}秒") - return result - - except Exception as e: - logger.error(f"执行任务失败: {e}") - return { - "success": False, - "error": str(e), - "duration": (datetime.now() - start_time).total_seconds() - } - - async def _execute_action_task( - self, - task: Dict[str, Any], - tool_manager: Any, - context: Dict[str, Any] - ) -> Dict[str, Any]: - """执行动作任务""" - tool_name = task.get("tool", "") - parameters = task.get("parameters", {}) - - # 合并上下文参数 - full_parameters = {**parameters, **context} - - # 调用工具 - result = await tool_manager.execute_tool(tool_name, full_parameters) - - return { - "success": True, - "tool": tool_name, - "parameters": full_parameters, - "result": result - } - - async def _execute_condition_task( - self, - task: Dict[str, Any], - tool_manager: Any, - context: Dict[str, Any] - ) -> Dict[str, Any]: - """执行条件任务""" - condition = task.get("condition", "") - branches = task.get("branches", {}) - - # 评估条件 - condition_result = await self._evaluate_condition(condition, context) - - return { - "success": True, - "condition": condition, - "result": condition_result, - "available_branches": list(branches.keys()) - } - - async def _execute_control_task( - self, - task: Dict[str, Any], - tool_manager: Any, - context: Dict[str, Any] - ) -> Dict[str, Any]: - """执行控制任务""" - control_type = task.get("control_type", "general") - - if control_type == "iteration": - return await self._execute_iteration_control(task, context) - elif control_type == "loop": - return await self._execute_loop_control(task, context) - else: - return { - "success": True, - "control_type": control_type, - "message": "控制任务执行完成" - } - - async def _execute_general_task( - self, - task: Dict[str, Any], - tool_manager: Any, - context: Dict[str, Any] - ) -> Dict[str, Any]: - """执行通用任务""" - description = task.get("description", "") - - # 这里可以实现通用的任务执行逻辑 - # 例如:调用LLM生成响应、执行数据库操作等 - - return { - "success": True, - "description": description, - "message": "通用任务执行完成" - } - - async def _execute_tasks_parallel( - self, - tasks: List[Dict[str, Any]], - tool_manager: Any, - context: Dict[str, Any] - ) -> List[Dict[str, Any]]: - """并行执行多个任务""" - async def execute_task(task): - return await self._execute_single_task(task, tool_manager, context) - - # 创建并行任务 - parallel_tasks = [execute_task(task) for task in tasks] - - # 等待所有任务完成 - results = await asyncio.gather(*parallel_tasks, return_exceptions=True) - - # 处理结果 - processed_results = [] - for i, result in enumerate(results): - if isinstance(result, Exception): - processed_results.append({ - "task_id": tasks[i].get("id"), - "result": {"success": False, "error": str(result)}, - "timestamp": datetime.now().isoformat() - }) - else: - processed_results.append({ - "task_id": tasks[i].get("id"), - "result": result, - "timestamp": datetime.now().isoformat() - }) - - return processed_results - - def _group_tasks_for_parallel_execution(self, plan: List[Dict[str, Any]]) -> List[Dict[str, Any]]: - """将任务分组以便并行执行""" - groups = [] - current_group = [] - - for task in plan: - if task.get("type") == "parallel_group": - if current_group: - groups.append({ - "execution_mode": "sequential", - "tasks": current_group - }) - current_group = [] - groups.append(task) - else: - current_group.append(task) - - if current_group: - groups.append({ - "execution_mode": "sequential", - "tasks": current_group - }) - - return groups - - async def _check_dependencies(self, task: Dict[str, Any], results: List[Dict[str, Any]]) -> bool: - """检查任务依赖是否满足""" - dependencies = task.get("dependencies", []) - - if not dependencies: - return True - - # 检查所有依赖是否已完成 - completed_task_ids = [r["task_id"] for r in results if r["result"].get("success", False)] - - for dep in dependencies: - if dep not in completed_task_ids: - return False - - return True - - def _check_success_criteria(self, task: Dict[str, Any], result: Dict[str, Any]) -> bool: - """检查任务是否满足成功条件""" - success_criteria = task.get("success_criteria", {}) - - if not success_criteria: - return result.get("success", False) - - # 检查每个成功条件 - for criterion, expected_value in success_criteria.items(): - actual_value = result.get(criterion) - if actual_value != expected_value: - return False - - return True - - def _select_branch(self, condition_result: Dict[str, Any], branch_tasks: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]: - """根据条件结果选择分支""" - condition_value = condition_result.get("result", "") - - for branch_task in branch_tasks: - branch_condition = branch_task.get("condition", "") - if branch_condition == condition_value: - return branch_task - - return None - - def _check_convergence(self, iteration_result: Dict[str, Any], convergence_criteria: Dict[str, Any]) -> bool: - """检查迭代是否收敛""" - if not convergence_criteria: - return False - - # 检查收敛条件 - for criterion, threshold in convergence_criteria.items(): - actual_value = iteration_result.get(criterion) - if actual_value is None: - continue - - # 这里可以实现更复杂的收敛判断逻辑 - if isinstance(threshold, dict): - if threshold.get("type") == "less_than": - if actual_value >= threshold.get("value"): - return False - elif threshold.get("type") == "greater_than": - if actual_value <= threshold.get("value"): - return False - - return True - - async def _evaluate_condition(self, condition: str, context: Dict[str, Any]) -> str: - """评估条件表达式""" - # 这里可以实现条件评估逻辑 - # 例如:解析条件表达式、查询上下文等 - - # 简单的条件评估示例 - if "satisfaction" in condition: - return "high" if context.get("satisfaction_score", 0) > 0.7 else "low" - elif "priority" in condition: - return context.get("priority", "medium") - else: - return "default" - - async def _execute_iteration_control(self, task: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]: - """执行迭代控制""" - max_iterations = task.get("max_iterations", 10) - current_iteration = context.get("current_iteration", 0) - - return { - "success": True, - "max_iterations": max_iterations, - "current_iteration": current_iteration, - "continue": current_iteration < max_iterations - } - - async def _execute_loop_control(self, task: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]: - """执行循环控制""" - loop_condition = task.get("loop_condition", "") - - return { - "success": True, - "loop_condition": loop_condition, - "continue": True # 这里应该根据实际条件判断 - } - - def get_execution_status(self, execution_id: str) -> Optional[Dict[str, Any]]: - """获取执行状态""" - return self.active_executions.get(execution_id) - - def get_all_executions(self) -> Dict[str, Any]: - """获取所有执行记录""" - return self.active_executions diff --git a/src/agent/goal_manager.py b/src/agent/goal_manager.py deleted file mode 100644 index 085bf23..0000000 --- a/src/agent/goal_manager.py +++ /dev/null @@ -1,573 +0,0 @@ - -# -*- coding: utf-8 -*- -""" -目标管理器 -负责目标设定、跟踪和评估 -""" - -import logging -from typing import Dict, List, Any, Optional -from datetime import datetime -import json - -from ..core.llm_client import QwenClient - -logger = logging.getLogger(__name__) - -class GoalManager: - """目标管理器""" - - def __init__(self): - self.llm_client = QwenClient() - self.active_goals = {} - self.goal_history = [] - self.goal_templates = { - "problem_solving": self._create_problem_solving_goal, - "information_gathering": self._create_information_gathering_goal, - "task_execution": self._create_task_execution_goal, - "analysis": self._create_analysis_goal, - "communication": self._create_communication_goal - } - - async def create_goal( - self, - intent: Dict[str, Any], - request: Dict[str, Any], - current_state: Any - ) -> Dict[str, Any]: - """创建目标""" - try: - goal_type = self._determine_goal_type(intent, request) - - if goal_type in self.goal_templates: - goal = await self.goal_templates[goal_type](intent, request, current_state) - else: - goal = await self._create_general_goal(intent, request, current_state) - - # 生成唯一目标ID - goal_id = f"goal_{datetime.now().strftime('%Y%m%d_%H%M%S')}" - goal["id"] = goal_id - goal["created_at"] = datetime.now().isoformat() - goal["status"] = "active" - - # 添加到活跃目标 - self.active_goals[goal_id] = goal - - logger.info(f"创建目标: {goal_id}, 类型: {goal_type}") - return goal - - except Exception as e: - logger.error(f"创建目标失败: {e}") - return self._create_fallback_goal(intent, request) - - def _determine_goal_type(self, intent: Dict[str, Any], request: Dict[str, Any]) -> str: - """确定目标类型""" - main_intent = intent.get("main_intent", "general_query") - - goal_type_mapping = { - "problem_solving": ["problem_consultation", "issue_resolution", "troubleshooting"], - "information_gathering": ["information_query", "data_collection", "research"], - "task_execution": ["work_order_creation", "task_assignment", "action_request"], - "analysis": ["data_analysis", "report_generation", "performance_review"], - "communication": ["notification", "message_delivery", "user_interaction"] - } - - for goal_type, intents in goal_type_mapping.items(): - if main_intent in intents: - return goal_type - - return "general" - - async def _create_problem_solving_goal( - self, - intent: Dict[str, Any], - request: Dict[str, Any], - current_state: Any - ) -> Dict[str, Any]: - """创建问题解决目标""" - prompt = f""" - 请为以下问题解决请求创建目标: - - 用户意图: {json.dumps(intent, ensure_ascii=False)} - 请求内容: {json.dumps(request, ensure_ascii=False)} - - 请定义: - 1. 目标描述 - 2. 成功标准 - 3. 所需步骤 - 4. 预期结果 - 5. 时间限制 - 6. 资源需求 - - 请以JSON格式返回目标定义。 - """ - - messages = [ - {"role": "system", "content": "你是一个目标设定专家,擅长为问题解决任务设定清晰的目标。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return self._create_default_problem_solving_goal(intent, request) - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - goal_data = json.loads(json_match.group()) - goal_data["type"] = "problem_solving" - return goal_data - else: - return self._create_default_problem_solving_goal(intent, request) - - async def _create_information_gathering_goal( - self, - intent: Dict[str, Any], - request: Dict[str, Any], - current_state: Any - ) -> Dict[str, Any]: - """创建信息收集目标""" - prompt = f""" - 请为以下信息收集请求创建目标: - - 用户意图: {json.dumps(intent, ensure_ascii=False)} - 请求内容: {json.dumps(request, ensure_ascii=False)} - - 请定义: - 1. 信息收集范围 - 2. 信息质量要求 - 3. 收集方法 - 4. 验证标准 - 5. 整理格式 - - 请以JSON格式返回目标定义。 - """ - - messages = [ - {"role": "system", "content": "你是一个信息收集专家,擅长设定信息收集目标。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return self._create_default_information_goal(intent, request) - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - goal_data = json.loads(json_match.group()) - goal_data["type"] = "information_gathering" - return goal_data - else: - return self._create_default_information_goal(intent, request) - - async def _create_task_execution_goal( - self, - intent: Dict[str, Any], - request: Dict[str, Any], - current_state: Any - ) -> Dict[str, Any]: - """创建任务执行目标""" - prompt = f""" - 请为以下任务执行请求创建目标: - - 用户意图: {json.dumps(intent, ensure_ascii=False)} - 请求内容: {json.dumps(request, ensure_ascii=False)} - - 请定义: - 1. 任务描述 - 2. 执行步骤 - 3. 完成标准 - 4. 质量要求 - 5. 时间安排 - - 请以JSON格式返回目标定义。 - """ - - messages = [ - {"role": "system", "content": "你是一个任务执行专家,擅长设定任务执行目标。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return self._create_default_task_goal(intent, request) - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - goal_data = json.loads(json_match.group()) - goal_data["type"] = "task_execution" - return goal_data - else: - return self._create_default_task_goal(intent, request) - - async def _create_analysis_goal( - self, - intent: Dict[str, Any], - request: Dict[str, Any], - current_state: Any - ) -> Dict[str, Any]: - """创建分析目标""" - prompt = f""" - 请为以下分析请求创建目标: - - 用户意图: {json.dumps(intent, ensure_ascii=False)} - 请求内容: {json.dumps(request, ensure_ascii=False)} - - 请定义: - 1. 分析范围 - 2. 分析方法 - 3. 分析深度 - 4. 输出格式 - 5. 质量指标 - - 请以JSON格式返回目标定义。 - """ - - messages = [ - {"role": "system", "content": "你是一个分析专家,擅长设定分析目标。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return self._create_default_analysis_goal(intent, request) - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - goal_data = json.loads(json_match.group()) - goal_data["type"] = "analysis" - return goal_data - else: - return self._create_default_analysis_goal(intent, request) - - async def _create_communication_goal( - self, - intent: Dict[str, Any], - request: Dict[str, Any], - current_state: Any - ) -> Dict[str, Any]: - """创建沟通目标""" - prompt = f""" - 请为以下沟通请求创建目标: - - 用户意图: {json.dumps(intent, ensure_ascii=False)} - 请求内容: {json.dumps(request, ensure_ascii=False)} - - 请定义: - 1. 沟通对象 - 2. 沟通内容 - 3. 沟通方式 - 4. 预期效果 - 5. 反馈机制 - - 请以JSON格式返回目标定义。 - """ - - messages = [ - {"role": "system", "content": "你是一个沟通专家,擅长设定沟通目标。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return self._create_default_communication_goal(intent, request) - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - goal_data = json.loads(json_match.group()) - goal_data["type"] = "communication" - return goal_data - else: - return self._create_default_communication_goal(intent, request) - - async def _create_general_goal( - self, - intent: Dict[str, Any], - request: Dict[str, Any], - current_state: Any - ) -> Dict[str, Any]: - """创建通用目标""" - return { - "type": "general", - "description": intent.get("main_intent", "处理用户请求"), - "success_criteria": { - "completion": True, - "user_satisfaction": 0.7 - }, - "steps": ["理解请求", "执行任务", "返回结果"], - "expected_result": "用户需求得到满足", - "time_limit": 300, # 5分钟 - "resource_requirements": ["llm_client", "knowledge_base"] - } - - def _create_default_problem_solving_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]: - """创建默认问题解决目标""" - return { - "type": "problem_solving", - "description": "解决用户问题", - "success_criteria": { - "problem_identified": True, - "solution_provided": True, - "user_satisfaction": 0.7 - }, - "steps": ["分析问题", "寻找解决方案", "提供建议", "验证效果"], - "expected_result": "问题得到解决或提供有效建议", - "time_limit": 300, - "resource_requirements": ["knowledge_base", "llm_client"] - } - - def _create_default_information_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]: - """创建默认信息收集目标""" - return { - "type": "information_gathering", - "description": "收集相关信息", - "success_criteria": { - "information_complete": True, - "information_accurate": True, - "information_relevant": True - }, - "steps": ["确定信息需求", "搜索信息源", "收集信息", "整理信息"], - "expected_result": "提供准确、完整、相关的信息", - "time_limit": 180, - "resource_requirements": ["knowledge_base", "search_tools"] - } - - def _create_default_task_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]: - """创建默认任务执行目标""" - return { - "type": "task_execution", - "description": "执行指定任务", - "success_criteria": { - "task_completed": True, - "quality_met": True, - "time_met": True - }, - "steps": ["理解任务", "制定计划", "执行任务", "验证结果"], - "expected_result": "任务成功完成", - "time_limit": 600, - "resource_requirements": ["task_tools", "monitoring"] - } - - def _create_default_analysis_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]: - """创建默认分析目标""" - return { - "type": "analysis", - "description": "执行数据分析", - "success_criteria": { - "analysis_complete": True, - "insights_meaningful": True, - "report_clear": True - }, - "steps": ["收集数据", "分析数据", "提取洞察", "生成报告"], - "expected_result": "提供有价值的分析报告", - "time_limit": 900, - "resource_requirements": ["analytics_tools", "data_sources"] - } - - def _create_default_communication_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]: - """创建默认沟通目标""" - return { - "type": "communication", - "description": "与用户沟通", - "success_criteria": { - "message_delivered": True, - "response_received": True, - "understanding_achieved": True - }, - "steps": ["准备消息", "发送消息", "等待响应", "确认理解"], - "expected_result": "成功沟通并达成理解", - "time_limit": 120, - "resource_requirements": ["communication_tools"] - } - - def _create_fallback_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]: - """创建备用目标""" - return { - "type": "fallback", - "description": "处理用户请求", - "success_criteria": {"completion": True}, - "steps": ["处理请求"], - "expected_result": "返回响应", - "time_limit": 60, - "resource_requirements": ["basic_tools"] - } - - async def update_goal_progress(self, goal_id: str, progress_data: Dict[str, Any]) -> bool: - """更新目标进度""" - try: - if goal_id not in self.active_goals: - return False - - goal = self.active_goals[goal_id] - goal["progress"] = progress_data - goal["updated_at"] = datetime.now().isoformat() - - # 检查是否完成 - if self._check_goal_completion(goal): - goal["status"] = "completed" - goal["completed_at"] = datetime.now().isoformat() - - # 移动到历史记录 - self.goal_history.append(goal) - del self.active_goals[goal_id] - - logger.info(f"目标 {goal_id} 已完成") - - return True - - except Exception as e: - logger.error(f"更新目标进度失败: {e}") - return False - - def _check_goal_completion(self, goal: Dict[str, Any]) -> bool: - """检查目标是否完成""" - success_criteria = goal.get("success_criteria", {}) - - if not success_criteria: - return True - - progress = goal.get("progress", {}) - - # 检查每个成功标准 - for criterion, required_value in success_criteria.items(): - actual_value = progress.get(criterion) - if actual_value != required_value: - return False - - return True - - async def evaluate_goal_performance(self, goal_id: str) -> Dict[str, Any]: - """评估目标性能""" - try: - if goal_id in self.active_goals: - goal = self.active_goals[goal_id] - elif goal_id in [g["id"] for g in self.goal_history]: - goal = next(g for g in self.goal_history if g["id"] == goal_id) - else: - return {"error": "目标不存在"} - - evaluation = { - "goal_id": goal_id, - "type": goal.get("type"), - "status": goal.get("status"), - "created_at": goal.get("created_at"), - "completed_at": goal.get("completed_at"), - "duration": self._calculate_goal_duration(goal), - "success_rate": self._calculate_success_rate(goal), - "efficiency": self._calculate_efficiency(goal), - "quality_score": self._calculate_quality_score(goal) - } - - return evaluation - - except Exception as e: - logger.error(f"评估目标性能失败: {e}") - return {"error": str(e)} - - def _calculate_goal_duration(self, goal: Dict[str, Any]) -> float: - """计算目标持续时间""" - created_at = datetime.fromisoformat(goal.get("created_at", datetime.now().isoformat())) - - if goal.get("completed_at"): - completed_at = datetime.fromisoformat(goal["completed_at"]) - return (completed_at - created_at).total_seconds() - else: - return (datetime.now() - created_at).total_seconds() - - def _calculate_success_rate(self, goal: Dict[str, Any]) -> float: - """计算成功率""" - if goal.get("status") == "completed": - return 1.0 - elif goal.get("status") == "failed": - return 0.0 - else: - # 根据进度计算部分成功率 - progress = goal.get("progress", {}) - success_criteria = goal.get("success_criteria", {}) - - if not success_criteria: - return 0.5 - - completed_criteria = 0 - for criterion in success_criteria: - if progress.get(criterion) == success_criteria[criterion]: - completed_criteria += 1 - - return completed_criteria / len(success_criteria) - - def _calculate_efficiency(self, goal: Dict[str, Any]) -> float: - """计算效率""" - duration = self._calculate_goal_duration(goal) - time_limit = goal.get("time_limit", 300) - - if duration <= time_limit: - return 1.0 - else: - # 超时惩罚 - return max(0.0, 1.0 - (duration - time_limit) / time_limit) - - def _calculate_quality_score(self, goal: Dict[str, Any]) -> float: - """计算质量分数""" - # 这里可以根据具体的目标类型和质量指标计算 - # 暂时返回一个基于成功率的简单计算 - success_rate = self._calculate_success_rate(goal) - efficiency = self._calculate_efficiency(goal) - - return (success_rate + efficiency) / 2 - - def get_active_goals(self) -> List[Dict[str, Any]]: - """获取活跃目标""" - return list(self.active_goals.values()) - - def get_goal_history(self, limit: int = 10) -> List[Dict[str, Any]]: - """获取目标历史""" - return self.goal_history[-limit:] if self.goal_history else [] - - def get_goal_statistics(self) -> Dict[str, Any]: - """获取目标统计""" - total_goals = len(self.active_goals) + len(self.goal_history) - completed_goals = len([g for g in self.goal_history if g.get("status") == "completed"]) - active_goals = len(self.active_goals) - - return { - "total_goals": total_goals, - "active_goals": active_goals, - "completed_goals": completed_goals, - "completion_rate": completed_goals / total_goals if total_goals > 0 else 0, - "goal_types": self._get_goal_type_distribution() - } - - def _get_goal_type_distribution(self) -> Dict[str, int]: - """获取目标类型分布""" - distribution = {} - - # 统计活跃目标 - for goal in self.active_goals.values(): - goal_type = goal.get("type", "unknown") - distribution[goal_type] = distribution.get(goal_type, 0) + 1 - - # 统计历史目标 - for goal in self.goal_history: - goal_type = goal.get("type", "unknown") - distribution[goal_type] = distribution.get(goal_type, 0) + 1 - - return distribution diff --git a/src/agent/intelligent_agent.py b/src/agent/intelligent_agent.py deleted file mode 100644 index be70d46..0000000 --- a/src/agent/intelligent_agent.py +++ /dev/null @@ -1,371 +0,0 @@ - -# -*- coding: utf-8 -*- -""" -智能Agent核心 - 集成大模型和智能决策 -高效实现Agent的智能处理能力 -""" - -import logging -import asyncio -import json -from typing import Dict, Any, List, Optional, Tuple -from datetime import datetime -from dataclasses import dataclass -from enum import Enum - -logger = logging.getLogger(__name__) - -class ActionType(Enum): - """动作类型枚举""" - ALERT_RESPONSE = "alert_response" - KNOWLEDGE_UPDATE = "knowledge_update" - WORKORDER_CREATE = "workorder_create" - SYSTEM_OPTIMIZE = "system_optimize" - USER_NOTIFY = "user_notify" - -class ConfidenceLevel(Enum): - """置信度等级""" - HIGH = "high" # 高置信度 (>0.8) - MEDIUM = "medium" # 中等置信度 (0.5-0.8) - LOW = "low" # 低置信度 (<0.5) - -@dataclass -class AgentAction: - """Agent动作""" - action_type: ActionType - description: str - priority: int # 1-5, 5最高 - confidence: float # 0-1 - parameters: Dict[str, Any] - estimated_time: int # 预计执行时间(秒) - -@dataclass -class AlertContext: - """预警上下文""" - alert_id: str - alert_type: str - severity: str - description: str - affected_systems: List[str] - metrics: Dict[str, Any] - -@dataclass -class KnowledgeContext: - """知识库上下文""" - question: str - answer: str - confidence: float - source: str - category: str - -class IntelligentAgent: - """智能Agent核心""" - - def __init__(self, llm_client=None): - self.llm_client = llm_client - self.action_history = [] - self.learning_data = {} - self.confidence_thresholds = { - 'high': 0.8, - 'medium': 0.5, - 'low': 0.3 - } - - async def process_alert(self, alert_context: AlertContext) -> List[AgentAction]: - """处理预警信息,生成智能动作""" - try: - # 构建预警分析提示 - prompt = self._build_alert_analysis_prompt(alert_context) - - # 调用大模型分析 - analysis = await self._call_llm(prompt) - - # 解析动作 - actions = self._parse_alert_actions(analysis, alert_context) - - # 按优先级排序 - actions.sort(key=lambda x: x.priority, reverse=True) - - return actions - - except Exception as e: - logger.error(f"处理预警失败: {e}") - return [self._create_default_alert_action(alert_context)] - - async def process_knowledge_confidence(self, knowledge_context: KnowledgeContext) -> List[AgentAction]: - """处理知识库置信度问题""" - try: - if knowledge_context.confidence >= self.confidence_thresholds['high']: - return [] # 高置信度,无需处理 - - # 构建知识增强提示 - prompt = self._build_knowledge_enhancement_prompt(knowledge_context) - - # 调用大模型增强知识 - enhancement = await self._call_llm(prompt) - - # 生成增强动作 - actions = self._parse_knowledge_actions(enhancement, knowledge_context) - - return actions - - except Exception as e: - logger.error(f"处理知识库置信度失败: {e}") - return [self._create_default_knowledge_action(knowledge_context)] - - async def execute_action(self, action: AgentAction) -> Dict[str, Any]: - """执行Agent动作""" - try: - logger.info(f"执行Agent动作: {action.action_type.value} - {action.description}") - - if action.action_type == ActionType.ALERT_RESPONSE: - return await self._execute_alert_response(action) - elif action.action_type == ActionType.KNOWLEDGE_UPDATE: - return await self._execute_knowledge_update(action) - elif action.action_type == ActionType.WORKORDER_CREATE: - return await self._execute_workorder_create(action) - elif action.action_type == ActionType.SYSTEM_OPTIMIZE: - return await self._execute_system_optimize(action) - elif action.action_type == ActionType.USER_NOTIFY: - return await self._execute_user_notify(action) - else: - return {"success": False, "error": "未知动作类型"} - - except Exception as e: - logger.error(f"执行动作失败: {e}") - return {"success": False, "error": str(e)} - - def _build_alert_analysis_prompt(self, alert_context: AlertContext) -> str: - """构建预警分析提示""" - return f""" -作为TSP智能助手,请分析以下预警信息并提供处理建议: - -预警信息: -- 类型: {alert_context.alert_type} -- 严重程度: {alert_context.severity} -- 描述: {alert_context.description} -- 影响系统: {', '.join(alert_context.affected_systems)} -- 指标数据: {json.dumps(alert_context.metrics, ensure_ascii=False)} - -请提供以下格式的JSON响应: -{{ - "analysis": "预警原因分析", - "immediate_actions": [ - {{ - "action": "立即执行的动作", - "priority": 5, - "confidence": 0.9, - "parameters": {{"key": "value"}} - }} - ], - "follow_up_actions": [ - {{ - "action": "后续跟进动作", - "priority": 3, - "confidence": 0.7, - "parameters": {{"key": "value"}} - }} - ], - "prevention_measures": [ - "预防措施1", - "预防措施2" - ] -}} -""" - - def _build_knowledge_enhancement_prompt(self, knowledge_context: KnowledgeContext) -> str: - """构建知识增强提示""" - return f""" -作为TSP智能助手,请分析以下知识库条目并提供增强建议: - -知识条目: -- 问题: {knowledge_context.question} -- 答案: {knowledge_context.answer} -- 置信度: {knowledge_context.confidence} -- 来源: {knowledge_context.source} -- 分类: {knowledge_context.category} - -请提供以下格式的JSON响应: -{{ - "confidence_analysis": "置信度分析", - "enhancement_suggestions": [ - "增强建议1", - "增强建议2" - ], - "actions": [ - {{ - "action": "知识更新动作", - "priority": 4, - "confidence": 0.8, - "parameters": {{"enhanced_answer": "增强后的答案"}} - }} - ], - "learning_opportunities": [ - "学习机会1", - "学习机会2" - ] -}} -""" - - async def _call_llm(self, prompt: str) -> Dict[str, Any]: - """调用大模型""" - try: - if self.llm_client: - # 使用真实的大模型客户端 - response = await self.llm_client.generate(prompt) - return json.loads(response) - else: - # 模拟大模型响应 - return self._simulate_llm_response(prompt) - except Exception as e: - logger.error(f"调用大模型失败: {e}") - return self._simulate_llm_response(prompt) - - def _simulate_llm_response(self, prompt: str) -> Dict[str, Any]: - """模拟大模型响应 - 千问模型风格""" - if "预警信息" in prompt: - return { - "analysis": "【千问分析】系统性能下降,需要立即处理。根据历史数据分析,这可能是由于资源不足或配置问题导致的。", - "immediate_actions": [ - { - "action": "重启相关服务", - "priority": 5, - "confidence": 0.9, - "parameters": {"service": "main_service", "reason": "服务响应超时"} - } - ], - "follow_up_actions": [ - { - "action": "检查系统日志", - "priority": 3, - "confidence": 0.7, - "parameters": {"log_level": "error", "time_range": "last_hour"} - } - ], - "prevention_measures": [ - "增加监控频率,提前发现问题", - "优化系统配置,提升性能", - "建立预警机制,减少故障影响" - ] - } - else: - return { - "confidence_analysis": "【千问分析】当前答案置信度较低,需要更多上下文信息。建议结合用户反馈和历史工单数据来提升答案质量。", - "enhancement_suggestions": [ - "添加更多实际案例和操作步骤", - "提供详细的故障排除指南", - "结合系统架构图进行说明" - ], - "actions": [ - { - "action": "更新知识库条目", - "priority": 4, - "confidence": 0.8, - "parameters": {"enhanced_answer": "基于千问模型分析的增强答案"} - } - ], - "learning_opportunities": [ - "收集用户反馈,持续优化答案", - "分析相似问题,建立知识关联", - "利用千问模型的学习能力,提升知识质量" - ] - } - - def _parse_alert_actions(self, analysis: Dict[str, Any], alert_context: AlertContext) -> List[AgentAction]: - """解析预警动作""" - actions = [] - - # 立即动作 - for action_data in analysis.get("immediate_actions", []): - action = AgentAction( - action_type=ActionType.ALERT_RESPONSE, - description=action_data["action"], - priority=action_data["priority"], - confidence=action_data["confidence"], - parameters=action_data["parameters"], - estimated_time=30 - ) - actions.append(action) - - # 后续动作 - for action_data in analysis.get("follow_up_actions", []): - action = AgentAction( - action_type=ActionType.SYSTEM_OPTIMIZE, - description=action_data["action"], - priority=action_data["priority"], - confidence=action_data["confidence"], - parameters=action_data["parameters"], - estimated_time=300 - ) - actions.append(action) - - return actions - - def _parse_knowledge_actions(self, enhancement: Dict[str, Any], knowledge_context: KnowledgeContext) -> List[AgentAction]: - """解析知识库动作""" - actions = [] - - for action_data in enhancement.get("actions", []): - action = AgentAction( - action_type=ActionType.KNOWLEDGE_UPDATE, - description=action_data["action"], - priority=action_data["priority"], - confidence=action_data["confidence"], - parameters=action_data["parameters"], - estimated_time=60 - ) - actions.append(action) - - return actions - - def _create_default_alert_action(self, alert_context: AlertContext) -> AgentAction: - """创建默认预警动作""" - return AgentAction( - action_type=ActionType.USER_NOTIFY, - description=f"通知管理员处理{alert_context.alert_type}预警", - priority=3, - confidence=0.5, - parameters={"alert_id": alert_context.alert_id}, - estimated_time=10 - ) - - def _create_default_knowledge_action(self, knowledge_context: KnowledgeContext) -> AgentAction: - """创建默认知识库动作""" - return AgentAction( - action_type=ActionType.KNOWLEDGE_UPDATE, - description="标记低置信度知识条目,等待人工审核", - priority=2, - confidence=0.3, - parameters={"question": knowledge_context.question}, - estimated_time=5 - ) - - async def _execute_alert_response(self, action: AgentAction) -> Dict[str, Any]: - """执行预警响应动作""" - # 这里实现具体的预警响应逻辑 - logger.info(f"执行预警响应: {action.description}") - return {"success": True, "message": "预警响应已执行"} - - async def _execute_knowledge_update(self, action: AgentAction) -> Dict[str, Any]: - """执行知识库更新动作""" - # 这里实现具体的知识库更新逻辑 - logger.info(f"执行知识库更新: {action.description}") - return {"success": True, "message": "知识库已更新"} - - async def _execute_workorder_create(self, action: AgentAction) -> Dict[str, Any]: - """执行工单创建动作""" - # 这里实现具体的工单创建逻辑 - logger.info(f"执行工单创建: {action.description}") - return {"success": True, "message": "工单已创建"} - - async def _execute_system_optimize(self, action: AgentAction) -> Dict[str, Any]: - """执行系统优化动作""" - # 这里实现具体的系统优化逻辑 - logger.info(f"执行系统优化: {action.description}") - return {"success": True, "message": "系统优化已执行"} - - async def _execute_user_notify(self, action: AgentAction) -> Dict[str, Any]: - """执行用户通知动作""" - # 这里实现具体的用户通知逻辑 - logger.info(f"执行用户通知: {action.description}") - return {"success": True, "message": "用户已通知"} diff --git a/src/agent/planner.py b/src/agent/planner.py deleted file mode 100644 index 66fa553..0000000 --- a/src/agent/planner.py +++ /dev/null @@ -1,409 +0,0 @@ - -# -*- coding: utf-8 -*- -""" -任务规划器 -负责制定执行计划和任务分解 -""" - -import logging -from typing import Dict, List, Any, Optional -from datetime import datetime -import json - -from ..core.llm_client import QwenClient - -logger = logging.getLogger(__name__) - -class TaskPlanner: - """任务规划器""" - - def __init__(self): - self.llm_client = QwenClient() - self.planning_strategies = { - "sequential": self._create_sequential_plan, - "parallel": self._create_parallel_plan, - "conditional": self._create_conditional_plan, - "iterative": self._create_iterative_plan - } - - async def create_plan( - self, - goal: Dict[str, Any], - available_tools: List[Dict[str, Any]], - constraints: Dict[str, Any] - ) -> List[Dict[str, Any]]: - """创建执行计划""" - try: - # 1. 分析目标复杂度 - complexity = await self._analyze_goal_complexity(goal) - - # 2. 选择规划策略 - strategy = self._select_planning_strategy(complexity, goal) - - # 3. 生成计划 - plan = await self.planning_strategies[strategy](goal, available_tools, constraints) - - # 4. 优化计划 - optimized_plan = await self._optimize_plan(plan, constraints) - - logger.info(f"创建计划成功,包含 {len(optimized_plan)} 个任务") - return optimized_plan - - except Exception as e: - logger.error(f"创建计划失败: {e}") - return [] - - async def _analyze_goal_complexity(self, goal: Dict[str, Any]) -> Dict[str, Any]: - """分析目标复杂度""" - prompt = f""" - 请分析以下目标的复杂度: - - 目标: {goal.get('description', '')} - 类型: {goal.get('type', '')} - 上下文: {goal.get('context', {})} - - 请从以下维度评估复杂度(1-10分): - 1. 任务数量 - 2. 依赖关系复杂度 - 3. 所需工具数量 - 4. 时间要求 - 5. 资源需求 - - 请以JSON格式返回分析结果。 - """ - - messages = [ - {"role": "system", "content": "你是一个任务规划专家,擅长分析任务复杂度。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return {"complexity_score": 5, "strategy": "sequential"} - - try: - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - if json_match: - analysis = json.loads(json_match.group()) - return analysis - else: - return {"complexity_score": 5, "strategy": "sequential"} - except Exception as e: - logger.error(f"解析复杂度分析失败: {e}") - return {"complexity_score": 5, "strategy": "sequential"} - - def _select_planning_strategy(self, complexity: Dict[str, Any], goal: Dict[str, Any]) -> str: - """选择规划策略""" - complexity_score = complexity.get("complexity_score", 5) - goal_type = goal.get("type", "general") - - if complexity_score <= 3: - return "sequential" - elif complexity_score <= 6: - if goal_type in ["analysis", "monitoring"]: - return "parallel" - else: - return "conditional" - else: - return "iterative" - - async def _create_sequential_plan( - self, - goal: Dict[str, Any], - available_tools: List[Dict[str, Any]], - constraints: Dict[str, Any] - ) -> List[Dict[str, Any]]: - """创建顺序执行计划""" - prompt = f""" - 请为以下目标创建一个顺序执行计划: - - 目标: {goal.get('description', '')} - 可用工具: {[tool.get('name', '') for tool in available_tools]} - - 请将目标分解为具体的执行步骤,每个步骤包含: - 1. 任务描述 - 2. 所需工具 - 3. 输入参数 - 4. 预期输出 - 5. 成功条件 - - 请以JSON数组格式返回计划。 - """ - - messages = [ - {"role": "system", "content": "你是一个任务规划专家,擅长创建顺序执行计划。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return self._create_fallback_plan(goal) - - try: - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\[.*\]', response_content, re.DOTALL) - if json_match: - plan = json.loads(json_match.group()) - return self._format_plan_tasks(plan) - else: - return self._create_fallback_plan(goal) - except Exception as e: - logger.error(f"解析顺序计划失败: {e}") - return self._create_fallback_plan(goal) - - async def _create_parallel_plan( - self, - goal: Dict[str, Any], - available_tools: List[Dict[str, Any]], - constraints: Dict[str, Any] - ) -> List[Dict[str, Any]]: - """创建并行执行计划""" - # 先创建基础任务 - base_tasks = await self._create_sequential_plan(goal, available_tools, constraints) - - # 分析任务间的依赖关系 - parallel_groups = self._group_parallel_tasks(base_tasks) - - return parallel_groups - - async def _create_conditional_plan( - self, - goal: Dict[str, Any], - available_tools: List[Dict[str, Any]], - constraints: Dict[str, Any] - ) -> List[Dict[str, Any]]: - """创建条件执行计划""" - prompt = f""" - 请为以下目标创建一个条件执行计划: - - 目标: {goal.get('description', '')} - 上下文: {goal.get('context', {})} - - 计划应该包含: - 1. 初始条件检查 - 2. 分支逻辑 - 3. 每个分支的具体任务 - 4. 合并条件 - - 请以JSON格式返回计划。 - """ - - messages = [ - {"role": "system", "content": "你是一个任务规划专家,擅长创建条件执行计划。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return await self._create_sequential_plan(goal, available_tools, constraints) - - try: - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - if json_match: - plan = json.loads(json_match.group()) - return self._format_conditional_plan(plan) - else: - return await self._create_sequential_plan(goal, available_tools, constraints) - except Exception as e: - logger.error(f"解析条件计划失败: {e}") - return await self._create_sequential_plan(goal, available_tools, constraints) - - async def _create_iterative_plan( - self, - goal: Dict[str, Any], - available_tools: List[Dict[str, Any]], - constraints: Dict[str, Any] - ) -> List[Dict[str, Any]]: - """创建迭代执行计划""" - # 创建基础计划 - base_plan = await self._create_sequential_plan(goal, available_tools, constraints) - - # 添加迭代控制任务 - iteration_control = { - "id": "iteration_control", - "type": "control", - "description": "迭代控制", - "max_iterations": constraints.get("max_iterations", 10), - "convergence_criteria": goal.get("success_criteria", {}), - "tasks": base_plan - } - - return [iteration_control] - - def _group_parallel_tasks(self, tasks: List[Dict[str, Any]]) -> List[Dict[str, Any]]: - """将任务分组为可并行执行的任务组""" - groups = [] - current_group = [] - - for task in tasks: - # 简单的分组逻辑:相同类型的任务可以并行 - if not current_group or current_group[0].get("type") == task.get("type"): - current_group.append(task) - else: - if current_group: - groups.append({ - "type": "parallel_group", - "tasks": current_group, - "execution_mode": "parallel" - }) - current_group = [task] - - if current_group: - groups.append({ - "type": "parallel_group", - "tasks": current_group, - "execution_mode": "parallel" - }) - - return groups - - def _format_plan_tasks(self, raw_plan: List[Dict[str, Any]]) -> List[Dict[str, Any]]: - """格式化计划任务""" - formatted_tasks = [] - - for i, task in enumerate(raw_plan): - formatted_task = { - "id": f"task_{i+1}", - "type": task.get("type", "action"), - "description": task.get("description", ""), - "tool": task.get("tool", ""), - "parameters": task.get("parameters", {}), - "expected_output": task.get("expected_output", ""), - "success_criteria": task.get("success_criteria", {}), - "dependencies": task.get("dependencies", []), - "priority": task.get("priority", 0.5), - "timeout": task.get("timeout", 60) - } - formatted_tasks.append(formatted_task) - - return formatted_tasks - - def _format_conditional_plan(self, raw_plan: Dict[str, Any]) -> List[Dict[str, Any]]: - """格式化条件计划""" - formatted_tasks = [] - - # 添加条件检查任务 - condition_task = { - "id": "condition_check", - "type": "condition", - "description": "条件检查", - "condition": raw_plan.get("condition", ""), - "branches": raw_plan.get("branches", {}) - } - formatted_tasks.append(condition_task) - - # 添加分支任务 - for branch_name, branch_tasks in raw_plan.get("branches", {}).items(): - branch_task = { - "id": f"branch_{branch_name}", - "type": "branch", - "description": f"执行分支: {branch_name}", - "condition": branch_name, - "tasks": self._format_plan_tasks(branch_tasks) - } - formatted_tasks.append(branch_task) - - return formatted_tasks - - async def _optimize_plan(self, plan: List[Dict[str, Any]], constraints: Dict[str, Any]) -> List[Dict[str, Any]]: - """优化计划""" - optimized_plan = [] - - for task in plan: - # 检查时间约束 - if task.get("timeout", 60) > constraints.get("timeout", 300): - task["timeout"] = constraints.get("timeout", 300) - - # 检查资源约束 - if task.get("resource_usage", 0) > constraints.get("memory_limit", 1000): - # 分解大任务 - subtasks = await self._decompose_task(task) - optimized_plan.extend(subtasks) - else: - optimized_plan.append(task) - - return optimized_plan - - async def _decompose_task(self, task: Dict[str, Any]) -> List[Dict[str, Any]]: - """分解大任务为小任务""" - prompt = f""" - 请将以下大任务分解为更小的子任务: - - 任务: {task.get('description', '')} - 类型: {task.get('type', '')} - 参数: {task.get('parameters', {})} - - 请返回分解后的子任务列表,每个子任务应该是独立的、可执行的。 - """ - - messages = [ - {"role": "system", "content": "你是一个任务分解专家,擅长将复杂任务分解为简单任务。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return [task] # 如果分解失败,返回原任务 - - try: - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\[.*\]', response_content, re.DOTALL) - if json_match: - subtasks = json.loads(json_match.group()) - return self._format_plan_tasks(subtasks) - else: - return [task] - except Exception as e: - logger.error(f"任务分解失败: {e}") - return [task] - - def _create_fallback_plan(self, goal: Dict[str, Any]) -> List[Dict[str, Any]]: - """创建备用计划""" - return [{ - "id": "fallback_task", - "type": "action", - "description": goal.get("description", "执行目标"), - "tool": "general_response", - "parameters": {"goal": goal}, - "expected_output": "目标完成", - "success_criteria": {"completion": True}, - "priority": 0.5, - "timeout": 60 - }] - - def validate_plan(self, plan: List[Dict[str, Any]]) -> Dict[str, Any]: - """验证计划的有效性""" - validation_result = { - "valid": True, - "issues": [], - "warnings": [] - } - - for task in plan: - # 检查必要字段 - if not task.get("id"): - validation_result["issues"].append("任务缺少ID") - validation_result["valid"] = False - - if not task.get("description"): - validation_result["warnings"].append(f"任务 {task.get('id', 'unknown')} 缺少描述") - - # 检查依赖关系 - dependencies = task.get("dependencies", []) - task_ids = [t.get("id") for t in plan] - for dep in dependencies: - if dep not in task_ids: - validation_result["issues"].append(f"任务 {task.get('id')} 的依赖 {dep} 不存在") - validation_result["valid"] = False - - return validation_result diff --git a/src/agent/react_agent.py b/src/agent/react_agent.py new file mode 100644 index 0000000..23c0969 --- /dev/null +++ b/src/agent/react_agent.py @@ -0,0 +1,345 @@ +# -*- coding: utf-8 -*- +""" +ReAct Agent - 基于 ReAct 模式的智能代理 +用单次 LLM 调用 + 工具循环替代原有的多步流水线 +""" + +import logging +import json +import re +from typing import Dict, Any, List, Optional +from datetime import datetime + +from src.agent.llm_client import LLMManager +from src.config.unified_config import get_config + +logger = logging.getLogger(__name__) + +# ── 工具定义(供 LLM 理解可用能力) ────────────────────────── + +TOOL_DEFINITIONS = [ + { + "name": "search_knowledge", + "description": "搜索知识库,根据关键词查找相关的问题和答案", + "parameters": { + "query": {"type": "string", "description": "搜索关键词", "required": True}, + "top_k": {"type": "integer", "description": "返回结果数量,默认3", "required": False} + } + }, + { + "name": "add_knowledge", + "description": "向知识库添加新的问答条目", + "parameters": { + "question": {"type": "string", "description": "问题", "required": True}, + "answer": {"type": "string", "description": "答案", "required": True}, + "category": {"type": "string", "description": "分类", "required": False} + } + }, + { + "name": "query_vehicle", + "description": "查询车辆信息,支持按VIN码或车牌号查询", + "parameters": { + "vin": {"type": "string", "description": "VIN码", "required": False}, + "plate_number": {"type": "string", "description": "车牌号", "required": False} + } + }, + { + "name": "get_analytics", + "description": "获取系统数据分析报告,如每日统计、分类统计等", + "parameters": { + "report_type": { + "type": "string", + "description": "报告类型: daily_analytics / summary / category_performance", + "required": True + } + } + }, + { + "name": "send_feishu_message", + "description": "通过飞书发送消息通知", + "parameters": { + "message": {"type": "string", "description": "消息内容", "required": True}, + "chat_id": {"type": "string", "description": "飞书群聊ID(可选)", "required": False} + } + }, +] + + +def _build_tools_prompt() -> str: + """构建工具描述文本供 system prompt 使用""" + lines = [] + for t in TOOL_DEFINITIONS: + params_desc = [] + for pname, pinfo in t["parameters"].items(): + req = "必填" if pinfo.get("required") else "可选" + params_desc.append(f" - {pname} ({pinfo['type']}, {req}): {pinfo['description']}") + lines.append(f"- {t['name']}: {t['description']}\n 参数:\n" + "\n".join(params_desc)) + return "\n".join(lines) + + +SYSTEM_PROMPT = f"""你是 TSP 智能客服助手,帮助用户解决车辆售后问题、查询知识库、管理客诉信息。 + +你可以使用以下工具来完成任务: +{_build_tools_prompt()} + +## 回复规则 +1. 如果你需要使用工具,请严格按以下 JSON 格式回复(不要包含其他内容): +```json +{{"tool": "工具名", "parameters": {{"参数名": "参数值"}}}} +``` + +2. 如果你不需要使用工具,可以直接用自然语言回复用户。 +3. 每次只调用一个工具。 +4. 根据工具返回的结果,综合生成最终回复。 +5. 回复要简洁专业,使用中文。 +""" + + +class ReactAgent: + """基于 ReAct 模式的 Agent""" + + MAX_TOOL_ROUNDS = 5 # 最多工具调用轮次,防止死循环 + + def __init__(self): + config = get_config() + self.llm = LLMManager(config.llm) + self._tool_handlers = self._register_tool_handlers() + self.execution_history: List[Dict[str, Any]] = [] + logger.info("ReactAgent 初始化完成") + + # ── 工具处理器注册 ────────────────────────────────────── + + def _register_tool_handlers(self) -> Dict[str, Any]: + return { + "search_knowledge": self._tool_search_knowledge, + "add_knowledge": self._tool_add_knowledge, + "query_vehicle": self._tool_query_vehicle, + "get_analytics": self._tool_get_analytics, + "send_feishu_message": self._tool_send_feishu_message, + } + + # ── 主处理入口 ────────────────────────────────────────── + + async def chat( + self, + message: str, + user_id: str = "anonymous", + conversation_history: Optional[List[Dict[str, str]]] = None, + ) -> Dict[str, Any]: + """处理用户消息,返回最终回复""" + messages = [{"role": "system", "content": SYSTEM_PROMPT}] + + # 加入历史对话(最近 10 轮) + if conversation_history: + messages.extend(conversation_history[-10:]) + + messages.append({"role": "user", "content": message}) + + tool_calls_log = [] + + for round_idx in range(self.MAX_TOOL_ROUNDS): + # 调用 LLM + try: + response_text = await self.llm.chat(messages, temperature=0.3, max_tokens=2000) + except Exception as e: + logger.error(f"LLM 调用失败: {e}") + return self._error_response(str(e)) + + # 尝试解析工具调用 + tool_call = self._parse_tool_call(response_text) + + if tool_call is None: + # 没有工具调用 → 这是最终回复 + self._record_execution(message, user_id, tool_calls_log, response_text) + return { + "success": True, + "response": response_text, + "tool_calls": tool_calls_log, + "rounds": round_idx + 1, + } + + # 执行工具 + tool_name = tool_call["tool"] + tool_params = tool_call.get("parameters", {}) + logger.info(f"[Round {round_idx+1}] 调用工具: {tool_name}, 参数: {tool_params}") + + tool_result = await self._execute_tool(tool_name, tool_params) + tool_calls_log.append({ + "tool": tool_name, + "parameters": tool_params, + "result": tool_result, + "round": round_idx + 1, + }) + + # 把工具调用和结果加入对话上下文 + messages.append({"role": "assistant", "content": response_text}) + messages.append({ + "role": "user", + "content": f"工具 `{tool_name}` 返回结果:\n```json\n{json.dumps(tool_result, ensure_ascii=False, default=str)}\n```\n请根据以上结果回复用户。" + }) + + # 超过最大轮次 + self._record_execution(message, user_id, tool_calls_log, "[达到最大工具调用轮次]") + return { + "success": True, + "response": "抱歉,处理过程较复杂,请稍后重试或换个方式描述您的问题。", + "tool_calls": tool_calls_log, + "rounds": self.MAX_TOOL_ROUNDS, + } + + # ── 工具调用解析 ────────────────────────────────────── + + def _parse_tool_call(self, text: str) -> Optional[Dict[str, Any]]: + """从 LLM 回复中解析工具调用 JSON""" + if not text: + return None + + # 尝试从 ```json ... ``` 代码块中提取 + code_block = re.search(r'```json\s*(\{.*?\})\s*```', text, re.DOTALL) + if code_block: + try: + data = json.loads(code_block.group(1)) + if "tool" in data: + return data + except json.JSONDecodeError: + pass + + # 尝试直接解析整段文本为 JSON + try: + data = json.loads(text.strip()) + if isinstance(data, dict) and "tool" in data: + return data + except json.JSONDecodeError: + pass + + # 尝试从文本中找到第一个 JSON 对象 + json_match = re.search(r'\{[^{}]*"tool"\s*:\s*"[^"]+?"[^{}]*\}', text, re.DOTALL) + if json_match: + try: + data = json.loads(json_match.group()) + if "tool" in data: + return data + except json.JSONDecodeError: + pass + + return None + + # ── 工具执行 ────────────────────────────────────────── + + async def _execute_tool(self, tool_name: str, params: Dict[str, Any]) -> Dict[str, Any]: + """执行指定工具""" + handler = self._tool_handlers.get(tool_name) + if not handler: + return {"error": f"未知工具: {tool_name}"} + try: + return await handler(**params) + except Exception as e: + logger.error(f"工具 {tool_name} 执行失败: {e}") + return {"error": str(e)} + + # ── 具体工具实现 ────────────────────────────────────── + + async def _tool_search_knowledge(self, query: str, top_k: int = 3, **kw) -> Dict[str, Any]: + """搜索知识库""" + try: + from src.knowledge_base.knowledge_manager import KnowledgeManager + km = KnowledgeManager() + results = km.search_knowledge(query, top_k) + return {"results": results, "count": len(results)} + except Exception as e: + return {"error": str(e)} + + async def _tool_add_knowledge(self, question: str, answer: str, category: str = "通用", **kw) -> Dict[str, Any]: + """添加知识库条目""" + try: + from src.knowledge_base.knowledge_manager import KnowledgeManager + km = KnowledgeManager() + success = km.add_knowledge_entry(question=question, answer=answer, category=category) + return {"success": success} + except Exception as e: + return {"error": str(e)} + + async def _tool_query_vehicle(self, vin: str = None, plate_number: str = None, **kw) -> Dict[str, Any]: + """查询车辆信息""" + try: + from src.vehicle.vehicle_data_manager import VehicleDataManager + vm = VehicleDataManager() + if vin: + result = vm.get_latest_vehicle_data_by_vin(vin) + return {"vehicle_data": result} if result else {"error": "未找到该VIN的车辆数据"} + elif plate_number: + return {"error": "暂不支持按车牌号查询,请使用VIN码"} + else: + return {"error": "请提供 VIN 码"} + except Exception as e: + return {"error": str(e)} + + async def _tool_get_analytics(self, report_type: str = "summary", **kw) -> Dict[str, Any]: + """获取分析报告""" + try: + from src.analytics.analytics_manager import AnalyticsManager + am = AnalyticsManager() + if report_type == "daily_analytics": + return am.generate_daily_analytics() + elif report_type == "summary": + return am.get_analytics_summary() + elif report_type == "category_performance": + return am.get_category_performance() + else: + return {"error": f"不支持的报告类型: {report_type}"} + except Exception as e: + return {"error": str(e)} + + async def _tool_send_feishu_message(self, message: str, chat_id: str = None, **kw) -> Dict[str, Any]: + """发送飞书消息""" + try: + from src.integrations.feishu_service import FeishuService + fs = FeishuService() + if not chat_id: + return {"error": "请提供飞书群聊 chat_id"} + success = fs.send_message(receive_id=chat_id, content=message, receive_id_type="chat_id") + return {"success": success} + except Exception as e: + return {"error": str(e)} + + # ── 辅助方法 ────────────────────────────────────────── + + def _record_execution(self, message: str, user_id: str, tool_calls: list, response: str): + """记录执行历史""" + record = { + "timestamp": datetime.now().isoformat(), + "user_id": user_id, + "message": message, + "tool_calls": tool_calls, + "response": response[:500], + } + self.execution_history.append(record) + if len(self.execution_history) > 500: + self.execution_history = self.execution_history[-500:] + + def _error_response(self, error_msg: str) -> Dict[str, Any]: + return { + "success": False, + "response": "抱歉,系统处理出现问题,请稍后重试。", + "error": error_msg, + "tool_calls": [], + "rounds": 0, + } + + def get_tool_definitions(self) -> List[Dict[str, Any]]: + """返回工具定义列表(供 API 展示)""" + return TOOL_DEFINITIONS + + def get_execution_history(self, limit: int = 50) -> List[Dict[str, Any]]: + """获取执行历史""" + return self.execution_history[-limit:] + + def get_status(self) -> Dict[str, Any]: + """获取 Agent 状态""" + return { + "status": "active", + "available_tools": [t["name"] for t in TOOL_DEFINITIONS], + "tool_count": len(TOOL_DEFINITIONS), + "history_count": len(self.execution_history), + "max_tool_rounds": self.MAX_TOOL_ROUNDS, + } diff --git a/src/agent/reasoning_engine.py b/src/agent/reasoning_engine.py deleted file mode 100644 index 6512eb0..0000000 --- a/src/agent/reasoning_engine.py +++ /dev/null @@ -1,479 +0,0 @@ - -# -*- coding: utf-8 -*- -""" -推理引擎 -负责逻辑推理和决策制定 -""" - -import logging -from typing import Dict, List, Any, Optional -from datetime import datetime -import json - -from ..core.llm_client import QwenClient - -logger = logging.getLogger(__name__) - -class ReasoningEngine: - """推理引擎""" - - def __init__(self): - self.llm_client = QwenClient() - self.reasoning_patterns = { - "causal": self._causal_reasoning, - "deductive": self._deductive_reasoning, - "inductive": self._inductive_reasoning, - "abductive": self._abductive_reasoning, - "analogical": self._analogical_reasoning - } - self.reasoning_history = [] - - async def analyze_intent( - self, - message: str, - context: Dict[str, Any], - history: List[Dict[str, Any]] - ) -> Dict[str, Any]: - """分析用户意图""" - try: - prompt = f""" - 请分析以下用户消息的意图: - - 用户消息: {message} - 上下文: {json.dumps(context, ensure_ascii=False)} - 历史记录: {json.dumps(history, ensure_ascii=False)} - - 请从以下维度分析: - 1. 主要意图(问题咨询、工单创建、系统查询等) - 2. 情感倾向(积极、消极、中性) - 3. 紧急程度(高、中、低) - 4. 所需工具类型 - 5. 预期响应类型 - 6. 关键信息提取 - - 请以JSON格式返回分析结果。 - """ - - messages = [ - {"role": "system", "content": "你是一个意图分析专家,擅长理解用户需求和意图。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return self._create_fallback_intent(message) - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - intent_analysis = json.loads(json_match.group()) - intent_analysis["timestamp"] = datetime.now().isoformat() - return intent_analysis - else: - return self._create_fallback_intent(message) - - except Exception as e: - logger.error(f"意图分析失败: {e}") - return self._create_fallback_intent(message) - - async def make_decision( - self, - situation: Dict[str, Any], - options: List[Dict[str, Any]], - criteria: Dict[str, Any] - ) -> Dict[str, Any]: - """制定决策""" - try: - prompt = f""" - 请根据以下情况制定决策: - - 当前情况: {json.dumps(situation, ensure_ascii=False)} - 可选方案: {json.dumps(options, ensure_ascii=False)} - 决策标准: {json.dumps(criteria, ensure_ascii=False)} - - 请分析每个方案的优缺点,并选择最佳方案。 - 返回格式: - {{ - "selected_option": "方案ID", - "reasoning": "选择理由", - "confidence": 0.8, - "risks": ["风险1", "风险2"], - "mitigation": "风险缓解措施" - }} - """ - - messages = [ - {"role": "system", "content": "你是一个决策制定专家,擅长分析情况并做出最优决策。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return self._create_fallback_decision(options) - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - decision = json.loads(json_match.group()) - decision["timestamp"] = datetime.now().isoformat() - return decision - else: - return self._create_fallback_decision(options) - - except Exception as e: - logger.error(f"决策制定失败: {e}") - return self._create_fallback_decision(options) - - async def reason_about_problem( - self, - problem: str, - available_information: Dict[str, Any], - reasoning_type: str = "causal" - ) -> Dict[str, Any]: - """对问题进行推理""" - try: - if reasoning_type not in self.reasoning_patterns: - reasoning_type = "causal" - - reasoning_func = self.reasoning_patterns[reasoning_type] - result = await reasoning_func(problem, available_information) - - # 记录推理历史 - self.reasoning_history.append({ - "timestamp": datetime.now().isoformat(), - "problem": problem, - "reasoning_type": reasoning_type, - "result": result - }) - - return result - - except Exception as e: - logger.error(f"问题推理失败: {e}") - return {"error": str(e)} - - async def _causal_reasoning(self, problem: str, information: Dict[str, Any]) -> Dict[str, Any]: - """因果推理""" - prompt = f""" - 请使用因果推理分析以下问题: - - 问题: {problem} - 可用信息: {json.dumps(information, ensure_ascii=False)} - - 请分析: - 1. 问题的根本原因 - 2. 可能的因果关系链 - 3. 影响因素分析 - 4. 解决方案的预期效果 - - 请以JSON格式返回分析结果。 - """ - - messages = [ - {"role": "system", "content": "你是一个因果推理专家,擅长分析问题的因果关系。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return {"reasoning_type": "causal", "error": "推理失败"} - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - return json.loads(json_match.group()) - else: - return {"reasoning_type": "causal", "analysis": response_content} - - async def _deductive_reasoning(self, problem: str, information: Dict[str, Any]) -> Dict[str, Any]: - """演绎推理""" - prompt = f""" - 请使用演绎推理分析以下问题: - - 问题: {problem} - 可用信息: {json.dumps(information, ensure_ascii=False)} - - 请分析: - 1. 一般性规则或原理 - 2. 具体事实或条件 - 3. 逻辑推导过程 - 4. 必然结论 - - 请以JSON格式返回分析结果。 - """ - - messages = [ - {"role": "system", "content": "你是一个演绎推理专家,擅长从一般原理推导具体结论。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return {"reasoning_type": "deductive", "error": "推理失败"} - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - return json.loads(json_match.group()) - else: - return {"reasoning_type": "deductive", "analysis": response_content} - - async def _inductive_reasoning(self, problem: str, information: Dict[str, Any]) -> Dict[str, Any]: - """归纳推理""" - prompt = f""" - 请使用归纳推理分析以下问题: - - 问题: {problem} - 可用信息: {json.dumps(information, ensure_ascii=False)} - - 请分析: - 1. 观察到的具体现象 - 2. 寻找共同模式 - 3. 形成一般性假设 - 4. 验证假设的合理性 - - 请以JSON格式返回分析结果。 - """ - - messages = [ - {"role": "system", "content": "你是一个归纳推理专家,擅长从具体现象归纳一般规律。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return {"reasoning_type": "inductive", "error": "推理失败"} - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - return json.loads(json_match.group()) - else: - return {"reasoning_type": "inductive", "analysis": response_content} - - async def _abductive_reasoning(self, problem: str, information: Dict[str, Any]) -> Dict[str, Any]: - """溯因推理""" - prompt = f""" - 请使用溯因推理分析以下问题: - - 问题: {problem} - 可用信息: {json.dumps(information, ensure_ascii=False)} - - 请分析: - 1. 观察到的现象 - 2. 可能的最佳解释 - 3. 解释的合理性评估 - 4. 需要进一步验证的假设 - - 请以JSON格式返回分析结果。 - """ - - messages = [ - {"role": "system", "content": "你是一个溯因推理专家,擅长寻找现象的最佳解释。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return {"reasoning_type": "abductive", "error": "推理失败"} - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - return json.loads(json_match.group()) - else: - return {"reasoning_type": "abductive", "analysis": response_content} - - async def _analogical_reasoning(self, problem: str, information: Dict[str, Any]) -> Dict[str, Any]: - """类比推理""" - prompt = f""" - 请使用类比推理分析以下问题: - - 问题: {problem} - 可用信息: {json.dumps(information, ensure_ascii=False)} - - 请分析: - 1. 寻找相似的问题或情况 - 2. 识别相似性和差异性 - 3. 应用类比关系 - 4. 调整解决方案以适应当前情况 - - 请以JSON格式返回分析结果。 - """ - - messages = [ - {"role": "system", "content": "你是一个类比推理专家,擅长通过类比解决问题。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return {"reasoning_type": "analogical", "error": "推理失败"} - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - return json.loads(json_match.group()) - else: - return {"reasoning_type": "analogical", "analysis": response_content} - - async def extract_insights( - self, - execution_result: Dict[str, Any], - goal: Dict[str, Any] - ) -> Dict[str, Any]: - """从执行结果中提取洞察""" - try: - prompt = f""" - 请从以下执行结果中提取洞察: - - 执行结果: {json.dumps(execution_result, ensure_ascii=False)} - 目标: {json.dumps(goal, ensure_ascii=False)} - - 请分析: - 1. 成功模式(什么导致了成功) - 2. 失败模式(什么导致了失败) - 3. 性能指标(效率、准确性等) - 4. 改进建议 - 5. 新发现的知识 - - 请以JSON格式返回分析结果。 - """ - - messages = [ - {"role": "system", "content": "你是一个洞察提取专家,擅长从执行结果中提取有价值的洞察。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return {"error": "洞察提取失败"} - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - insights = json.loads(json_match.group()) - insights["timestamp"] = datetime.now().isoformat() - return insights - else: - return {"analysis": response_content, "timestamp": datetime.now().isoformat()} - - except Exception as e: - logger.error(f"洞察提取失败: {e}") - return {"error": str(e)} - - async def evaluate_solution( - self, - problem: str, - solution: Dict[str, Any], - criteria: Dict[str, Any] - ) -> Dict[str, Any]: - """评估解决方案""" - try: - prompt = f""" - 请评估以下解决方案: - - 问题: {problem} - 解决方案: {json.dumps(solution, ensure_ascii=False)} - 评估标准: {json.dumps(criteria, ensure_ascii=False)} - - 请从以下维度评估: - 1. 有效性(是否能解决问题) - 2. 效率(资源消耗和时间成本) - 3. 可行性(实施难度) - 4. 风险(潜在问题) - 5. 创新性(新颖程度) - - 请以JSON格式返回评估结果。 - """ - - messages = [ - {"role": "system", "content": "你是一个解决方案评估专家,擅长全面评估解决方案的质量。"}, - {"role": "user", "content": prompt} - ] - - result = self.llm_client.chat_completion(messages, temperature=0.3) - - if "error" in result: - return {"error": "解决方案评估失败"} - - response_content = result["choices"][0]["message"]["content"] - import re - json_match = re.search(r'\{.*\}', response_content, re.DOTALL) - - if json_match: - evaluation = json.loads(json_match.group()) - evaluation["timestamp"] = datetime.now().isoformat() - return evaluation - else: - return {"evaluation": response_content, "timestamp": datetime.now().isoformat()} - - except Exception as e: - logger.error(f"解决方案评估失败: {e}") - return {"error": str(e)} - - def _create_fallback_intent(self, message: str) -> Dict[str, Any]: - """创建备用意图分析""" - return { - "main_intent": "general_query", - "emotion": "neutral", - "urgency": "medium", - "required_tools": ["generate_response"], - "expected_response": "text", - "key_information": {"message": message}, - "confidence": 0.5, - "timestamp": datetime.now().isoformat() - } - - def _create_fallback_decision(self, options: List[Dict[str, Any]]) -> Dict[str, Any]: - """创建备用决策""" - if not options: - return { - "selected_option": None, - "reasoning": "无可用选项", - "confidence": 0.0, - "timestamp": datetime.now().isoformat() - } - - # 选择第一个选项作为默认选择 - return { - "selected_option": options[0].get("id", "option_1"), - "reasoning": "默认选择", - "confidence": 0.3, - "risks": ["决策质量未知"], - "mitigation": "需要进一步验证", - "timestamp": datetime.now().isoformat() - } - - def get_reasoning_history(self, limit: int = 10) -> List[Dict[str, Any]]: - """获取推理历史""" - return self.reasoning_history[-limit:] if self.reasoning_history else [] - - def clear_reasoning_history(self): - """清空推理历史""" - self.reasoning_history = [] - logger.info("推理历史已清空") diff --git a/src/agent/tool_manager.py b/src/agent/tool_manager.py deleted file mode 100644 index 0453d5e..0000000 --- a/src/agent/tool_manager.py +++ /dev/null @@ -1,435 +0,0 @@ - -# -*- coding: utf-8 -*- -""" -工具管理器 -负责管理和执行各种工具 -""" - -import logging -import asyncio -from typing import Dict, List, Any, Optional, Callable -from datetime import datetime -import json - -logger = logging.getLogger(__name__) - -class ToolManager: - """工具管理器""" - - def __init__(self): - self.tools = {} - self.tool_usage_stats = {} - self.tool_performance = {} - self._register_default_tools() - - def _register_default_tools(self): - """注册默认工具""" - # 注册基础工具 - self.register_tool("search_knowledge", self._search_knowledge_tool) - self.register_tool("create_work_order", self._create_work_order_tool) - self.register_tool("update_work_order", self._update_work_order_tool) - self.register_tool("generate_response", self._generate_response_tool) - self.register_tool("analyze_data", self._analyze_data_tool) - self.register_tool("send_notification", self._send_notification_tool) - self.register_tool("schedule_task", self._schedule_task_tool) - self.register_tool("web_search", self._web_search_tool) - self.register_tool("file_operation", self._file_operation_tool) - self.register_tool("database_query", self._database_query_tool) - - logger.info(f"已注册 {len(self.tools)} 个默认工具") - - def register_tool(self, name: str, func: Callable, metadata: Optional[Dict[str, Any]] = None): - """注册工具""" - self.tools[name] = { - "function": func, - "metadata": metadata or {}, - "usage_count": 0, - "last_used": None, - "success_rate": 0.0 - } - - logger.info(f"注册工具: {name}") - - def unregister_tool(self, name: str) -> bool: - """注销工具""" - if name in self.tools: - del self.tools[name] - logger.info(f"注销工具: {name}") - return True - return False - - async def execute_tool(self, tool_name: str, parameters: Dict[str, Any]) -> Dict[str, Any]: - """执行工具""" - if tool_name not in self.tools: - return { - "success": False, - "error": f"工具 '{tool_name}' 不存在" - } - - tool = self.tools[tool_name] - start_time = datetime.now() - - try: - # 更新使用统计 - tool["usage_count"] += 1 - tool["last_used"] = start_time - - # 执行工具 - if asyncio.iscoroutinefunction(tool["function"]): - result = await tool["function"](**parameters) - else: - result = tool["function"](**parameters) - - # 更新性能统计 - execution_time = (datetime.now() - start_time).total_seconds() - self._update_tool_performance(tool_name, True, execution_time) - - logger.info(f"工具 '{tool_name}' 执行成功,耗时: {execution_time:.2f}秒") - - return { - "success": True, - "result": result, - "execution_time": execution_time, - "tool": tool_name - } - - except Exception as e: - logger.error(f"工具 '{tool_name}' 执行失败: {e}") - - # 更新性能统计 - execution_time = (datetime.now() - start_time).total_seconds() - self._update_tool_performance(tool_name, False, execution_time) - - return { - "success": False, - "error": str(e), - "execution_time": execution_time, - "tool": tool_name - } - - def _update_tool_performance(self, tool_name: str, success: bool, execution_time: float): - """更新工具性能统计""" - if tool_name not in self.tool_performance: - self.tool_performance[tool_name] = { - "total_executions": 0, - "successful_executions": 0, - "total_time": 0.0, - "avg_execution_time": 0.0, - "success_rate": 0.0 - } - - perf = self.tool_performance[tool_name] - perf["total_executions"] += 1 - perf["total_time"] += execution_time - perf["avg_execution_time"] = perf["total_time"] / perf["total_executions"] - - if success: - perf["successful_executions"] += 1 - - perf["success_rate"] = perf["successful_executions"] / perf["total_executions"] - - # 更新工具的成功率 - self.tools[tool_name]["success_rate"] = perf["success_rate"] - - def get_available_tools(self) -> List[Dict[str, Any]]: - """获取可用工具列表""" - tools_info = [] - - for name, tool in self.tools.items(): - tool_info = { - "name": name, - "metadata": tool["metadata"], - "usage_count": tool["usage_count"], - "last_used": tool["last_used"].isoformat() if tool["last_used"] else None, - "success_rate": tool["success_rate"] - } - - # 添加性能信息 - if name in self.tool_performance: - perf = self.tool_performance[name] - tool_info.update({ - "avg_execution_time": perf["avg_execution_time"], - "total_executions": perf["total_executions"] - }) - - tools_info.append(tool_info) - - return tools_info - - def get_tool_info(self, tool_name: str) -> Optional[Dict[str, Any]]: - """获取工具信息""" - if tool_name not in self.tools: - return None - - tool = self.tools[tool_name] - info = { - "name": tool_name, - "metadata": tool["metadata"], - "usage_count": tool["usage_count"], - "last_used": tool["last_used"].isoformat() if tool["last_used"] else None, - "success_rate": tool["success_rate"] - } - - if tool_name in self.tool_performance: - info.update(self.tool_performance[tool_name]) - - return info - - def update_usage_stats(self, tool_usage: List[Dict[str, Any]]): - """更新工具使用统计""" - for usage in tool_usage: - tool_name = usage.get("tool") - if tool_name in self.tools: - self.tools[tool_name]["usage_count"] += usage.get("count", 1) - - # 默认工具实现 - - async def _search_knowledge_tool(self, query: str, top_k: int = 3, **kwargs) -> Dict[str, Any]: - """搜索知识库工具""" - try: - from ..knowledge_base.knowledge_manager import KnowledgeManager - knowledge_manager = KnowledgeManager() - - results = knowledge_manager.search_knowledge(query, top_k) - - return { - "query": query, - "results": results, - "count": len(results) - } - except Exception as e: - logger.error(f"搜索知识库失败: {e}") - return {"error": str(e)} - - async def _create_work_order_tool(self, title: str, description: str, category: str, priority: str = "medium", **kwargs) -> Dict[str, Any]: - """创建工单工具""" - try: - from ..dialogue.dialogue_manager import DialogueManager - dialogue_manager = DialogueManager() - - result = dialogue_manager.create_work_order(title, description, category, priority) - - return result - except Exception as e: - logger.error(f"创建工单失败: {e}") - return {"error": str(e)} - - async def _update_work_order_tool(self, work_order_id: int, **kwargs) -> Dict[str, Any]: - """更新工单工具""" - try: - from ..dialogue.dialogue_manager import DialogueManager - dialogue_manager = DialogueManager() - - success = dialogue_manager.update_work_order(work_order_id, **kwargs) - - return { - "success": success, - "work_order_id": work_order_id, - "updated_fields": list(kwargs.keys()) - } - except Exception as e: - logger.error(f"更新工单失败: {e}") - return {"error": str(e)} - - async def _generate_response_tool(self, message: str, context: str = "", **kwargs) -> Dict[str, Any]: - """生成回复工具""" - try: - from ..core.llm_client import QwenClient - llm_client = QwenClient() - - result = llm_client.generate_response(message, context) - - return result - except Exception as e: - logger.error(f"生成回复失败: {e}") - return {"error": str(e)} - - async def _analyze_data_tool(self, data_type: str, date_range: str = "last_7_days", **kwargs) -> Dict[str, Any]: - """数据分析工具""" - try: - from ..analytics.analytics_manager import AnalyticsManager - analytics_manager = AnalyticsManager() - - if data_type == "daily_analytics": - result = analytics_manager.generate_daily_analytics() - elif data_type == "summary": - result = analytics_manager.get_analytics_summary() - elif data_type == "category_performance": - result = analytics_manager.get_category_performance() - else: - result = {"error": f"不支持的数据类型: {data_type}"} - - return result - except Exception as e: - logger.error(f"数据分析失败: {e}") - return {"error": str(e)} - - async def _send_notification_tool(self, message: str, recipients: List[str], notification_type: str = "info", **kwargs) -> Dict[str, Any]: - """发送通知工具""" - try: - # 这里可以实现具体的通知逻辑 - # 例如:发送邮件、短信、推送通知等 - - notification_data = { - "message": message, - "recipients": recipients, - "type": notification_type, - "timestamp": datetime.now().isoformat() - } - - # 模拟发送通知 - logger.info(f"发送通知: {message} 给 {recipients}") - - return { - "success": True, - "notification_id": f"notif_{datetime.now().strftime('%Y%m%d_%H%M%S')}", - "data": notification_data - } - except Exception as e: - logger.error(f"发送通知失败: {e}") - return {"error": str(e)} - - async def _schedule_task_tool(self, task_name: str, schedule_time: str, task_data: Dict[str, Any], **kwargs) -> Dict[str, Any]: - """调度任务工具""" - try: - # 这里可以实现任务调度逻辑 - # 例如:使用APScheduler、Celery等 - - schedule_data = { - "task_name": task_name, - "schedule_time": schedule_time, - "task_data": task_data, - "created_at": datetime.now().isoformat() - } - - logger.info(f"调度任务: {task_name} 在 {schedule_time}") - - return { - "success": True, - "schedule_id": f"schedule_{datetime.now().strftime('%Y%m%d_%H%M%S')}", - "data": schedule_data - } - except Exception as e: - logger.error(f"调度任务失败: {e}") - return {"error": str(e)} - - async def _web_search_tool(self, query: str, max_results: int = 5, **kwargs) -> Dict[str, Any]: - """网络搜索工具""" - try: - # 这里可以实现网络搜索逻辑 - # 例如:使用Google Search API、Bing Search API等 - - search_results = [ - { - "title": f"搜索结果 {i+1}", - "url": f"https://example.com/result{i+1}", - "snippet": f"这是关于 '{query}' 的搜索结果摘要 {i+1}" - } - for i in range(min(max_results, 3)) - ] - - logger.info(f"网络搜索: {query}") - - return { - "query": query, - "results": search_results, - "count": len(search_results) - } - except Exception as e: - logger.error(f"网络搜索失败: {e}") - return {"error": str(e)} - - async def _file_operation_tool(self, operation: str, file_path: str, content: str = "", **kwargs) -> Dict[str, Any]: - """文件操作工具""" - try: - import os - - if operation == "read": - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - return {"success": True, "content": content, "operation": "read"} - - elif operation == "write": - with open(file_path, 'w', encoding='utf-8') as f: - f.write(content) - return {"success": True, "operation": "write", "file_path": file_path} - - elif operation == "exists": - exists = os.path.exists(file_path) - return {"success": True, "exists": exists, "file_path": file_path} - - else: - return {"error": f"不支持的文件操作: {operation}"} - - except Exception as e: - logger.error(f"文件操作失败: {e}") - return {"error": str(e)} - - async def _database_query_tool(self, query: str, query_type: str = "select", **kwargs) -> Dict[str, Any]: - """数据库查询工具""" - try: - from ..core.database import db_manager - - with db_manager.get_session() as session: - if query_type == "select": - result = session.execute(query).fetchall() - return { - "success": True, - "result": [dict(row) for row in result], - "count": len(result) - } - else: - session.execute(query) - session.commit() - return {"success": True, "operation": query_type} - - except Exception as e: - logger.error(f"数据库查询失败: {e}") - return {"error": str(e)} - - def get_tool_performance_report(self) -> Dict[str, Any]: - """获取工具性能报告""" - report = { - "total_tools": len(self.tools), - "tool_performance": {}, - "summary": { - "most_used": None, - "most_reliable": None, - "fastest": None, - "slowest": None - } - } - - if not self.tool_performance: - return report - - # 分析性能数据 - most_used_count = 0 - most_reliable_rate = 0 - fastest_time = float('inf') - slowest_time = 0 - - for tool_name, perf in self.tool_performance.items(): - report["tool_performance"][tool_name] = perf - - # 找出最常用的工具 - if perf["total_executions"] > most_used_count: - most_used_count = perf["total_executions"] - report["summary"]["most_used"] = tool_name - - # 找出最可靠的工具 - if perf["success_rate"] > most_reliable_rate: - most_reliable_rate = perf["success_rate"] - report["summary"]["most_reliable"] = tool_name - - # 找出最快的工具 - if perf["avg_execution_time"] < fastest_time: - fastest_time = perf["avg_execution_time"] - report["summary"]["fastest"] = tool_name - - # 找出最慢的工具 - if perf["avg_execution_time"] > slowest_time: - slowest_time = perf["avg_execution_time"] - report["summary"]["slowest"] = tool_name - - return report diff --git a/src/agent_assistant.py b/src/agent_assistant.py index 5449a67..c399dc6 100644 --- a/src/agent_assistant.py +++ b/src/agent_assistant.py @@ -12,6 +12,7 @@ from datetime import datetime from src.config.unified_config import get_config from src.agent.llm_client import LLMManager from src.web.service_manager import service_manager +from src.agent.react_agent import ReactAgent logger = logging.getLogger(__name__) @@ -25,7 +26,10 @@ class TSPAgentAssistant: self.is_agent_mode = True self.execution_history = [] - # 工具注册表 + # ReAct Agent(核心) + self.react_agent = ReactAgent() + + # 工具注册表(保留兼容旧 API) self.tools = {} self.tool_performance = {} @@ -194,13 +198,15 @@ class TSPAgentAssistant: def get_agent_status(self) -> Dict[str, Any]: """获取Agent状态""" try: + react_status = self.react_agent.get_status() return { "success": True, "is_active": self.is_agent_mode, "ai_monitoring_active": self.ai_monitoring_active, - "total_tools": len(self.tools), - "total_executions": len(self.execution_history), - "tools": self.get_available_tools(), + "total_tools": react_status["tool_count"], + "available_tools": react_status["available_tools"], + "total_executions": len(self.execution_history) + react_status["history_count"], + "react_agent": react_status, "performance": self.get_tool_performance_report() } except Exception as e: @@ -321,29 +327,23 @@ class TSPAgentAssistant: async def process_message_agent(self, message: str, user_id: str = "admin", work_order_id: Optional[int] = None, enable_proactive: bool = True) -> Dict[str, Any]: - """处理消息 (实战化)""" + """处理消息 - 使用 ReAct Agent""" try: logger.info(f"Agent收到消息: {message}") - - # 1. 识别意图和推荐工具 - prompt = f"用户消息: {message}\n请分析用户意图,并从工具列表中选择最合适的工具。工具列表: {json.dumps(self.get_available_tools())}\n请直接返回你的分析和建议响应。" - - response_text = await self.llm_manager.generate(prompt) - - # 2. 模拟动作生成 - actions = [] - if "工单" in message or "查询" in message: - actions.append({"type": "tool_call", "tool": "search_work_order", "status": "suggested"}) - - return { - "success": True, - "response": response_text, - "actions": actions, - "user_id": user_id, - "work_order_id": work_order_id, - "status": "completed", - "timestamp": datetime.now().isoformat() - } + result = await self.react_agent.chat( + message=message, + user_id=user_id, + ) + result["user_id"] = user_id + result["work_order_id"] = work_order_id + result["status"] = "completed" if result.get("success") else "error" + result["timestamp"] = datetime.now().isoformat() + # 兼容旧字段 + result["actions"] = [ + {"type": "tool_call", "tool": tc["tool"], "status": "executed"} + for tc in result.get("tool_calls", []) + ] + return result except Exception as e: logger.error(f"处理消息失败: {e}") return {"error": str(e)} diff --git a/src/agent_assistant_new.py b/src/agent_assistant_new.py deleted file mode 100644 index 190b698..0000000 --- a/src/agent_assistant_new.py +++ /dev/null @@ -1,322 +0,0 @@ -# -*- coding: utf-8 -*- -""" -增强版TSP助手 - 集成Agent功能 -重构版本:模块化设计,降低代码复杂度 -""" - -import logging -import asyncio -from typing import Dict, Any, List, Optional -from datetime import datetime - -from src.agent.agent_assistant_core import TSPAgentAssistantCore -from src.agent.agent_message_handler import AgentMessageHandler -from src.agent.agent_sample_actions import AgentSampleActions - -logger = logging.getLogger(__name__) - -class TSPAgentAssistant(TSPAgentAssistantCore): - """TSP Agent助手 - 重构版本""" - - def __init__(self, llm_config=None): - # 初始化核心功能 - super().__init__(llm_config) - - # 初始化消息处理器 - self.message_handler = AgentMessageHandler(self) - - # 初始化示例动作处理器 - self.sample_actions = AgentSampleActions(self) - - logger.info("TSP Agent助手初始化完成(重构版本)") - - # ==================== 消息处理功能 ==================== - - async def process_message_agent(self, message: str, user_id: str = "admin", - work_order_id: Optional[int] = None, - enable_proactive: bool = True) -> Dict[str, Any]: - """使用Agent处理消息""" - return await self.message_handler.process_message_agent( - message, user_id, work_order_id, enable_proactive - ) - - async def process_conversation_agent(self, conversation_data: Dict[str, Any]) -> Dict[str, Any]: - """使用Agent处理对话""" - return await self.message_handler.process_conversation_agent(conversation_data) - - async def process_workorder_agent(self, workorder_data: Dict[str, Any]) -> Dict[str, Any]: - """使用Agent处理工单""" - return await self.message_handler.process_workorder_agent(workorder_data) - - async def process_alert_agent(self, alert_data: Dict[str, Any]) -> Dict[str, Any]: - """使用Agent处理预警""" - return await self.message_handler.process_alert_agent(alert_data) - - # ==================== 建议功能 ==================== - - def get_conversation_suggestions(self, context: Dict[str, Any]) -> List[str]: - """获取对话建议""" - return self.message_handler.get_conversation_suggestions(context) - - def get_workorder_suggestions(self, workorder_data: Dict[str, Any]) -> List[str]: - """获取工单建议""" - return self.message_handler.get_workorder_suggestions(workorder_data) - - def get_alert_suggestions(self, alert_data: Dict[str, Any]) -> List[str]: - """获取预警建议""" - return self.message_handler.get_alert_suggestions(alert_data) - - # ==================== 示例动作功能 ==================== - - async def trigger_sample_actions(self) -> Dict[str, Any]: - """触发示例动作""" - return await self.sample_actions.trigger_sample_actions() - - async def run_performance_test(self) -> Dict[str, Any]: - """运行性能测试""" - return await self.sample_actions.run_performance_test() - - # ==================== 兼容性方法 ==================== - - def get_agent_status(self) -> Dict[str, Any]: - """获取Agent状态(兼容性方法)""" - return super().get_agent_status() - - def toggle_agent_mode(self, enabled: bool) -> bool: - """切换Agent模式(兼容性方法)""" - return super().toggle_agent_mode(enabled) - - def start_proactive_monitoring(self) -> bool: - """启动主动监控(兼容性方法)""" - return super().start_proactive_monitoring() - - def stop_proactive_monitoring(self) -> bool: - """停止主动监控(兼容性方法)""" - return super().stop_proactive_monitoring() - - def run_proactive_monitoring(self) -> Dict[str, Any]: - """运行主动监控检查(兼容性方法)""" - return super().run_proactive_monitoring() - - def run_intelligent_analysis(self) -> Dict[str, Any]: - """运行智能分析(兼容性方法)""" - return super().run_intelligent_analysis() - - def get_action_history(self, limit: int = 50) -> List[Dict[str, Any]]: - """获取动作执行历史(兼容性方法)""" - return super().get_action_history(limit) - - def clear_execution_history(self) -> Dict[str, Any]: - """清空执行历史(兼容性方法)""" - return super().clear_execution_history() - - def get_llm_usage_stats(self) -> Dict[str, Any]: - """获取LLM使用统计(兼容性方法)""" - return super().get_llm_usage_stats() - - # ==================== 高级功能 ==================== - - async def comprehensive_analysis(self) -> Dict[str, Any]: - """综合分析 - 结合多个模块的分析结果""" - try: - # 运行智能分析 - intelligent_analysis = self.run_intelligent_analysis() - - # 运行主动监控 - proactive_monitoring = self.run_proactive_monitoring() - - # 运行性能测试 - performance_test = await self.run_performance_test() - - # 综合结果 - comprehensive_result = { - "timestamp": self.execution_history[-1]["timestamp"] if self.execution_history else None, - "intelligent_analysis": intelligent_analysis, - "proactive_monitoring": proactive_monitoring, - "performance_test": performance_test, - "overall_status": self._determine_overall_status( - intelligent_analysis, proactive_monitoring, performance_test - ) - } - - # 记录综合分析 - self._record_execution("comprehensive_analysis", comprehensive_result) - - return comprehensive_result - - except Exception as e: - logger.error(f"综合分析失败: {e}") - return {"error": str(e)} - - def _determine_overall_status(self, intelligent_analysis: Dict, - proactive_monitoring: Dict, - performance_test: Dict) -> str: - """确定整体状态""" - try: - # 检查各个模块的状态 - statuses = [] - - if intelligent_analysis.get("success"): - statuses.append("intelligent_analysis_ok") - else: - statuses.append("intelligent_analysis_error") - - if proactive_monitoring.get("success"): - statuses.append("proactive_monitoring_ok") - else: - statuses.append("proactive_monitoring_error") - - if performance_test.get("success"): - statuses.append("performance_test_ok") - else: - statuses.append("performance_test_error") - - # 根据状态确定整体状态 - if all("ok" in status for status in statuses): - return "excellent" - elif any("error" in status for status in statuses): - return "needs_attention" - else: - return "good" - - except Exception: - return "unknown" - - async def batch_process_requests(self, requests: List[Dict[str, Any]]) -> List[Dict[str, Any]]: - """批量处理请求""" - try: - results = [] - - for request in requests: - request_type = request.get("type", "message") - - if request_type == "message": - result = await self.process_message_agent( - request.get("message", ""), - request.get("user_id", "admin"), - request.get("work_order_id"), - request.get("enable_proactive", True) - ) - elif request_type == "conversation": - result = await self.process_conversation_agent(request) - elif request_type == "workorder": - result = await self.process_workorder_agent(request) - elif request_type == "alert": - result = await self.process_alert_agent(request) - else: - result = {"error": f"未知请求类型: {request_type}"} - - results.append(result) - - # 记录批量处理 - self._record_execution("batch_process", { - "request_count": len(requests), - "results": results - }) - - return results - - except Exception as e: - logger.error(f"批量处理请求失败: {e}") - return [{"error": str(e)} for _ in requests] - - def get_system_summary(self) -> Dict[str, Any]: - """获取系统摘要""" - try: - # 获取各种状态信息 - agent_status = self.get_agent_status() - system_health = self.get_system_health() - workorders_status = self._check_workorders_status() - - # 计算摘要指标 - summary = { - "timestamp": datetime.now().isoformat(), - "agent_status": agent_status, - "system_health": system_health, - "workorders_status": workorders_status, - "execution_history_count": len(self.execution_history), - "llm_usage_stats": self.get_llm_usage_stats(), - "overall_health_score": system_health.get("health_score", 0) - } - - return summary - - except Exception as e: - logger.error(f"获取系统摘要失败: {e}") - return {"error": str(e)} - - def export_agent_data(self) -> Dict[str, Any]: - """导出Agent数据""" - try: - export_data = { - "export_timestamp": datetime.now().isoformat(), - "agent_status": self.get_agent_status(), - "execution_history": self.execution_history, - "llm_usage_stats": self.get_llm_usage_stats(), - "system_summary": self.get_system_summary() - } - - return { - "success": True, - "data": export_data, - "message": "Agent数据导出成功" - } - - except Exception as e: - logger.error(f"导出Agent数据失败: {e}") - return { - "success": False, - "error": str(e) - } - - def import_agent_data(self, data: Dict[str, Any]) -> Dict[str, Any]: - """导入Agent数据""" - try: - # 验证数据格式 - if not isinstance(data, dict): - raise ValueError("数据格式不正确") - - # 导入执行历史 - if "execution_history" in data: - self.execution_history = data["execution_history"] - - # 其他数据的导入逻辑... - - return { - "success": True, - "message": "Agent数据导入成功" - } - - except Exception as e: - logger.error(f"导入Agent数据失败: {e}") - return { - "success": False, - "error": str(e) - } - -# 测试函数 -async def main(): - """测试函数""" - print("🚀 TSP Agent助手测试") - - # 创建Agent助手实例 - agent_assistant = TSPAgentAssistant() - - # 测试基本功能 - status = agent_assistant.get_agent_status() - print("Agent状态:", status) - - # 测试消息处理 - result = await agent_assistant.process_message_agent("你好,请帮我分析系统状态") - print("消息处理结果:", result) - - # 测试示例动作 - sample_result = await agent_assistant.trigger_sample_actions() - print("示例动作结果:", sample_result) - - # 测试综合分析 - analysis_result = await agent_assistant.comprehensive_analysis() - print("综合分析结果:", analysis_result) - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/src/config/unified_config.py b/src/config/unified_config.py index 62be676..fc0e2ac 100644 --- a/src/config/unified_config.py +++ b/src/config/unified_config.py @@ -71,6 +71,19 @@ class AIAccuracyConfig: human_resolution_confidence: float = 0.90 +@dataclass +class EmbeddingConfig: + """Embedding 向量配置""" + enabled: bool = True + api_key: Optional[str] = None # 本地模式不需要 + base_url: Optional[str] = None # 本地模式不需要 + model: str = "BAAI/bge-small-zh-v1.5" # 本地轻量中文模型 + dimension: int = 512 # bge-small-zh 输出维度 + batch_size: int = 32 + similarity_threshold: float = 0.5 # 语义搜索相似度阈值 + cache_ttl: int = 86400 # embedding 缓存过期时间(秒),默认 1 天 + + @dataclass class RedisConfig: """Redis缓存配置""" @@ -99,6 +112,7 @@ class UnifiedConfig: self.feishu = self._load_feishu_from_env() self.ai_accuracy = self._load_ai_accuracy_from_env() self.redis = self._load_redis_from_env() + self.embedding = self._load_embedding_from_env() self.validate_config() def _load_database_from_env(self) -> DatabaseConfig: @@ -172,6 +186,18 @@ class UnifiedConfig: logger.info("Redis config loaded.") return config + def _load_embedding_from_env(self) -> EmbeddingConfig: + config = EmbeddingConfig( + enabled=os.getenv("EMBEDDING_ENABLED", "True").lower() in ('true', '1', 't'), + model=os.getenv("EMBEDDING_MODEL", "BAAI/bge-small-zh-v1.5"), + dimension=int(os.getenv("EMBEDDING_DIMENSION", 512)), + batch_size=int(os.getenv("EMBEDDING_BATCH_SIZE", 32)), + similarity_threshold=float(os.getenv("EMBEDDING_SIMILARITY_THRESHOLD", 0.5)), + cache_ttl=int(os.getenv("EMBEDDING_CACHE_TTL", 86400)), + ) + logger.info("Embedding config loaded.") + return config + def validate_config(self): """在启动时验证关键配置""" if not self.database.url: @@ -193,6 +219,7 @@ class UnifiedConfig: 'feishu': asdict(self.feishu), 'ai_accuracy': asdict(self.ai_accuracy), 'redis': asdict(self.redis), + 'embedding': asdict(self.embedding), } # --- 全局单例模式 --- diff --git a/src/core/embedding_client.py b/src/core/embedding_client.py new file mode 100644 index 0000000..1a8f0d8 --- /dev/null +++ b/src/core/embedding_client.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +""" +Embedding 向量客户端(本地模型方案) +使用 sentence-transformers 在本地运行轻量级中文 embedding 模型 +零 API 调用、零成本、低延迟 +""" + +import logging +import hashlib +import threading +from typing import List, Optional + +from src.config.unified_config import get_config +from src.core.cache_manager import cache_manager + +logger = logging.getLogger(__name__) + + +class EmbeddingClient: + """本地 Embedding 向量客户端""" + + def __init__(self): + config = get_config() + self.enabled = config.embedding.enabled + self.model_name = config.embedding.model + self.dimension = config.embedding.dimension + self.cache_ttl = config.embedding.cache_ttl + + self._model = None + self._lock = threading.Lock() + + if self.enabled: + logger.info(f"Embedding 客户端初始化: model={self.model_name} (本地模式)") + else: + logger.info("Embedding 功能已禁用,将使用关键词匹配降级") + + def _get_model(self): + """延迟加载模型(首次调用时下载并加载)""" + if self._model is not None: + return self._model + + with self._lock: + if self._model is not None: + return self._model + + try: + import os + # 设置 HuggingFace 镜像,解决国内下载问题 + os.environ.setdefault("HF_ENDPOINT", "https://hf-mirror.com") + + from sentence_transformers import SentenceTransformer + logger.info(f"正在加载 embedding 模型: {self.model_name} ...") + self._model = SentenceTransformer(self.model_name) + logger.info(f"Embedding 模型加载完成: {self.model_name}") + return self._model + except ImportError: + logger.error( + "sentence-transformers 未安装,请运行: pip install sentence-transformers" + ) + self.enabled = False + return None + except Exception as e: + logger.error(f"加载 embedding 模型失败: {e}") + self.enabled = False + return None + + # ------------------------------------------------------------------ + # 公开接口 + # ------------------------------------------------------------------ + + def embed_text(self, text: str) -> Optional[List[float]]: + """对单条文本生成 embedding 向量,优先从缓存读取""" + if not self.enabled or not text.strip(): + return None + + cache_key = self._cache_key(text) + cached = cache_manager.get(cache_key) + if cached is not None: + return cached + + model = self._get_model() + if model is None: + return None + + try: + vec = model.encode(text, normalize_embeddings=True).tolist() + cache_manager.set(cache_key, vec, self.cache_ttl) + return vec + except Exception as e: + logger.error(f"Embedding 生成失败: {e}") + return None + + def embed_batch(self, texts: List[str]) -> List[Optional[List[float]]]: + """批量生成 embedding""" + if not self.enabled: + return [None] * len(texts) + + results: List[Optional[List[float]]] = [None] * len(texts) + uncached_indices = [] + uncached_texts = [] + + # 1. 先查缓存 + for i, t in enumerate(texts): + if not t.strip(): + continue + cached = cache_manager.get(self._cache_key(t)) + if cached is not None: + results[i] = cached + else: + uncached_indices.append(i) + uncached_texts.append(t) + + if not uncached_texts: + return results + + # 2. 批量推理 + model = self._get_model() + if model is None: + return results + + try: + vectors = model.encode( + uncached_texts, normalize_embeddings=True, batch_size=32 + ).tolist() + + for j, vec in enumerate(vectors): + idx = uncached_indices[j] + results[idx] = vec + cache_manager.set(self._cache_key(uncached_texts[j]), vec, self.cache_ttl) + except Exception as e: + logger.error(f"批量 embedding 生成失败: {e}") + + return results + + def test_connection(self) -> bool: + """测试模型是否可用""" + try: + vec = self.embed_text("测试连接") + return vec is not None and len(vec) > 0 + except Exception as e: + logger.error(f"Embedding 模型测试失败: {e}") + return False + + # ------------------------------------------------------------------ + # 内部方法 + # ------------------------------------------------------------------ + + @staticmethod + def _cache_key(text: str) -> str: + """生成缓存键(基于文本哈希)""" + h = hashlib.md5(text.encode("utf-8")).hexdigest() + return f"emb:{h}" diff --git a/src/core/models.py b/src/core/models.py index 81eab1a..edf2476 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -58,11 +58,49 @@ class WorkOrder(Base): # 关联处理过程记录 process_history = relationship("WorkOrderProcessHistory", back_populates="work_order", order_by="WorkOrderProcessHistory.process_time") +class ChatSession(Base): + """对话会话模型 — 将多轮对话组织为一个会话""" + __tablename__ = "chat_sessions" + + id = Column(Integer, primary_key=True) + session_id = Column(String(100), unique=True, nullable=False) # 唯一会话标识 + user_id = Column(String(100), nullable=True) # 用户标识 + work_order_id = Column(Integer, ForeignKey("work_orders.id"), nullable=True) + title = Column(String(200), nullable=True) # 会话标题(取首条消息摘要) + status = Column(String(20), default="active") # active, ended + message_count = Column(Integer, default=0) # 消息轮数 + source = Column(String(50), nullable=True) # 来源:websocket, api, feishu + ip_address = Column(String(45), nullable=True) + created_at = Column(DateTime, default=datetime.now) + updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) + ended_at = Column(DateTime, nullable=True) + + # 关联消息 + messages = relationship("Conversation", back_populates="chat_session", order_by="Conversation.timestamp") + + def to_dict(self): + return { + 'id': self.id, + 'session_id': self.session_id, + 'user_id': self.user_id, + 'work_order_id': self.work_order_id, + 'title': self.title, + 'status': self.status, + 'message_count': self.message_count, + 'source': self.source, + 'ip_address': self.ip_address, + 'created_at': self.created_at.isoformat() if self.created_at else None, + 'updated_at': self.updated_at.isoformat() if self.updated_at else None, + 'ended_at': self.ended_at.isoformat() if self.ended_at else None, + } + + class Conversation(Base): """对话记录模型""" __tablename__ = "conversations" id = Column(Integer, primary_key=True) + session_id = Column(String(100), ForeignKey("chat_sessions.session_id"), nullable=True) # 关联会话 work_order_id = Column(Integer, ForeignKey("work_orders.id")) user_message = Column(Text, nullable=False) assistant_response = Column(Text, nullable=False) @@ -79,6 +117,7 @@ class Conversation(Base): cpu_usage = Column(Float) # CPU使用率 work_order = relationship("WorkOrder", back_populates="conversations") + chat_session = relationship("ChatSession", back_populates="messages") class KnowledgeEntry(Base): """知识库条目模型""" diff --git a/src/core/query_optimizer.py b/src/core/query_optimizer.py index ed5afb1..74e9fd6 100644 --- a/src/core/query_optimizer.py +++ b/src/core/query_optimizer.py @@ -86,6 +86,7 @@ class QueryOptimizer: for conv in conversations: conversation_list.append({ 'id': conv.id, + 'session_id': conv.session_id, 'user_message': conv.user_message, 'assistant_response': conv.assistant_response, 'timestamp': conv.timestamp.isoformat() if conv.timestamp else None, diff --git a/src/core/vector_store.py b/src/core/vector_store.py new file mode 100644 index 0000000..cdf1a51 --- /dev/null +++ b/src/core/vector_store.py @@ -0,0 +1,164 @@ +# -*- coding: utf-8 -*- +""" +向量存储与检索 +使用 numpy 实现轻量级向量索引(无需额外依赖) +支持从 DB 加载已有 embedding 构建索引,增量更新 +""" + +import logging +import json +import threading +import numpy as np +from typing import List, Dict, Any, Optional, Tuple + +from src.core.database import db_manager +from src.core.models import KnowledgeEntry + +logger = logging.getLogger(__name__) + + +class VectorStore: + """轻量级向量存储,基于 numpy 余弦相似度""" + + def __init__(self): + self._lock = threading.RLock() + # 索引数据: entry_id -> embedding vector + self._ids: List[int] = [] + self._matrix: Optional[np.ndarray] = None # shape: (n, dim) + self._loaded = False + + # ------------------------------------------------------------------ + # 索引管理 + # ------------------------------------------------------------------ + + def load_from_db(self): + """从数据库加载所有已有 embedding 构建索引""" + try: + with db_manager.get_session() as session: + entries = session.query( + KnowledgeEntry.id, KnowledgeEntry.vector_embedding + ).filter( + KnowledgeEntry.is_active == True, + KnowledgeEntry.vector_embedding.isnot(None), + KnowledgeEntry.vector_embedding != '' + ).all() + + ids = [] + vectors = [] + for entry_id, vec_json in entries: + try: + vec = json.loads(vec_json) + if isinstance(vec, list) and len(vec) > 0: + ids.append(entry_id) + vectors.append(vec) + except (json.JSONDecodeError, TypeError): + continue + + with self._lock: + if vectors: + self._ids = ids + self._matrix = np.array(vectors, dtype=np.float32) + # L2 归一化,方便后续用点积算余弦相似度 + norms = np.linalg.norm(self._matrix, axis=1, keepdims=True) + norms[norms == 0] = 1.0 + self._matrix = self._matrix / norms + else: + self._ids = [] + self._matrix = None + self._loaded = True + + logger.info(f"向量索引加载完成: {len(ids)} 条记录") + + except Exception as e: + logger.error(f"加载向量索引失败: {e}") + self._loaded = True # 标记为已加载,避免重复尝试 + + def add(self, entry_id: int, vector: List[float]): + """增量添加一条向量""" + with self._lock: + vec = np.array(vector, dtype=np.float32).reshape(1, -1) + norm = np.linalg.norm(vec) + if norm > 0: + vec = vec / norm + + if self._matrix is not None: + self._ids.append(entry_id) + self._matrix = np.vstack([self._matrix, vec]) + else: + self._ids = [entry_id] + self._matrix = vec + + def remove(self, entry_id: int): + """移除一条向量""" + with self._lock: + if entry_id in self._ids: + idx = self._ids.index(entry_id) + self._ids.pop(idx) + if self._matrix is not None and len(self._ids) > 0: + self._matrix = np.delete(self._matrix, idx, axis=0) + else: + self._matrix = None + + def update(self, entry_id: int, vector: List[float]): + """更新一条向量""" + self.remove(entry_id) + self.add(entry_id, vector) + + # ------------------------------------------------------------------ + # 检索 + # ------------------------------------------------------------------ + + def search( + self, + query_vector: List[float], + top_k: int = 5, + threshold: float = 0.0 + ) -> List[Tuple[int, float]]: + """ + 向量相似度检索 + + Returns: + [(entry_id, similarity_score), ...] 按相似度降序 + """ + if not self._loaded: + self.load_from_db() + + with self._lock: + if self._matrix is None or len(self._ids) == 0: + return [] + + q = np.array(query_vector, dtype=np.float32).reshape(1, -1) + norm = np.linalg.norm(q) + if norm > 0: + q = q / norm + + # 余弦相似度 = 归一化向量的点积 + similarities = (self._matrix @ q.T).flatten() + + # 筛选超过阈值的 + valid_mask = similarities >= threshold + valid_indices = np.where(valid_mask)[0] + + if len(valid_indices) == 0: + return [] + + # 取 top_k + if len(valid_indices) > top_k: + top_indices = valid_indices[np.argsort(-similarities[valid_indices])[:top_k]] + else: + top_indices = valid_indices[np.argsort(-similarities[valid_indices])] + + results = [] + for idx in top_indices: + results.append((self._ids[idx], float(similarities[idx]))) + + return results + + @property + def size(self) -> int: + with self._lock: + return len(self._ids) + + +# 全局单例 +vector_store = VectorStore() diff --git a/src/dialogue/conversation_history.py b/src/dialogue/conversation_history.py index ad40895..3056e50 100644 --- a/src/dialogue/conversation_history.py +++ b/src/dialogue/conversation_history.py @@ -11,7 +11,7 @@ from datetime import datetime, timedelta from sqlalchemy.orm import Session from ..core.database import db_manager -from ..core.models import Conversation, WorkOrder, WorkOrderSuggestion, KnowledgeEntry +from ..core.models import Conversation, WorkOrder, WorkOrderSuggestion, KnowledgeEntry, ChatSession from ..core.redis_manager import redis_manager from src.config.unified_config import get_config from sqlalchemy import and_, or_, desc @@ -45,7 +45,8 @@ class ConversationHistoryManager: response_time: Optional[float] = None, knowledge_used: Optional[List[int]] = None, ip_address: Optional[str] = None, - invocation_method: Optional[str] = None + invocation_method: Optional[str] = None, + session_id: Optional[str] = None ) -> int: """保存对话记录到数据库和Redis""" conversation_id = 0 @@ -54,6 +55,7 @@ class ConversationHistoryManager: # 保存到数据库 with db_manager.get_session() as session: conversation = Conversation( + session_id=session_id, work_order_id=work_order_id, user_message=user_message, assistant_response=assistant_response, @@ -715,3 +717,130 @@ class ConversationHistoryManager: except Exception as e: logger.error(f"获取对话分析数据失败: {e}") return {} + + # ==================== 会话管理方法 ==================== + + def get_sessions_paginated( + self, + page: int = 1, + per_page: int = 20, + status: Optional[str] = None, + search: str = '', + date_filter: str = '' + ) -> Dict[str, Any]: + """分页获取会话列表""" + try: + with db_manager.get_session() as session: + query = session.query(ChatSession) + + if status: + query = query.filter(ChatSession.status == status) + + if search: + query = query.filter( + or_( + ChatSession.title.contains(search), + ChatSession.session_id.contains(search), + ChatSession.user_id.contains(search) + ) + ) + + if date_filter: + now = datetime.now() + if date_filter == 'today': + start_date = now.replace(hour=0, minute=0, second=0, microsecond=0) + elif date_filter == 'week': + start_date = now - timedelta(days=7) + elif date_filter == 'month': + start_date = now - timedelta(days=30) + else: + start_date = None + if start_date: + query = query.filter(ChatSession.created_at >= start_date) + + total = query.count() + sessions_list = query.order_by( + ChatSession.updated_at.desc() + ).offset((page - 1) * per_page).limit(per_page).all() + + result = [] + for s in sessions_list: + result.append(s.to_dict()) + + return { + 'sessions': result, + 'page': page, + 'per_page': per_page, + 'total': total, + 'total_pages': (total + per_page - 1) // per_page + } + + except Exception as e: + logger.error(f"获取会话列表失败: {e}") + return {'sessions': [], 'page': page, 'per_page': per_page, 'total': 0, 'total_pages': 0} + + def get_session_messages( + self, + session_id: str, + limit: int = 100, + offset: int = 0 + ) -> Dict[str, Any]: + """获取某个会话的所有消息(按时间正序)""" + try: + with db_manager.get_session() as session: + # 获取会话元数据 + chat_session = session.query(ChatSession).filter( + ChatSession.session_id == session_id + ).first() + + if not chat_session: + return {'error': '会话不存在'} + + # 获取消息 + messages = session.query(Conversation).filter( + Conversation.session_id == session_id + ).order_by(Conversation.timestamp.asc()).offset(offset).limit(limit).all() + + message_list = [] + for msg in messages: + message_list.append({ + 'id': msg.id, + 'user_message': msg.user_message, + 'assistant_response': msg.assistant_response, + 'timestamp': msg.timestamp.isoformat() if msg.timestamp else None, + 'confidence_score': msg.confidence_score, + 'response_time': msg.response_time, + 'knowledge_used': json.loads(msg.knowledge_used) if msg.knowledge_used else [], + }) + + return { + 'session': chat_session.to_dict(), + 'messages': message_list, + 'total': len(message_list) + } + + except Exception as e: + logger.error(f"获取会话消息失败: {e}") + return {'error': str(e)} + + def delete_session(self, session_id: str) -> bool: + """删除会话及其所有消息""" + try: + with db_manager.get_session() as session: + # 删除关联消息 + session.query(Conversation).filter( + Conversation.session_id == session_id + ).delete(synchronize_session=False) + + # 删除会话 + session.query(ChatSession).filter( + ChatSession.session_id == session_id + ).delete(synchronize_session=False) + + session.commit() + logger.info(f"删除会话成功: {session_id}") + return True + + except Exception as e: + logger.error(f"删除会话失败: {e}") + return False diff --git a/src/dialogue/realtime_chat.py b/src/dialogue/realtime_chat.py index 22feaf5..a7930e1 100644 --- a/src/dialogue/realtime_chat.py +++ b/src/dialogue/realtime_chat.py @@ -13,7 +13,7 @@ from dataclasses import dataclass from ..core.llm_client import QwenClient from ..knowledge_base.knowledge_manager import KnowledgeManager from ..core.database import db_manager -from ..core.models import Conversation, WorkOrder +from ..core.models import Conversation, WorkOrder, ChatSession from ..vehicle.vehicle_data_manager import VehicleDataManager logger = logging.getLogger(__name__) @@ -54,6 +54,21 @@ class RealtimeChatManager: self.active_sessions[session_id] = session_data self.message_history[session_id] = [] + + # 持久化会话到数据库 + try: + with db_manager.get_session() as db_session: + chat_session = ChatSession( + session_id=session_id, + user_id=user_id, + work_order_id=work_order_id, + status="active", + message_count=0, + ) + db_session.add(chat_session) + db_session.commit() + except Exception as e: + logger.warning(f"持久化会话记录失败(不影响使用): {e}") logger.info(f"创建新会话: {session_id}") return session_id @@ -356,7 +371,7 @@ class RealtimeChatManager: """保存对话到数据库""" try: with db_manager.get_session() as session: - # 统一为一条记录:包含用户消息与助手回复 + # 计算响应时间 try: response_time = None if assistant_msg.timestamp and user_msg.timestamp: @@ -364,26 +379,36 @@ class RealtimeChatManager: except Exception: response_time = None - # 在知识字段中打上会话标记,便于结束时合并清理 - marked_knowledge = assistant_msg.knowledge_used or [] - try: - marked_knowledge = list(marked_knowledge) - marked_knowledge.append({"session_id": session_id, "type": "session_marker"}) - except Exception: - pass + # 保存知识库使用记录(不再塞 session_marker) + knowledge_data = assistant_msg.knowledge_used or [] conversation = Conversation( + session_id=session_id, work_order_id=assistant_msg.work_order_id or user_msg.work_order_id, user_message=user_msg.content or "", assistant_response=assistant_msg.content or "", timestamp=assistant_msg.timestamp or user_msg.timestamp, confidence_score=assistant_msg.confidence_score, - knowledge_used=json.dumps(marked_knowledge, ensure_ascii=False) if marked_knowledge else None, + knowledge_used=json.dumps(knowledge_data, ensure_ascii=False) if knowledge_data else None, response_time=response_time, ip_address=ip_address, invocation_method=invocation_method ) session.add(conversation) + + # 更新会话元数据 + chat_session = session.query(ChatSession).filter( + ChatSession.session_id == session_id + ).first() + if chat_session: + chat_session.message_count = (chat_session.message_count or 0) + 1 + chat_session.updated_at = datetime.now() + chat_session.source = invocation_method + chat_session.ip_address = ip_address + # 首条消息时设置会话标题 + if chat_session.message_count == 1 and user_msg.content: + chat_session.title = user_msg.content[:100] + session.commit() except Exception as e: @@ -470,57 +495,21 @@ class RealtimeChatManager: def end_session(self, session_id: str) -> bool: """结束会话""" try: - if session_id in self.active_sessions: - session_meta = self.active_sessions[session_id] - # 汇总本会话为一条记录 - history = self.message_history.get(session_id, []) - if history: - user_parts = [] - assistant_parts = [] - response_times = [] - first_ts = None - last_ts = None - for i in range(len(history)): - msg = history[i] - if first_ts is None: - first_ts = msg.timestamp - last_ts = msg.timestamp - if msg.role == "user": - user_parts.append(msg.content) - # 计算到下一条助手回复的间隔 - if i + 1 < len(history) and history[i+1].role == "assistant": - try: - rt = max(0.0, (history[i+1].timestamp - msg.timestamp).total_seconds() * 1000.0) - response_times.append(rt) - except Exception: - pass - elif msg.role == "assistant": - assistant_parts.append(msg.content) - agg_user = "\n\n".join([p for p in user_parts if p]) - agg_assistant = "\n\n".join([p for p in assistant_parts if p]) - avg_rt = sum(response_times)/len(response_times) if response_times else None - - from ..core.database import db_manager as _db - from ..core.models import Conversation as _Conv - import json as _json - with _db.get_session() as dbs: - agg = _Conv( - work_order_id=session_meta.get("work_order_id"), - user_message=agg_user, - assistant_response=agg_assistant, - timestamp=last_ts or first_ts, - confidence_score=None, - knowledge_used=_json.dumps({"session_id": session_id, "aggregated": True}, ensure_ascii=False), - response_time=avg_rt - ) - dbs.add(agg) - # 删除本会话标记的分散记录 - try: - pattern = f'%"session_id":"{session_id}"%' - dbs.query(_Conv).filter(_Conv.knowledge_used.like(pattern)).delete(synchronize_session=False) - except Exception: - pass + # 更新数据库中的会话状态 + try: + with db_manager.get_session() as dbs: + chat_session = dbs.query(ChatSession).filter( + ChatSession.session_id == session_id + ).first() + if chat_session: + chat_session.status = "ended" + chat_session.ended_at = datetime.now() dbs.commit() + except Exception as e: + logger.warning(f"更新会话状态失败: {e}") + + # 清理内存 + if session_id in self.active_sessions: del self.active_sessions[session_id] if session_id in self.message_history: diff --git a/src/knowledge_base/knowledge_manager.py b/src/knowledge_base/knowledge_manager.py index 1b923c3..f5c66f5 100644 --- a/src/knowledge_base/knowledge_manager.py +++ b/src/knowledge_base/knowledge_manager.py @@ -10,6 +10,9 @@ from sqlalchemy import func from ..core.database import db_manager from ..core.models import KnowledgeEntry, WorkOrder, Conversation from ..core.llm_client import QwenClient +from ..core.embedding_client import EmbeddingClient +from ..core.vector_store import vector_store +from ..config.unified_config import get_config logger = logging.getLogger(__name__) @@ -18,12 +21,18 @@ class KnowledgeManager: def __init__(self): self.llm_client = QwenClient() + self.embedding_client = EmbeddingClient() + self.embedding_enabled = get_config().embedding.enabled + self.similarity_threshold = get_config().embedding.similarity_threshold self.vectorizer = TfidfVectorizer( max_features=1000, stop_words=None, # 不使用英文停用词,因为数据是中文 ngram_range=(1, 2) ) self._load_vectorizer() + # 加载向量索引(embedding 模式) + if self.embedding_enabled: + vector_store.load_from_db() def _load_vectorizer(self): """加载向量化器""" @@ -71,17 +80,34 @@ class KnowledgeManager: existing_entry.updated_at = datetime.now() if work_order.satisfaction_score: existing_entry.confidence_score = work_order.satisfaction_score + # 更新 embedding + if self.embedding_enabled: + vec = self.embedding_client.embed_text(question + " " + answer) + if vec: + existing_entry.vector_embedding = json.dumps(vec) + vector_store.update(existing_entry.id, vec) else: # 创建新条目 logger.info(f"未发现相似条目,正在为工单 {work_order_id} 创建新知识点") + embedding_json = None + vec = None + if self.embedding_enabled: + vec = self.embedding_client.embed_text(question + " " + answer) + if vec: + embedding_json = json.dumps(vec) + new_entry = KnowledgeEntry( question=question, answer=answer, category=work_order.category, confidence_score=work_order.satisfaction_score or 0.5, - usage_count=1 + usage_count=1, + vector_embedding=embedding_json ) session.add(new_entry) + session.flush() # 获取 ID + if vec and new_entry.id: + vector_store.add(new_entry.id, vec) session.commit() logger.info(f"从工单 {work_order_id} 学习知识成功") @@ -94,6 +120,22 @@ class KnowledgeManager: def _find_similar_entry(self, question: str, session) -> Optional[KnowledgeEntry]: """查找相似的知识库条目""" try: + # 优先使用 embedding 查找 + if self.embedding_enabled: + query_vec = self.embedding_client.embed_text(question) + if query_vec: + candidates = vector_store.search(query_vec, top_k=1, threshold=0.8) + if candidates: + entry_id, score = candidates[0] + entry = session.query(KnowledgeEntry).filter( + KnowledgeEntry.id == entry_id, + KnowledgeEntry.is_active == True + ).first() + if entry: + logger.info(f"Embedding 匹配成功: 相似度 {score:.4f}, ID={entry_id}") + return entry + + # 降级:TF-IDF 匹配 entries = session.query(KnowledgeEntry).filter( KnowledgeEntry.is_active == True ).all() @@ -101,7 +143,6 @@ class KnowledgeManager: if not entries: return None - # 计算相似度 texts = [entry.question for entry in entries] question_vector = self.vectorizer.transform([question]) entry_vectors = self.vectorizer.transform(texts) @@ -110,13 +151,11 @@ class KnowledgeManager: max_similarity_idx = np.argmax(similarities) max_score = similarities[max_similarity_idx] - logger.debug(f"相似度检索完成: 最高分值={max_score:.4f}, 目标ID={entries[max_similarity_idx].id if entries else 'N/A'}") + logger.debug(f"TF-IDF 相似度检索: 最高分值={max_score:.4f}") - if max_score > 0.8: # 相似度阈值 - logger.info(f"匹配成功: 相似度 {max_score:.4f} 超过阈值 0.8") + if max_score > 0.8: return entries[max_similarity_idx] - logger.debug(f"匹配跳过: 相似度 {max_score:.4f} 未达到阈值 0.8") return None except Exception as e: @@ -124,7 +163,85 @@ class KnowledgeManager: return None def search_knowledge(self, query: str, top_k: int = 3, verified_only: bool = True) -> List[Dict[str, Any]]: - """搜索知识库""" + """搜索知识库 — 优先使用 embedding 语义检索,降级为关键词匹配""" + try: + # 尝试 embedding 语义检索 + if self.embedding_enabled: + results = self._search_by_embedding(query, top_k, verified_only) + if results: + return results + logger.debug("Embedding 检索无结果,降级为关键词匹配") + + # 降级:关键词匹配 + return self._search_by_keyword(query, top_k, verified_only) + + except Exception as e: + logger.error(f"搜索知识库失败: {e}") + return [] + + def _search_by_embedding(self, query: str, top_k: int = 3, verified_only: bool = True) -> List[Dict[str, Any]]: + """基于 embedding 向量的语义检索""" + try: + query_vec = self.embedding_client.embed_text(query) + if query_vec is None: + return [] + + # 向量检索 + candidates = vector_store.search( + query_vector=query_vec, + top_k=top_k * 3, # 多取一些,后面过滤 + threshold=self.similarity_threshold + ) + + if not candidates: + return [] + + # 从 DB 获取完整条目并过滤 + candidate_ids = [cid for cid, _ in candidates] + score_map = {cid: score for cid, score in candidates} + + with db_manager.get_session() as session: + query_filter = session.query(KnowledgeEntry).filter( + KnowledgeEntry.id.in_(candidate_ids), + KnowledgeEntry.is_active == True + ) + if verified_only: + query_filter = query_filter.filter(KnowledgeEntry.is_verified == True) + + entries = query_filter.all() + + # 如果 verified_only 没结果,回退到全部 + if not entries and verified_only: + entries = session.query(KnowledgeEntry).filter( + KnowledgeEntry.id.in_(candidate_ids), + KnowledgeEntry.is_active == True + ).all() + + results = [] + for entry in entries: + results.append({ + "id": entry.id, + "question": entry.question, + "answer": entry.answer, + "category": entry.category, + "confidence_score": entry.confidence_score, + "similarity_score": score_map.get(entry.id, 0.0), + "usage_count": entry.usage_count, + "is_verified": entry.is_verified + }) + + results.sort(key=lambda x: x['similarity_score'], reverse=True) + results = results[:top_k] + + logger.info(f"Embedding 搜索 '{query[:30]}' 返回 {len(results)} 个结果") + return results + + except Exception as e: + logger.error(f"Embedding 搜索失败: {e}") + return [] + + def _search_by_keyword(self, query: str, top_k: int = 3, verified_only: bool = True) -> List[Dict[str, Any]]: + """基于关键词的搜索(降级方案)""" try: with db_manager.get_session() as session: # 构建查询条件 @@ -221,6 +338,14 @@ class KnowledgeManager: ) -> bool: """添加知识库条目""" try: + # 生成 embedding + embedding_json = None + text_for_embedding = question + " " + answer + if self.embedding_enabled: + vec = self.embedding_client.embed_text(text_for_embedding) + if vec: + embedding_json = json.dumps(vec) + with db_manager.get_session() as session: entry = KnowledgeEntry( question=question, @@ -228,12 +353,18 @@ class KnowledgeManager: category=category, confidence_score=confidence_score, usage_count=0, - is_verified=is_verified + is_verified=is_verified, + vector_embedding=embedding_json ) session.add(entry) session.commit() + entry_id = entry.id - # 重新训练向量化器 + # 更新向量索引 + if vec and entry_id: + vector_store.add(entry_id, vec) + + # 重新训练 TF-IDF 向量化器 self._load_vectorizer() logger.info(f"添加知识库条目成功: {question[:50]}...") @@ -261,15 +392,26 @@ class KnowledgeManager: if not entry: return False + content_changed = False if question: entry.question = question + content_changed = True if answer: entry.answer = answer + content_changed = True if category: entry.category = category if confidence_score is not None: entry.confidence_score = confidence_score + # 内容变更时重新生成 embedding + if content_changed and self.embedding_enabled: + text_for_embedding = (question or entry.question) + " " + (answer or entry.answer) + vec = self.embedding_client.embed_text(text_for_embedding) + if vec: + entry.vector_embedding = json.dumps(vec) + vector_store.update(entry_id, vec) + entry.updated_at = datetime.now() session.commit() @@ -383,12 +525,14 @@ class KnowledgeManager: entry.is_active = False session.commit() + # 从向量索引中移除 + vector_store.remove(entry_id) + # 重新训练向量化器(如果还有活跃条目) try: self._load_vectorizer() except Exception as vectorizer_error: logger.warning(f"重新加载向量化器失败: {vectorizer_error}") - # 即使向量化器加载失败,删除操作仍然成功 logger.info(f"删除知识库条目成功: {entry_id}") return True diff --git a/src/web/blueprints/agent.py b/src/web/blueprints/agent.py index 8a1bf32..189c203 100644 --- a/src/web/blueprints/agent.py +++ b/src/web/blueprints/agent.py @@ -185,20 +185,20 @@ def get_agent_tools_stats(): try: from src.web.service_manager import service_manager agent_assistant = service_manager.get_agent_assistant() - tools = agent_assistant.agent_core.tool_manager.get_available_tools() - performance = agent_assistant.agent_core.tool_manager.get_tool_performance_report() + react_status = agent_assistant.react_agent.get_status() + tools = agent_assistant.react_agent.get_tool_definitions() + history = agent_assistant.react_agent.get_execution_history(20) return jsonify({ "success": True, "tools": tools, - "performance": performance + "status": react_status, + "recent_history": history }) except Exception as e: - # 返回默认工具列表,避免500错误 return jsonify({ "success": False, "tools": [], - "performance": {}, - "error": "工具统计暂时不可用" + "error": str(e) }) @@ -213,15 +213,23 @@ def execute_agent_tool(): if not tool_name: return jsonify({"error": "缺少工具名称tool"}), 400 - result = service_manager.get_agent_assistant().agent_core.tool_manager.execute_tool_sync(tool_name, parameters) - return jsonify(result) + agent = service_manager.get_agent_assistant() + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: + result = loop.run_until_complete( + agent.react_agent._execute_tool(tool_name, parameters) + ) + finally: + loop.close() + return jsonify({"success": True, "result": result}) except Exception as e: return jsonify({"error": str(e)}), 500 @agent_bp.route('/tools/register', methods=['POST']) def register_custom_tool(): - """注册自定义工具(仅登记元数据,函数为占位符)""" + """注册自定义工具(仅登记元数据)""" try: from src.web.service_manager import service_manager data = request.get_json() or {} @@ -230,14 +238,9 @@ def register_custom_tool(): if not name: return jsonify({"error": "缺少工具名称"}), 400 - def _placeholder_tool(**kwargs): - return {"message": f"自定义工具 {name} 已登记(占位),当前不可执行", "params": kwargs} - - service_manager.get_agent_assistant().agent_core.tool_manager.register_tool( - name, - _placeholder_tool, - metadata={"description": description, "custom": True} - ) + agent = service_manager.get_agent_assistant() + agent.register_tool(name, lambda **kw: {"message": f"自定义工具 {name} 占位", "params": kw}, + metadata={"description": description, "custom": True}) return jsonify({"success": True, "message": "工具已注册"}) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -248,7 +251,7 @@ def unregister_custom_tool(name): """注销自定义工具""" try: from src.web.service_manager import service_manager - success = service_manager.get_agent_assistant().agent_core.tool_manager.unregister_tool(name) + success = service_manager.get_agent_assistant().unregister_tool(name) return jsonify({"success": success}) except Exception as e: return jsonify({"error": str(e)}), 500 diff --git a/src/web/blueprints/conversations.py b/src/web/blueprints/conversations.py index c8603be..273aee9 100644 --- a/src/web/blueprints/conversations.py +++ b/src/web/blueprints/conversations.py @@ -349,3 +349,68 @@ def get_conversation_analytics(): except Exception as e: logger.error(f"获取对话分析数据失败: {e}") return jsonify({"error": str(e)}), 500 + + +# ==================== 会话管理 API ==================== + +@conversations_bp.route('/sessions') +def get_sessions(): + """获取会话列表(分页)""" + try: + page = request.args.get('page', 1, type=int) + per_page = request.args.get('per_page', 20, type=int) + status = request.args.get('status', '') # active, ended, 空=全部 + search = request.args.get('search', '') + date_filter = request.args.get('date_filter', '') + + result = history_manager.get_sessions_paginated( + page=page, + per_page=per_page, + status=status or None, + search=search, + date_filter=date_filter + ) + + return jsonify(result) + + except Exception as e: + logger.error(f"获取会话列表失败: {e}") + return jsonify({"error": str(e)}), 500 + + +@conversations_bp.route('/sessions/') +def get_session_detail(session_id): + """获取某个会话的完整消息列表""" + try: + limit = request.args.get('limit', 100, type=int) + offset = request.args.get('offset', 0, type=int) + + result = history_manager.get_session_messages( + session_id=session_id, + limit=limit, + offset=offset + ) + + if 'error' in result: + return jsonify(result), 404 + + return jsonify({'success': True, **result}) + + except Exception as e: + logger.error(f"获取会话详情失败: {e}") + return jsonify({"error": str(e)}), 500 + + +@conversations_bp.route('/sessions/', methods=['DELETE']) +def delete_session(session_id): + """删除会话及其所有消息""" + try: + success = history_manager.delete_session(session_id) + if success: + return jsonify({"success": True, "message": "会话已删除"}) + else: + return jsonify({"error": "删除失败"}), 500 + + except Exception as e: + logger.error(f"删除会话失败: {e}") + return jsonify({"error": str(e)}), 500 diff --git a/src/web/blueprints/monitoring.py b/src/web/blueprints/monitoring.py index 4f1d62a..6e3c8b6 100644 --- a/src/web/blueprints/monitoring.py +++ b/src/web/blueprints/monitoring.py @@ -437,7 +437,6 @@ def get_error_log(): ).limit(50).all() errors = [] - error_id = 1 for conv in conversations: error_type = None @@ -460,14 +459,17 @@ def get_error_log(): # 只记录有错误的对话 if error_type: errors.append({ - 'id': error_id, + 'id': conv.id, 'timestamp': conv.timestamp.isoformat() if conv.timestamp else None, 'error_type': error_type, 'error_message': error_message, - 'model': 'qwen-turbo', # 实际使用的模型 - 'user_id': f'user_{conv.id}' + 'model': 'qwen-turbo', + 'user_id': f'user_{conv.id}', + 'user_message': (conv.user_message or '')[:100], + 'assistant_response': (conv.assistant_response or '')[:200], + 'confidence_score': conv.confidence_score, + 'response_time': conv.response_time, }) - error_id += 1 return jsonify({ 'success': True, @@ -476,6 +478,31 @@ def get_error_log(): except Exception as e: return jsonify({"error": str(e)}), 500 + +@monitoring_bp.route('/ai-monitor/error-log/') +def get_error_detail(conv_id): + """获取单条错误详情""" + try: + with db_manager.get_session() as session: + conv = session.query(Conversation).filter_by(id=conv_id).first() + if not conv: + return jsonify({"error": "记录不存在"}), 404 + return jsonify({ + 'success': True, + 'detail': { + 'id': conv.id, + 'timestamp': conv.timestamp.isoformat() if conv.timestamp else None, + 'user_message': conv.user_message, + 'assistant_response': conv.assistant_response, + 'confidence_score': conv.confidence_score, + 'response_time': conv.response_time, + 'category': conv.category, + 'source': conv.source, + } + }) + except Exception as e: + return jsonify({"error": str(e)}), 500 + @monitoring_bp.route('/ai-monitor/error-log', methods=['DELETE']) def clear_error_log(): """清空错误日志""" diff --git a/src/web/static/js/dashboard.js b/src/web/static/js/dashboard.js index 58ed840..a4edf2d 100644 --- a/src/web/static/js/dashboard.js +++ b/src/web/static/js/dashboard.js @@ -3863,6 +3863,44 @@ class TSPDashboard { } } + async viewErrorDetail(convId) { + const modal = new bootstrap.Modal(document.getElementById('errorDetailModal')); + const body = document.getElementById('errorDetailBody'); + body.innerHTML = '
加载中...
'; + modal.show(); + + try { + const response = await fetch(`/api/ai-monitor/error-log/${convId}`); + const data = await response.json(); + + if (data.success) { + const d = data.detail; + body.innerHTML = ` + + + + + + + + + +
记录ID${d.id}
时间${d.timestamp ? new Date(d.timestamp).toLocaleString() : '-'}
分类${d.category || '-'}
来源${d.source || '-'}
置信度${d.confidence_score != null ? d.confidence_score : '-'}
响应时间${d.response_time != null ? d.response_time + ' ms' : '-'}
用户消息
${this.escapeHtml(d.user_message || '-')}
助手回复
${this.escapeHtml(d.assistant_response || '-')}
+ `; + } else { + body.innerHTML = `
${data.error || '加载失败'}
`; + } + } catch (error) { + body.innerHTML = `
请求失败: ${error.message}
`; + } + } + + escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; + } + // 系统优化 async loadSystemOptimizer() { try { diff --git a/src/web/templates/dashboard.html b/src/web/templates/dashboard.html index 9aa9963..3fcdacf 100644 --- a/src/web/templates/dashboard.html +++ b/src/web/templates/dashboard.html @@ -2469,6 +2469,24 @@
+ + + diff --git a/tsp_assistant.db b/tsp_assistant.db index 23fc91867b51882d9afd93b5d6d92f2dcb4764c7..6b37e00b2e540564a61fa437c0769c6e0d86e2e7 100644 GIT binary patch delta 357 zcmZo@U}-qOGC^99i-Cbb2#8^TYod-ZD;I-a-p-9FbKgs{uVdh3UBbY(l{cH$fmf8f zjPn`$Hr6HV#hV2Mx>y~WefZeL)zul>!b=j9a#E8s5=-KXQ;Ul;^Ye;PID*bWu8twD z3L%b8KCTLIsmYGNyO;zsjT9YH!z7ar{<+hzVlyca?d{=4w$6YJ-1Q(|#v7t= QK|#r}dDC}4jzt0r0BdJ)rvLx| delta 71 zcmZoTz|zpbGC^99lYxOj0El6LbE1whD<^|qxYx#%x$i}R!tCtZ82GmGX7f7migK56 SK4aguSx{gB`{r%my*L2oA`o%_