254 lines
6.7 KiB
Python
254 lines
6.7 KiB
Python
"""
|
||
工具函数定义
|
||
包含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]
|
||
}
|