OpenAI ha recentemente introdotto nuove funzionalità che mostrano un'architettura simile ad un agente, come l'API Assistant. Secondo OpenAI:
L'API Assistants ti consente di creare assistenti IA all'interno delle tue applicazioni. Un assistente fornisce istruzioni e può sfruttare modelli, strumenti e file per rispondere alle domande degli utenti. L'API Assistants attualmente supporta tre tipi di strumenti: interprete di codice, ricerca di file e chiamata di funzione.
Sebbene questi progressi siano promettenti, sono ancora in ritardo rispetto a LangChain. LangChain consente la creazione di sistemi simili ad agenti basati su LLM con maggiore flessibilità nell'elaborazione dell'input del linguaggio naturale e nell'esecuzione di azioni basate sul contesto.
Tuttavia, questo è solo l'inizio.
Ad alto livello, l'interazione con l'API Assistant può essere immaginata come un ciclo:
- Dato l'input dell'utente, viene chiamato un LLM per determinare se fornire una risposta o intraprendere azioni specifiche.
- Se la decisione del LLM è sufficiente per rispondere alla domanda, il ciclo termina.
- Se un'azione porta a una nuova osservazione, questa osservazione viene inclusa nel prompt e l'LLM viene richiamato nuovamente.
- Il ciclo quindi ricomincia.
Sfortunatamente, nonostante i vantaggi annunciati, ho trovato la documentazione per l'API mal realizzata, soprattutto per quanto riguarda le interazioni con chiamate di funzioni personalizzate e la creazione di app utilizzando framework come Streamlit.
In questo post del blog ti guiderò attraverso la creazione di un assistente AI utilizzando l'API OpenAI Assistant con chiamate di funzioni personalizzate, abbinate a un'interfaccia Streamlit, per aiutare coloro che sono interessati a utilizzare in modo efficace l'API Assistant.
In questo post del blog, mostrerò un semplice esempio: un assistente AI in grado di calcolare le tasse in base a un determinato reddito. Gli utenti di Langchain possono facilmente venire in mente implementandolo creando un file agente con uno strumento di “calcolo delle imposte”.
Questo strumento includerebbe le fasi di calcolo necessarie e una richiesta ben progettata per garantire che LLM sappia quando chiamare lo strumento ogni volta che una domanda riguarda entrate o tasse.
Tuttavia, questo processo non è esattamente lo stesso con l'API OpenAI Assistant. Mentre l'interprete del codice e gli strumenti di ricerca dei file possono essere utilizzati direttamente in modo semplice secondo La documentazione di OpenAIgli strumenti personalizzati richiedono un approccio leggermente diverso.
assistant = client.beta.assistants.create(
name="Data visualizer",
description="You are great at creating beautiful data visualizations. You analyze data present in .csv files, understand trends, and come up with data visualizations relevant to those trends. You also share a brief text summary of the trends observed.",
model="gpt-4o",
tools=({"type": "code_interpreter"}),
)
Analizziamolo passo dopo passo. Il nostro obiettivo è:
- Definire una funzione che calcoli l'imposta in base a determinate entrate.
- Sviluppare uno strumento utilizzando questa funzione.
- Crea un assistente che possa accedere a questo strumento e chiamarlo ogni volta che è necessario il calcolo delle tasse.
Tieni presente che lo strumento di calcolo delle imposte descritto nel paragrafo seguente è concepito come un esempio giocattolo per dimostrare come utilizzare l'API discussa nel post. Non dovrebbe essere utilizzato per i calcoli fiscali effettivi.
Consideriamo la seguente funzione a tratti, che restituisce il valore fiscale per un dato ricavo. Tieni presente che l'input è impostato come una stringa per un'analisi più semplice:
def calculate_tax(revenue: str):
try:
revenue = float(revenue)
except ValueError:
raise ValueError("The revenue should be a string representation of a number.")if revenue <= 10000:
tax = 0
elif revenue <= 30000:
tax = 0.10 * (revenue - 10000)
elif revenue <= 70000:
tax = 2000 + 0.20 * (revenue - 30000)
elif revenue <= 150000:
tax = 10000 + 0.30 * (revenue - 70000)
else:
tax = 34000 + 0.40 * (revenue - 150000)
return tax
Successivamente, definiamo l'assistente:
function_tools = (
{
"type": "function",
"function": {
"name": "calculate_tax",
"description": "Get the tax for given revenue in euro",
"parameters": {
"type": "object",
"properties": {
"revenue": {
"type": "string",
"description": "Annual revenue in euro"
}
},
"required": ("revenue")
}
}
}
)
# Define the assistant
assistant = client.beta.assistants.create(
name="Assistant",
instructions="",
tools=function_tools,
model="gpt-4o",
)
Ora il punto essenziale:
In che modo l'assistente utilizza la funzione quando viene chiamato “calculate_tax”? Questa parte è scarsamente documentata nell'assistente OpenAI e molti utenti potrebbero rimanere confusi la prima volta che la utilizzano. Per gestire questo, dobbiamo definire an EventHandler
per gestire diversi eventi nel flusso di risposta, in particolare come gestire l'evento quando viene chiamato lo strumento “calculate_tax”.
def handle_requires_action(self, data, run_id):
tool_outputs = ()for tool in data.required_action.submit_tool_outputs.tool_calls:
if tool.function.name == "calculate_tax":
try:
# Extract revenue from tool parameters
revenue = ast.literal_eval(tool.function.arguments)("revenue")
# Call your calculate_tax function to get the tax
tax_result = calculate_tax(revenue)
# Append tool output in the required format
tool_outputs.append({"tool_call_id": tool.id, "output": f"{tax_result}"})
except ValueError as e:
# Handle any errors when calculating tax
tool_outputs.append({"tool_call_id": tool.id, "error": str(e)})
# Submit all tool_outputs at the same time
self.submit_tool_outputs(tool_outputs)
Il codice sopra funziona come segue: Per ogni chiamata allo strumento che richiede un'azione:
- Controlla se il nome della funzione è “calculate_tax”.
- Estrarre il valore delle entrate dai parametri dello strumento.
- Chiama il
calculate_tax
lavorare con le entrate per calcolare l’imposta. (È qui che avviene la vera interazione.) - Dopo aver elaborato tutte le chiamate allo strumento, inviare i risultati raccolti.
Ora puoi interagire con l'assistente seguendo questi passaggi standard documentati da OpenAI (per questo motivo non fornirò molti dettagli in questa sezione):
- Crea una discussione: Rappresenta una conversazione tra un utente e l'assistente.
- Aggiungi messaggi utente: Questi possono includere sia testo che file, che vengono aggiunti al thread.
- Crea una corsa: Utilizza il modello e gli strumenti associati all'assistente per generare una risposta. Questa risposta viene quindi aggiunta nuovamente al thread.
Lo snippet di codice seguente mostra come eseguire l'assistente nel mio caso d'uso specifico: il codice imposta un'interazione in streaming con un assistente utilizzando parametri specifici, tra cui un ID thread e un ID assistente. UN EventHandler
l'istanza gestisce gli eventi durante lo streaming. IL stream.until_done()
Il metodo mantiene attivo il flusso fino al completamento di tutte le interazioni. IL with
L'istruzione garantisce che il flusso venga successivamente chiuso correttamente.
with client.beta.threads.runs.stream(thread_id=st.session_state.thread_id,
assistant_id=assistant.id,
event_handler=EventHandler(),
temperature=0) as stream:
stream.until_done()
Anche se il mio post potrebbe finire qui, ho notato numerose richieste sul forum Streamlit (come questo) in cui gli utenti faticano a far funzionare lo streaming sull'interfaccia, anche se funziona perfettamente nel terminale. Ciò mi ha spinto ad approfondire.
Per integrare con successo lo streaming nella tua app, dovrai estendere la funzionalità della classe EventHandler menzionata in precedenza, concentrandoti in particolare sulla gestione della creazione del testo, dei delta del testo e del completamento del testo. Ecco i tre passaggi chiave necessari per visualizzare il testo nell'interfaccia Streamlit durante la gestione della cronologia chat:
- Gestione della creazione del testo (
on_text_created
): Avvia e visualizza una nuova casella di testo per ogni risposta dell'assistente, aggiornando l'interfaccia utente per riflettere lo stato delle azioni precedenti. - Gestione delta testo (
on_text_delta
): Aggiorna dinamicamente la casella di testo corrente mentre l'assistente genera testo, consentendo modifiche incrementali senza aggiornare l'intera interfaccia utente. - Gestione del completamento del testo (
on_text_done
): Finalizza ogni segmento di interazione aggiungendo una nuova casella di testo vuota, preparandosi per l'interazione successiva. Inoltre, registra i segmenti di conversazione completati inchat_history
.
Ad esempio, considera il seguente snippet di codice per la gestione dei delta di testo:
def on_text_delta(self, delta: TextDelta, snapshot: Text):
"""
Handler for when a text delta is created
"""
# Clear the latest text box
st.session_state.text_boxes(-1).empty()# If there is new text, append it to the latest element in the assistant text list
if delta.value:
st.session_state.assistant_text(-1) += delta.value
# Re-display the updated assistant text in the latest text box
st.session_state.text_boxes(-1).info("".join(st.session_state("assistant_text")(-1)))
Questo codice svolge tre compiti principali:
- Cancellazione dell'ultima casella di testo: Svuota il contenuto della casella di testo più recente (
st.session_state.text_boxes(-1)
) per prepararlo al nuovo input. - Aggiunta del valore Delta al testo assistente: Se il nuovo testo (
delta.value
) è presente, lo aggiunge al testo dell'assistente in corso memorizzato inst.session_state.assistant_text(-1)
. - Rivisualizzazione del testo dell'assistente aggiornato: Aggiorna il contenuto dell'ultima casella di testo per riflettere il contenuto combinato di tutto il testo dell'assistente accumulato finora (
st.session_state("assistant_text")(-1)
).
Questo post del blog ha dimostrato come utilizzare l'API OpenAI Assistant e Streamlit per creare un assistente AI in grado di calcolare le tasse.
Ho realizzato questo semplice progetto per evidenziare le funzionalità dell'API Assistant, nonostante la sua documentazione poco chiara. Il mio obiettivo era chiarire le ambiguità e fornire alcune indicazioni a coloro che sono interessati a utilizzare l'API Assistant. Spero che questo post sia stato utile e ti incoraggi a esplorare ulteriori possibilità con questo potente strumento.
A causa dei limiti di spazio, ho cercato di evitare di includere frammenti di codice non necessari. Tuttavia, se necessario, visita il mio Deposito Github per visualizzare l'implementazione completa.
Fonte: towardsdatascience.com