ChatbotRAG / tools_service.py
minhvtt's picture
Upload 14 files
91fe002 verified
raw
history blame
6.39 kB
"""
Tools Service for LLM Function Calling
HuggingFace-compatible với prompt engineering
"""
import httpx
from typing import List, Dict, Any, Optional
import json
import asyncio
class ToolsService:
"""
Manages external API tools that LLM can call via prompt engineering
"""
def __init__(self, base_url: str = "https://www.festavenue.site"):
self.base_url = base_url
self.client = httpx.AsyncClient(timeout=10.0)
def get_tools_prompt(self) -> str:
"""
Return prompt instruction for HuggingFace LLM về available tools
"""
return """
AVAILABLE TOOLS:
Bạn có thể sử dụng các công cụ sau để lấy thông tin chi tiết:
1. get_event_details(event_code: str)
- Mô tả: Lấy thông tin đầy đủ về một sự kiện từ hệ thống
- Khi nào dùng: Khi user hỏi về ngày giờ chính xác, địa điểm cụ thể, thông tin liên hệ, hoặc chi tiết khác về một sự kiện
- Tham số: event_code (mã sự kiện, ví dụ: "Y-CONCERT", "EVT001")
- Ví dụ: User hỏi "Ngày tổ chức Y-CONCERT là khi nào?" → Dùng get_event_details("Y-CONCERT")
CÚ PHÁP GỌI TOOL:
Khi bạn cần gọi tool, hãy trả lời CHÍNH XÁC theo format JSON này:
```json
{
"tool_call": true,
"function_name": "get_event_details",
"arguments": {
"event_code": "Y-CONCERT"
},
"reason": "Cần lấy thông tin chính xác về ngày giờ tổ chức"
}
```
QUAN TRỌNG:
- CHỈ trả JSON khi BẮT BUỘC cần gọi tool
- Nếu có thể trả lời từ context sẵn có, đừng gọi tool
- Sau khi nhận kết quả từ tool, hãy trả lời user bằng ngôn ngữ tự nhiên
"""
async def parse_and_execute(self, llm_response: str) -> Optional[Dict[str, Any]]:
"""
Parse LLM response và execute tool nếu có
Returns:
None nếu không có tool call
Dict với tool result nếu có tool call
"""
# Try to extract JSON from response
try:
# Tìm JSON block trong response
if "```json" in llm_response:
json_start = llm_response.find("```json") + 7
json_end = llm_response.find("```", json_start)
json_str = llm_response[json_start:json_end].strip()
elif "{" in llm_response and "}" in llm_response:
# Fallback: tìm JSON object đầu tiên
json_start = llm_response.find("{")
json_end = llm_response.rfind("}") + 1
json_str = llm_response[json_start:json_end]
else:
return None
tool_call = json.loads(json_str)
# Validate tool call structure
if not tool_call.get("tool_call"):
return None
function_name = tool_call.get("function_name")
arguments = tool_call.get("arguments", {})
# Execute tool
if function_name == "get_event_details":
result = await self._get_event_details(arguments.get("event_code"))
return {
"function": function_name,
"arguments": arguments,
"result": result
}
else:
return {
"function": function_name,
"arguments": arguments,
"result": {"success": False, "error": f"Unknown function: {function_name}"}
}
except (json.JSONDecodeError, KeyError, ValueError) as e:
# Không phải tool call, response bình thường
return None
async def _get_event_details(self, event_code: str) -> Dict[str, Any]:
"""
Call getEventByEventCode API
"""
try:
response = await self.client.get(
f"{self.base_url}/event/get-event-by-event-code",
params={"eventCode": event_code}
)
response.raise_for_status()
data = response.json()
# Extract relevant fields
event = data.get("data", {})
if not event:
return {
"success": False,
"error": "Event not found",
"message": f"Không tìm thấy sự kiện với mã {event_code}"
}
return {
"success": True,
"event_code": event.get("eventCode"),
"event_name": event.get("eventName"),
"description": event.get("description"),
"short_description": event.get("shortDescription"),
"start_time": event.get("startTimeEventTime"),
"end_time": event.get("endTimeEventTime"),
"start_sale": event.get("startTicketSaleTime"),
"end_sale": event.get("endTicketSaleTime"),
"location": {
"address": event.get("location", {}).get("address"),
"city": event.get("location", {}).get("city"),
},
"contact": {
"email": event.get("publicContactEmail"),
"phone": event.get("publicContactPhone"),
"website": event.get("website")
},
"capacity": event.get("capacity"),
"hashtags": event.get("hashtags", [])
}
except httpx.HTTPStatusError as e:
return {
"success": False,
"error": f"HTTP {e.response.status_code}",
"message": f"API trả về lỗi khi truy vấn sự kiện {event_code}"
}
except Exception as e:
return {
"success": False,
"error": str(e),
"message": "Không thể kết nối đến API để lấy thông tin sự kiện"
}
async def close(self):
"""Close HTTP client"""
await self.client.aclose()