Plataformes i Frameworks d'Intel·ligència Artificial
Introducció
El 2025, el panorama de les plataformes d'IA es radicalment diferent del que era fa tan sols tres anys. Existeix una proliferacio de models, APIs, frameworks d'orquestracio i eines MLOps que pot ser aclaparadora. Un professional d'IA no ha de coneixer-les totes en profunditat, pero si entendre l'arquitectura general, saber triar la plataforma adequada per a cada cas d'us i ser capac d'integrar-les en una aplicacio real.
Aquest tema cobreix les plataformes mes rellevants de 2025, agrupades en tres categories: APIs comercials, solucions open-source/locals i frameworks d'orquestracio. Al final, analitzem les eines de MLOps per a la gestio del cicle de vida dels models.
1. APIs Comercials de Models de Llenguatge
1.1 OpenAI API
OpenAI es el proveïdor de referencia en LLMs. La seva API es la mes usada del sector i el seu disseny ha marcat l'estandard que segueixen la resta.
Models disponibles el 2025:
| Model | Parametres | Context | Punt Fort | Cost (per 1M tokens) |
|---|---|---|---|---|
| GPT-4o | ~200B | 128K | Millor rendiment general, multimodal | $5 entrada / $15 sortida |
| GPT-4o-mini | ~8B | 128K | Rapid i economi c | $0.15 entrada / $0.60 sortida |
| o1 | ~? | 128K | Raonament complex, matemàtiques | $15 entrada / $60 sortida |
| o1-mini | ~? | 128K | Raonament economi c | $3 entrada / $12 sortida |
| text-embedding-3-small | — | 8K | Embeddings eficients | $0.02 |
| text-embedding-3-large | — | 8K | Embeddings d'alta qualitat | $0.13 |
| dall-e-3 | — | — | Generacio d'imatges | $0.04-0.12 per imatge |
| whisper-1 | — | — | Transcripcio d'audio | $0.006 per minut |
Exemple complet amb OpenAI API:
from openai import OpenAI
from pydantic import BaseModel
import os
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
# 1. Chat basic
resposta = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Ets un assistent tecnic expert en IA. Respon en catala."},
{"role": "user", "content": "Explica que es RAG en 3 linies."}
],
temperature=0.7,
max_tokens=200
)
print(resposta.choices[0].message.content)
print(f"Tokens usats: {resposta.usage.total_tokens}")
# 2. Structured Output (JSON mode)
class AnalisiTecnica(BaseModel):
tecnologia: str
pros: list[str]
cons: list[str]
cas_us_recomanat: str
resposta_json = client.beta.chat.completions.parse(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Analitza tecnologies d'IA i retorna JSON estructurat."},
{"role": "user", "content": "Analitza l'us de RAG per a una empresa de serveis juridics."}
],
response_format=AnalisiTecnica
)
analisi = resposta_json.choices[0].message.parsed
print(f"Tecnologia: {analisi.tecnologia}")
print(f"Pros: {analisi.pros}")
# 3. Function calling / Tool use
tools = [
{
"type": "function",
"function": {
"name": "cercar_preu_accio",
"description": "Cerca el preu actual d'una accio de borsa",
"parameters": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Simbol de l'accio (ex: AAPL, NVDA)"
}
},
"required": ["ticker"]
}
}
}
]
resposta_tool = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Quin es el preu d'NVIDIA avui?"}],
tools=tools,
tool_choice="auto"
)
# Comprovar si el model vol usar una tool
if resposta_tool.choices[0].message.tool_calls:
tool_call = resposta_tool.choices[0].message.tool_calls[0]
print(f"El model vol cridar: {tool_call.function.name}")
print(f"Amb arguments: {tool_call.function.arguments}")
# 4. Embeddings
textos = ["Python per a IA", "Machine Learning amb scikit-learn", "LangChain per a agents"]
embeddings = client.embeddings.create(
model="text-embedding-3-small",
input=textos
)
vectors = [e.embedding for e in embeddings.data]
print(f"Dimensio dels embeddings: {len(vectors[0])}") # 1536
# 5. Visio (GPT-4o)
import base64
from pathlib import Path
def codificar_imatge(path: str) -> str:
with open(path, "rb") as f:
return base64.b64encode(f.read()).decode("utf-8")
resposta_visio = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "Descriu aquesta imatge en catala:"},
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{codificar_imatge('diagrama.jpg')}",
"detail": "high"
}
}
]
}
],
max_tokens=300
)
Bones pràctiques amb l'API d'OpenAI
- Usa sempre
gpt-4o-miniper a prototips i proves; reservagpt-4oper a produccio quan la qualitat ho justifiqui. - Implementa retry amb backoff exponencial per gestionar errors
429(rate limit). - Usa streaming (
stream=True) per a aplicacions de chat per millorar la percep cio de velocitat. - Guarda les claus API sempre en variables d'entorn, mai al codi.
1.2 Anthropic API — Claude
Anthropic, fundada el 2021 per ex-membres d'OpenAI (incloent Dario i Daniela Amodei), va crear Claude amb un enfocament en la seguretat i l'alineament (Constitutional AI). El 2025, Claude 3.5 Sonnet es un dels millors models per a tasques de codi i analisi de documents llargs.
Models Claude el 2025:
| Model | Context | Punt Fort | Cost (per 1M tokens) |
|---|---|---|---|
| Claude 3.5 Sonnet | 200K | Millor en codi, analisi, escriptura | $3 entrada / $15 sortida |
| Claude 3.5 Haiku | 200K | Rapid i economi c | $0.80 entrada / $4 sortida |
| Claude 3 Opus | 200K | Raonament complex | $15 entrada / $75 sortida |
La finestra de 200K tokens de Claude es especialment util per a analisi de documents llargs (contractes, informes, codi base gran).
import anthropic
import os
client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
# 1. Chat basic amb Claude 3.5 Sonnet
missatge = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
system="Ets un expert en programacio Python i IA. Respon sempre en catala.",
messages=[
{"role": "user", "content": "Escriu una funcio Python per calcular la similitud cosinus entre dos vectors."}
]
)
print(missatge.content[0].text)
print(f"Input tokens: {missatge.usage.input_tokens}")
print(f"Output tokens: {missatge.usage.output_tokens}")
# 2. Analisi de document llarg (gracies als 200K tokens de context)
document_llarg = Path("informe_anual.txt").read_text(encoding="utf-8")
analisi = client.messages.create(
model="claude-3-5-haiku-20241022", # Haiku per a tasques rapids
max_tokens=2048,
messages=[
{
"role": "user",
"content": f"""Analitza aquest informe anual i extreu:
1. Els 5 punts mes importants
2. Les principals amenaçes identificades
3. Les oportunitats de creixement
INFORME:
{document_llarg}"""
}
]
)
# 3. Streaming
with client.messages.stream(
model="claude-3-5-sonnet-20241022",
max_tokens=500,
messages=[{"role": "user", "content": "Explica el teorema de Bayes i la seva aplicacio en ML."}]
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
# 4. Tool use amb Claude
tools_claude = [
{
"name": "calcular_cost_api",
"description": "Calcula el cost d'una crida a una API d'IA",
"input_schema": {
"type": "object",
"properties": {
"model": {"type": "string", "description": "Nom del model"},
"tokens_entrada": {"type": "integer"},
"tokens_sortida": {"type": "integer"}
},
"required": ["model", "tokens_entrada", "tokens_sortida"]
}
}
]
resposta_tool = client.messages.create(
model="claude-3-5-haiku-20241022",
max_tokens=300,
tools=tools_claude,
messages=[{
"role": "user",
"content": "Quant costaria analitzar 1000 documents de 500 tokens cada un amb Claude 3.5 Haiku?"
}]
)
1.3 Google AI: Gemini
Google va llançar la familia Gemini el desembre de 2023, i el 2025 ofereix models molt competitius, especialment per a tasques multimodals i per a finestres de context extremadament llargues.
Models Gemini el 2025:
| Model | Context | Punt Fort |
|---|---|---|
| Gemini 1.5 Pro | 2M tokens | Finestra de context enorme, video, audio |
| Gemini 1.5 Flash | 1M tokens | Rapid i economi c |
| Gemini 2.0 Flash | 1M tokens | El mes recent, multimodal natiu |
| text-embedding-004 | 8K | Embeddings multilinguals |
import google.generativeai as genai
import os
genai.configure(api_key=os.environ["GOOGLE_API_KEY"])
# Usar Gemini 1.5 Flash (economi c i rapid)
model = genai.GenerativeModel(
"gemini-1.5-flash",
system_instruction="Ets un assistent d'IA especialitzat. Respon en catala."
)
resposta = model.generate_content(
"Quins son els avantatges de LangGraph sobre LangChain Classic?",
generation_config=genai.types.GenerationConfig(
temperature=0.7,
max_output_tokens=500
)
)
print(resposta.text)
# Finestra de context enorme: analitzar un video o audio llarg
# (Unic a Gemini 1.5 Pro/Flash)
import PIL.Image
imatge = PIL.Image.open("diagrama_arquitectura.png")
resposta_multimodal = model.generate_content([
"Descriu i analitza aquesta arquitectura de sistema en catala:",
imatge
])
print(resposta_multimodal.text)
Vertex AI es la plataforma empresarial de Google Cloud per a IA. Ofereix els mateixos models Gemini pero amb SLAs empresarials, VPCSC, auditoria i integracio amb l'ecosistema GCP.
1.4 AWS Bedrock
AWS Bedrock es la plataforma gestionada d'Amazon per accedir a models de tercers sense gestionar infraestructura. El 2025, ofereix acces a:
- Anthropic Claude (3.5 Sonnet, 3 Haiku)
- Meta Llama 3 (8B, 70B, 405B)
- Mistral (7B, 8x7B Mixture of Experts)
- Amazon Titan (models propis d'AWS)
- Stability AI (SDXL per a imatges)
L'avantatge de Bedrock es la integra cio nativa amb AWS (IAM, CloudWatch, VPC, S3) i el compliment normatiu (SOC 2, HIPAA, ISO 27001).
import boto3
import json
bedrock = boto3.client(
service_name="bedrock-runtime",
region_name="eu-west-1" # disponible a Europa
)
# Cridar Claude 3.5 Haiku via Bedrock
cos_peticio = json.dumps({
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 500,
"messages": [
{"role": "user", "content": "Que es el federated learning? Respon en catala."}
]
})
resposta = bedrock.invoke_model(
body=cos_peticio,
modelId="anthropic.claude-3-5-haiku-20241022-v1:0",
accept="application/json",
contentType="application/json"
)
cos_resposta = json.loads(resposta.get("body").read())
print(cos_resposta["content"][0]["text"])
1.5 Azure AI Foundry
Azure AI Foundry (successor d'Azure OpenAI Service) es la plataforma empresarial de Microsoft per a IA. Ofereix acces a models GPT-4o, Claude, Llama i Mistral amb:
- Xarxa privada: els models s'executen en la teva subscripcio Azure
- Data residency: les dades no surten de la teva regio
- SLA del 99.9% per a produccio
- Integracio amb Microsoft 365 (Copilot Studio)
Es la plataforma preferida per a grans empreses europees que necesiten compliment GDPR estricte.
1.6 Mistral AI
Mistral AI, empresa francesa fundada el 2023, ha demostrat que es possible competir amb OpenAI amb models mes petits i eficients. La seva filosofia open-source parcial i la seva localitzacio europea la fan especialment interessant per a empreses que volen sobirania de dades.
| Model | Parametres | Punt Fort |
|---|---|---|
| Mistral Large | ~70B | Millor model de Mistral, codi i multilingue |
| Mistral Small | ~22B | Balanc rendiment/preu |
| Mistral 7B | 7B | Altament eficient, disponible open-source |
| Mixtral 8x7B | 56B (MoE) | Mixture of Experts, molt eficient |
| Codestral | ~22B | Especialitzat en generacio de codi |
from mistralai import Mistral
import os
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
resposta = client.chat.complete(
model="mistral-small-latest",
messages=[
{"role": "user", "content": "Explica la diferencia entre MoE i dense transformers."}
]
)
print(resposta.choices[0].message.content)
2. Plataformes Open Source i Models Locals
2.1 Ollama: models locals amb facilitat
Ollama es la manera mes senzilla d'executar LLMs de manera local, sense cost d'API i amb privacitat total. El 2025, suporta desenes de models.
Instal·lació i primers passos:
# Instal·lar Ollama (Linux/Mac)
curl -fsSL https://ollama.com/install.sh | sh
# O via Docker (recomanat per a practiques)
docker run -d \
--name ollama-joan-garcia \
-p 11434:11434 \
-v ollama-data:/root/.ollama \
ollama/ollama
# Descarregar models
docker exec ollama-joan-garcia ollama pull llama3.1 # 8B, ~4.7GB
docker exec ollama-joan-garcia ollama pull mistral # 7B, ~4.1GB
docker exec ollama-joan-garcia ollama pull phi3 # 3.8B, ~2.3GB (petit pero potent)
docker exec ollama-joan-garcia ollama pull gemma2 # 9B, ~5.5GB
docker exec ollama-joan-garcia ollama pull llama3.1:70b # 70B, necessita 40GB RAM
# Llistar models descarregats
docker exec ollama-joan-garcia ollama list
# Xatejar directament
docker exec -it ollama-joan-garcia ollama run llama3.1 "Que es RAG?"
Integrar Ollama amb Python:
import ollama
import requests
import json
# 1. Via biblioteca oficial ollama-python
client = ollama.Client(host="http://localhost:11434")
resposta = client.chat(
model="llama3.1",
messages=[
{"role": "system", "content": "Ets un assistent tecnic. Respon en catala."},
{"role": "user", "content": "Quina diferencia hi ha entre LangChain i LlamaIndex?"}
]
)
print(resposta["message"]["content"])
# 2. Streaming
for chunk in client.chat(
model="llama3.1",
messages=[{"role": "user", "content": "Explica els transformers pas a pas."}],
stream=True
):
print(chunk["message"]["content"], end="", flush=True)
# 3. Embeddings amb Ollama
embedding = client.embeddings(
model="nomic-embed-text", # model d'embeddings petit i rapid
prompt="Intel·ligencia Artificial i Big Data"
)
print(f"Dimensio: {len(embedding['embedding'])}") # 768
# 4. Via API REST (compatible amb OpenAI)
# Aixo permet usar Ollama com a drop-in replacement d'OpenAI!
from openai import OpenAI
client_openai = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama" # Ollama no necessita clau real
)
resposta = client_openai.chat.completions.create(
model="llama3.1",
messages=[{"role": "user", "content": "Hola! Qui ets?"}]
)
print(resposta.choices[0].message.content)
Models Ollama recomanats per a les practiques:
| Model | Mida | RAM Necessaria | Cas d'us |
|---|---|---|---|
| llama3.1 | 4.7 GB | 8 GB | Us general, codi, catala acceptable |
| mistral | 4.1 GB | 8 GB | Instruccions, codi, rapid |
| phi3 | 2.3 GB | 6 GB | Dispositius limitats, sorprenentment bo |
| gemma2 | 5.5 GB | 10 GB | Google, multilingue |
| nomic-embed-text | 274 MB | 2 GB | Embeddings (per a RAG) |
| llava | 4.7 GB | 8 GB | Visio (analisi d'imatges) |
Requisits de maquinari per a Ollama
Per executar models de 7-8B parametres de manera fluid, necessites almenys 8 GB de RAM. Per a models de 70B, necessites 40+ GB. Si el teu ordinador te menys de 8 GB, usa phi3 (3.8B) que ofereix bon rendiment amb pocs recursos. Alternativament, usa Google Colab o les APIs comercials.
Miniactivitat
Desplega Ollama amb Docker i descarrega el model phi3 o llama3.1. Escriu un script Python que faci 5 preguntes sobre el curs IABD i guardi les respostes en un fitxer JSON. Usa el client openai apuntant a http://localhost:11434/v1 per demostrar la compatibilitat.
2.2 LM Studio: interfície gràfica per a models locals
LM Studio ofereix una interficie grafica d'escriptori per descarregar i executar models GGUF localment. Es la manera mes facil per a usuaris que prefereixen GUI sobre CLI:
- Descarrega models directament des de Hugging Face Hub
- Interficie de chat integrada
- Servidor local compatible amb OpenAI API
- Suporta Apple Silicon (Metal GPU), NVIDIA (CUDA) i CPU
Es especialment util per a demostracions i per a alumnat que vol experimentar amb models sense Docker.
2.3 Hugging Face Transformers
El Hub de Hugging Face allotja mes de 500.000 models (febrer 2025). La biblioteca transformers permet usar-los amb una API unificada:
from transformers import (
pipeline,
AutoTokenizer,
AutoModelForCausalLM,
BitsAndBytesConfig
)
import torch
# 1. Pipeline d'alt nivell (la manera mes senzilla)
generador = pipeline(
"text-generation",
model="mistralai/Mistral-7B-Instruct-v0.3",
device_map="auto", # Distribueix automàticament entre CPU/GPU
torch_dtype=torch.bfloat16 # Quantitzacio per estalviar memoria
)
resultat = generador(
"<s>[INST] Explica que es un vector store en el context del RAG. [/INST]",
max_new_tokens=300,
do_sample=True,
temperature=0.7
)
print(resultat[0]["generated_text"])
# 2. Quantitzacio de 4 bits per a models grans en GPU limitada
config_quant = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True
)
# Carrega un model de 7B en ~4GB GPU VRAM (en lloc dels 14GB normals)
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.3")
model = AutoModelForCausalLM.from_pretrained(
"mistralai/Mistral-7B-Instruct-v0.3",
quantization_config=config_quant,
device_map="auto"
)
# 3. Tasques especifiques: embeddings multilinguals
from sentence_transformers import SentenceTransformer
import numpy as np
embed_model = SentenceTransformer("sentence-transformers/paraphrase-multilingual-mpnet-base-v2")
# Calcul de similitud semantica en catala
frases = [
"El gat esta sobre la taula.",
"El felí reposa damunt el moble.",
"El gos juga al jardí."
]
embeddings = embed_model.encode(frases, normalize_embeddings=True)
# Matriu de similituds
similituds = embeddings @ embeddings.T
print("Matriu de similituds:")
for i, frase in enumerate(frases):
for j, frase2 in enumerate(frases):
print(f" {i+1}-{j+1}: {similituds[i,j]:.3f}")
3. Frameworks d'Orquestració
3.1 LangChain: el marc dominant per a LLMs
LangChain (versio 0.3+) ha evolucionat molt des del seu llançament el 2022. La versio actual adopta LCEL (LangChain Expression Language), que usa el patró pipe (|) per compondre cadenes:
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_anthropic import ChatAnthropic
from langchain_community.llms import Ollama
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
from langchain_community.vectorstores import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader, WebBaseLoader
# 1. Cadena senzilla
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
prompt = ChatPromptTemplate.from_messages([
("system", "Ets un expert tecnic en IA. Respon sempre en catala i de manera concisa."),
("human", "{pregunta}")
])
# LCEL: els components es combinen amb |
cadena_basica = prompt | llm | StrOutputParser()
print(cadena_basica.invoke({"pregunta": "Que es LangGraph?"}))
# 2. Cadena amb memoria
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
historial = InMemoryChatMessageHistory()
cadena_amb_historial = RunnableWithMessageHistory(
cadena_basica,
lambda session_id: historial,
input_messages_key="pregunta",
history_messages_key="historial"
)
# 3. Pipeline RAG complet
def crear_rag_complet(pdf_path: str) -> any:
"""Construeix un sistema RAG des d'un PDF."""
# Carregar i dividir document
loader = PyPDFLoader(pdf_path)
documents = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", ".", "!", "?", ",", " "]
)
chunks = splitter.split_documents(documents)
print(f"Document dividit en {len(chunks)} chunks")
# Crear embeddings i vector store
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
collection_name="rag-documents",
persist_directory="./chroma_db"
)
retriever = vectorstore.as_retriever(
search_type="mmr", # Maximum Marginal Relevance (mes diversitat)
search_kwargs={"k": 5, "fetch_k": 20}
)
# Prompt RAG
prompt_rag = ChatPromptTemplate.from_messages([
("system", """Ets un assistent expert. Usa EXCLUSIVAMENT la informacio del context per respondre.
Si la informacio no es al context, indica-ho clarament.
CONTEXT:
{context}
Respon sempre en catala."""),
("human", "{pregunta}")
])
def format_docs(docs):
return "\n\n---\n\n".join(
f"[Font: {doc.metadata.get('source', 'desconeguda')}, "
f"Pagina: {doc.metadata.get('page', '?')}]\n{doc.page_content}"
for doc in docs
)
# Cadena RAG amb LCEL
cadena_rag = (
{
"context": retriever | format_docs,
"pregunta": RunnablePassthrough()
}
| prompt_rag
| ChatOpenAI(model="gpt-4o-mini")
| StrOutputParser()
)
return cadena_rag
3.2 LangGraph: agents amb estat
LangGraph (versio 0.2+) permet construir agents com a grafs d'estat. Es mes potent que les cadenes lineals de LangChain per a workflows complexos:
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from typing import TypedDict, Annotated
import operator
# Definir l'estat de l'agent
class EstatAgent(TypedDict):
missatges: Annotated[list, operator.add] # els missatges s'acumulen
iteracio_actual: int
resultat_final: str
# Definir tools
@tool
def calcular(expressio: str) -> str:
"""Calcula una expressio matematica. Exemple: '2 + 3 * 4'"""
try:
resultat = eval(expressio) # en produccio, usar ast.literal_eval
return f"Resultat de '{expressio}' = {resultat}"
except Exception as e:
return f"Error: {e}"
@tool
def cercar_informacio(consulta: str) -> str:
"""Cerca informacio sobre un tema. (Demo: retorna text static)"""
# En produccio, integraria Tavily, Serper o similar
return f"Informacio trobada sobre '{consulta}': [Resultat de la cerca simulada]"
# LLM amb tools
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
tools = [calcular, cercar_informacio]
llm_amb_tools = llm.bind_tools(tools)
# Nodes del graf
def node_agent(estat: EstatAgent) -> dict:
"""Node principal: crida al LLM."""
resposta = llm_amb_tools.invoke(estat["missatges"])
return {"missatges": [resposta]}
# Construir el graf
builder = StateGraph(EstatAgent)
builder.add_node("agent", node_agent)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "agent")
builder.add_conditional_edges(
"agent",
tools_condition, # Si el model vol usar una tool -> "tools"; sino -> END
{"tools": "tools", END: END}
)
builder.add_edge("tools", "agent") # Despres de la tool, torna a l'agent
agent_compilat = builder.compile()
# Executar l'agent
resultat = agent_compilat.invoke({
"missatges": [
SystemMessage(content="Ets un assistent matematich. Usa les tools disponibles."),
HumanMessage(content="Calcula 15 * 23 + 47 i explica el resultat.")
],
"iteracio_actual": 0,
"resultat_final": ""
})
print(resultat["missatges"][-1].content)
3.3 LlamaIndex: RAG avançat
LlamaIndex esta especialitzat en RAG i indexacio de dades per a LLMs. Ofereix mes opcions que LangChain per a casos d'us complexos:
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.core.node_parser import SentenceSplitter
# Configuracio global
Settings.llm = OpenAI(model="gpt-4o-mini", temperature=0)
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
Settings.node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=50)
# Carregar documents i crear index
documents = SimpleDirectoryReader("./documents").load_data()
index = VectorStoreIndex.from_documents(documents)
# Query engine
query_engine = index.as_query_engine(
similarity_top_k=5,
response_mode="tree_summarize" # Millor per a preguntes sobre documents llargs
)
resposta = query_engine.query("Quines son les principals limitacions dels LLMs actuals?")
print(resposta.response)
print("\nFonts:")
for node in resposta.source_nodes:
print(f" - {node.metadata.get('file_name', 'desconegut')}: score={node.score:.3f}")
3.4 CrewAI: sistemes multi-agent
CrewAI permet definir equips d'agents especialitzats que col·laboren per resoldre tasques complexes:
from crewai import Agent, Task, Crew, Process
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
# Definir agents especialitzats
investigador = Agent(
role="Investigador d'IA",
goal="Investigar i recopilar informacio actualitzada sobre temes d'IA",
backstory="""Ets un investigador expert en IA amb acces a les ultimes publicacions.
Sempre cites les fonts i valides la informacio.""",
llm=llm,
verbose=True
)
redactor = Agent(
role="Redactor Tecnic",
goal="Redactar articles tecnics clars i precisos en catala",
backstory="""Ets un redactor tecnic expert que transforma informacio complexa
en contingut accessible per a professionals del sector.""",
llm=llm,
verbose=True
)
# Definir tasques
tasca_investigar = Task(
description="Investiga l'estat actual dels models multimodals el 2025: GPT-4o, Gemini 1.5, LLaVA",
expected_output="Informe de recerca amb comparativa de capacitats i casos d'us",
agent=investigador
)
tasca_redactar = Task(
description="Redacta un article de 500 paraules en catala sobre els models multimodals del 2025",
expected_output="Article complet en catala, formatat en Markdown",
agent=redactor,
context=[tasca_investigar] # Depe n de la tasca anterior
)
# Crear i executar el crew
crew = Crew(
agents=[investigador, redactor],
tasks=[tasca_investigar, tasca_redactar],
process=Process.sequential,
verbose=True
)
resultat = crew.kickoff()
print(resultat.raw)
4. MLOps: Cicle de vida dels models
4.1 MLflow: experiment tracking
MLflow es l'eina open-source estandard per a tracking d'experiments de ML:
import mlflow
import mlflow.sklearn
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, f1_score
import pandas as pd
# Configurar servidor MLflow (local o remot)
mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("classificador-sentiment-5073")
with mlflow.start_run(run_name="rf-baseline"):
# Parametres
n_estimators = 200
max_depth = 10
mlflow.log_params({
"model": "RandomForestClassifier",
"n_estimators": n_estimators,
"max_depth": max_depth,
"features": "tfidf-5000"
})
# Entrenar
clf = RandomForestClassifier(n_estimators=n_estimators, max_depth=max_depth)
clf.fit(X_train, y_train)
# Metriques
y_pred = clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred, average='weighted')
mlflow.log_metrics({"accuracy": accuracy, "f1_weighted": f1})
# Guardar el model
mlflow.sklearn.log_model(clf, "model", registered_model_name="sentiment-classifier")
print(f"Accuracy: {accuracy:.4f}, F1: {f1:.4f}")
print(f"Run ID: {mlflow.active_run().info.run_id}")
# Desplegar MLflow amb Docker
docker run -d \
--name mlflow-joan-garcia \
-p 5000:5000 \
-v $(pwd)/mlruns:/mlruns \
ghcr.io/mlflow/mlflow:latest \
mlflow server \
--host 0.0.0.0 \
--port 5000 \
--backend-store-uri sqlite:///mlruns/mlflow.db \
--default-artifact-root /mlruns/artifacts
4.2 Weights & Biases (W&B)
W&B ofereix visualitzacio d'experiments mes rica que MLflow, especialment per a deep learning:
import wandb
import torch
wandb.init(
project="modul-5073-dl",
name="transformer-sentiment-v1",
config={
"learning_rate": 2e-5,
"epochs": 10,
"batch_size": 32,
"model": "bert-base-multilingual-cased",
"dataset": "twitter-catala-sentiment"
}
)
# Durant l'entrenament
for epoch in range(config.epochs):
train_loss = entrenar_epoch(model, train_loader, optimizer)
val_metrics = avaluar(model, val_loader)
wandb.log({
"epoch": epoch,
"train/loss": train_loss,
"val/accuracy": val_metrics["accuracy"],
"val/f1": val_metrics["f1"],
"lr": optimizer.param_groups[0]["lr"]
})
# Guardar el millor model
if val_metrics["f1"] > best_f1:
wandb.save("model_best.pt")
wandb.finish()
5. Comparativa de costos i casos d'ús
graph TD
PROBLEMA["Problema d'IA"] --> Q1{"Necessita privacitat\ntotal de les dades?"}
Q1 -->|"Si"| LOCAL["Models Locals\n(Ollama + LLaMA 3.1)"]
Q1 -->|"No"| Q2{"Quin es el\nvolum de peticions?"}
Q2 -->|"Alt (>1M/mes)"| Q3{"Prioritat?"}
Q2 -->|"Baix (<100K/mes)"| API_COM["API Comercial\n(OpenAI, Anthropic)"]
Q3 -->|"Cost"| BEDROCK["AWS Bedrock\nor Azure AI Foundry"]
Q3 -->|"Rendiment"| GPT4O["OpenAI GPT-4o\nor Claude 3.5 Sonnet"]
API_COM --> Q4{"Tasca complexa\no senzilla?"}
Q4 -->|"Senzilla"| MINI["GPT-4o-mini\nor Claude Haiku"]
Q4 -->|"Complexa"| POTENT["GPT-4o\nor Claude Sonnet"]
| Cas d'us | Plataforma recomanada | Motiu |
|---|---|---|
| Prototipat rapid | Ollama + LLaMA 3.1 | Gratu it, rapid d'iniciar |
| Produccio de baix cost | GPT-4o-mini o Claude Haiku | Equilibri preu/qualitat |
| Analisi de documents llargs | Claude 3.5 Sonnet (200K ctx) | Context enorm |
| Tasques de codi | Claude 3.5 Sonnet o GPT-4o | Millors en codi |
| Multimodal (imatges/audio) | GPT-4o o Gemini 1.5 | Natius multimodals |
| Empresa amb GDPR estricte | Azure AI Foundry | Dades a Europa, SLA |
| Recerca amb GPU local | Hugging Face + PyTorch | Control total |
| MLOps a escala | AWS SageMaker + Bedrock | Integracio AWS |
Exercici pràctic
AC5073/01-1 — Comparativa de plataformes
Implementa un script Python que faci la mateixa pregunta tecnica a tres plataformes diferentes (OpenAI GPT-4o-mini, Claude 3.5 Haiku i Ollama LLaMA 3.1) i compari:
- Qualitat de la resposta (valoracio subjectiva 1-5)
- Temps de resposta (mesurat amb
time.perf_counter()) - Cost estimat (calculat a partir dels tokens usats)
- Format de la resposta (clar, estructurat, amb exemples?)
Entrega un informe en Markdown amb els resultats i les teves conclusions sobre quin model triaries per a cada cas d'us.
Preguntes de reflexio
- Per quin motiu Azure AI Foundry es preferible a OpenAI directament per a una empresa bancaria europea?
- Quins son els avantatges i desavantatges d'usar Ollama respecte a les APIs comercials en un entorn de produccio?
- Quan triaries LlamaIndex per sobre de LangChain per a un projecte RAG?
- Que es el "vendor lock-in" en el context de les APIs d'IA i com es pot mitigar?