Luigi D'Addona
modificate istruzioni generali dell'agent
cd0253f
import os
from dotenv import load_dotenv
import traceback
import time
import pprint
from typing import Annotated,Sequence, TypedDict
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph.message import add_messages # helper function to add messages to the state
from langchain_core.messages import ToolMessage
from langchain_core.runnables import RunnableConfig
from langgraph.graph import StateGraph, END
from langchain_google_genai import ChatGoogleGenerativeAI
# Local imports
from tools import get_search_tool, get_tavily_search_tool, get_wikipedia_tool, wikipedia_search, wikipedia_search_3,\
execute_python_code_from_file, download_taskid_file, analyze_excel_file, get_analyze_mp3_tool,\
get_analyze_image_tool, arxiv_search, get_youtube_transcript
# Nota: per i test in locale si usa il .env
# su HuggingFace invece si usano le variabili definite in Settings/"Variables and secrets"
load_dotenv()
GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
GEMINI_MODEL = os.environ.get("GEMINI_MODEL")
GEMINI_BASE_URL = os.environ.get("GEMINI_BASE_URL")
GEMINI_TEMPERATURE = float(os.environ.get("GEMINI_TEMPERATURE"))
TOOLS_CALL_DELAY = 1.5
# V1
# GENERAL_AGENT_INSTRUCTIONS = """You are a helpful assistant tasked with answering questions using a set of tools.
# Now, I will ask you a question. Analyze the question and provide your answer.
# Your answer should be a number OR as few words as possible OR a comma separated list of numbers and/or strings.
# If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise.
# If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise.
# If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
# Provide only the answer, without notes, explanations or comments."""
# V2
# GENERAL_AGENT_INSTRUCTIONS = """
# You are a general AI assistant. Your purpose is to answer questions and complete tasks accurately and concisely.
# You have access to various tools to help you gather information and perform actions.
# Always prioritize using your tools to find factual information if a question requires it.
# If a question can be answered directly from your knowledge, do so.
# If you use a tool, provide only the direct result or answer based on the tool's output.
# Do not include any conversational filler, explanations of your thought process, or pleasantries unless specifically asked.
# """
# V3
# GENERAL_AGENT_INSTRUCTIONS = """
# You are a general AI assistant. Your purpose is to answer questions and complete tasks accurately and concisely.
# You have access to various tools to help you gather information and perform actions.
# Always prioritize using your tools to find factual information if a question requires it.
# Your answer should be a number OR as few words as possible OR a comma separated list of numbers and/or strings.
# If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise.
# If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise.
# If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
# Provide only the answer, without notes, explanations or comments.
# To complete this task successfully, follow these steps carefully:
# 1. Comprehend the task and identify the intended goal.
# 2. Break the task into clear, logical steps.
# 3. Select and prepare the tools or resources you need.
# 4. Revise your plan if necessary based on feedback.
# 5. Maintain internal state and track progress.
# 6. Verify that the goal has been fully achieved.
# 7. Present the final result clearly and concisely."""
# V4
# GENERAL_AGENT_INSTRUCTIONS = """
# You are a general AI assistant. Your purpose is to answer questions and complete tasks accurately and concisely.
# You have access to various tools to help you gather information and perform actions.
# Always prioritize using your tools to find factual information if a question requires it.
# Fo instance, if the question mentions Wikpedia, use the wikpedia tool.
# Your answer should be a number OR as few words as possible OR a comma separated list of numbers and/or strings.
# If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise.
# If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise.
# If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
# Provide only the answer, without notes, explanations or comments."""
# V5
GENERAL_AGENT_INSTRUCTIONS = """
You are a general AI assistant. Your purpose is to answer questions and complete tasks accurately and concisely.
You have access to various tools to help you gather information and perform actions.
Always prioritize using your tools to find factual information if a question requires it.
If the question mentions Wikpedia, use the wikpedia tool; if the question mentions a youtube url, use get_youtube_transcript tool.
Analyze the question and plan the necessary steps to get the answer.
Your answer should be a number OR as few words as possible OR a comma separated list of numbers and/or strings.
If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise.
If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise.
If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
Provide only the answer, without notes, explanations or comments."""
#
# Inizializza il modello e gli associa i tool
#
# ChatGoogleGenerativeAI è il package ufficiale di LangChain per interagire con i modelli Gemini
# https://python.langchain.com/docs/integrations/chat/google_generative_ai/
chat = ChatGoogleGenerativeAI(
model=GEMINI_MODEL,
google_api_key=GEMINI_API_KEY,
temperature = GEMINI_TEMPERATURE)
# Imposta i tool
#search_tool = get_search_tool()
search_tool = get_tavily_search_tool()
#wikipedia_tool = get_wikipedia_tool()
analyze_mp3_tool = get_analyze_mp3_tool(chat)
analyze_png_tool = get_analyze_image_tool(chat)
tools = [search_tool,
wikipedia_search_3,
execute_python_code_from_file,
download_taskid_file,
analyze_excel_file,
analyze_mp3_tool,
analyze_png_tool,
arxiv_search,
get_youtube_transcript]
# Bind tools to the model
chat_with_tools = chat.bind_tools(tools)
tools_by_name = {tool.name: tool for tool in tools}
# debug
print("Tools:")
for tool in tools:
print(" {}".format(tool.name))
#
# Definisce il grafo
#
class AgentState(TypedDict):
"""The state of the agent."""
messages: Annotated[Sequence[BaseMessage], add_messages]
number_of_steps: int
# Define our tool node
def call_tool(state: AgentState):
outputs = []
# Iterate over the tool calls in the last message
for i, tool_call in enumerate(state["messages"][-1].tool_calls):
# Get the tool by name
tool_result = tools_by_name[tool_call["name"]].invoke(tool_call["args"])
# print(f"\n--- DEBUG: Tool Raw Output (Length: {len(tool_result)} chars) ---")
# print(tool_result)
# print("------------------------------------------------------------------\n")
outputs.append(
ToolMessage(
content=tool_result,
name=tool_call["name"],
tool_call_id=tool_call["id"],
)
)
# Add a delay after each tool call, but not after the very last one
if i < len(state["messages"][-1].tool_calls) - 1:
time.sleep(TOOLS_CALL_DELAY)
return {"messages": outputs}
def call_model( state: AgentState, config: RunnableConfig):
# Modo 1)
# Invoke the model with the system prompt and the messages
#response = chat_with_tools.invoke(state["messages"], config)
# Modo 2) - aggiunge in fondo alcune istruzioni
# Create a copy to avoid modifying the original state and append instruction to the end
# messages = state["messages"][:]
# messages.append(
# HumanMessage(content="Provide only the answer, without explanations or comments.")
# ) # Append instruction to the end
# response = chat_with_tools.invoke(messages, config)
# Modo 3)
# Create a new list for messages to send to the LLM
# Start with the general instructions
messages_to_send = [HumanMessage(content=GENERAL_AGENT_INSTRUCTIONS)]
# Append all existing messages from the agent state
messages_to_send.extend(state["messages"])
response = chat_with_tools.invoke(messages_to_send)
# We return a list, because this will get added to the existing messages state using the add_messages reducer
return {"messages": [response]}
# Define the conditional edge that determines whether to continue or not
def should_continue(state: AgentState):
messages = state["messages"]
# If the last message is not a tool call, then we finish
if not messages[-1].tool_calls:
return "end"
# default to continue
return "continue"
def get_agent():
# Creazione del grafo
workflow = StateGraph(AgentState)
# 1. Add our nodes
workflow.add_node("llm", call_model)
workflow.add_node("tools", call_tool)
# 2. Set the entrypoint as `agent`, this is the first node called
workflow.set_entry_point("llm")
# 3. Add a conditional edge after the `llm` node is called.
workflow.add_conditional_edges(
# Edge is used after the `llm` node is called.
"llm",
# The function that will determine which node is called next.
should_continue,
# Mapping for where to go next, keys are strings from the function return, and the values are other nodes.
# END is a special node marking that the graph is finish.
{
# If `tools`, then we call the tool node.
"continue": "tools",
# Otherwise we finish.
"end": END,
},
)
# 4. Add a normal edge after `tools` is called, `llm` node is called next.
workflow.add_edge("tools", "llm")
# 5. Now we can compile our graph
react_graph = workflow.compile()
return react_graph
# Riferimenti
#
# https://ai.google.dev/gemini-api/docs/langgraph-example