""" 工具函数定义 包含RAG、GraphRAG、VLM、ASR、TTS等工具的function call封装 """ import os import json import re import httpx import asyncio from typing import Optional, Dict, Any, List from langchain_core.tools import tool from dotenv import load_dotenv from config import RAG_CONFIG, ASR_CONFIG from modelsAPI.model_api import OpenaiAPI from utils.function_tracker import track_function_calls load_dotenv() from .graph_tools import ( graph_rag_search, fault_graph_rag_search, operation_graph_rag_search, ) from .rag_tools import ( rag_search_tool, file_to_text_tool, ) from .vlm_tools import ( vlm_image_to_text, image_rag_describe, ) from .fault_statistics import fault_type_statistics_tool, fault_frequency_statistics_tool, spare_parts_statistics_tool @tool def detect_input_type(raw_input: Dict[str, Any]) -> Dict[str, Any]: """ 判断输入类型工具(异步版本):严格对齐 MainAgentState 字段。 识别:image, audio/asr, file, query (text) """ if not isinstance(raw_input, dict): return { "has_image": False, "has_audio": False, "has_file": False, "has_query": False, "input_type": "unknown", "error": "raw_input 必须是 dict" } has_image = False img_v = raw_input.get("image") if img_v: has_image = True has_audio = False aud_v = raw_input.get("audio") if aud_v: has_audio = True has_file = False file_v = raw_input.get("file_text") if file_v: has_file = True has_query = False query_v = raw_input.get("query") if isinstance(query_v, str) and query_v.strip(): has_query = True return { "has_image": has_image, "has_audio": has_audio, "has_file": has_file, "has_query": has_query, } async def asr_audio_to_text_async( audio_path: str, model: Optional[str] = None, language: Optional[str] = None ) -> Dict[str, Any]: """ ASR语音转文本工具(异步实现) 将音频文件转换为文本内容 Args: audio_path: 音频文件路径(如:audio/test.wav) model: ASR模型名称(可选,默认从配置读取,如"base") language: 语言代码(可选,默认从配置读取,如"zh"表示中文) Returns: 包含success和text字段的字典 """ if not os.path.exists(audio_path): return { "success": False, "error": f"音频文件不存在: {audio_path}", "text": "" } api_base = ASR_CONFIG["api_base"] asr_model = model or ASR_CONFIG["model"] asr_language = language or ASR_CONFIG["language"] timeout = ASR_CONFIG["timeout"] api_endpoint = f"{api_base}/audio/transcriptions" try: with open(audio_path, "rb") as f: audio_data = f.read() files = { "file": (os.path.basename(audio_path), audio_data, "audio/wav") } data = { "model": asr_model, "language": asr_language, } async with httpx.AsyncClient(timeout=timeout) as client: response = await client.post(api_endpoint, files=files, data=data) if response.status_code == 200: result = response.json() text = result.get("text", "") return { "success": True, "text": text if text else "", "error": None } else: return { "success": False, "error": f"HTTP {response.status_code}: {response.text}", "text": "" } except Exception as e: return { "success": False, "error": str(e), "text": "" } @tool async def asr_audio_to_text( audio_path: str, model: Optional[str] = None, language: Optional[str] = None ) -> str: """ ASR语音转文本工具(异步版本) 将音频文件转换为文本内容 Args: audio_path: 音频文件路径(如:audio/test.wav) model: ASR模型名称(可选,默认从配置读取,如"base") language: 语言代码(可选,默认从配置读取,如"zh"表示中文) Returns: 识别出的文本内容 """ result = await asr_audio_to_text_async(audio_path, model, language) if not result.get("success", False): return f"语音识别失败: {result.get('error', '未知错误')}" return result.get("text", "") @tool async def extract_device_and_fault(query: str) -> Dict[str, Any]: """ 从用户查询中提取设备名称和故障现象(使用大模型) Args: query: 用户查询文本 Returns: 包含device和fault字段的字典 """ if not query or not query.strip(): return {"device": "", "fault": ""} prompt = f"""从以下用户查询中提取设备名称和故障现象: 查询:{query} 请以JSON格式返回: {{"device": "设备名称", "fault": "故障现象"}} 如果无法提取,请返回空字符串。""" try: result_text = await OpenaiAPI.open_api_chat_without_thinking( query=prompt, json_output=True ) result_text = result_text.strip() result_text = re.sub(r'^```json\s*', '', result_text, flags=re.IGNORECASE) result_text = re.sub(r'^```\s*', '', result_text) result_text = re.sub(r'```$', '', result_text) json_match = re.search(r'\{.*\}', result_text, re.DOTALL) if json_match: result = json.loads(json_match.group(0)) return { "device": result.get("device", ""), "fault": result.get("fault", "") } return {"device": "", "fault": ""} except Exception as e: print(f"提取设备和故障失败: {str(e)}") return {"device": "", "fault": ""} # 工具列表导出 ALL_TOOLS = [ detect_input_type, graph_rag_search, fault_graph_rag_search, operation_graph_rag_search, rag_search_tool, vlm_image_to_text, asr_audio_to_text, extract_device_and_fault, image_rag_describe, file_to_text_tool, fault_type_statistics_tool, fault_frequency_statistics_tool, spare_parts_statistics_tool ] # 按类别分组的工具 TOOL_CATEGORIES = { "input_detection": [detect_input_type], "rag": [graph_rag_search, rag_search_tool, fault_graph_rag_search, operation_graph_rag_search], "vlm": [vlm_image_to_text], "asr": [asr_audio_to_text], "extraction": [extract_device_and_fault], "image_rag": [image_rag_describe], "file_process": [file_to_text_tool], "statistics": [fault_type_statistics_tool, fault_frequency_statistics_tool, spare_parts_statistics_tool] }