Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Codi, eina o agent

Aquest capítol ajuda a triar entre tres nivells per resoldre una tasca de desenvolupament: eines deterministes, eines semàntiques locals, i agents amb LLM. L’opció per defecte en un flux AI-first és demanar-ho a l’agent — però abans val la pena una pausa breu, perquè la mateixa feina sovint la fa millor un nivell inferior, amb menys cost, més precisió i resultats reproduïbles.

El marc de decisió

Tres nivells per resoldre una tasca:

  • Eines deterministes. El flux és definit i previsible. Mateixa entrada, mateixa sortida. El camí és conegut i el codi el descriu completament.
  • Eines semàntiques locals. Operacions més intel·ligents que un grep — cerca per similitud, navegació estructural, indexació de símbols — però executades offline, sense tokens, amb resultats reproduïbles. El camí és determinista; la representació interna és apresa.
  • Agent amb LLM. El sistema interpreta la tasca, decideix què fer i ho executa cridant eines (tool calling — vegeu Fonaments de programació amb IA). El flux és dinàmic. El camí és desconegut, però les eines disponibles sí.

L’observació clau — i la més útil per decidir — és que les eines que crida l’agent són les mateixes operacions dels dos primers nivells que tu podries cridar directament: read_file, bash, grep, edicions de fitxers, git. Hi ha un paral·lelisme net: quan tu treballes deterministament també estàs cridant aquestes mateixes eines del sistema; només que la coreografia — quina eina, en quin ordre, amb quins arguments — surt del teu criteri en lloc del model. Tots dos accediu a la mateixa capa d’eines; el que canvia és d’on ve la decisió.

La pregunta clau no és “quin paradigma?” sinó de qui ve la coreografia? Quan el camí ja el coneixes tu, la teva és més ràpida, més precisa i sense cost per execució; quan és genuïnament incert, val la pena pagar la del model.

El problema típic no és triar malament entre els nivells — és no aturar-se a triar: delegar a l’agent una tasca que era determinista o semàntica local, o intentar codificar amb regles una que demanava judici.

Una pregunta a cada tasca

Puc descriure aquesta tasca com una transformació amb regles clares — f(input) → output?

  • Si la resposta és sí: tens un problema determinista. Vols un script o una eina de precisió, no l’agent.
  • Si la resposta és no però la tasca és de cerca o navegació: considera primer si una eina semàntica local la resol sense tokens.
  • Si la resposta és no i requereix judici sobre el context: cal preguntar-se per què — la resposta determinarà si l’agent és realment la millor opció o si el que et falta és entendre el problema.

Tres tipus de problema

  • Determinista (formalitzable). Les regles existeixen i les coneixes. Calcular la nota d’un examen, validar un format, executar tests, comptar ocurrències, transformar JSON. La feina és implementar les regles. Posar un agent al mig converteix una funció reproduïble en una resposta plausible.
  • Ambigüitat real (cal judici). El problema depèn del context i no té una única resposta correcta. “Refactoritza aquest mòdul perquè respecti el patró de UserService, “per què falla aquest test inestable?”, “és segur aquest canvi de schema?”. Codificar totes les heurístiques explícitament és inviable o es desactualitza ràpid. És el cas legítim per a un agent.
  • Desconeixement temporal del domini. Les regles existeixen, però encara no les coneixes. És el cas més perillós: l’agent produeix sortida raonable, i això elimina la pressió per entendre-ho. La trampa: usar l’agent per evitar pensar, no per raonar millor. L’estratègia correcta és inversa — explora el problema amb l’agent, entén-lo, i un cop el tens clar formalitza-ho en codi.

Eines deterministes de precisió

Abans de delegar al segon o tercer nivell, val la pena conèixer les eines del primer nivell que fan operacions que semblen intel·ligents però que en realitat són completament deterministes:

