wx-agent/tools/function_tool.py
2026-06-30 13:47:51 +08:00

254 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
工具函数定义
包含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]
}