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

better



Co-authored-by: default avatarCopilot <copilot@github.com>
parent b64a3e0e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@
  - [Tipus de projecte](./aitools/06_tipus_de_projecte.md)
  - [Pràctiques de treball amb agents](./aitools/07_practiques_i_tancament.md)
  - [Reflexions finals](./aitools/08_reflexions_finals.md)
  - [Desplegament autoallotjat](./aitools/09_desplegament.md)
- [Programació en Java](./java.md)
  - [Java bàsic](./java/basic_java.md)
  - [Recursivitat](./java/recursivitat.md)
+4 −0
Original line number Diff line number Diff line
@@ -12,3 +12,7 @@ Guia sobre eines IA per al desenvolupament de programari: fonaments, criteri d'
- [Tipus de projecte](./aitools/06_tipus_de_projecte.md)
- [Pràctiques de treball amb agents](./aitools/07_practiques_i_tancament.md)
- [Reflexions finals](./aitools/08_reflexions_finals.md)

Un annex que parla sobre com desplegar un sistema auto-allotjat:

- [Desplegament autoallotjat](./aitools/09_desplegament.md)
+301 −0
Original line number Diff line number Diff line
# Desplegament autoallotjat

<!-- toc -->

Aquest capítol resumeix com desplegar un stack d'eines d'IA autoallotjat per a programació.

L'objectiu no és inventariar productes, sinó descriure quines peces cal muntar, quines capacitats tècniques exigeix cada patró d'ús i quines decisions d'arquitectura tenen més impacte.

## Decisió i abast

### Abast del desplegament

Un desplegament útil per a desenvolupament acostuma a voler cobrir un o més d'aquests patrons:

- **Compleció inline** dins l'editor
- **Xat IDE** amb context del workspace
- **Agent IDE** amb lectura, escriptura i execució visibles
- **Agent CLI** des del terminal
- **Xat web** per a ús general

La tria importa perquè cada patró demana capacitats diferents del model i de la infraestructura.

| Patró | Superfície | Requisits tècnics principals |
| ----- | ---------- | ---------------------------- |
| Compleció inline | Editor | model ràpid, FIM, latència baixa |
| Xat IDE | IDE | instruction-following, coneixement de codi |
| Agent IDE | IDE | tool calling, context llarg, sandboxing |
| Agent CLI | Terminal | tool calling, context llarg, shell fiable |
| Xat web | Navegador | instruction-following, autenticació, observabilitat |

En pràctica, un desplegament institucional no ha de servir necessàriament tots cinc patrons, però sí que hauria de decidir explícitament quins dona suport.

### Quan compensa autoallotjar

L'autoallotjament té sentit sobretot quan el **codi o les dades no poden sortir a un proveïdor extern**, quan **hi ha prou usuaris** perquè el cost per seient comercial sigui rellevant, o quan cal una **configuració comuna per a tot un centre o departament** sense dependre de les decisions de producte d'un tercer.

No acostuma a compensar quan l'equip és petit i el trànsit és baix, quan no hi ha capacitat real d'operar GPU i serveis interns, o quan la UX de les eines comercials pesa més que el control. Si el cost operacional intern supera l'estalvi en llicències, rarament val la pena.

### La bretxa de qualitat i quan és acceptable

Abans de decidir, convé ser honest sobre una limitació estructural: un desplegament autoallotjat d'eines OSS **no arribarà, en general, al nivell de rendiment dels serveis comercials de primera línia**.

Els grans proveïdors (GitHub Copilot, Cursor amb models de frontera, etc.) operen models entrenats amb milers de milions de paràmetres, infraestructura dedicada a escala, cicles d'RLHF continus i GPU especialitzades que cap institució petita o mitjana pot replicar. La diferència no és de configuració, és estructural.

Això no invalida l'autoallotjament, però sí que exigeix preguntar: **per a quins casos d'ús és acceptable la bretxa?**

**Contextos on la bretxa acostuma a ser acceptable:**

- **Formació i educació** (FP, universitat, centres de formació): els estudiants no necessiten el millor model del mercat per aprendre a programar. El que importa és que el model sigui útil per a tasques de complexitat mitjana, que les dades dels alumnes no surtin fora, i que el cost no depengui del nombre de seients.