CapacitatDescripcióExemples
Cerca lexicalText exacte, expressions regulars, sobre tot el repositorigrep, rg
Cerca de fitxersPer nom, extensió, patró de rutafind, fd
Cerca estructural (AST-aware)Entén sintaxi: troba patrons de codi independentment del formatast-grep, comby
Índex de símbolsFuncions, classes i mètodes indexats offlinectags i equivalents
Transformació de dadesJSON, CSV, formats estructuratsjq, awk
Linters i formatadorsValidació determinista de qualitateslint, ruff, prettier
Graf de dependènciesMòduls, paquets, importseines específiques de l’ecosistema
Build i anàlisiCompilació, dependències, testsMaven, Gradle, make

La distinció clau és entre cerca lexical i cerca estructural. Les eines lexicals busquen text; les estructurals entenen sintaxi — saben que function foo() i const foo = () => són la mateixa cosa en JS. Quan la tasca és trobar totes les crides a getUser independentment del format, una eina estructural és millor que el model — és exacta, ràpida i no al·lucina.

Eines semàntiques locals

Entre les eines deterministes de precisió i el LLM hi ha un nivell que sovint s’oblida: les eines semàntiques locals. Aporten comprensió per similitud — no per coincidència exacta de text — però funcionen offline, sense tokens, i amb resultats reproduïbles per al mateix índex.

L’arquitectura de referència: RAG local

El patró és el mateix per a documentació i per a codi font — canvia el chunker i les metadades:

Indexació de documentació (semantic doc search):

  • Chunker basat en capçaleres Markdown (un chunk per secció, amb breadcrumb i rang de línies)
  • Model d’embeddings local (sentence-transformers o equivalent)
  • Vector store FAISS persistit localment (gitignored)
  • Mapa de metadades id → path, rang de línies, breadcrumb en JSONL o SQLite
  • CLI: index (construir/reconstruir), search (top-k semàntic amb citació), open (imprimir la secció exacta per id/línies)

Indexació de codi font (semantic code search):

  • Chunker estructural multi-llengua (via parser AST com tree-sitter): funcions, classes, mètodes + fallback de finestra fixa
  • Metadades per chunk: símbol, tipus (function/class/method), llengua, ruta
  • CLI: index, search, open + filtres --lang, --kind, --path
  • Complementat per eines deterministes (cerca lexical, índex de símbols, cerca estructural) per navegació d’alta precisió

Per a quines tasques és el nivell adequat

Usa eines semàntiques locals quan:

  • Navegació conceptual: “on s’implementa la lògica de retry?”, “quines seccions de la doc parlen de configuració?”rg busca paraules; l’embedding busca conceptes.
  • Trobar codi similar a un fragment donat sense saber el nom exacte de la funció.
  • Generar context rellevant per a l’agent sense passar tot el repositori — un search semàntic retorna les N seccions més rellevants; l’agent treballa sobre elles.
  • Refer una tasca de cerca repetidament — una consulta semàntica local no té latència de model ni cost per crida.

No és el nivell adequat quan:

  • Necessites coincidència exacta (un rg literal és millor i més ràpid).
  • L’índex és obsolet i no el pots reconstruir (el vector store és un artefacte; s’ha de mantenir).
  • La tasca requereix raonament sobre el contingut, no només trobar-lo.

Integració amb l’agent

Aquestes eines no exclouen l’agent — el complementen. Dues formes principals:

  1. Com a pas previ manual: llances una cerca semàntica local (“retry logic”), obtens les seccions rellevants amb citació exacta, les poses al context de l’agent. Menys tokens, context més precís.
  2. Com a tool de l’agent (MCP o tool calling): exposes search i open com a eines que l’agent pot cridar. L’agent decideix quan i amb quina query cercar; l’eina retorna citacions exactes, no estimacions. La secció Donar eines fiables a l’agent desenvolupa aquest patró.

Senyals per decidir

Els tres nivells es tradueixen en senyals concrets que pots reconèixer al moment.

