# LangGraph è un framework che consente di creare applicazioni pronte per la produzione offrendo strumenti di controllo sul flusso del proprio agente. # LangGraph è un framework sviluppato da LangChain per gestire il flusso di controllo delle applicazioni che integrano un LLM. # https://www.langchain.com/ # LangChain fornisce un'interfaccia standard per interagire con modelli e altri componenti, utile per il recupero, le chiamate LLM e le chiamate agli strumenti. # Le classi di LangChain possono essere utilizzate in LangGraph, ma non è obbligatorio. # Quando usare LangGraph? Quando si progettano applicazioni di intelligenza artificiale, ci si trova di fronte a un compromesso fondamentale tra controllo e libertà: # Gli agenti, come quelli che si possono trovare in smolagents, sono molto liberi. Possono richiamare più strumenti in un'unica fase di azione, creare i propri strumenti, ecc. # Tuttavia, questo comportamento può renderli meno prevedibili e meno controllabili di un normale agente che lavora con JSON! # LangGraph è all'altro estremo dello spettro, ed è utile quando hai bisogno di "controllo" sull'esecuzione del tuo agente. # In parole povere, se la tua applicazione prevede una serie di passaggi che devono essere orchestrati in un modo specifico, con decisioni prese a ogni punto di congiunzione, LangGraph fornisce la struttura di cui hai bisogno. # Come funziona LangGraph? # Nodes : I nodi rappresentano singole fasi di elaborazione (come la chiamata di un LLM, l'utilizzo di uno strumento o la presa di una decisione). # Edges : definiscono le possibili transizioni tra i passaggi. # State : È definito e gestito dall'utente e trasmesso tra i nodi durante l'esecuzione. Quando decidiamo quale nodo indirizzare successivamente, questo è lo stato attuale che prendiamo in considerazione. # Building Blocks of LangGraph # Per creare applicazioni con LangGraph, è necessario comprenderne i componenti principali. Esploriamo gli elementi fondamentali che compongono un'applicazione LangGraph. #Un'applicazione in LangGraph inizia da un punto di ingresso e, a seconda dell'esecuzione, il flusso può passare a una funzione o all'altra fino a raggiungere la FINE. # 1. State Lo stato è il concetto centrale di LangGraph. Rappresenta tutte le informazioni che fluiscono attraverso l'applicazione. # 2. Nodes I nodi sono funzioni Python. Ogni nodo: Accetta lo stato come input Esegue un'operazione Restituisce aggiornamenti allo stato # Ad esempio, i nodi possono contenere: Chiamate LLM: generare testo o prendere decisioni Chiamate strumenti: interagire con sistemi esterni Logica condizionale: determinare i passaggi successivi Intervento umano: ottenere input dagli utenti # 3. Edges collegano i nodi e definiscono i possibili percorsi attraverso il grafico # 4. StateGraph è il contenitore che contiene l'intero flusso di lavoro dell'agente: # Esempio: https://huggingface.co/agents-course/notebooks/blob/main/unit2/langgraph/mail_sorting.ipynb # pip install langgraph langchain_openai import datasets from langchain.docstore.document import Document from langchain_community.retrievers import BM25Retriever from langchain.tools import Tool from typing import TypedDict, Annotated from langgraph.graph.message import add_messages from langchain_core.messages import AnyMessage, HumanMessage, AIMessage from langgraph.prebuilt import ToolNode from langgraph.graph import START, StateGraph from langgraph.prebuilt import tools_condition #from langchain_huggingface import HuggingFaceEndpoint, ChatHuggingFace from langchain_huggingface import HuggingFaceEndpoint from langchain_community.tools import DuckDuckGoSearchRun import gradio as gr import os ####################################### ## Give Your Agent Access to the Web ## ####################################### search_tool = DuckDuckGoSearchRun() # results = search_tool.invoke("Who's the current President of France?") # print(results) ########################################## ## Step 1: Load and Prepare the Dataset ## ########################################## # Per prima cosa, dobbiamo trasformare i dati grezzi dei nostri ospiti in un formato ottimizzato per il recupero. # Utilizzeremo la libreria di set di dati Hugging Face per caricare il set di dati e convertirlo in un elenco di oggetti Document dal modulo langchain.docstore.document. # Load the dataset guest_dataset = datasets.load_dataset("agents-course/unit3-invitees", split="train") # Convert dataset entries into Document objects docs = [ Document( page_content="\n".join([ f"Name: {guest['name']}", f"Relation: {guest['relation']}", f"Description: {guest['description']}", f"Email: {guest['email']}" ]), metadata={"name": guest["name"]} ) for guest in guest_dataset ] # Nel codice sopra: Carichiamo il dataset Convertiamo ogni voce ospite in un oggetto Documento con contenuto formattato Memorizziamo gli oggetti Documento in un elenco # Ciò significa che tutti i nostri dati sono prontamente disponibili e possiamo iniziare a configurare il recupero. ########################################## ## Step 2: Create the Retriever Tool ## ########################################## # Ora creiamo uno strumento personalizzato che Alfred potrà utilizzare per cercare le informazioni sui nostri ospiti. # Utilizzeremo BM25Retriever dal modulo langchain_community.retrievers per creare uno strumento di recupero. # BM25Retriever è un ottimo punto di partenza per il recupero, ma per una ricerca semantica più avanzata, potresti prendere in considerazione l'utilizzo di retriever basati sull'incorporamento come quelli di sentence-transformers. bm25_retriever = BM25Retriever.from_documents(docs) def extract_text(query: str) -> str: """Retrieves detailed information about gala guests based on their name or relation.""" results = bm25_retriever.invoke(query) if results: return "\n\n".join([doc.page_content for doc in results[:3]]) else: return "No matching guest information found." guest_info_tool = Tool( name="guest_info_retriever", func=extract_text, description="Retrieves detailed information about gala guests based on their name or relation." ) # Analizziamo questo strumento passo dopo passo. # Il nome e la descrizione aiutano l'agente a capire quando e come utilizzare questo strumento. # I decoratori di tipo definiscono i parametri che lo strumento si aspetta (in questo caso, una query di ricerca). # Utilizziamo BM25Retriever, un potente algoritmo di recupero del testo che non richiede incorporamenti. # Il metodo elabora la query e restituisce le informazioni più rilevanti sull'ospite. ############################################ ## Step 2: Integrate the Tool with Alfred ## ############################################ # Infine, mettiamo insieme il tutto creando il nostro agente e dotandolo del nostro strumento personalizzato: # Generate the chat interface, including the tools llm = HuggingFaceEndpoint( repo_id="Qwen/Qwen2.5-Coder-32B-Instruct", huggingfacehub_api_token=os.getenv("HF_TOKEN"), ) #chat = ChatHuggingFace(llm=llm, verbose=True) #tools = [guest_info_tool,search_tool] tools = [search_tool] #chat_with_tools = chat.bind_tools(tools) # Generate the AgentState and Agent graph class AgentState(TypedDict): messages: Annotated[list[AnyMessage], add_messages] # def assistant(state: AgentState): # return { # "messages": [chat_with_tools.invoke(state["messages"])], # } def assistant(state: AgentState): # Prendi l'ultimo messaggio umano human_msg = [msg for msg in state["messages"] if isinstance(msg, HumanMessage)][-1] # Chiama il modello LLM direttamente con il testo response_text = llm.invoke(human_msg.content) # Crea una risposta AIMessage ai_message = HumanMessage(content=response_text) # o AIMessage se preferisci return { "messages": state["messages"] + [ai_message] } ## The graph builder = StateGraph(AgentState) # Define nodes: these do the work builder.add_node("assistant", assistant) builder.add_node("tools", ToolNode(tools)) # Define edges: these determine how the control flow moves builder.add_edge(START, "assistant") builder.add_conditional_edges( "assistant", # If the latest message requires a tool, route to tools # Otherwise, provide a direct response tools_condition, ) builder.add_edge("tools", "assistant") alfred = builder.compile() # Funzione Gradio def run_agent(input_text): try: response = alfred.invoke({"messages": input_text}) return response['messages'][-1].content except Exception as e: return f"Errore: {str(e)}" iface = gr.Interface( fn=run_agent, inputs="text", outputs="text", title="Wikipedia AI Agent", description="Scrivi un argomento, l'agente recupera un riassunto da Wikipedia e lo spiega usando un LLM." ) iface.launch() # messages = [HumanMessage(content="Tell me about our guest named 'Lady Ada Lovelace'.")] # response = alfred.invoke({"messages": messages}) # print("🎩 Alfred's Response:") # print(response['messages'][-1].content)