- **Laboratoris de recerca**: quan l'objectiu és experimentar amb arquitectures, fer fine-tuning o reproduir resultats, el control sobre el model importa més que el rendiment absolut. A més, les dades de recerca sovint no es poden cedir a tercers.

- **Empreses amb sobirania de dades obligatòria**: sectors regulats (legal, financer, salut, defensa) on les dades no poden sortir de la infraestructura pròpia. Aquí la bretxa de qualitat és un cost acceptat, no una opció.

- **Organitzacions amb volum suficient**: a partir d'un cert nombre d'usuaris —sovint a partir de 50-100 desenvolupadors actius—, el cost per seient dels serveis comercials es converteix en una variable rellevant. Si la bretxa de qualitat és petita per als casos d'ús concrets, el càlcul pot canviar.

- **Equips que volen independència de proveïdor**: no quedar lligat a la disponibilitat, els preus o les decisions de producte d'un únic servei extern.

**Contextos on la bretxa acostuma a ser un problema real:**

- Equips petits que necessiten màxima productivitat i no tenen recursos per operar infraestructura.
- Casos d'ús que exigeixen raonament complex, planificació multi-pas o generació de codi arquitectònicament exigent: aquí els models de frontera marquen una diferència visible.
- Qualsevol organització que no pugui assumir el cost operacional tècnic de mantenir el servei.

La conclusió pràctica és que l'autoallotjament no és "la mateixa cosa però gratis i amb control". És un conjunt diferent de *trade-offs*: menys qualitat màxima a canvi de més control, privacitat i independència. Decidir-lo bé requereix saber quins *trade-offs* són acceptables per als patrons d'ús concrets de l'organització.


## Requisits dels models

### Capacitats mínimes

Per desplegament interessa pensar en capacitats entrenades, no només en "mida del model".

La capacitat d'**instruction-following** és el mínim per a qualsevol ús de xat o agent. El **coneixement de codi** importa per a IDE, agents i revisió. El **FIM (*fill-in-the-middle*)** és imprescindible per a compleció inline real però no cal per a res més. El **tool calling** és necessari per a qualsevol agent que cridi shell, fitxers o serveis externs. El **context llarg** determina fins on arriben les tasques multi-fitxer i les sessions d'agent.

La relació pràctica és aquesta:

| Patró | Instruccions | Codi | FIM | Tool calling | Context llarg |
| ----- | :----------: | :--: | :-: | :----------: | :-----------: |
| Compleció inline | - | sí | sí | - | - |
| Xat IDE | sí | sí | - | opcional | opcional |
| Agent IDE | sí | sí | - | sí | sí |
| Agent CLI | sí | sí | - | sí | sí |
| Xat web | sí | opcional | - | - | opcional |

Una conclusió operativa: sovint és millor servir més d'un model. El patró habitual és un **model petit i ràpid** per a xat curt o compleció, un **model més fort** per a agents, i opcionalment un model especialitzat per a codi.

### Tool calling: la peça crítica dels agents

Per a agents, el risc no és només si el model genera bon text, sinó si sap fer servir eines de manera fiable.

Hi ha tres capes diferents:

1. **API de tool calling** que veu el client
2. **Format natiu de tool calls** que ha après el model
3. **Capacitat semàntica** de triar l'eina correcta

El servidor pot adaptar la primera a la segona, però no pot inventar la tercera. La implicació pràctica és clara: no n'hi ha prou que el servidor "suporti tools". Cal provar el model concret amb les eines reals del flux, perquè un model pot emetre JSON correcte i, tot i així, triar malament quina eina usar.

### Compleció inline: requisits específics

La compleció inline és el patró més sensible a latència i format. Requereix un **model amb FIM**, respostes curtes, latència molt baixa i context local petit però precís.

És habitual que un model bo per a agents no sigui el millor model per a compleció inline. Si aquest patró és prioritari, convé tractar-lo com una necessitat separada.

### Context llarg: nominal vs efectiu

