ColettoG's picture
fix: only english
af914d1
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."