Salta el contingut

4.1 Patrons de Planificació d'Agents

Per Què Planificació?

Un LLM sense planificació és com un programador que escriu tot el codi en una sola funció. La planificació permet descompondre tasques complexes, adaptar-se als resultats, i detectar errors en el flux d'execució.


📋 El Problema de la Planificació

Per a tasques simples, un agent pot respondre directament. Però per a tasques complexes cal descompondre:

Tasca complexa: "Analitza la seguretat del servidor web de l'empresa 
                  i genera un informe amb recomanacions"

Sense planificació:
→ L'agent intenta fer-ho tot d'una sola vegada → resposta vaga i incompleta

Amb planificació:
1. Identificar el servidor web (IP, domini)
2. Escanejar ports oberts (nmap conceptual)
3. Comprovar versions de serveis (Apache, Nginx, PHP...)
4. Cercar CVEs per les versions trobades
5. Analitzar configuració SSL/TLS
6. Generar recomanacions per gravetat
7. Redactar informe final

🔄 Patró 1: ReAct (Reason + Act) — Planificació Iterativa

Ja vist a la Unitat 4.2. El patró més usat. L'agent planifica un pas cada vegada, observa el resultat, i decideix el pròxim pas.

Quan usar-lo: Tasques on no es pot saber tota la seqüència d'accions a priori.


📋 Patró 2: Plan-and-Execute — Planificació Explícita

from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate

# Fase 1: PLANIFICACIÓ (genera tot el pla primer)
PLANNING_PROMPT = PromptTemplate(
    template="""Ets un expert planificador. Per a la tasca donada,
genera un pla detallat d'accions a executar.

Tasca: {task}
Eines disponibles: {tools}

Genera un pla pas a pas en format JSON:
[
  {{"pas": 1, "accio": "...", "eina": "...", "input": "..."}},
  {{"pas": 2, "accio": "...", "eina": "...", "input": "..."}}
]

Pla:""",
    input_variables=["task", "tools"]
)

llm = ChatOpenAI(model="gpt-4o", temperature=0)
planner = LLMChain(llm=llm, prompt=PLANNING_PROMPT)

# Exemple d'ús
tools_desc = "cerca_web, calculadora, ping_host, comprovar_port"
pla = planner.invoke({
    "task": "Verifica que el servidor web de sapalomera.cat funciona correctament",
    "tools": tools_desc
})

# Pla generat (exemple):
# [
#   {"pas": 1, "accio": "Resoldre IP", "eina": "cerca_web", "input": "sapalomera.cat IP address"},
#   {"pas": 2, "accio": "Ping al servidor", "eina": "ping_host", "input": "sapalomera.cat"},
#   {"pas": 3, "accio": "Comprovar port 80", "eina": "comprovar_port", "input": "sapalomera.cat:80"},
#   {"pas": 4, "accio": "Comprovar port 443", "eina": "comprovar_port", "input": "sapalomera.cat:443"}
# ]

# Fase 2: EXECUCIÓ (executa el pla)
import json
pla_parsed = json.loads(pla["text"])
for step in pla_parsed:
    print(f"\nPas {step['pas']}: {step['accio']}")
    # Executar l'eina corresponent...

Quan usar-lo: Tasques estructurades on el pla és predictible i les eines sempre les mateixes.


🌳 Patró 3: Tree of Thoughts (ToT) — Explorar Múltiples Camins

Extensió de CoT que explora múltiples raonaments en paral·lel i selecciona el millor.

          ┌─── Enfocament A ──► Resultat A (avaluat: 7/10)
Problema ─┤
          ├─── Enfocament B ──► Resultat B (avaluat: 9/10) ← SELECCIONAT
          └─── Enfocament C ──► Resultat C (avaluat: 5/10)

Quan usar-lo: Problemes de disseny, decisions estratègiques, problemes matemàtics complexos.


🔁 Patró 4: Reflexion — Aprendre dels Errors

# L'agent genera una resposta → la critica → la millora iterativament

ACTOR_PROMPT = "Respon la pregunta: {question}"
CRITIC_PROMPT = """Critica la resposta següent. 
Identifica: errors factuals, mancances, millores possibles.
Resposta: {response}
Crítica:"""
REFINE_PROMPT = """Millora la resposta original basant-te en la crítica.
Resposta original: {response}
Crítica: {critique}
Resposta millorada:"""

def reflexion_agent(question: str, iterations: int = 2) -> str:
    """Genera, critica i millora iterativament una resposta."""
    response = llm.invoke(ACTOR_PROMPT.format(question=question)).content

    for i in range(iterations):
        critique = llm.invoke(CRITIC_PROMPT.format(response=response)).content
        response = llm.invoke(REFINE_PROMPT.format(
            response=response, critique=critique
        )).content
        print(f"Iteració {i+1}: resposta millorada")

    return response

# Exemple
answer = reflexion_agent(
    "Explica els avantatges i desavantatges d'usar Docker en producció",
    iterations=2
)

📊 Comparativa de Patrons

Patró Complexitat Cost Tokens Millor Per a
ReAct Baixa Mig Tasques generals amb eines
Plan-and-Execute Mitja Mig-Alt Tasques estructurades predictibles
Tree of Thoughts Alta Alt Decisions complexes, múltiples opcions
Reflexion Mitja Alt Quan la qualitat és crítica
Multi-Agent Alta Molt Alt Tasques que requereixen especialització

✅ Activitats

Exercici 4.1.1 — Implementar Plan-and-Execute

Implementa un agent Plan-and-Execute per a la tasca: "Comprova que tots els serveis crítics d'un servidor estan actius i genera un report." El pla ha d'adaptar-se als resultats intermedis.

Exercici 4.1.2 — Reflexion per a Documentació

Implementa un agent Reflexion que generi documentació tècnica per a una funció Python. Executa 3 iteracions i compara la qualitat entre la versió inicial i la final.

Exercici 4.1.3 — Comparativa Empírica

Per a la mateixa tasca complexa (p.ex. "Dissenya l'arquitectura de xarxa per a un aula informàtica de 30 equips"), compara la qualitat de les respostes amb: (a) LLM directe, (b) ReAct, (c) Reflexion. Quines diferències observes?