En fitxes tècniques és habitual veure contextos de 128k o 256k tokens. Això no garanteix que el model raoni bé a aquesta longitud.

Per desplegament convé distingir el **context nominal** —el màxim que el servidor accepta— del **context efectiu**: la longitud real a la qual el model encara respon amb qualitat útil.

Els agents pateixen especialment si aquesta distinció s'ignora. El sistema pot "acceptar" molts fitxers i, tot i així, perdre qualitat de decisió.

## Arquitectura del stack

### Tres capes de referència

La forma més estable de desplegar aquest stack és separar tres peces:

1. **Servidor d'inferència**
2. **Gateway**
3. **Clients**

#### Servidor d'inferència

És la capa que carrega pesos, tokenitza, genera i retorna respostes.

Les seves responsabilitats centrals són servir un o més models, gestionar VRAM i concurrència, fer streaming, exposar tool calling i maximitzar el throughput sota càrrega. **Ollama** és l'opció senzilla d'operar, adequada per a entorns petits o prototips; **vLLM** és més adequat quan hi ha concurrència real i models grans.

#### Gateway

És la capa que desacobla clients i backends.

Les seves responsabilitats principals són l'**autenticació** i gestió de claus, els **rate limits i quotes**, el **routing entre models**, els **logs i auditoria**, i l'observabilitat d'errors, latència i ús. Opcionalment pot incorporar filtres de seguretat o redacció de secrets. L'exemple habitual és **LiteLLM Proxy**.

#### Clients

Són les eines que consumeixen el servei: extensions d'IDE, agents CLI i interfícies web. La regla útil és que els clients apuntin sempre al gateway, no directament als servidors d'inferència.

### API interna

La decisió més pragmàtica avui és exposar **OpenAI-compatible API** com a estàndard intern.

Això dona compatibilitat amb la majoria de clients OSS, menys feina d'integració i la possibilitat de canviar de backend sense tocar els clients. Els endpoints més rellevants acostumen a ser `/v1/chat/completions`, `/v1/models` i `/v1/embeddings`.

Si el desplegament és per a agents, cal verificar explícitament el suport de streaming, el format de `tools` i `tool_calls`, el comportament sota context llarg, i si el model realment suporta tool calling o només ho fa el servidor.

Afegir API nativa d'Ollama, endpoints propis i altres dialectes al mateix temps només compensa si hi ha un client concret que els necessita. Com a regla general, una API comuna simplifica configuració; més dialectes vol dir més testing i més compatibilitat a validar.

### Tria del servidor d'inferència

La decisió acostuma a ser menys "quin producte és millor?" i més "quina càrrega hem de suportar?".

**Ollama** encaixa bé amb equips petits, baixa concurrència i situacions on la simplicitat operativa és prioritària: és la millor opció per posar models en marxa ràpid sense gaire manteniment. **vLLM** és la millor tria quan hi ha molts usuaris simultanis, ús intensiu d'agents o models grans, i quan cal throughput alt i un millor control sobre el runtime. La migració entre tots dos és més fàcil si el gateway i els clients ja parlen només OpenAI-compatible.

## Operació del servei

### Autenticació i identificació d'usuaris

En un desplegament compartit, algun mecanisme d'autenticació és imprescindible. La solució més simple i més compatible és treballar amb **API keys**.

Les claus no serveixen només per "protegir" l'endpoint: serveixen sobretot per **atribuir cada petició a un usuari o servei concret**, aplicar quotes i límits, revocar accessos de manera individual i tenir observabilitat real sobre qui consumeix què. Sense identificació per usuari, el sistema només veu trànsit agregat, cosa que dificulta saber qui satura el servei, separar usos legítims d'abús o auditar incidents.

La pràctica recomanable és **una clau per usuari o per client de servei**, gestionada al gateway —no als servidors d'inferència—, vinculada a un rol o equip i amb logs amb atribució per clau.

En entorns petits, això es pot gestionar manualment. En entorns institucionals, és millor automatitzar la provisió i revocació de claus des d'una capa central.

### Gateway com a punt de control

El gateway és on convé centralitzar les polítiques del servei.

