La lliçó anterior va acabar amb una pregunta que el nostre flux d'un sol pas no pot respondre: "el meu projecte Atlas supera el límit de tasques del meu pla?". Calen dues consultes a fonts diferents — la documentació per al límit del pla, l'API per a les tasques actuals — i una comparació que només té sentit quan totes dues dades són sobre la taula. Cap guió fix escrit per endavant no ho resol amb elegància, perquè què consultar i en quin ordre depèn de la pregunta. La solució és deixar que el model dirigeixi: cridar-lo en bucle, donant-li a cada volta els resultats del que va demanar, fins que decideixi que ja pot respondre. Aquest bucle — raonar, triar eina, executar, observar, repetir — és el que converteix un LLM en un agent, i és la paraula més sobrecarregada del sector explicada en vint línies de Python. En aquesta lliçó les escrivim: agent.py, amb les seves condicions de sortida, les seves tres eines (una de les quals serà el RAG del mòdul 4, reciclat com a eina), la seva primera acció d'escriptura amb confirmació humana, i les seves traces. I tancarem amb la pregunta més important i menys formulada: quan no fer servir un agent.
Contingut
- Què converteix un LLM en agent: el bucle raonar-actuar-observar
- El bucle en Python:
agent.pyi les seves dues condicions de sortida - Les tres eines de DocuBot (i el RAG convertit en eina)
- Accions d'escriptura:
crear_tiquet_suporti la confirmació humana - Traces: veure el que l'agent fa pas a pas
- Quan NO fer servir un agent: pipeline fix vs bucle agèntic
Què converteix un LLM en agent: el bucle raonar-actuar-observar
A 05-01 el flux era una partitura: primera crida, executar eina, segona crida, fi. Funcionava perquè el guió l'escrivíem nosaltres. Un agent inverteix el repartiment de papers: nosaltres escrivim el bucle i el model escriu el guió — decideix a cada iteració si necessita una altra eina, quina, amb quins arguments, i quan ha acabat. L'estructura és sempre la mateixa:
flowchart TD
P["Petició de l'usuari"] --> L["Crida al LLM<br/>(historial complet + eines)"]
L --> D{stop_reason}
D -- "end_turn:<br/>el model dona la tasca per resolta" --> R[Resposta final a l'usuari]
D -- "tool_use:<br/>el model demana una o més eines" --> E["El teu codi executa<br/>(validant, amb mínim privilegi)"]
E --> T["tool_result(s) afegits a l'historial"]
T --> C{"iteracions < màxim?"}
C -- "sí" --> L
C -- "no" --> S["Sortida de seguretat:<br/>informar del límit assolit"]Cada volta del bucle és un cicle raonar → actuar → observar: el model raona sobre l'historial (que creix amb cada resultat), actua demanant una eina, i observa el tool_result a la crida següent. La memòria de l'agent entre passos és simplement la llista messages — la mateixa mecànica de conversa de 03-03, només que aquí els torns els genera el mateix procés en lloc d'un humà.
Dues conseqüències d'aquest disseny que convé interioritzar abans d'escriure codi:
- L'agent és no determinista per partida doble: en el text que genera (això ja ho sabíem) i en el camí que recorre. Dues execucions de la mateixa pregunta poden fer servir les eines en ordre diferent, o en nombre diferent. Aquesta és la seva força (flexibilitat) i la seva factura (previsibilitat), i dona forma a tota la secció final.
- Cada iteració és una crida completa a l'API amb l'historial acumulat: el cost i la latència creixen amb cada volta (i l'historial s'engreixa amb cada
tool_result). Un agent que fa cinc voltes costa, com a mínim, cinc crides — recorda-ho quan revisitem la projecció de costos de 03-04.
El bucle en Python: agent.py i les seves dues condicions de sortida
El bucle complet. És sorprenentment curt — la robustesa l'heretem, com sempre, de les capes anteriors (cridar_amb_reintents de 03-02, executar_eina de 05-01):
# agent.py — el bucle agèntic de DocuBot
import json
from client import MODEL, extreure_text
from reintents import cridar_amb_reintents
from prompts import SYSTEM_DOCUBOT_AGENT
from eines import EINES, executar_eina
from traces import registrar_pas
MAX_ITERACIONS = 8 # cinturó de seguretat: mai en bucle infinit
MISSATGE_LIMIT = (
"No he pogut completar la petició en el nombre màxim de passos permès. "
"Prova de formular-la de manera més concreta o divideix-la en parts."
)
def bucle_agent(pregunta: str,
eines: list[dict] = EINES,
max_iteracions: int = MAX_ITERACIONS) -> str:
"""Raonar -> actuar -> observar, fins que el model acabi o s'esgoti el límit."""
messages = [{"role": "user", "content": pregunta}]
for iteracio in range(1, max_iteracions + 1):
# RAONAR: el model veu tot l'historial i les eines
resposta = cridar_amb_reintents(
model=MODEL,
max_tokens=1024,
system=SYSTEM_DOCUBOT_AGENT,
tools=eines,
messages=messages,
)
# Condició de sortida natural: el model dona la tasca per acabada
if resposta.stop_reason != "tool_use":
registrar_pas(iteracio, "resposta_final", None)
return extreure_text(resposta)
# ACTUAR: executar TOTS els tool_use del torn (n'hi pot haver més d'un)
messages.append({"role": "assistant", "content": resposta.content})
resultats = []
for bloc in resposta.content:
if bloc.type != "tool_use":
continue # els blocs de text no s'executen
registrar_pas(iteracio, bloc.name, bloc.input)
try:
sortida = executar_eina(bloc.name, bloc.input)
resultats.append({
"type": "tool_result",
"tool_use_id": bloc.id,
"content": json.dumps(sortida, ensure_ascii=False),
})
except Exception as e:
resultats.append({
"type": "tool_result",
"tool_use_id": bloc.id,
"content": str(e),
"is_error": True, # el model decidirà com reaccionar
})
# OBSERVAR: els resultats entren a l'historial per a la volta següent
messages.append({"role": "user", "content": resultats})
# Condició de sortida d'emergència: el cinturó de seguretat
registrar_pas(max_iteracions, "limit_assolit", None)
return MISSATGE_LIMITLes dues condicions de sortida són l'anatomia del bucle i mereixen nom propi:
| Sortida | Mecanisme | Què significa |
|---|---|---|
| Natural | stop_reason != "tool_use" |
El model, després d'observar els resultats, ha decidit que ja pot respondre. És el final feliç i l'habitual. |
| D'emergència | max_iteracions esgotades |
Alguna cosa va malament: el model entra en cercles (reintenta una eina que falla, o "dubta" entre dues), i sense límit el bucle giraria per sempre, cremant diners a cada volta. |
El valor de MAX_ITERACIONS es tria mirant els teus casos reals: per a DocuBot, amb tres eines i preguntes que rarament necessiten més de 2-3 passos, 8 dona marge sense permetre desastres. És el mateix esperit que el circuit breaker de 03-02 — un mecanisme que tant de bo no salti mai, i que no és opcional.
Falta el system. SYSTEM_DOCUBOT_AGENT estén el de 05-01 amb les regles de quan fer servir què, i recupera les nostres dues frases sagrades:
# prompts.py (afegit a 05-02) SYSTEM_DOCUBOT_AGENT = """Ets DocuBot, l'assistent intern de Nubelia. Només respons sobre Nubelia. Eines i quan fer-les servir: - Preguntes sobre com funciona Nubelia (plans, permisos, integracions, API...): fes servir SEMPRE cercar_documentacio abans de respondre, i cita les fonts. - Dades en temps real de projectes de l'usuari: fes servir consultar_projecte. - Crear un tiquet de suport: fes servir crear_tiquet_suport NOMÉS si l'usuari ho demana explícitament. No en creïs mai per iniciativa pròpia. Regles: - Si cercar_documentacio no retorna res de rellevant, respon exactament: "No trobo aquesta informació a la documentació disponible." - No inventis mai dades de projectes ni continguts de la documentació. - Respon en l'idioma de l'usuari, amb to professional i concís."""
Les tres eines de DocuBot (i el RAG convertit en eina)
L'agent estrena arsenal: a consultar_projecte (05-01) s'hi sumen dues eines noves. La primera és un moment important del curs: el pipeline RAG del mòdul 4 es converteix en eina. No reescrivim res — cercar_documentacio és un embolcall fi sobre recuperar() i construir_documentacio() de rag.py:
# eines.py (ampliat a 05-02)
from rag import recuperar, construir_documentacio, LLINDAR_RELLEVANCIA
import tickets
def cercar_documentacio(consulta: str) -> dict:
"""RAG com a eina: recuperar chunks i retornar-los amb les seves fonts."""
chunks = recuperar(consulta) # k=4, col·lecció docs_nubelia
if not chunks or chunks[0]["similitud"] < LLINDAR_RELLEVANCIA:
return {"resultat": "sense_informacio_rellevant", "fragments": ""}
return {
"resultat": "ok",
"fragments": construir_documentacio(chunks), # numerats i amb font
"fonts": sorted({c["url"] for c in chunks}),
}Fixa't en el canvi de perspectiva respecte de 04-04: allà, respondre_amb_rag() era l'amo del flux (recuperar → augmentar → generar). Aquí la generació l'orquestra el bucle, així que l'eina només recupera i augmenta — retorna els fragments i deixa que l'agent decideixi què fer-ne (potser necessita a més consultar l'API abans de redactar). El guarda-rail del llindar continua dempeus: si res no és rellevant, l'eina ho diu amb un senyal inequívoc (sense_informacio_rellevant), i el system ordena al model respondre amb la frase anti-al·lucinació de sempre. Les defenses de 04-04 no es perden; canvien de lloc.
Les definicions de les tres eines, amb els seus JSON Schema:
EINES = [
{
"name": "cercar_documentacio",
"description": (
"Cerca a la documentació oficial de Nubelia i retorna els fragments "
"més rellevants amb les seves fonts. Fes-la servir SEMPRE abans de "
"respondre preguntes sobre el funcionament de Nubelia: plans, límits, "
"permisos, facturació, integracions, API. Si retorna "
"'sense_informacio_rellevant', no inventis la resposta."
),
"input_schema": {
"type": "object",
"properties": {
"consulta": {"type": "string",
"description": "Consulta de cerca, específica i autocontinguda."},
},
"required": ["consulta"],
},
},
{ # sense canvis des de 05-01
"name": "consultar_projecte",
"description": "Consulta l'estat EN TEMPS REAL d'un projecte de l'usuari...",
"input_schema": {
"type": "object",
"properties": {"nom": {"type": "string"}},
"required": ["nom"],
},
},
{
"name": "crear_tiquet_suport",
"description": (
"Crea un tiquet al sistema de suport de Nubelia. És una ACCIÓ "
"D'ESCRIPTURA: fes-la servir només quan l'usuari demani explícitament "
"obrir un tiquet, mai per iniciativa pròpia. Resumeix el problema al "
"títol i aporta el detall a la descripció."
),
"input_schema": {
"type": "object",
"properties": {
"titol": {"type": "string", "description": "Resum breu del problema."},
"descripcio": {"type": "string", "description": "Detall del problema i context."},
"prioritat": {"type": "string", "enum": ["baixa", "mitjana", "alta"]},
},
"required": ["titol", "descripcio", "prioritat"],
},
},
]I el despatxador creix amb les dues entrades noves (amb un dict de funcions, que escala millor que la cadena d'if de 05-01):
_REGISTRE = {
"cercar_documentacio": lambda args: cercar_documentacio(args["consulta"]),
"consultar_projecte": lambda args: api_nubelia.obtenir_projecte(args["nom"]),
"crear_tiquet_suport": lambda args: tickets.crear_amb_confirmacio(args),
}
def executar_eina(nom: str, arguments: dict):
if nom not in _REGISTRE:
raise ValueError(f"Eina desconeguda: {nom}")
return _REGISTRE[nom](arguments)Accions d'escriptura: crear_tiquet_suport i la confirmació humana
Fins ara totes les eines de DocuBot eren de lectura: si el model s'equivocava, el dany era una resposta dolenta. crear_tiquet_suport creua una línia nova — modifica el món (crea alguna cosa en un altre sistema, molesta un equip humà, no es desfà refrescant la pàgina). Per a accions així, dos principis que ja vam apuntar a 05-01 deixen de ser consell i passen a ser disseny:
- Mínim privilegi: la credencial d'aquesta eina només pot crear tiquets — no llegir-los tots, no tancar-los, no tocar res més. Si demà un prompt maliciós enganya l'agent (l'atac estrella de 06-01), el radi de l'explosió és el que tu has acotat avui.
- Confirmació humana (human-in-the-loop): el model proposa l'acció amb tots els seus arguments, però un humà l'aprova abans que passi. La decisió d'actuar sobre el món és sempre humana; l'agent prepara, no dispara.
El patró, en la seva versió mínima de consola:
# tickets.py — acció d'escriptura amb confirmació humana
_SEGUENT_ID = 1001
def crear_amb_confirmacio(args: dict) -> dict:
"""Mostra l'acció proposada i només l'executa si un humà l'aprova."""
print("\n--- L'agent proposa crear un tiquet ---")
print(f" Títol: {args['titol']}")
print(f" Prioritat: {args['prioritat']}")
print(f" Descripció: {args['descripcio']}")
if input("Confirmes la creació? (s/n): ").strip().lower() != "s":
# El rebuig NO és un error del sistema: és informació per al model
return {"resultat": "cancelat",
"detall": "L'usuari no ha autoritzat la creació del tiquet."}
return _crear_tiquet(args) # aquí aniria la crida real a l'API
def _crear_tiquet(args: dict) -> dict:
global _SEGUENT_ID
tiquet_id = f"NUB-{_SEGUENT_ID}"
_SEGUENT_ID += 1
return {"resultat": "creat", "id": tiquet_id, "prioritat": args["prioritat"]}El detall més fi és al rebuig: es retorna com a tool_result normal (no is_error), perquè no ha fallat res — l'humà ha exercit el seu dret de vet, i el model se n'ha d'assabentar per reaccionar bé ("D'acord, no he creat el tiquet. Vols que canviï alguna cosa abans de tornar-ho a intentar?"). L'input() és la versió de joguina del patró; en una aplicació real la confirmació seria un botó a la interfície o un pas d'aprovació asíncron, però l'arquitectura — l'eina d'escriptura no executa sense un sí humà — és exactament la mateixa. A 06-01 veurem per què aquesta línia de defensa importa encara més del que avui sembla.
Traces: veure el que l'agent fa pas a pas
Un pipeline fix es depura llegint el codi: el camí és sempre el mateix. Un agent, no — el camí el decideix el model a cada execució, així que si no registres el que ha fet, no saps el que ha fet. D'aquí el registrar_pas que esquitxa el bucle:
# traces.py — versió mínima (06-04 la professionalitzarà)
import json, time
def registrar_pas(iteracio: int, accio: str, arguments: dict | None):
marca = time.strftime("%H:%M:%S")
args = json.dumps(arguments, ensure_ascii=False) if arguments else ""
print(f"[{marca}] pas {iteracio}: {accio} {args}")Amb això, la pregunta que obria la lliçó deixa de ser una caixa negra. bucle_agent("El meu projecte Atlas supera el límit de tasques del meu pla?") produeix una traça com aquesta:
[10:42:01] pas 1: cercar_documentacio {"consulta": "límit de tasques per projecte segons el pla"}
[10:42:04] pas 2: consultar_projecte {"nom": "Atlas"}
[10:42:07] pas 3: resposta_finalI la resposta final combina totes dues fonts: "El teu pla Professional permet fins a 200 tasques obertes per projecte (font: /docs/plans). Atlas en té ara 27 d'obertes, així que ets molt lluny del límit." Llegeix-la una altra vegada a poc a poc: la primera meitat ve del RAG, la segona de l'API, i la comparació la va fer el model a la volta 3 en observar tots dos resultats a l'historial. Cap codi nostre no sabia que aquesta pregunta necessitava dues eines — això és l'agent.
Les traces són, a més, el teu detector de patologies: una traça on consultar_projecte apareix quatre vegades amb el mateix argument delata un model encallat reintentant; una on cercar_documentacio no apareix abans d'una resposta sobre plans delata que el system no està subjectant el model. Aquesta llavor — registrar cada decisió amb la seva marca de temps — germinarà a 06-04 com a observabilitat de debò (traces estructurades, tokens i cost per pas, panells); de moment, un print honest val or.
Quan NO fer servir un agent: pipeline fix vs bucle agèntic
La pregunta de tancament, i la més rendible de la lliçó. Ara que DocuBot pot funcionar com a agent, ho ha de fer sempre? No — i la taula explica per què el respondre_amb_rag() de 04-04 no es jubila:
| Criteri | Pipeline fix (p. ex. respondre_amb_rag) |
Agent (bucle_agent) |
|---|---|---|
| Cost | Conegut i constant: 1 crida LLM (+embeddings) | Variable: N crides, historial creixent a cadascuna |
| Latència | Predictible (~una crida) | Impredictible: cada volta suma segons |
| Previsibilitat | El mateix camí sempre; testejable com una funció | El camí canvia entre execucions; testejar costa més (06-03) |
| Depuració | Llegir el codi | Llegir les traces |
| Flexibilitat | Només resol el cas per al qual es va dissenyar | Combina eines per a casos no previstos |
| Quan brilla | Tasca coneguda amb passos coneguts | Tasca el pla de la qual depèn de la petició |
La regla pràctica que se'n deriva: fes servir l'eina més simple que resolgui el problema. "Com configuro l'SSO?" és una pregunta de documentació pura: respondre_amb_rag() la resol amb una crida, cost fix i el contracte JSON validat de sempre — enviar-la a l'agent és pagar una flexibilitat que no es fa servir. "Atlas supera el límit del meu pla?" necessita pla dinàmic: agent. Un bon DocuBot de producció probablement combinaria tots dos — un classificador al davant (et sona prompt_classificacio() de 02-02?) que encamina allò simple al pipeline i allò compost a l'agent. Aquesta orquestració de fluxos amb branques és justament el terreny dels frameworks, i és la lliçó següent.
Errors Comuns i Consells
- Bucle sense límit d'iteracions. "El model sempre acaba" és veritat fins que una eina trencada el fica en un cicle de reintents.
max_iteracionsés com el timeout d'una petició HTTP: no negociable. - Rebentar el bucle quan una eina falla. Una excepció no capturada mata l'agent a mitja tasca. Captura-la i retorna-la com a
tool_resultambis_error— sovint el model es recupera sol (prova un altre argument, o una altra eina) i aquesta resiliència és gratis. - Processar només el primer
tool_usedel torn. El model pot demanar diverses eines alhora (el nostre exemple d'Atlas de vegades demana documentació i projecte a la mateixa volta). Cadatool_use_idexigeix el seutool_result; si en falta un, l'API rebutja la crida següent. - Eines d'escriptura sense fre humà. Si l'agent pot crear/modificar/esborrar sense confirmació, has delegat una decisió irreversible a un procés no determinista. Proposa l'agent, decideix l'humà — sobretot mentre no tinguis l'avaluació (06-03) i les defenses (06-01) que justificarien relaxar el fre en casos acotats.
- Convertir-ho tot en agent. El símptoma: latències que es tripliquen i factures que no quadren amb la projecció de 03-04, per a preguntes que un pipeline fix resolia igual de bé. Repassa la taula; l'agent és un martell excel·lent, però no tot són claus.
- Traces que arriben tard. Afegir registre després del primer comportament estrany vol dir depurar a cegues la primera vegada. La traça s'escriu amb el bucle, no després de l'incident.
Exercicis
- Traces amb cost. Amplia
registrar_pas(i el bucle) per registrar també, a cada crida al LLM, els tokens d'entrada i de sortida (resposta.usage.input_tokens/output_tokens) i el cost estimat ambestimar_cost()depreus.py(03-04). Executa la pregunta d'Atlas i respon: quina proporció del cost total se'n va anar a l'última volta, i per què és la més cara? - Sortida d'emergència més digna. Quan s'esgota
max_iteracions,MISSATGE_LIMITno aprofita res del que s'ha esbrinat. Modifica el bucle perquè, en esgotar-se el límit, faci una última crida sense eines (pista:tool_choice: {"type": "none"}) demanant al model que resumeixi què ha trobat fins al moment i què li ha faltat. - Pipeline o agent? Per a cada petició, decideix quin camí prendria un DocuBot ben dissenyat i justifica-ho en una frase amb els criteris de la taula: (a) "Quins mètodes de pagament accepteu?"; (b) "Compara les tasques obertes dels meus tres projectes i digue'm quin va pitjor"; (c) "Com creo un webhook?"; (d) "El webhook de Boreal ha deixat de funcionar; si és un problema conegut digue-m'ho i si no, obre un tiquet".
Solucions
- Canvis mínims:
# Al bucle, després de cada crida:
cost = estimar_cost(resposta.usage.input_tokens, resposta.usage.output_tokens)
registrar_pas(iteracio, "crida_llm", {
"entrada": resposta.usage.input_tokens,
"sortida": resposta.usage.output_tokens,
"cost": round(cost, 5),
})L'última volta és la més cara en tokens d'entrada perquè l'historial acumula tots els tool_result anteriors (els fragments de documentació pesen especialment); típicament concentra el 40-60% del cost total. És la raó econòmica del "fes servir l'eina més simple": cada volta evitada estalvia la més cara de les crides que haurien vingut després. (I sí: el prefix estable — system + definicions d'eines — és candidat ideal al prompt caching de 03-04.)
- Substituir el
return MISSATGE_LIMITfinal per:
messages.append({"role": "user", "content": (
"Has arribat al nombre màxim de passos. No facis servir més eines: "
"resumeix què has esbrinat fins ara, què t'ha faltat per confirmar "
"i què podria fer l'usuari a continuació."
)})
final = cridar_amb_reintents(
model=MODEL, max_tokens=1024,
system=SYSTEM_DOCUBOT_AGENT,
tools=EINES,
tool_choice={"type": "none"}, # prohibit demanar més eines
messages=messages,
)
return extreure_text(final)Degradació elegant, edició agèntica: la mateixa filosofia que RESPOSTA_FALLBACK a 03-02 — si no pots donar-ho tot, dona el millor que tinguis i sigues honest sobre el que falta.
- (a) Pipeline (
respondre_amb_rag): documentació pura, camí conegut, cost fix. (b) Agent: nombre de passos dinàmic (llistar projectes, consultar cadascun, comparar) que cap pipeline fix no preveu amb elegància. (c) Pipeline: una altra pregunta de documentació amb disfressa tècnica. (d) Agent, i de manual: requereix cercar a la documentació (problema conegut?), potser consultar el projecte, i condicionalment una acció d'escriptura amb confirmació humana — és literalment el bucle raonar-actuar-observar amb les tres eines de la lliçó.
Conclusió
Vint línies de bucle han canviat la naturalesa de DocuBot. Repassem el que hem construït: el cicle raonar → actuar → observar implementat a agent.py::bucle_agent(), amb la sortida natural per stop_reason i el cinturó de seguretat de max_iteracions; tres eines on abans n'hi havia una — cercar_documentacio (el RAG del mòdul 4 reconvertit en eina: recuperar i augmentar, deixant la generació al bucle), consultar_projecte (05-01) i crear_tiquet_suport, la primera acció d'escriptura del curs, estrenant el patró human-in-the-loop: l'agent proposa amb arguments complets, l'humà confirma, i el rebuig torna al model com a informació, no com a error; traces que converteixen cada execució en una història llegible (la llavor de 06-04); i una taula que ens vacuna contra l'entusiasme — el pipeline fix de 04-04 continua sent la millor resposta per a la pregunta simple, i l'agent es reserva per a quan el pla depèn de la petició. Ara bé: tot això ho hem escrit a mà — el bucle, el registre d'eines, la memòria a messages, les traces, la confirmació. Ha estat deliberat (no hi ha millor manera d'entendre què fa un agent que escriure'n un), però segurament t'has preguntat si no existeix ja una peça de prestatgeria que porti tot això resolt. Existeix — n'existeixen desenes, i aquest és precisament el problema. La lliçó següent hi posa ordre: què resolen els frameworks d'orquestració, què costen en abstracció i dependències, com es comparen LangChain/LangGraph, LlamaIndex, Haystack i els SDKs d'agents dels proveïdors, què és aquell estàndard anomenat MCP de què tothom parla — i per què, sabent-ho tot, DocuBot continuarà portant el seu bucle escrit a mà.
Curs d'IA Generativa i LLMs per a Desenvolupadors
Mòdul 1: Fonaments de la IA generativa
- Què és la IA generativa i per què importa als desenvolupadors
- Com funciona un LLM: tokens, embeddings i atenció
- Panorama de models i proveïdors
- Limitacions i riscos: al·lucinacions, context i costos
Mòdul 2: Prompt engineering
- Anatomia del prompt: rols, instruccions i context
- Tècniques de prompting: few-shot, cadena de raonament i plantilles
- Sortides estructurades: JSON i control de format
- Iteració i avaluació de prompts
Mòdul 3: Integració de LLMs en aplicacions
- Primera integració amb l'API d'un LLM
- Streaming, errors i reintents
- Converses i gestió del context
- Costos, latència i caching
Mòdul 4: RAG - Generació augmentada per recuperació
- Embeddings i cerca semàntica
- Bases de dades vectorials
- Ingesta i chunking de documents
- Construcció d'un pipeline RAG complet
- Avaluació de sistemes RAG
Mòdul 5: Function calling i agents
- Function calling: connectar el LLM amb el teu codi
- De LLM a agent: el bucle de raonament i acció
- Frameworks d'orquestració
