Introduzione
Il 2025 è l’anno in cui si parla sempre più spesso di AI Agent, cioè sistemi basati su modelli linguistici (LLM) come ChatGPT che non si limitano a rispondere a domande, ma ragionano, decidono e agiscono in autonomia.
Un Agent non è un semplice chatbot: è un’intelligenza artificiale dotata di obiettivi, memoria e strumenti. Questo significa che può:
analizzare informazioni,
scegliere un’azione,
utilizzare API esterne o database,
verificare i risultati,
ripetere il ciclo finché non raggiunge l’obiettivo.
In questo articolo vedremo in dettaglio:
cosa sono i ChatGPT Agent,
come funzionano tecnicamente,
quali framework usare,
esempi pratici in Python e C#,
vantaggi e limiti per PMI e professionisti.
Cosa sono i ChatGPT Agent
Un ChatGPT Agent è un sistema costruito su un modello di linguaggio (LLM) che ha 3 caratteristiche chiave:
Obiettivo: viene definito uno scopo (es. “analizza queste email e segnala quelle sospette”).
Strumenti (Tools): l’Agent può usare API, database, funzioni custom.
Memoria: può ricordare interazioni passate e migliorare il contesto.
Obiettivo → Reasoning → Azione → Feedback → Nuovo ciclo
Architettura tecnica di un Agent
Componenti principali
Core LLM: ChatGPT (GPT-4.1, GPT-5), o modelli open source (LLaMA 3, Mistral).
Tool/Plugin: collegamenti a API esterne (es. Gmail API, SQL Server, Zapier).
Memoria:
Short-term: mantiene il contesto della conversazione.
Long-term: database (SQLite, Redis, vector DB con embedding).
Orchestratore: gestisce il ciclo reasoning/azione → esempi: LangChain, Semantic Kernel, AutoGen.
Come funzionano in pratica
Un Agent segue un ciclo continuo:
Riceve un obiettivo.
Ragiona sui passi da compiere.
Usa strumenti o API.
Verifica l’output.
Decide se l’obiettivo è stato raggiunto o ripete il ciclo.
Esempio pratico:
Un’azienda imposta un Agent che:
legge email in arrivo,
usa un classificatore AI per filtrare phishing,
salva i risultati in un database,
invia notifiche agli utenti.
Framework principali nel 2025
LangChain (Python/JS)
Libreria più diffusa.
Permette di definire “Chain” di operazioni e collegare tool/memoria.
Semantic Kernel (C# / .NET)
Framework Microsoft, perfetto se lavori già con .NET.
Ottima integrazione con Azure OpenAI e servizi aziendali.
AutoGen (Microsoft Research)
Specializzato in multi-agent collaboration: più agenti che collaborano tra loro.
OpenAI Assistants API
Integrazione diretta con ChatGPT, gestione tool, file e memoria.
Utile per sviluppi rapidi senza framework esterni.
Un esempio pratico: Support Triage Agent
Per rendere il concetto concreto, costruiamo un Support Triage Agent.
Obiettivo: ricevere un messaggio di un cliente → capire di cosa si tratta, decidere la priorità, usare tool esterni (API, DB) e rispondere in italiano in modo professionale.
Funzionalità dell’Agent
Classificazione intent/priority (reset password, ordine, bug, generico).
Uso tool REST per recuperare stato ordini.
Creazione ticket in DB se si tratta di bug.
Generazione risposta (oggetto + corpo email).
Audit logging delle operazioni.
Esempio in Python
# --- Support Triage Agent (Python) ---
# Features:
# - Classifica intent/priority da messaggi clienti (schema Pydantic)
# - Usa tool esterni (REST mock: stato ordine; DB: audit/ticket)
# - Genera risposta in italiano (oggetto + corpo)
# - Guard-rails per evitare ID inventati; retry su I/O
# --------------------------------------------------------------
import json
import os
from typing import Literal, Optional
import psycopg2
import requests
from pydantic import BaseModel, Field
from tenacity import retry, stop_after_attempt, wait_exponential
# LLM (LangChain + OpenAI)
from langchain.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
# --- CONFIG ---
# Imposta la tua API key prima di eseguire
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
# --- (FACOLTATIVO) DB: sostituisci con il tuo driver preferito ---
# Per demo uso Postgres; puoi adattare a SQL Server / MySQL
DB_DSN = "host=localhost port=5432 dbname=support user=support_user password=***"
# ---------- DOMAIN MODELS ----------
class TriageDecision(BaseModel):
intent: Literal["reset_password", "order_issue", "billing", "bug", "generic"]
priority: Literal["low", "medium", "high", "urgent"]
order_id: Optional[str] = None
needs_handoff: bool = False
summary_it: str = Field(..., description="Riepilogo breve in italiano")
# ---------- TOOLS ----------
def get_order_status(order_id: str) -> str:
"""Mock REST: stato ordine. Sostituisci con la tua API."""
# es: requests.get(f"https://api.example.com/orders/{order_id}", timeout=10).json()
data = {
"orderId": order_id,
"status": "In transito",
"eta": "2 giorni lavorativi",
"carrier": "GLS",
"tracking": "GLS123456",
}
return json.dumps(data, ensure_ascii=False)
def send_response(to: str, subject: str, body: str) -> str:
"""Stub: invio/accodamento risposta cliente."""
# Integra con SMTP/SendGrid/Queue
return f"QUEUED to={to} subject={subject}"
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=0.5, max=4))
def audit_log(event: str) -> str:
"""Audit append-only (idempotente a livello applicativo)."""
with psycopg2.connect(DB_DSN) as conn, conn.cursor() as cur:
cur.execute("INSERT INTO audit_logs(event) VALUES (%s) RETURNING id;", (event,))
new_id = cur.fetchone()[0]
return f"audit_log_id={new_id}"
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=0.5, max=4))
def log_ticket(payload_json: str) -> str:
"""Registra ticket (per bug/escalation)."""
with psycopg2.connect(DB_DSN) as conn, conn.cursor() as cur:
cur.execute(
"INSERT INTO tickets(summary, payload_json) VALUES (%s, %s) RETURNING id;",
("Bug segnalato dal cliente", payload_json),
)
new_id = cur.fetchone()[0]
return f"ticket_id={new_id}"
# ---------- LLM & PROMPTS ----------
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.2)
TRIAGE_PROMPT = PromptTemplate.from_template(
"""
Sei un sistema di triage. Classifica il messaggio secondo questo schema JSON (Pydantic):
{schema}
Regole:
- Non inventare order_id: se non presente o non plausibile → null.
- intent: reset_password | order_issue | billing | bug | generic.
- priority: urgent (sicurezza/perdita dati), high (blocco produzione), altrimenti medium/low.
- summary_it: breve riassunto in italiano.
Messaggio utente:
{message}
Rispondi SOLO con JSON valido.
"""
)
def classify_message(message: str) -> TriageDecision:
schema = TriageDecision.schema_json(indent=2)
prompt = TRIAGE_PROMPT.format(schema=schema, message=message)
raw = llm.invoke(prompt).content
# Estrazione JSON robusta
i, j = raw.find("{"), raw.rfind("}")
parsed = json.loads(raw[i : j + 1])
return TriageDecision(**parsed)
REPLY_PROMPT = PromptTemplate.from_template(
"""
Sei un assistente clienti. Scrivi una risposta in italiano, cortese e concisa.
Vincoli:
- Tono professionale.
- Se 'order_info' è presente: includi stato, ETA e tracking.
- Se intent=bug: chiedi 3 informazioni tecniche (passi, screenshot, orario) e comunica apertura ticket.
- Chiudi con: "Resto a disposizione." e firma "Intes Informatica".
Dati:
intent={intent}
priority={priority}
order_info={order_info}
messaggio_cliente="""
{user_message}
"""
Scrivi:
- Oggetto email (max 78 caratteri)
- Corpo email (anche con elenco puntato)
"""
)
def draft_reply(
decision: TriageDecision, user_message: str, order_info_json: Optional[str]
) -> dict:
out = llm.invoke(
REPLY_PROMPT.format(
intent=decision.intent,
priority=decision.priority,
order_info=order_info_json or "null",
user_message=user_message,
)
).content
subject = "Richiesta di assistenza"
body = out
for line in out.splitlines():
if line.lower().startswith("oggetto"):
subject = line.split(":", 1)[-1].strip()
body = out.replace(line, "").strip()
break
return {"subject": subject, "body": body}
# ---------- ORCHESTRAZIONE AGENT ----------
def run_agent(user_email: str, user_message: str) -> dict:
# 1) Classificazione
decision = classify_message(user_message)
# 2) Tool opzionali
order_info_json = None
if decision.intent == "order_issue" and decision.order_id:
order_info_json = get_order_status(decision.order_id)
ticket_ref = None
if decision.intent == "bug":
ticket_ref = log_ticket(
json.dumps(
{"summary": decision.summary_it, "raw_message": user_message},
ensure_ascii=False,
)
)
# 3) Bozza risposta
reply = draft_reply(decision, user_message, order_info_json)
# 4) Invio (stub) + audit
send_ref = send_response(user_email, reply["subject"], reply["body"])
audit_ref = audit_log(
f"triage={decision.model_dump_json()} send={send_ref} ticket={ticket_ref}"
)
return {
"decision": decision.model_dump(),
"order_info": json.loads(order_info_json) if order_info_json else None,
"ticket_ref": ticket_ref,
"send_ref": send_ref,
"audit_ref": audit_ref,
"reply": reply,
}
# ---------- ESEMPIO D'USO ----------
if __name__ == "__main__":
# Esempio: issue su ordine con priorità alta
message = (
"Ciao, il mio ordine ORD-9127 è fermo da una settimana. "
"Mi serve entro venerdì, potete verificare lo stato? È urgente."
)
result = run_agent("cliente@example.com", message)
print(json.dumps(result, ensure_ascii=False, indent=2))
Esempio in C# (Semantic Kernel + Azure OpenAI)
Package:
dotnet add package Microsoft.SemanticKernel
dotnet add package Microsoft.SemanticKernel.Connectors.OpenAI
dotnet add package Dapper
dotnet add package System.Data.SqlClient
using System;
using System.Data.SqlClient;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Dapper;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
namespace SupportAgent
{
// --- Strongly-typed DTOs ---
public record TriageDecision(
string intent, // reset_password | order_issue | billing | bug | generic
string priority, // low | medium | high | urgent
string? order_id,
bool needs_handoff,
string summary_it
);
public static class Tools
{
static readonly HttpClient http = new HttpClient();
// Call to external REST for order status (mock)
public static async Task GetOrderStatusAsync(string orderId)
{
// In real life: var resp = await http.GetAsync($"https://api.example.com/orders/{orderId}");
var payload = new
{
orderId,
status = "In transito",
eta = "2 giorni lavorativi",
carrier = "GLS",
tracking = "GLS123456"
};
return JsonSerializer.Serialize(payload);
}
public static async Task SendResponseAsync(string to, string subject, string body)
{
// Stub: enqueue to your MTA or queue
await Task.Delay(5);
return $"QUEUED: to={to} subject={subject}";
}
public static async Task AuditLogAsync(string eventText, string connStr)
{
using var conn = new SqlConnection(connStr);
var id = await conn.ExecuteScalarAsync(
"INSERT INTO audit_logs(event) OUTPUT INSERTED.id VALUES (@event)",
new { @event = eventText }
);
return $"audit_log_id={id}";
}
public static async Task LogTicketAsync(string payloadJson, string connStr)
{
using var conn = new SqlConnection(connStr);
var id = await conn.ExecuteScalarAsync(
"INSERT INTO tickets(summary, payload_json) OUTPUT INSERTED.id VALUES (@s, @p)",
new { s = "Bug segnalato dal cliente", p = payloadJson }
);
return $"ticket_id={id}";
}
}
class Program
{
// ---- Configure Azure OpenAI (recommended) ----
// Set env: AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, AZURE_OPENAI_DEPLOYMENT
static async Task Main()
{
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!;
var apiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY")!;
var deployment = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT")!; // e.g., "gpt-4o"
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(deployment, endpoint, apiKey);
var kernel = builder.Build();
var chat = kernel.GetRequiredService();
// --- System prompt with guard-rails ---
var system = """
You are a support triage system. Output must be compact, JSON-only for classification step.
Guard-rails:
- Never invent order IDs; if not present, use null.
- intent: reset_password | order_issue | billing | bug | generic
- priority: urgent if security/data loss; high if prod blocking; else medium/low.
- Provide Italian 'summary_it'.
""";
var userMessage = "Buongiorno, l'ordine ORD-9127 risulta fermo. Mi serve entro venerdì, potete aiutarmi?";
var triageJson = await ClassifyAsync(chat, system, userMessage);
var decision = JsonSerializer.Deserialize(triageJson)!;
string? orderInfo = null;
string? ticketRef = null;
if (decision.intent == "order_issue" && !string.IsNullOrWhiteSpace(decision.order_id))
{
orderInfo = await Tools.GetOrderStatusAsync(decision.order_id);
}
if (decision.intent == "bug")
{
ticketRef = await Tools.LogTicketAsync(
JsonSerializer.Serialize(new { decision, raw = userMessage }),
connStr: "Server=.;Database=Support;Trusted_Connection=True;TrustServerCertificate=True;");
}
var reply = await DraftReplyAsync(chat, userMessage, decision, orderInfo);
var sendRef = await Tools.SendResponseAsync("cliente@example.com", reply.subject, reply.body);
var auditRef = await Tools.AuditLogAsync(
$"triage={triageJson} send={sendRef} ticket={ticketRef}",
connStr: "Server=.;Database=Support;Trusted_Connection=True;TrustServerCertificate=True;"
);
Console.WriteLine($"Subject: {reply.subject}");
Console.WriteLine($"Body:n{reply.body}");
Console.WriteLine($"Audit: {auditRef}");
}
static async Task ClassifyAsync(IChatCompletionService chat, string system, string userMessage)
{
var history = new ChatHistory();
history.AddSystemMessage(system + "nReturn strictly valid JSON with keys: intent, priority, order_id, needs_handoff, summary_it.");
history.AddUserMessage(userMessage);
var resp = await chat.GetChatMessageContentAsync(history, new ChatRequestSettings
{
Temperature = 0.1
});
// Extract minimal JSON
var text = resp.ToString();
var i = text.IndexOf('{'); var j = text.LastIndexOf('}');
return text.Substring(i, j - i + 1);
}
static async Task<(string subject, string body)> DraftReplyAsync(
IChatCompletionService chat,
string userMessage,
TriageDecision decision,
string? orderInfoJson)
{
var history = new ChatHistory();
history.AddSystemMessage("""
Sei un assistente clienti. Scrivi risposta breve e professionale in italiano.
- Tono cortese e chiaro.
- Se disponibile order_info, includi stato, ETA e tracking.
- Se intent=bug, chiedi 3 info tecniche (passi, screenshot, orario) e comunica apertura ticket.
- Chiudi con "Resto a disposizione." e firma "Intes Informatica".
Rispondi con:
1) Oggetto:
2) Corpo:
""");
history.AddUserMessage($"""
Dati:
intent={decision.intent}
priority={decision.priority}
order_info={(orderInfoJson ?? "null")}
messaggio_cliente="""
{userMessage}
"""
""");
var resp = await chat.GetChatMessageContentAsync(history, new ChatRequestSettings { Temperature = 0.2 });
var text = resp.ToString();
var subject = "Richiesta di assistenza";
var body = text;
foreach (var line in text.Split('n'))
{
if (line.Trim().StartsWith("Oggetto", StringComparison.OrdinalIgnoreCase))
{
subject = line.Split(':', 2)[1].Trim();
body = text.Replace(line, "").Trim();
break;
}
}
return (subject, body);
}
}
}
Use case per PMI e professionisti
Supporto IT: un Agent risponde automaticamente ai ticket e apre segnalazioni.
Analisi email: classifica posta in arrivo, segnala phishing, crea report.
CRM: collega AI al database clienti per estrarre informazioni e generare offerte.
Business Intelligence: genera report automatici partendo da dati aziendali.
E-commerce: chatbot intelligente collegato al catalogo prodotti.
Sfide tecniche e rischi
Costi: API OpenAI costano, soprattutto con GPT-4/5 → necessità di caching e ottimizzazione.
Sicurezza: l’Agent può chiamare API sensibili → servono controlli e logging.
Hallucination: rischio di risposte inventate → mitigare con tool di validazione.
Governance: chi decide cosa può fare un Agent? Serve definire policy.
Come iniziare a sperimentare un ChatGPT Agent
Definisci un obiettivo semplice (es. analisi email, recupero dati da DB).
Scegli il framework:
Python → LangChain
.NET → Semantic Kernel
No-code rapido → Assistants API
Collega un tool: API meteo, DB SQL, file system.
Aggiungi memoria: Redis o SQLite per contesto persistente.
Monitora costi e performance: log di richieste, caching.
Conclusione
Gli Agent di ChatGPT non sono più un concetto futuristico: nel 2025 sono già realtà concreta, applicabile a PMI e professionisti.
La differenza rispetto a un normale chatbot è enorme: un Agent non risponde soltanto, ma pianifica, agisce, verifica e ripete.
Con framework come LangChain, Semantic Kernel e l’Assistants API, chiunque abbia competenze tecniche può iniziare a sperimentare.
Il consiglio è di partire con progetti pilota semplici, per poi evolvere verso automazioni sempre più complesse.
Il futuro dell’AI non è “chiedere risposte”, ma delegare compiti a sistemi intelligenti che imparano e agiscono per noi.