Senyals que indiquen eines deterministes de precisió:

  • Flux conegut i estable — si pots dibuixar dades → A → B → resultat, no necessites un agent que ho redescobreixi a cada execució.
  • Necessites garanties — correctesa, reproduïbilitat, idempotència. Un avaluador d’exàmens no pot retornar resultats lleugerament diferents a cada execució; un LLM, sí.
  • Tasca repetitiva i d’alt volum — la pagaràs en temps i tokens cada cop. Un script l’executes infinites vegades a cost zero.
  • Latència importagit hooks, validació en temps real, comprovacions a CI.
  • Transformació puntual i mecànica — renombrar, convertir formats, comptar.
  • Cerca o substitució sobre sintaxi — eines de cerca estructural (AST-aware) quan la forma del codi importa.

Senyals que indiquen eines semàntiques locals:

  • Cerca conceptual sobre un repositori gran — trobar on es fa quelcom sense saber el nom exacte.
  • Recuperació de context per a l’agent — limitar el que li passes a les seccions rellevants, no tot.
  • Operació repetida sobre la mateixa base de coneixement — indexa un cop, consulta gratis.
  • Navegació cross-repo o cross-doc — saltar entre codi i documentació seguint similitud semàntica.

Senyals que indiquen agent:

  • Composició dinàmica d’eines“per què falla aquest test?” pot requerir llegir logs, codi, executar de nou amb verbositat, mirar dependències; el camí canvia segons el que trobes.
  • Raonament sobre el codi i el context — adaptar un patró d’un mòdul a un altre, decidir què refactoritzar dins una capa, identificar duplicació conceptual.
  • Exploració — investigar un codebase nou, prototipar una aproximació abans de comprometre-t’hi, generar alternatives per comparar.
  • Tasques d’una sola vegada on escriure el script costaria més que la sessió d’agent més la verificació.

El patró comú del costat agent: el camí no és previsible, però les eines disponibles sí. L’agent decideix la seqüència; les eines (REST, BD, scripts, fitxers, indexadors locals) continuen sent codi determinista o semàntica local.

Tasques deterministes mal delegades

Casos on el problema és clarament determinista i, malgrat tot, es delega a l’agent per defecte:

  • Renombrar variables, classes o fitxers → eines de refactoring de l’IDE o cerca estructural (AST-aware): entenen sintaxi i abast.
  • Cerca i substitució estructural → eines AST-aware (ast-grep, comby…) o textual (sed, awk, grep).
  • Operacions massives sobre fitxersfind, shell loops, xargs.
  • Conversió de formatsjq per a JSON, eines equivalents per a CSV, XML, documents.
  • Generació de boilerplate a partir d’un esquema → un code generator (Protobuf, OpenAPI, Prisma…) produeix sempre el mateix codi.
  • Validació o comptatge → linters, scripts de validació, cerca lexical amb comptatge.
  • Cerca de símbols → indexadors de símbols (ctags…) combinats amb cerca lexical o estructural.
  • Migracions de dades o configuració → scripts amb passos explícits, idempotents i reversibles.

Per què guanya el determinista

Per què invertir cinc minuts a escriure un script — o un one-liner, o configurar un refactor de l’IDE — en lloc de delegar la tasca? Perquè aporta propietats que un agent estructuralment no pot oferir:

  • Determinisme. Mateix input, mateixa sortida, sempre. Pots escriure un test que digui “aquest input ha de produir aquest output” i confiar-hi. Amb un agent, aquest test és impossible: dues execucions del mateix prompt poden divergir per temperature, actualitzacions del model, o mostreig estocàstic.
  • Auditabilitat abans de l’execució. Llegint deu línies saps exactament què passarà — pots aprovar-ho abans que toqui res. Amb un agent, l’única revisió possible és a posteriori: ja ha tocat fitxers, i et toca examinar el diff per descobrir si ha fet el que volies. Si la tasca era destructiva, ja l’has pagada.
  • El codi és l’especificació. No hi ha distància entre intenció i execució. Una instrucció a l’agent és una aproximació en llenguatge natural; el codi de l’agent en pot fer una interpretació diferent. El script no s’interpreta — s’executa.
  • Cost de revisió baix. Revisar dotze línies que descriuen una transformació és molt més barat cognitivament que revisar el diff de cinquanta fitxers que aquella transformació ha produït. El script és l’abstracció.
  • Persistència i reús. Una sessió d’agent és efímera; un script viu al repositori. La pròxima vegada el cost és zero, i qualsevol membre de l’equip el pot llançar i obtenir el mateix resultat.
  • Composabilitat i cost zero per execució. Encaixa amb pipes, Makefiles, cron, git hooks, CI. Sense latència ni tokens: viable a qualsevol freqüència. Un agent no es composa fiablement i no és viable dins d’un git hook o en execucions massives.