Com a mínim hauria de permetre saber **qui ha fet cada petició**, limitar volum per usuari o grup, decidir quin model usa cada client, desactivar ràpidament un model problemàtic i registrar errors, latència i volum de tokens. Sense això, un desplegament multiusuari és opac i difícil de governar.

### Dimensionament

El dimensionament depèn sobretot de la **mida dels models**, la **quantitat de VRAM** disponible, la **concurrència esperada** i el **tipus de càrrega**: xat curt, compleció o agents llargs.

Una institució amb pocs usuaris però molts agents simultanis pot necessitar més capacitat que una amb molts usuaris fent només xat puntual.

El throughput sota càrrega importa més que la velocitat percebuda en una demo amb un únic usuari.

### Pressupost de VRAM

La VRAM d'una GPU es reparteix entre dues coses que competeixen: els **pesos del model** i el **KV cache** de les peticions actives.

Els pesos ocupen un volum fix determinat per la mida del model i la quantitat. El KV cache creix amb cada petició activa: a més context i més concurrència, més memòria consumeix.

Aquesta tensió porta a una decisió de repartiment que val la pena fer explícita:

- si es carreguen **diversos models** simultàniament, els pesos consumeixen una fracció gran de la VRAM i queda poc marge per al KV cache. Cada model servit puja el cost fix.
- si es manté **un sol model de qualitat**, queda més VRAM disponible per al KV cache, cosa que millora la concurrència real i la capacitat d'atendre contextos llargs.

En un entorn amb pocs usuaris però necessitats exigents —agents amb molt de context, o concurrència moderada però sostinguda— la segona opció acostuma a rendir millor. Un model bo amb prou marge de KV cache supera un model millor que s'ofega per manca de memòria.

El corol·lari pràctic és que **afegir models a la mateixa GPU no és gratuït**: cada model que es carrega comprimeix el pressupost de KV cache de tots els altres. Si la VRAM és limitada, convé triar el model principal amb cura i reservar un marge explícit per al cache, en lloc d'omplir la GPU amb models addicionals que s'usen poc.

### Cues i compartició de GPU

Quan diversos usuaris comparteixen la mateixa GPU, la cua no és un detall d'implementació: és un requisit del sistema.

La raó és que la GPU no pot executar un nombre arbitrari de peticions alhora. Cada petició consumeix memòria, temps de còmput i, si el context és llarg, una part important del **KV cache**. Si arriben més peticions de les que la GPU pot servir simultàniament, algunes han d'esperar.

Aquí convé separar dues capes:

1. **Gateway**: controla quantes peticions poden arribar i de qui són.
2. **Servidor d'inferència**: decideix quines peticions entren realment a la GPU i en quin ordre.

#### Control al gateway

El gateway serveix per fer **admission control** abans que la petició arribi al model.

És el lloc natural per aplicar **límits de peticions i tokens per minut**, **quotes per usuari o grup**, accés diferenciat per rol i límits de context específics per patró o equip. Sense aquesta capa, un únic usuari pot saturar el backend encara que el servidor d'inferència sigui correcte.

#### Scheduling al servidor d'inferència

Un cop la petició ha passat el gateway, el servidor d'inferència encara ha de decidir quan entra a la GPU.

Aquí apareixen tres problemes reals: quantes seqüències poden estar actives alhora, quina prioritat tenen les peticions i quanta memòria reserva cadascuna per context i KV cache.

Aquest és el motiu pel qual la qualitat del *scheduler* importa tant en desplegaments multiusuari. `vLLM` està pensat per a aquest escenari; `Ollama` és molt més simple i funciona millor quan la concurrència és baixa.

#### El context llarg pressiona la cua

No totes les peticions consumeixen la GPU de la mateixa manera.

Una compleció inline a 4k és lleugera, un xat curt a 8–16k és moderat, però un agent a 64k o 128k pot ocupar una part molt gran del pressupost de KV cache.

Per això la capacitat no s'ha de pensar només en "peticions per minut", sinó en **contextos concurrents**. Una sola sessió llarga d'agent pot fer esperar moltes peticions petites.

