Files
assist/src/agent_assistant.py

559 lines
21 KiB
Python
Raw Normal View History

2025-09-06 21:06:18 +08:00
# -*- coding: utf-8 -*-
"""
TSP Agent助手 - 简化版本
提供基本的Agent功能和工具管理
2025-09-06 21:06:18 +08:00
"""
import logging
import asyncio
import json
2025-09-06 21:06:18 +08:00
from typing import Dict, Any, List, Optional
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
2026-03-20 16:50:26 +08:00
from src.agent.react_agent import ReactAgent
2025-09-06 21:06:18 +08:00
logger = logging.getLogger(__name__)
class TSPAgentAssistant:
"""TSP Agent助手"""
def __init__(self):
# 初始化基础功能
config = get_config()
self.llm_manager = LLMManager(config.llm)
self.is_agent_mode = True
self.execution_history = []
2026-03-20 16:50:26 +08:00
# ReAct Agent核心
self.react_agent = ReactAgent()
# 工具注册表(保留兼容旧 API
self.tools = {}
self.tool_performance = {}
# AI监控状态
self.ai_monitoring_active = False
self.monitoring_thread = None
logger.info("TSP Agent助手初始化完成")
2025-09-06 21:06:18 +08:00
def register_tool(self, name: str, func, metadata: Dict[str, Any] = None):
"""注册工具"""
2025-09-06 21:06:18 +08:00
try:
self.tools[name] = {
"function": func,
"metadata": metadata or {},
"usage_count": 0,
"success_count": 0,
"last_used": None
2025-09-06 21:06:18 +08:00
}
logger.info(f"工具 {name} 注册成功")
return True
2025-09-06 21:06:18 +08:00
except Exception as e:
logger.error(f"注册工具 {name} 失败: {e}")
return False
2025-09-06 21:06:18 +08:00
def unregister_tool(self, name: str) -> bool:
"""注销工具"""
try:
if name in self.tools:
del self.tools[name]
logger.info(f"工具 {name} 注销成功")
return True
return False
except Exception as e:
logger.error(f"注销工具 {name} 失败: {e}")
return False
2025-09-06 21:06:18 +08:00
def get_available_tools(self) -> List[Dict[str, Any]]:
"""获取可用工具列表"""
2025-09-06 21:06:18 +08:00
try:
tools_list = []
for name, tool_info in self.tools.items():
tools_list.append({
"name": name,
"metadata": tool_info["metadata"],
"usage_count": tool_info["usage_count"],
"success_count": tool_info["success_count"],
"last_used": tool_info["last_used"]
})
return tools_list
2025-09-06 21:06:18 +08:00
except Exception as e:
logger.error(f"获取工具列表失败: {e}")
return []
2025-09-06 21:06:18 +08:00
async def execute_tool(self, tool_name: str, parameters: Dict[str, Any] = None) -> Dict[str, Any]:
"""执行工具"""
2025-09-06 21:06:18 +08:00
try:
if tool_name not in self.tools:
return {"error": f"工具 {tool_name} 不存在"}
tool_info = self.tools[tool_name]
func = tool_info["function"]
# 记录使用
tool_info["usage_count"] += 1
tool_info["last_used"] = datetime.now().isoformat()
# 执行工具
start_time = datetime.now()
try:
if asyncio.iscoroutinefunction(func):
result = await func(**(parameters or {}))
else:
result = func(**(parameters or {}))
# 记录成功
tool_info["success_count"] += 1
execution_time = (datetime.now() - start_time).total_seconds()
# 记录执行历史
self._record_execution(tool_name, parameters, result, True, execution_time)
2025-09-06 21:06:18 +08:00
return {
"success": True,
"result": result,
"execution_time": execution_time,
"tool_name": tool_name
2025-09-06 21:06:18 +08:00
}
except Exception as e:
execution_time = (datetime.now() - start_time).total_seconds()
self._record_execution(tool_name, parameters, str(e), False, execution_time)
2025-09-06 21:06:18 +08:00
return {
"success": False,
"error": str(e),
"execution_time": execution_time,
"tool_name": tool_name
2025-09-06 21:06:18 +08:00
}
2025-09-06 21:06:18 +08:00
except Exception as e:
logger.error(f"执行工具 {tool_name} 失败: {e}")
return {"error": str(e)}
2025-09-06 21:06:18 +08:00
def _record_execution(self, tool_name: str, parameters: Dict[str, Any],
result: Any, success: bool, execution_time: float):
"""记录执行历史"""
2025-09-06 21:06:18 +08:00
try:
execution_record = {
"timestamp": datetime.now().isoformat(),
"tool_name": tool_name,
"parameters": parameters,
"result": result,
"success": success,
"execution_time": execution_time
2025-09-06 21:06:18 +08:00
}
self.execution_history.append(execution_record)
2025-09-06 21:06:18 +08:00
# 保持历史记录在合理范围内
if len(self.execution_history) > 1000:
self.execution_history = self.execution_history[-1000:]
2025-09-06 21:06:18 +08:00
except Exception as e:
logger.error(f"记录执行历史失败: {e}")
2025-09-06 21:06:18 +08:00
def get_tool_performance_report(self) -> Dict[str, Any]:
"""获取工具性能报告"""
2025-09-06 21:06:18 +08:00
try:
total_tools = len(self.tools)
total_executions = sum(tool["usage_count"] for tool in self.tools.values())
total_successes = sum(tool["success_count"] for tool in self.tools.values())
2025-09-06 21:06:18 +08:00
success_rate = (total_successes / total_executions * 100) if total_executions > 0 else 0
2025-09-06 21:06:18 +08:00
return {
"total_tools": total_tools,
"total_executions": total_executions,
"total_successes": total_successes,
"success_rate": round(success_rate, 2),
"tools": self.get_available_tools()
2025-09-06 21:06:18 +08:00
}
except Exception as e:
logger.error(f"获取工具性能报告失败: {e}")
return {}
2025-09-06 21:06:18 +08:00
def get_action_history(self, limit: int = 50) -> List[Dict[str, Any]]:
"""获取动作执行历史"""
2025-09-06 21:06:18 +08:00
try:
return self.execution_history[-limit:] if limit > 0 else self.execution_history
2025-09-06 21:06:18 +08:00
except Exception as e:
logger.error(f"获取动作历史失败: {e}")
return []
2025-09-06 21:06:18 +08:00
def clear_execution_history(self) -> Dict[str, Any]:
"""清空执行历史"""
try:
count = len(self.execution_history)
self.execution_history.clear()
return {
"success": True,
"message": f"已清空 {count} 条执行历史"
}
except Exception as e:
logger.error(f"清空执行历史失败: {e}")
return {"success": False, "error": str(e)}
2025-09-06 21:06:18 +08:00
def get_agent_status(self) -> Dict[str, Any]:
"""获取Agent状态"""
2025-09-08 15:27:22 +08:00
try:
2026-03-20 16:50:26 +08:00
react_status = self.react_agent.get_status()
2025-09-08 15:27:22 +08:00
return {
"success": True,
"is_active": self.is_agent_mode,
"ai_monitoring_active": self.ai_monitoring_active,
2026-03-20 16:50:26 +08:00
"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()
2025-09-08 15:27:22 +08:00
}
except Exception as e:
logger.error(f"获取Agent状态失败: {e}")
return {
"success": False,
"error": str(e),
"is_active": False,
"ai_monitoring_active": False
2025-09-08 15:27:22 +08:00
}
2025-09-06 21:06:18 +08:00
def toggle_agent_mode(self, enabled: bool) -> bool:
"""切换Agent模式"""
try:
self.is_agent_mode = enabled
logger.info(f"Agent模式: {'启用' if enabled else '禁用'}")
return True
2025-09-06 21:06:18 +08:00
except Exception as e:
logger.error(f"切换Agent模式失败: {e}")
return False
def start_proactive_monitoring(self) -> bool:
"""启动主动监控"""
try:
if not self.ai_monitoring_active:
self.ai_monitoring_active = True
logger.info("主动监控已启动")
return True
return True
2025-09-06 21:06:18 +08:00
except Exception as e:
logger.error(f"启动主动监控失败: {e}")
return False
def stop_proactive_monitoring(self) -> bool:
"""停止主动监控"""
try:
self.ai_monitoring_active = False
logger.info("主动监控已停止")
return True
2025-09-06 21:06:18 +08:00
except Exception as e:
logger.error(f"停止主动监控失败: {e}")
return False
def run_proactive_monitoring(self) -> Dict[str, Any]:
"""运行主动监控检查"""
2025-09-06 21:06:18 +08:00
try:
return {
"success": True,
"message": "主动监控检查完成",
"timestamp": datetime.now().isoformat()
2025-09-06 21:06:18 +08:00
}
except Exception as e:
logger.error(f"运行主动监控失败: {e}")
return {"success": False, "error": str(e)}
def run_intelligent_analysis(self) -> Dict[str, Any]:
"""运行智能分析"""
try:
# 分析工具使用情况
tool_performance = self.get_tool_performance_report()
# 分析执行历史
recent_executions = self.get_action_history(20)
# 生成分析报告
analysis = {
"tool_performance": tool_performance,
"recent_activity": len(recent_executions),
"success_rate": tool_performance.get("success_rate", 0),
"recommendations": self._generate_recommendations(tool_performance)
}
return analysis
2025-09-06 21:06:18 +08:00
except Exception as e:
logger.error(f"运行智能分析失败: {e}")
return {"error": str(e)}
def _generate_recommendations(self, tool_performance: Dict[str, Any]) -> List[str]:
"""生成建议"""
recommendations = []
success_rate = tool_performance.get("success_rate", 100)
if success_rate < 90:
recommendations.append("工具成功率较低,建议检查工具实现")
total_executions = tool_performance.get("total_executions", 0)
if total_executions < 10:
recommendations.append("工具使用频率较低,建议增加工具调用")
return recommendations
def get_llm_usage_stats(self) -> Dict[str, Any]:
"""获取LLM使用统计"""
try:
return {
"total_requests": 0,
"total_tokens": 0,
"cost": 0.0,
"last_updated": datetime.now().isoformat()
}
except Exception as e:
logger.error(f"获取LLM使用统计失败: {e}")
return {}
def process_message_agent_sync(self, message: str, user_id: str = "admin",
work_order_id: Optional[int] = None,
enable_proactive: bool = True) -> Dict[str, Any]:
"""处理消息(同步桥接)"""
try:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return loop.run_until_complete(self.process_message_agent(message, user_id, work_order_id, enable_proactive))
except Exception as e:
logger.error(f"同步处理消息失败: {e}")
return {"error": str(e)}
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]:
2026-03-20 16:50:26 +08:00
"""处理消息 - 使用 ReAct Agent"""
try:
logger.info(f"Agent收到消息: {message}")
2026-03-20 16:50:26 +08:00
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)}
def execute_tool_sync(self, tool_name: str, parameters: Dict[str, Any] = None) -> Dict[str, Any]:
"""执行工具(同步桥接)"""
try:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return loop.run_until_complete(self.execute_tool(tool_name, parameters))
except Exception as e:
return {"error": str(e)}
def trigger_sample_actions_sync(self) -> Dict[str, Any]:
"""触发示例动作(同步桥接)"""
try:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return loop.run_until_complete(self.trigger_sample_actions())
except Exception as e:
return {"success": False, "error": str(e)}
async def trigger_sample_actions(self) -> Dict[str, Any]:
"""触发示例动作"""
try:
# 执行一个示例工具
result = await self.execute_tool("sample_tool", {"action": "test"})
return {
"success": True,
"message": "示例动作已执行",
"result": result
}
except Exception as e:
logger.error(f"触发示例动作失败: {e}")
return {"success": False, "error": str(e)}
async def process_file_to_knowledge(self, file_path: str, filename: str) -> Dict[str, Any]:
2025-09-06 21:06:18 +08:00
"""处理文件并生成知识库"""
try:
import os
import mimetypes
logger.info(f"开始处理知识库上传文件: {filename}")
2025-09-06 21:06:18 +08:00
# 检查文件类型
mime_type, _ = mimetypes.guess_type(file_path)
file_ext = os.path.splitext(filename)[1].lower()
2025-09-06 21:06:18 +08:00
# 读取文件内容
content = self._read_file_content(file_path, file_ext)
if not content:
logger.error(f"文件读取失败或内容为空: {filename}")
2025-09-06 21:06:18 +08:00
return {"success": False, "error": "无法读取文件内容"}
logger.info(f"文件读取成功: {filename}, 字符数={len(content)}")
# 使用LLM进行知识提取 (异步调用)
logger.info(f"正在对文件内容进行 AI 知识提取...")
knowledge_entries = await self._extract_knowledge_from_content(content, filename)
logger.info(f"知识提取完成: 共提取出 {len(knowledge_entries)} 个潜在条目")
2025-09-06 21:06:18 +08:00
# 保存到知识库
saved_count = 0
# 获取知识库管理器
try:
knowledge_manager = service_manager.get_assistant().knowledge_manager
except Exception as e:
logger.error(f"无法获取知识库管理器: {e}")
knowledge_manager = None
2025-09-06 21:06:18 +08:00
for i, entry in enumerate(knowledge_entries):
try:
logger.info(f"正在保存知识条目 [{i+1}/{len(knowledge_entries)}]: {entry.get('question', '')[:30]}...")
if knowledge_manager:
# 实际保存到数据库
knowledge_manager.add_knowledge_entry(
question=entry.get('question'),
answer=entry.get('answer'),
category=entry.get('category', '文档导入'),
confidence_score=entry.get('confidence_score', 0.8)
)
saved_count += 1
else:
# 如果无法获取管理器,仅记录日志(降级处理)
logger.warning("知识库管理器不可用,跳过保存")
2025-09-06 21:06:18 +08:00
except Exception as save_error:
logger.error(f"保存知识条目 {i+1} 时出错: {save_error}")
logger.info(f"文件处理任务结束: {filename}, 成功入库 {saved_count}")
2025-09-06 21:06:18 +08:00
return {
"success": True,
"knowledge_count": saved_count,
"total_extracted": len(knowledge_entries),
"filename": filename
}
except Exception as e:
logger.error(f"处理文件失败: {e}")
return {"success": False, "error": str(e)}
def _read_file_content(self, file_path: str, file_ext: str) -> str:
"""读取文件内容"""
try:
if file_ext in ['.txt', '.md']:
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
elif file_ext == '.pdf':
return "PDF文件需要安装PyPDF2库"
2025-09-06 21:06:18 +08:00
elif file_ext in ['.doc', '.docx']:
return "Word文件需要安装python-docx库"
2025-09-06 21:06:18 +08:00
else:
return "不支持的文件格式"
except Exception as e:
logger.error(f"读取文件失败: {e}")
return ""
async def _extract_knowledge_from_content(self, content: str, filename: str) -> List[Dict[str, Any]]:
"""从内容中提取知识 - 使用LLM"""
2025-09-06 21:06:18 +08:00
try:
# 限制内容长度避免超出token限制
# 假设每个汉字2个token保留前8000个字符作为上下文
truncated_content = content[:8000]
if len(content) > 8000:
truncated_content += "\n...(后续内容已省略)"
prompt = f"""
你是一个专业的知识库构建助手请分析以下文档内容提取出关键的"问题""答案"用于构建知识库
文档文件名{filename}
文档内容
{truncated_content}
要求
1. 提取文档中的核心知识点转化为"问题(question)""答案(answer)"的形式
2. "问题"应该清晰明确方便用户搜索
3. "答案"应该准确完整直接回答问题
4. "分类(category)"请根据内容自动归类故障排查操作指南系统配置业务流程等
5. 输出格式必须是合法的 JSON 数组不要包含Markdown标记
JSON格式示例
[
{{"question": "如何重置密码?", "answer": "请访问设置页面,点击重置密码按钮...", "category": "操作指南"}},
{{"question": "系统支持哪些浏览器?", "answer": "支持Chrome, Edge, Firefox...", "category": "系统配置"}}
]
"""
# 调用LLM生成
logger.info("正在调用LLM进行知识提取...")
response_text = await self.llm_manager.generate(prompt, temperature=0.3)
# 清理响应中的Markdown标记如果存在
cleaned_text = response_text.strip()
if cleaned_text.startswith("```json"):
cleaned_text = cleaned_text[7:]
if cleaned_text.startswith("```"):
cleaned_text = cleaned_text[3:]
if cleaned_text.endswith("```"):
cleaned_text = cleaned_text[:-3]
cleaned_text = cleaned_text.strip()
# 解析JSON
try:
entries = json.loads(cleaned_text)
except json.JSONDecodeError:
# 尝试修复常见的JSON错误
logger.warning(f"JSON解析失败尝试简单修复: {cleaned_text[:100]}...")
# 这里可以添加更复杂的修复逻辑,或者直接记录错误
return []
# 验证和标准化
valid_entries = []
for entry in entries:
if isinstance(entry, dict) and "question" in entry and "answer" in entry:
valid_entries.append({
"question": entry["question"],
"answer": entry["answer"],
"category": entry.get("category", "文档导入"),
"confidence_score": 0.9 # LLM生成的置信度较高
})
return valid_entries
2025-09-06 21:06:18 +08:00
except Exception as e:
logger.error(f"提取知识失败: {e}")
2025-09-06 21:06:18 +08:00
return []
# 使用示例
async def main():
"""主函数示例"""
# 创建Agent助手
agent_assistant = TSPAgentAssistant()
# 测试Agent功能
print("=== TSP Agent助手测试 ===")
# 测试Agent模式处理消息
response = await agent_assistant.process_message_agent(
message="我的账户无法登录,请帮助我解决这个问题",
user_id="user123"
)
print("Agent模式响应:", response)
# 获取Agent状态
agent_status = agent_assistant.get_agent_status()
print("Agent状态:", agent_status)
if __name__ == "__main__":
asyncio.run(main())