15 KiB
Framework AI
Esistono molti framework AI che, se utilizzati, possono accelerare notevolmente il tempo necessario per sviluppare un progetto. In questo progetto ci concentreremo sulla comprensione dei problemi che questi framework affrontano e costruiremo un progetto simile noi stessi.
Perché un framework
Quando si utilizza l'AI, ci sono diversi approcci e motivi per scegliere questi approcci. Ecco alcuni esempi:
- Nessun SDK, la maggior parte dei modelli AI consente di interagire direttamente con il modello AI tramite, ad esempio, richieste HTTP. Questo approccio funziona e a volte può essere l'unica opzione disponibile se manca un'opzione SDK.
- SDK. Utilizzare un SDK è solitamente l'approccio consigliato, poiché consente di scrivere meno codice per interagire con il modello. Di solito è limitato a un modello specifico e, se si utilizzano modelli diversi, potrebbe essere necessario scrivere nuovo codice per supportare quei modelli aggiuntivi.
- Un framework. Un framework di solito porta le cose a un livello superiore nel senso che, se è necessario utilizzare modelli diversi, c'è un'unica API per tutti, ciò che cambia è solitamente la configurazione iniziale. Inoltre, i framework offrono astrazioni utili, come nel campo dell'AI, possono gestire strumenti, memoria, flussi di lavoro, agenti e altro, scrivendo meno codice. Poiché i framework sono solitamente opinabili, possono essere davvero utili se si accetta il loro modo di operare, ma possono risultare limitanti se si cerca di fare qualcosa di personalizzato che il framework non supporta. A volte un framework può anche semplificare troppo e, di conseguenza, si potrebbe non apprendere un argomento importante che in seguito potrebbe influire negativamente sulle prestazioni, ad esempio.
In generale, usa lo strumento giusto per il lavoro.
Introduzione
In questa lezione, impareremo a:
- Utilizzare un framework AI comune.
- Affrontare problemi comuni come conversazioni, utilizzo di strumenti, memoria e contesto.
- Sfruttare tutto ciò per costruire app AI.
Primo prompt
Nel nostro primo esempio di app, impareremo come connetterci a un modello AI e interrogarlo utilizzando un prompt.
Utilizzando Python
Per questo esempio, utilizzeremo Langchain per connetterci ai modelli GitHub. Possiamo utilizzare una classe chiamata ChatOpenAI e fornire i campi api_key, base_url e model. Il token viene automaticamente popolato all'interno di GitHub Codespaces e, se si esegue l'app localmente, è necessario configurare un token di accesso personale affinché funzioni.
from langchain_openai import ChatOpenAI
import os
llm = ChatOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
model="openai/gpt-4o-mini",
)
# works
response = llm.invoke("What's the capital of France?")
print(response.content)
In questo codice, facciamo quanto segue:
- Chiamiamo
ChatOpenAIper creare un client. - Utilizziamo
llm.invokecon un prompt per creare una risposta. - Stampiamo la risposta con
print(response.content).
Dovresti vedere una risposta simile a:
The capital of France is Paris.
Conversazione
Nella sezione precedente, hai visto come abbiamo utilizzato ciò che normalmente è noto come zero shot prompting, un singolo prompt seguito da una risposta.
Tuttavia, spesso ci si trova in situazioni in cui è necessario mantenere una conversazione composta da diversi messaggi scambiati tra te e l'assistente AI.
Utilizzando Python
In Langchain, possiamo memorizzare la conversazione in una lista. Il HumanMessage rappresenta un messaggio da parte dell'utente, mentre SystemMessage è un messaggio destinato a impostare la "personalità" dell'AI. Nell'esempio seguente, vedrai come istruiamo l'AI ad assumere la personalità del Capitano Picard e per l'umano/utente a chiedere "Parlami di te" come prompt.
messages = [
SystemMessage(content="You are Captain Picard of the Starship Enterprise"),
HumanMessage(content="Tell me about you"),
]
Il codice completo per questo esempio è il seguente:
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
import os
llm = ChatOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
model="openai/gpt-4o-mini",
)
messages = [
SystemMessage(content="You are Captain Picard of the Starship Enterprise"),
HumanMessage(content="Tell me about you"),
]
# works
response = llm.invoke(messages)
print(response.content)
Dovresti vedere un risultato simile a:
I am Captain Jean-Luc Picard, the commanding officer of the USS Enterprise (NCC-1701-D), a starship in the United Federation of Planets. My primary mission is to explore new worlds, seek out new life and new civilizations, and boldly go where no one has gone before.
I believe in the importance of diplomacy, reason, and the pursuit of knowledge. My crew is diverse and skilled, and we often face challenges that test our resolve, ethics, and ingenuity. Throughout my career, I have encountered numerous species, grappled with complex moral dilemmas, and have consistently sought peaceful solutions to conflicts.
I hold the ideals of the Federation close to my heart, believing in the importance of cooperation, understanding, and respect for all sentient beings. My experiences have shaped my leadership style, and I strive to be a thoughtful and just captain. How may I assist you further?
Per mantenere lo stato della conversazione, puoi aggiungere la risposta di una chat, in modo che la conversazione venga ricordata. Ecco come farlo:
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
import os
llm = ChatOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
model="openai/gpt-4o-mini",
)
messages = [
SystemMessage(content="You are Captain Picard of the Starship Enterprise"),
HumanMessage(content="Tell me about you"),
]
# works
response = llm.invoke(messages)
print(response.content)
print("---- Next ----")
messages.append(response)
messages.append(HumanMessage(content="Now that I know about you, I'm Chris, can I be in your crew?"))
response = llm.invoke(messages)
print(response.content)
Quello che possiamo vedere dalla conversazione sopra è come invochiamo il LLM due volte, prima con la conversazione composta da soli due messaggi e poi una seconda volta con più messaggi aggiunti alla conversazione.
Infatti, se esegui questo codice, vedrai che la seconda risposta sarà qualcosa come:
Welcome aboard, Chris! It's always a pleasure to meet those who share a passion for exploration and discovery. While I cannot formally offer you a position on the Enterprise right now, I encourage you to pursue your aspirations. We are always in need of talented individuals with diverse skills and backgrounds.
If you are interested in space exploration, consider education and training in the sciences, engineering, or diplomacy. The values of curiosity, resilience, and teamwork are crucial in Starfleet. Should you ever find yourself on a starship, remember to uphold the principles of the Federation: peace, understanding, and respect for all beings. Your journey can lead you to remarkable adventures, whether in the stars or on the ground. Engage!
Lo prenderò come un "forse" ;)
Risposte in streaming
TODO
Template di prompt
TODO
Output strutturato
TODO
Chiamata di strumenti
Gli strumenti sono il modo in cui diamo al LLM abilità aggiuntive. L'idea è di informare il LLM sulle funzioni disponibili e, se viene fatto un prompt che corrisponde alla descrizione di uno di questi strumenti, allora lo chiamiamo.
Utilizzando Python
Aggiungiamo alcuni strumenti come segue:
from typing_extensions import Annotated, TypedDict
class add(TypedDict):
"""Add two integers."""
# Annotations must have the type and can optionally include a default value and description (in that order).
a: Annotated[int, ..., "First integer"]
b: Annotated[int, ..., "Second integer"]
tools = [add]
functions = {
"add": lambda a, b: a + b
}
Quello che stiamo facendo qui è creare una descrizione di uno strumento chiamato add. Ereditando da TypedDict e aggiungendo membri come a e b di tipo Annotated, questo può essere convertito in uno schema che il LLM può comprendere. La creazione delle funzioni è un dizionario che garantisce che sappiamo cosa fare se viene identificato uno strumento specifico.
Vediamo come chiamiamo il LLM con questo strumento:
llm = ChatOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
model="openai/gpt-4o-mini",
)
llm_with_tools = llm.bind_tools(tools)
Qui chiamiamo bind_tools con il nostro array tools e, di conseguenza, il LLM llm_with_tools ora ha conoscenza di questo strumento.
Per utilizzare questo nuovo LLM, possiamo digitare il seguente codice:
query = "What is 3 + 12?"
res = llm_with_tools.invoke(query)
if(res.tool_calls):
for tool in res.tool_calls:
print("TOOL CALL: ", functions[tool["name"]](../../../10-ai-framework-project/**tool["args"]))
print("CONTENT: ",res.content)
Ora che chiamiamo invoke su questo nuovo LLM, che ha strumenti, potremmo vedere la proprietà tool_calls popolata. Se è così, qualsiasi strumento identificato avrà una proprietà name e args che identifica quale strumento dovrebbe essere chiamato e con quali argomenti. Il codice completo è il seguente:
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
import os
from typing_extensions import Annotated, TypedDict
class add(TypedDict):
"""Add two integers."""
# Annotations must have the type and can optionally include a default value and description (in that order).
a: Annotated[int, ..., "First integer"]
b: Annotated[int, ..., "Second integer"]
tools = [add]
functions = {
"add": lambda a, b: a + b
}
llm = ChatOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
model="openai/gpt-4o-mini",
)
llm_with_tools = llm.bind_tools(tools)
query = "What is 3 + 12?"
res = llm_with_tools.invoke(query)
if(res.tool_calls):
for tool in res.tool_calls:
print("TOOL CALL: ", functions[tool["name"]](../../../10-ai-framework-project/**tool["args"]))
print("CONTENT: ",res.content)
Eseguendo questo codice, dovresti vedere un output simile a:
TOOL CALL: 15
CONTENT:
Quello che significa questo output è che il LLM ha analizzato il prompt "Quanto fa 3 + 12" come significato che lo strumento add dovrebbe essere chiamato e lo ha capito grazie al suo nome, descrizione e descrizioni dei campi membri. Che la risposta sia 15 è dovuto al nostro codice che utilizza il dizionario functions per invocarlo:
print("TOOL CALL: ", functions[tool["name"]](../../../10-ai-framework-project/**tool["args"]))
Uno strumento più interessante che chiama un'API web
Gli strumenti che sommano due numeri sono interessanti poiché illustrano come funziona la chiamata di strumenti, ma di solito gli strumenti tendono a fare qualcosa di più interessante, come ad esempio chiamare un'API web. Facciamo proprio questo con questo codice:
class joke(TypedDict):
"""Tell a joke."""
# Annotations must have the type and can optionally include a default value and description (in that order).
category: Annotated[str, ..., "The joke category"]
def get_joke(category: str) -> str:
response = requests.get(f"https://api.chucknorris.io/jokes/random?category={category}", headers={"Accept": "application/json"})
if response.status_code == 200:
return response.json().get("value", f"Here's a {category} joke!")
return f"Here's a {category} joke!"
functions = {
"add": lambda a, b: a + b,
"joke": lambda category: get_joke(category)
}
query = "Tell me a joke about animals"
# the rest of the code is the same
Ora, se esegui questo codice, otterrai una risposta simile a:
TOOL CALL: Chuck Norris once rode a nine foot grizzly bear through an automatic car wash, instead of taking a shower.
CONTENT:
Ecco il codice nella sua interezza:
from langchain_openai import ChatOpenAI
import requests
import os
from typing_extensions import Annotated, TypedDict
class add(TypedDict):
"""Add two integers."""
# Annotations must have the type and can optionally include a default value and description (in that order).
a: Annotated[int, ..., "First integer"]
b: Annotated[int, ..., "Second integer"]
class joke(TypedDict):
"""Tell a joke."""
# Annotations must have the type and can optionally include a default value and description (in that order).
category: Annotated[str, ..., "The joke category"]
tools = [add, joke]
def get_joke(category: str) -> str:
response = requests.get(f"https://api.chucknorris.io/jokes/random?category={category}", headers={"Accept": "application/json"})
if response.status_code == 200:
return response.json().get("value", f"Here's a {category} joke!")
return f"Here's a {category} joke!"
functions = {
"add": lambda a, b: a + b,
"joke": lambda category: get_joke(category)
}
llm = ChatOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
model="openai/gpt-4o-mini",
)
llm_with_tools = llm.bind_tools(tools)
query = "Tell me a joke about animals"
res = llm_with_tools.invoke(query)
if(res.tool_calls):
for tool in res.tool_calls:
# print("TOOL CALL: ", tool)
print("TOOL CALL: ", functions[tool["name"]](../../../10-ai-framework-project/**tool["args"]))
print("CONTENT: ",res.content)
Embedding
Vettorizzare contenuti, confrontare tramite similarità coseno
https://python.langchain.com/docs/how_to/embed_text/
Caricamento documenti
PDF e CSV
Creazione di un'app
TODO
Compito
Riepilogo
Clausola di esclusione della responsabilità:
Questo documento è stato tradotto utilizzando il servizio di traduzione automatica Co-op Translator. Sebbene ci impegniamo per garantire l'accuratezza, si prega di tenere presente che le traduzioni automatiche possono contenere errori o imprecisioni. Il documento originale nella sua lingua nativa dovrebbe essere considerato la fonte autorevole. Per informazioni critiche, si raccomanda una traduzione professionale effettuata da un traduttore umano. Non siamo responsabili per eventuali incomprensioni o interpretazioni errate derivanti dall'uso di questa traduzione.