Un agente non è una catena di prompt con qualche chiamata HTTP in mezzo. In una catena l’ordine dei passi lo decidi tu quando scrivi il codice; in un agente lo decide il modello a ogni turno, sulla base di tre input che gli hai preparato: un ruolo scritto nelle istruzioni, un insieme di strumenti descritti in modo che si capisca quando servono, e un contesto che sopravvive da un turno all’altro. Progettare un agente vuol dire curare quei tre input, perché sono l’unica leva reale che hai sul suo comportamento.

Ruolo, obiettivo e forma dell’agente

Foundry Agent Service offre due tipi di agente. Il prompt agent si definisce per configurazione — istruzioni, modello, strumenti — e lo esegue Foundry: nessun codice di runtime da mantenere, nessun container da scalare. L’hosted agent è codice tuo, scritto con Microsoft Agent Framework, LangGraph, l’OpenAI Agents SDK o a mano, consegnato come container o archivio dei sorgenti; Foundry gli dà endpoint gestito, autoscaling, identità Microsoft Entra dedicata per agente e osservabilità. C’è poi una terza via: chiamare direttamente la Responses API dal proprio codice, ottenendo un agente effimero la cui definizione vive nell’applicazione invece che come risorsa persistita nel progetto.

Il criterio di scelta è quanto codice vuoi possedere: prompt agent per agenti di produzione senza orchestrazione custom, hosted agent quando l’agente deve chiamare logica tua o parlare protocolli particolari, agente effimero quando la definizione deve essere versionata insieme all’applicazione.

Il ruolo si scrive nelle istruzioni e deve dire tre cose: chi è l’agente, qual è l’obiettivo, cosa fare quando qualcosa va storto. Se hai strumenti che si sovrappongono, aggiungi una regola di decisione esplicita, per esempio «per i contenuti interni usa prima file search e solo dopo la ricerca web». Un dettaglio che cambia rispetto all’API classica: gli agenti sono identificati da nome più versione, non più da un GUID. Ogni modifica genera uno snapshot e la pubblicazione espone un endpoint stabile, indipendente dalla versione attiva.

Lo schema dello strumento è il contratto con il modello

Il modello non vede il tuo codice: vede nome, descrizione e schema JSON dei parametri. Uno strumento che non viene mai chiamato ha quasi sempre una descrizione vaga, non un bug.

from azure.ai.projects.models import FunctionTool

func_tool = FunctionTool(
    name="get_order_status",
    description="Restituisce lo stato di un ordine dato il suo codice.",
    parameters={
        "type": "object",
        "properties": {
            "order_id": {
                "type": "string",
                "description": "Codice ordine, per esempio ORD-10432",
            }
        },
        "required": ["order_id"],
        "additionalProperties": False,
    },
    strict=True,
)

Il ciclo è a due tempi e sei tu a doverlo chiudere. La prima risposta contiene un item con type uguale a function_call, con name, call_id e arguments (una stringa JSON); la tua applicazione esegue la funzione e invia una seconda richiesta con un item function_call_output che riporta lo stesso call_id. Se quell’output non torna indietro, l’agente resta fermo alla chiamata e non produce mai la risposta finale. I run scadono dieci minuti dopo la creazione, quindi per operazioni lente restituisci subito uno stato e fai polling.

Quando la scelta deve essere deterministica c’è tool_choice: auto lascia decidere al modello, required obbliga a chiamare almeno uno strumento, none lo vieta. Errori tipici: argomenti non validi significa schema con tipi sbagliati, parametri sbagliati significa descrizione ambigua, «tool not supported» significa che lo strumento richiede il supporto sia del modello sia della regione.

Come si tiene traccia della conversazione

Il runtime ha tre componenti: agents, conversations e responses. L’agente fornisce modello, istruzioni e strumenti riutilizzabili; la conversazione persiste la storia lato servizio; la response è l’unità di esecuzione che produce output. Questo modello sostituisce la coppia thread/run dell’API Assistants classica.

Una conversazione non contiene solo messaggi ma items: messaggi, chiamate a strumenti, output degli strumenti, output finali. Per questo serve al debug quanto alla continuità. I modi di portare avanti il contesto sono due: passare l’id della conversazione, oppure incatenare le risposte con previous_response_id. Se imposti store a false il servizio non persiste nulla e il contesto lo trasporti tu, rimandando gli item precedenti come input: è la scelta giusta in scenari a zero data retention. Dettaglio da ricordare: se la conversazione supera la finestra di contesto del modello viene troncato l’input usato per generare la risposta, non la conversazione, che resta intera.

Memoria e conoscenza recuperata non sono la stessa cosa

La storia della conversazione è memoria di sessione e dura quanto la sessione. La memoria di Foundry Agent Service (preview) è memoria a lungo termine gestita: lavora in tre fasi, extraction dalle conversazioni, consolidation per fondere duplicati e risolvere conflitti, retrieval al momento giusto; distingue tre tipi, user profile (preferenze durevoli, da recuperare a inizio conversazione), chat summary (sintesi dei temi precedenti, da recuperare a ogni turno) e procedural (routine ricorrenti). Si usa col memory search tool su un prompt agent, o con le API del memory store quando servono CRUD sui singoli item, scope e TTL. Il recupero di conoscenza è un’altra cosa: file search su un vector store, un indice Azure AI Search collegato per rispondere con citazioni, una knowledge base di Foundry IQ permission-aware.

Regola pratica: la memoria conserva quello che l’agente sa di quell’utente, il retrieval recupera quello che dice la documentazione aziendale. Avvertenza di sicurezza: le memorie vengono estratte da testo scritto dall’utente, quindi sono un bersaglio di prompt injection e memory corruption e vanno validate, non trattate come dati fidati.

Da ricordare per l’esame

  • Prompt agent, hosted agent e agente effimero via Responses API sono tre punti sullo stesso spettro: la discriminante è quanto codice di runtime mantieni.
  • Il modello sceglie lo strumento leggendo nome, descrizione e schema dei parametri: la descrizione è una scelta di progettazione, non cosmetica.
  • Il loop del function calling si chiude rimandando un function_call_output con lo stesso call_id, altrimenti la risposta finale non arriva mai.
  • Agents, conversations e responses sostituiscono thread e run; il contesto si porta avanti con la conversazione oppure con previous_response_id.
  • Memoria a lungo termine e retrieval risolvono problemi diversi e si configurano separatamente.