Tot plegat es resumeix així: un script captura la solució; un agent reprodueix el procés. Quan el procés és el mateix cada vegada, capturar-lo és sempre millor que reproduir-lo.

I si la tasca és única?

La reusabilitat és una raó forta per formalitzar, però no és l’única. Hi ha casos on una tasca determinista d’una sola vegada s’expressa millor com a comanda o script encara que no la tornis a executar mai més:

  • Operacions destructives o irreversibles. Esborrar fitxers en massa, modificar un schema, escriure dades a producció. El valor d’aprovar exactament què passarà abans que passi supera la conveniència de delegar. Una rm o mv la llegeixes en cinc segons; una sessió d’agent que fa el mateix només la pots revisar després — quan ja és massa tard.
  • Tasques tan petites que descriure-les costa més que fer-les. Un :%s/foo/bar/g, un find-replace a l’IDE, una comanda mv. La latència de l’agent — carregar context, planificar, executar, retornar — ja supera el cost manual de la tasca.
  • Quan saps exactament què vols i la precisió importa. Una regex o una crida explícita expressen la teva intenció sense marge d’interpretació. L’agent pot decidir que “de pas” arregla quelcom proper, o interpretar l’abast diferent del que esperes — desviacions silencioses molt difícils de detectar.
  • Per mantenir l’hàbit i el criteri. Cada vegada que deixes a l’agent una tasca que podries fer reflexivament estàs reforçant el reflex de delegar — i atrofiant la fluïdesa amb les eines que durant dècades han fet aquesta feina millor que cap LLM. grep, sed, awk, find, regex, eines de cerca estructural, refactors de l’IDE: són com un múscul. Sense aquest múscul, no sabràs reconèixer quan una tasca era senzilla — la veuràs sempre des del prompt.

La regla és més matisada que “si es repeteix → script, si no → agent”. Realment és:

Per a problemes deterministes, el camí determinista (script, comanda, refactor) és el bo per defecte. L’agent guanya només quan la fricció de fer-ho deterministement és real — no per defecte.

I si la tasca demana judici?

Hem dit que els problemes amb ambigüitat real són el cas legítim per a l’agent. Però legítim no vol dir automàtic. Hi ha casos on, fins i tot quan la tasca demana judici, la millor opció no és l’agent — ets tu:

  • Decisions arquitecturals i de disseny. Triar un patró, definir una interfície pública, decidir on cau la frontera entre dos mòduls. Són judici — però el teu judici, no el del model. La IA és un executor hàbil, no un arquitecte.
  • Codi sensible o crític. Lògica de seguretat, manipulació de dades de pagament, criptografia, validació d’entrada. Vols entendre cada línia, no acceptar quelcom plausible. Una vulnerabilitat introduïda per l’agent passa la mateixa revisió superficial que qualsevol altre codi seu — i no la detectaràs llegint el diff.
  • Quan tu ets l’únic que té el context. Decisions que depenen d’una conversa amb un client, d’un incident d’ahir a producció, d’un acord verbal de l’equip. L’agent treballa amb el que té al prompt i al codi; el coneixement implícit es perd.
  • Quan vols mantenir o construir criteri. Si delegues tot judici al model, no construeixes el criteri que necessites per saber quan delegar amb confiança. Cada cop que passes una decisió al model és un cop que no l’has practicat tu — i la fluïdesa amb el sistema es construeix exactament aquí.
  • Quan és la teva manera d’entendre el problema. Escriure el codi és sovint l’acte mateix d’entendre. Si saltes aquesta passa, et quedes amb una comprensió superficial — fins que el codi falla i et toca depurar quelcom que mai vas raonar.

