Commit d662691f authored by jg5dev's avatar jg5dev 💬
Browse files

better

parent 7be67240
Loading
Loading
Loading
Loading
+27 −18
Changes for src/apren/llms/llm_systems.md: 27 added lines, 18 removed lines.
Original line number Diff line number Diff line
@@ -257,35 +257,44 @@ El format de *chat completions* és el punt d'interoperabilitat entre proveïdor

### Abstraccions multi-proveïdor i SDKs

> 📝 Els dos exemples d'aquesta secció, LangChain i LiteLLM, són per **comparar formes**, no per executar-los: cap de les dues llibreries no forma part de l'entorn de treball d'aquests materials, que treballa directament amb l'SDK d'OpenAI contra un servidor propi. El que has de saber llegir és què canvia entre una abstracció i l'altra, i quin preu té cadascuna.
> 📝 En aquest repositori d'exercicis, la base és l'SDK d'OpenAI i l'ús directe del client del servidor d'inferència. Els frameworks de capa superior es fan servir com a referència conceptual, però no com a exemples executables aquí.

Per a codi multi-proveïdor, la solució habitual és el **patró estratègia** que ofereix LangChain: defineix tipus de missatge propis (`SystemMessage`, `HumanMessage`, `AIMessage`) i cada integració de proveïdor els tradueix al format natiu. La lògica de l'aplicació és idèntica entre proveïdors; el que canvia és la instanciació:
La forma més directa d'aconseguir el mateix patró de portabilitat és mantenir el format de `messages` i canviar només la configuració del client: la lògica de l'aplicació no canvia, i el proveïdor o el backend es decideix a la configuració.

```python
from langchain_core.messages import SystemMessage, HumanMessage
from langchain_openai import ChatOpenAI
# from langchain_anthropic import ChatAnthropic  # canvi de proveïdor: només aquestes dues línies
from openai import OpenAI
import instructor
from pydantic import BaseModel

class AnàlisiSentiment(BaseModel):
    sentiment: str
    confiança: float

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
client_estructurat = instructor.from_openai(client)

llm = ChatOpenAI(model=MODEL)
resposta = client_estructurat.chat.completions.create(
    model=MODEL,
    messages=[
    SystemMessage(content="Ets un analista de sentiment per a ressenyes de productes."),
    HumanMessage(content="El producte és fantàstic, però el lliurament va trigar massa."),
]
resposta = llm.invoke(messages)
        {"role": "user", "content": "El producte és fantàstic, però el lliurament va trigar massa."}
    ],
    response_model=AnàlisiSentiment,
)
```

No és una abstracció completament transparent: cal importar el paquet específic del proveïdor i instanciar la classe correcta. **LiteLLM** s'acosta més a la transparència total: manté el format de dicts natiu de Chat Completions i enruta les crides a qualsevol proveïdor canviant només el string `model` (la mateixa eina, desplegada com a servidor intermedi, és la capa de routing descrita a [Arquitectura de sistemes LLM](llm_architecture.md#abstraure-múltiples-proveïdors-la-capa-de-routing)):
Quan el backend ja és un servidor compatible amb OpenAI, el codi no necessita cap framework extra: el routing es fa a la capa de configuració, no a la lògica de negoci.

```python
import litellm
from openai import OpenAI

response = litellm.completion(
    model="ollama/<model>",  # o "<proveïdor>/<model>" per a qualsevol API comercial
    messages=[{"role": "user", "content": "El producte és fantàstic..."}]
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
response = client.chat.completions.create(
    model=MODEL,
    messages=[{"role": "user", "content": "El producte és fantàstic..."}],
)
```

A més, LiteLLM inclou ponts en les dues direccions cap a les APIs d'agents propietàries: si uses el format d'una API d'agents però apuntes a un model que no la suporta, tradueix la crida al format de chat completions internament. Això el converteix en l'abstracció més completa disponible, tot i que les funcions avançades pròpies de cada proveïdor continuen requerint baixar al SDK natiu.
Aquesta és la forma que s'usa al repo d'exercicis: sense abstraccions addicionals, amb un client compatible amb OpenAI i el model apuntant a un backend local o remot configurat a `base_url`.

### Elecció del SDK

@@ -368,8 +377,8 @@ class AnàlisiSentiment(BaseModel):
    confiança: float
    resum: str

# `client` és el client OpenAI ja configurat: instructor l'embolcalla i en conserva
# el `base_url`, cosa que importa quan apuntes a un servidor propi i no a l'API pública
# `client` és el client OpenAI ja configurat: instructor l'embolcalla i conserva la
# configuració del client, útil quan apunteu a un servidor propi i no a l'API pública
client_estructurat = instructor.from_openai(client)
resultat = client_estructurat.chat.completions.create(
    model=MODEL,
+4 −4
Changes for src/apren/llms/ml_transformers.md: 4 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -139,12 +139,12 @@ La majoria d'algorismes parteixen de fragments petits i fusionen iterativament e
**Exemple pràctic:**

```python
from transformers import AutoTokenizer
import tiktoken

tokenizer = AutoTokenizer.from_pretrained("bert-base-multilingual-cased")
tokens = tokenizer.tokenize("Les xarxes neuronals aprenen representacions")
tokenizer = tiktoken.get_encoding("cl100k_base")
tokens = tokenizer.encode("Les xarxes neuronals aprenen representacions")
print(tokens)
# ['Les', 'xa', '##rx', '##es', 'neuro', '##nal', '##s', 'aprenen', 'represent', '##acions']
# [123, 402, 6112, 918, 270, 7298, 1652, 265, 763, 4313, ...]
```

Observem que paraules menys freqüents com *"xarxes"* es divideixen en subparaules, mentre que paraules comunes com *"aprenen"* es mantenen senceres. Finalment, cada token es mapeja a un índex enter del vocabulari, i aquest índex es converteix en un **embedding** (vector numèric): l'entrada real que alimenta els blocs que veurem tot seguit.