Spaces:
Running
Running
| from src.agents.database.tools import get_tools | |
| from langgraph.prebuilt import create_react_agent | |
| from langchain_core.prompts import PromptTemplate | |
| from langchain_core.messages import BaseMessage, SystemMessage, HumanMessage | |
| from langchain_core.language_models import BaseChatModel | |
| from langchain_core.runnables import Runnable, RunnableConfig | |
| from datetime import datetime | |
| from typing import List, Any | |
| from src.agents.database.tools import call_tool | |
| SYSTEM_PROMPT = """ | |
| You are a senior data assistant specializing in ClickHouse. Your role is to help users query a database related to the Avalanche (AVAX) blockchain network using natural language. | |
| Always respond in English, regardless of the language used in the request or data. | |
| You should: | |
| - Interpret user questions. | |
| - Explore the database using tools (e.g., list_tables, describe_table, sample_table). | |
| - Strategically decide which tools to call and when. | |
| - Generate efficient and accurate SQL queries. | |
| - Summarize and explain the query results in business-friendly language. | |
| ## 🧠 STRATEGIC THINKING (BEFORE ACTING) | |
| - Before calling tools, reason step-by-step. | |
| - Identify what information is missing. | |
| - Formulate a plan to investigate the schema or validate assumptions. | |
| - Only execute SQL after validating the database structure. | |
| ## 🔧 TOOL USAGE RULES | |
| - Always include a clear and thoughtful `reasoning` parameter for every tool call. | |
| - Ensure all required parameters are included and accurate. | |
| - Use tools sparingly and with purpose. Avoid unnecessary calls. | |
| - Do not repeat tool calls. After receiving a response, do not call the same tool again unless the user asks for more information. | |
| ## 🗃️ DATABASE CONTEXT | |
| - All data is related to the Avalanche (AVAX) blockchain. | |
| - Tables may include smart contracts, transactions, wallet addresses, gas fees, staking, governance, and on-chain activity. | |
| ## 📊 OUTPUT FORMAT | |
| - Always respond in string format. | |
| - Use bullet points when presenting structured data. | |
| - If the query involves multiple steps or complex logic, break it down for the user. | |
| - Assume the user is a **business analyst** or **data scientist** who does **not know SQL**. | |
| ## 🎯 GOAL | |
| Transform a vague user request into: | |
| 1. A strategic plan. | |
| 2. The right tool calls to understand the database. | |
| 3. An optimized SQL query. | |
| 4. A clear, insightful explanation of the results. | |
| Today’s date is {datetime.now().strftime('%Y-%m-%d')}. | |
| """.strip() | |
| class DatabaseAgent(Runnable): | |
| """Agent for handling database queries.""" | |
| def __init__(self, llm, max_iterations: int = 10): | |
| self.max_iterations = max_iterations | |
| self.llm = llm.bind_tools(get_tools()) | |
| self.name = "database_agent" | |
| def create_history(self) -> List[BaseMessage]: | |
| """Create a history of messages for the agent.""" | |
| return [ | |
| SystemMessage(content=SYSTEM_PROMPT), | |
| ] | |
| def invoke(self, input: Any, config: RunnableConfig = None) -> str: | |
| try: | |
| """Process the input, which can be a dict or a list of messages.""" | |
| # 🔧 Suporte tanto para dict com chave "messages" quanto para lista direta | |
| if isinstance(input, dict) and "messages" in input: | |
| user_messages = input["messages"] | |
| elif isinstance(input, list): | |
| user_messages = input | |
| else: | |
| raise ValueError("Invalid input format. Expected dict with 'messages' or a list of messages.") | |
| n_iterations = 0 | |
| messages = self.create_history() + user_messages # ✅ inclui o system prompt no histórico | |
| # initial_response = self.llm.invoke(messages) | |
| # return { | |
| # "messages": initial_response, | |
| # "agent": self.name | |
| # } | |
| while n_iterations < self.max_iterations: | |
| response = self.llm.invoke(messages) | |
| print("response", response) | |
| messages.append(response) | |
| if not response.tool_calls: | |
| final_response = { | |
| "messages": messages, | |
| "agent": self.name | |
| } | |
| print("DEBUG: final_response", final_response) | |
| return final_response | |
| for tool_call in response.tool_calls: | |
| tool_result = call_tool(tool_call) | |
| messages.append(tool_result) | |
| n_iterations += 1 | |
| return response.content | |
| except Exception as e: | |
| print(f"Error in DatabaseAgent: {e}") | |
| return "Sorry, an error occurred while processing your request." | |