El patró general: l’agent és més ràpid implementant; tu ets insubstituïble decidint, sentint el sistema i construint criteri. Quan la tasca toca aquestes tres dimensions, fer-la tu mateix no és lentitud — és la feina.

Productivitat vs aprenentatge

Hi ha una tensió que val la pena nomenar explícitament: productivitat vs aprenentatge. L’agent gairebé sempre guanya en productivitat immediata — fa la feina més ràpid del que la faries tu. Però el cost d’aquella velocitat és el criteri que no construeixes mentre delegues. Cada decisió que passa pel model és una decisió que no has practicat — i el criteri només es construeix prenent-les tu, vivint amb les conseqüències, i veient què falla.

Per a un sènior amb criteri ja construït, la tensió es resol cas a cas: prou judici per delegar amb seguretat el rutinari, prou disciplina per fer-ho a mà quan la decisió pesa o quan toca mantenir la fluïdesa. Per a un estudiant o un junior, la balança s’inverteix: l’aprenentatge és la feina principal. La productivitat immediata que guanyes delegant és una hipoteca contra la teva carrera futura — perquè sense criteri propi, ni tan sols sabràs si el que el model et dóna és correcte. La velocitat del primer any no és res comparada amb el sostre de la teva carrera sencera.

La regla pràctica: demana’t quina inversió estàs fent cada vegada que decideixes delegar. Si el que guanyes és temps i el que perds és criteri que no necessites refrescar — és una bona inversió. Si el que guanyes és temps i el que perds és precisament el criteri que t’està faltant — has fet exactament el contrari del que necessites.

L’agent escriu l’script

La decisió no sempre és “agent o script” — sovint és “l’agent escriu el script”. En lloc de demanar “renombra totes les ocurrències de foo a bar, demana “escriu-me la comanda que renombri totes les ocurrències de foo a bar als fitxers .py entenent la sintaxi”. Obtens el millor dels dos paradigmes:

  • La intel·ligència de l’agent aplicada una sola vegada, en un artefacte concret.
  • Un script revisable abans d’executar-lo (--dry-run, sortida pas a pas).
  • Reusable: la pròxima vegada, ja el tens al repositori.
  • Compartible amb l’equip.

És la regla d’or del paradigma:

Si ho reutilitzaràs → formalitza-ho. Si ho exploraràs → usa l’agent.

Quan una exploració amb agent fa visible el camí, el següent pas natural és convertir-lo en codi determinista — no consolidar l’agent com a part permanent del flux.

Donar eines fiables a l’agent

Si saps com fer una tasca de manera determinista o semàntica local, no deixis que l’agent l’endevini estocàsticament — dóna-li l’eina perquè la faci bé.

Pensa en un exemple concret. Demanes a l’agent “comprova si el JSON de configuració és vàlid”. L’agent llegeix el fitxer, raona sobre l’estructura, i et diu que sembla correcte — però ha generat la resposta més plausible, no l’ha verificat. Ara imagina que l’agent, en lloc d’endevinar, crida un validador d’esquema que ja tens al projecte — un script de deu línies que retorna pass/fail amb els errors concrets. El resultat ja no és plausible: és cert.