Aquest punt és fàcil d'infravalorar: deu usuaris fent consultes curtes poden pressionar menys la GPU que un sol usuari amb un agent llarg.

#### Mecanismes de control

Per evitar que una sola sessió monopolitzi la GPU, convé combinar **límits de context per usuari al gateway**, un context màxim del servidor dimensionat al pitjor cas útil —no al màxim teòric del model—, un límit de seqüències actives simultànies al servidor i prioritats diferenciades per tipus d'usuari o de càrrega.

La regla pràctica és simple: si el sistema ha de ser compartit, ha de poder decidir **qui espera, quant pot consumir cadascú i quines peticions passen primer**.

Sense això, compartir GPU no és servir un servei multiusuari; és deixar competir peticions fins que la latència es dispara o la capacitat es col·lapsa.

### Seguretat

En aquest tipus de sistemes hi ha dos plans de seguretat diferents:

1. **seguretat de dades**: què surt del sistema i on es desa;
2. **seguretat d'execució**: què poden fer els agents amb les eines disponibles.

Per a la **seguretat de dades**, les mesures habituals són mantenir inferència i logs dins la infraestructura pròpia, definir polítiques de retenció, filtrar secrets o patrons sensibles i separar el trànsit intern del que va cap a proveïdors externs.

Per a la **seguretat d'execució**, les mesures principals són sandboxing, permisos mínims, confirmació explícita abans d'accions destructives i entorns separats per a experimentació i entorns sensibles.

### Observabilitat

Sense observabilitat és difícil saber si el sistema funciona bé o si només "sembla" que funciona.

Com a mínim convé recollir **latència per endpoint i model**, taxa d'errors, volum de peticions, ús per usuari o equip, saturació de GPU o cua i èxit o fracàs de tool calls en fluxos d'agents.

Per a decisions de capacitat, aquestes mètriques són més útils que opinions subjectives sobre "si va ràpid".

## Guia pràctica

### Costos reals

Per comparar autoallotjat amb comercial, cal mirar com a mínim el **hardware**, l'**electricitat**, l'**operació i manteniment**, el **temps del personal tècnic**, les llicències o subscripcions evitades i el cost —o risc— de no enviar dades fora.

La decisió rarament és purament econòmica. Sovint barreja cost, control i risc.

### Estratègia de desplegament

Una seqüència prudent acostuma a ser:

1. desplegar un únic servidor d'inferència;
2. posar-hi un gateway davant;
3. validar un petit grup de clients reals;
4. mesurar càrrega, latència i errors;
5. afegir un segon model només si apareix un cas d'ús clar;
6. optimitzar concurrència només quan hi ha dades de càrrega.

Això evita sobredissenyar.

### Recomanació de mínims

Per a un primer desplegament institucional raonable:

- **API**: OpenAI-compatible
- **Inferència**: Ollama si la càrrega és baixa; vLLM si hi ha concurrència real
- **Gateway**: LiteLLM Proxy
- **Clients**: només els necessaris per als patrons escollits
- **Governança**: autenticació, logging i quotes des del primer dia

### Heurística final

L'ordre correcte de decisió és aquest:

1. definir quins patrons d'ús es volen servir;
2. derivar-ne les capacitats mínimes dels models;
3. exposar una API comuna;
4. posar un gateway davant dels backends;
5. dimensionar segons concurrència i tipus de càrrega;
6. afegir control i observabilitat abans de créixer.

En un desplegament autoallotjat, el component més fàcil de canviar és el model. El més costós de canviar és una arquitectura mal desacoblada. Per això convé dissenyar primer la interfície i l'operació, i només després la tria exacta de models.
+4 −0
Original line number Diff line number Diff line
@@ -712,6 +712,10 @@
    <loc>https://apunts.jg5.dev/aitools/07_practiques_i_tancament.html</loc>
    <priority>1.0</priority>
  </url>
  <url>
    <loc>https://apunts.jg5.dev/aitools/09_desplegament.html</loc>
    <priority>1.0</priority>
  </url>  
  <url>
    <loc>https://apunts.jg5.dev/aitools/03_agent_o_determinista.html</loc>
    <priority>1.0</priority>