El principi s’estén als tres nivells:

  • Eines deterministes com a tools: un script de validació, una consulta SQL parametritzada, un linter configurat — l’agent els crida i obté fets, no estimacions.
  • Eines semàntiques locals com a tools: exposes el teu índex local (semantic doc search, semantic code search) perquè l’agent recuperi context precís en lloc de recordar o al·lucinar. L’agent decideix quan cercar i amb quina query; l’eina retorna citacions exactes (fitxer, línia, breadcrumb). El context sobre el qual raona l’agent és verificat, no interpolat.

Quan l’agent crida eines dels nivells inferiors, les seves decisions es basen en resultats verificats. Estàs aplicant harness engineering (descrit a Fer el projecte més automatitzable) dins del bucle de l’agent — no com a verificació posterior, sinó com a fonament de cada pas.

Com s’exposen aquests scripts perquè l’agent els pugui cridar? La via més simple és la més habitual: tenir-los al repositori i dir a l’agent que els faci servir — al prompt, o a les instruccions de projecte que l’agent carrega automàticament a cada sessió. Si el projecte té un scripts/validate-schema.sh o un índex semàntic local, n’hi ha prou amb indicar a l’agent que els faci servir perquè els cridi via terminal. Per a integracions més formals o sistemes externs, el MCP — introduït a Context, tools i MCP — permet declarar operacions com a tools que qualsevol agent compatible pot invocar directament. En tots dos casos, el valor d’enginyeria és a l’eina, no a com l’exposes.

Tot això implica que decidir quines operacions exposar, amb quin contracte i a quina granularitat és una decisió de disseny que determina el sostre del que l’agent pot fer de manera fiable. Aprendre a escriure scripts, validadors, transformacions deterministes i indexadors semàntics locals no és una habilitat “pre-IA” que perdrà valor — és el fonament per treure el màxim d’un agent.

Usar l’agent per no pensar

Tornem al tercer tipus de problema — el desconeixement temporal del domini. El cas perillós no és “tasca determinista delegada a l’agent” — és “tasca que semblava ambigua perquè encara no havíem entès el problema”. L’agent produeix una resposta raonable, i això elimina la pressió per fer la feina d’enginyeria de definir-lo.

Símptomes que indiquen que això està passant:

  • El sistema “funciona” però es comporta diferent a cada execució.
  • Cada vegada que cal modificar-lo, tornes a la sessió amb l’agent en lloc de canviar codi.
  • Ningú a l’equip pot explicar les regles que el sistema segueix.
  • Els bugs es manifesten com a “output inesperat”, no com a “line N exception”.

La mitigació no és deixar de fer servir l’agent — és fer servir l’agent per descobrir les regles, i llavors codificar-les. Si una tasca s’ha resolt amb l’agent dues o tres vegades, ja saps prou per formalitzar-la.

Heurística final

Abans de delegar, una pausa breu en tres passos:

  1. Podria fer-ho amb una cerca lexical, una substitució estructural, jq o una comanda de l’IDE? → Si sí, fes-ho.
  2. Podria fer-ho amb un indexador semàntic local (RAG local, semantic code search) sense tokens? → Si sí, construeix-lo o usa’l.
  3. Podria expressar això com 5-20 línies de codi? → Si sí, escriu-les — o demana a l’agent que les escrigui, però conserva-les.

Un script commitejat val més que deu sessions d’agent que han fet la mateixa feina. Un indexador semàntic local val més que passar tot el repositori al context cada vegada.

L’ordre és el que importa: primer pensa com a enginyer — quines són les regles, quin és el flux, què és reusable, què és cercable localment — i només després decideix si una part del problema demana un agent. La fascinació per la potència de l’agent fa fàcil saltar-se aquest pas; les eines deterministes i semàntiques que tenim segueixen sent les millors quan el problema es pot formular com una regla o com una cerca. Reconèixer quin tipus de problema tens al davant és la part de la feina que cap agent farà per tu.

Quan la tasca no és formalitzable fàcilment, o quan l’exploració i el judici sí que justifiquen un agent, el següent problema ja no és si delegar, sinó com fer-ho bé.

Last change: , commit: b7a8ca3