Salta el contingut

Sistemes Experts

Introducció

Els sistemes experts van ser la primera gran aplicació comercial exitosa de la intel·ligència artificial. Nascuts a la dècada dels 70, van prometre codificar el coneixement dels millors especialistes humans en sistemes informàtics que poguessin prendre decisions de qualitat experta. Van tenir èxit real en dominis molt específics (diagnòstic mèdic, configuració d'ordinadors, diagnosis d'averages industrials) i van generar milers de milions de dòlars de valor.

Avui, el 2025, els sistemes experts "purs" han evolucionat i en molts casos han estat complementats o substituïts per tècniques de machine learning. Però els seus principis fonamentals continuen essent rellevants: hi ha problemes on les regles explícites i el raonament traçable superen els models de ML, especialment en dominis d'alt risc com el mèdic, el legal o el financer, on cal poder explicar per quin motiu s'ha pres una decisió.

Sistemes experts avui

El 2025, els sistemes experts no han desaparegut: han evolucionat. Els motors de regles empresarials (Drools, IBM ODM) gestionen milers de decisions de negoci cada dia en banca, assegurances i sanitat. Els knowledge graphs (Google Knowledge Graph, Amazon Product Graph) organitzen bilions de fets. I la IA neuro-simbòlica intenta combinar el millor dels LLMs amb el raonament estructurat dels sistemes experts.


1. Arquitectura clàssica dels sistemes experts

1.1. Components principals

Un sistema expert clàssic consta de tres components principals:

graph TD
    USER[Expert Human\nUsuari Final] --> INT[Interficie d-Usuari\n- Entrada de fets\n- Presentacio resultats\n- Explicacio del raonament]
    INT --> ME[Motor d-Inferencia\n- Forward Chaining\n- Backward Chaining\n- Resolucio de conflictes]
    ME --> BC[Base de Coneixement\n- Regles IF-THEN\n- Fets i relacions\n- Meta-regles]
    ME --> MT[Memoria de Treball\n- Fets actuals\n- Estat de la cerca\n- Historial de conclusions]

    EXP[Enginyer del Coneixement] --> BC
    EXPERT_HUMA[Expert del Domini] --> EXP

Base de Coneixement (Knowledge Base) El cor del sistema. Emmagatzema dos tipus d'informació: - Fets (assertional knowledge): informació específica sobre el problema actual - Regles (terminological knowledge): coneixement sobre com raonar en el domini

Motor d'Inferència (Inference Engine) El "cervell" que aplica les regles als fets per derivar conclusions. Implementa l'estratègia de cerca (forward/backward chaining) i resol conflictes quan múltiples regles poden disparar-se simultàniament.

Memòria de Treball (Working Memory) Conté els fets del problema actual: tant els fets inicials introduïts per l'usuari com les conclusions intermèdies derivades durant el raonament.

Interfície d'Usuari Permet la interacció humana: introduir fets, fer preguntes, i —molt important— explicar el raonament (per quin motiu el sistema ha arribat a aquella conclusió, quines regles s'han disparat, etc.).

Mòdul d'Adquisició de Coneixement (opcional) Facilita a l'enginyer del coneixement (knowledge engineer) l'entrada i manteniment de les regles, sense necessitat de programar directament.

1.2. L'enginyer del coneixement

L'enginyer del coneixement és el perfil professional que fa de pont entre l'expert humà del domini (metge, enginyer, advocat) i el sistema informàtic. El seu treball inclou:

  1. Entrevistar l'expert per extreure el seu coneixement (knowledge elicitation)
  2. Formalitzar aquest coneixement en regles IF-THEN
  3. Codificar les regles en el sistema expert
  4. Validar el sistema amb casos de prova
  5. Mantenir i actualitzar el coneixement quan canvia el domini

L'adquisició del coneixement (knowledge acquisition bottleneck) és el principal coll d'ampolla dels sistemes experts: és un procés lent, car i que depèn de la disponibilitat dels experts.

1.3. Exemples clàssics de sistemes experts

MYCIN (Stanford University, 1972-1976) Sistema de diagnòstic de malalties infeccioses sanguínies i recomanació d'antibiòtics. Contenia ~600 regles. Va demostrar el 1979 que les seves recomanacions eren millors que les d'estudiants de medicina i equiparables a les dels especialistes.

Format de regles de MYCIN:

REGLA 52:
  IF el cultiu és positiu per bacteries
  AND la gramtinció mostra bacteria gram-negatiu
  AND la morfologia és bacil
  AND l'origen es septicemia
  THEN suggerir KLEBSIELLA com a possible organisme (CF=0.7)
       suggerir ENTEROBACTER com a possible organisme (CF=0.6)

MYCIN va introduir el concepte de Factor de Certesa (CF) per gestionar la incertesa (valors entre -1 i +1, on +1 = certesa total i -1 = negació total).

XCON (DEC, 1980-1998) Configuració automàtica de sistemes VAX de Digital Equipment Corporation. 10.000+ regles. Va estalviar a DEC $40 milions anuals però requeria actualització constant a mesura que apareixien nous productes.

DENDRAL (Stanford, 1965) Identificació de molècules orgàniques a partir d'espectrometria de masses. Primer sistema expert i primera demostració que un ordinador podia realitzar raonament científic.


2. Xarxes bayesianes i raonament probabilístic

2.1. Limitació dels factors de certesa

Els factors de certesa de MYCIN eren intuïtius però matemàticament inconsistents. El Teorema de Bayes proporciona una base matemàtica sòlida per al raonament sota incertesa.

Teorema de Bayes:

P(H | E) = P(E | H) × P(H) / P(E)

On:
  P(H | E) = probabilitat posterior (hipòtesi donada l'evidència)
  P(E | H) = versemblança (probabilitat d'observar l'evidència si la hipòtesi és certa)
  P(H) = probabilitat prior (probabilitat de la hipòtesi sense evidència)
  P(E) = probabilitat marginal de l'evidència

2.2. Xarxes bayesianes (Belief Networks)

Una xarxa bayesiana (Bayesian Network o Belief Network) és un model gràfic probabilístic que representa variables aleatòries i les seves dependències condicionals. Els nodes representen variables, els arcs representen dependències causals, i cada node té associada una taula de probabilitat condicional (CPT).

graph TD
    PLUJA[Pluja\nP=0.20] --> SPRINKLER[Aspersors\nP-si-pluja=0.01\nP-si-sec=0.40]
    PLUJA --> MULLAT[Gespa mullada\nP-si-pluja=0.80\nP-si-sec=0.10]
    SPRINKLER --> MULLAT

Exemple — Diagnòstic d'alarma de cotxe:

Variables: - Robatori (R): hi ha un robatori en curs (P=0.001) - Terratrèmol (T): hi ha un sisme (P=0.002) - Alarma sona (A): l'alarma del cotxe sona (depèn de R i T) - Joan truca (J): el veí Joan truca perquè ha sentit l'alarma - Maria truca (M): la veïna Maria truca

El poder de les xarxes bayesianes: podem calcular la probabilitat de qualsevol variable donada qualsevol combinació d'evidències observades.

Implementació Python amb pgmpy:

# pip install pgmpy
from pgmpy.models import BayesianNetwork
from pgmpy.factors.discrete import TabularCPD
from pgmpy.inference import VariableElimination

# Definir l'estructura de la xarxa
model = BayesianNetwork([
    ('Robatori', 'Alarma'),
    ('Terratrèmol', 'Alarma'),
    ('Alarma', 'JoanTruca'),
    ('Alarma', 'MariaTruca')
])

# Taules de probabilitat condicional (CPD)
cpd_robatori = TabularCPD(
    variable='Robatori',
    variable_card=2,  # 2 estats: False (0), True (1)
    values=[[0.999], [0.001]]  # P(Robatori=False), P(Robatori=True)
)

cpd_terratremol = TabularCPD(
    variable='Terratrèmol',
    variable_card=2,
    values=[[0.998], [0.002]]
)

# P(Alarma | Robatori, Terratremol)
cpd_alarma = TabularCPD(
    variable='Alarma',
    variable_card=2,
    values=[
        # Alarma=False: R=F,T=F | R=F,T=T | R=T,T=F | R=T,T=T
        [0.999,         0.71,          0.06,          0.05],
        # Alarma=True
        [0.001,         0.29,          0.94,          0.95]
    ],
    evidence=['Robatori', 'Terratrèmol'],
    evidence_card=[2, 2]
)

cpd_joan = TabularCPD(
    variable='JoanTruca',
    variable_card=2,
    values=[[0.95, 0.10], [0.05, 0.90]],  # False|A=F, False|A=T, True|A=F, True|A=T
    evidence=['Alarma'],
    evidence_card=[2]
)

cpd_maria = TabularCPD(
    variable='MariaTruca',
    variable_card=2,
    values=[[0.99, 0.30], [0.01, 0.70]],
    evidence=['Alarma'],
    evidence_card=[2]
)

# Afegir les CPDs al model
model.add_cpds(cpd_robatori, cpd_terratremol, cpd_alarma, cpd_joan, cpd_maria)

# Verificar la consistència del model
assert model.check_model()

# Inferència: quin és la probabilitat de robatori si Joan i Maria truquen?
inferencia = VariableElimination(model)
resultat = inferencia.query(
    variables=['Robatori'],
    evidence={'JoanTruca': 1, 'MariaTruca': 1}  # Ambdós truquen
)
print(f"P(Robatori | Joan=True, Maria=True):")
print(resultat)
# P(Robatori=True) ≈ 0.284 (28.4%)

3. Knowledge Graphs i ontologies

3.1. Knowledge Graphs

Un Knowledge Graph (KG) és una representació del coneixement en forma de grafo, on les entitats (nodes) estan connectades per relacions (arestes) etiquetades. A diferència d'una base de dades relacional, el KG no requereix esquema fix i permet inferir noves relacions.

Knowledge Graphs famosos: - Google Knowledge Graph: bilions de fets sobre entitats del món real. Alimenta els resultats de cerca rics ("knowledge panels") - Wikidata: KG lliure i col·laboratiu. Base de dades estructurada de Wikipedia. 100M+ d'entitats - Amazon Product Graph: relacions entre productes, categories, atributs i revisions - Microsoft Academic Graph: relacions entre papers, autors, institucions i conceptes científics

3.2. RDF i OWL: estàndards web semàntic

RDF (Resource Description Framework): model de dades per a KGs. El coneixement es representa com tripletes (subjecte, predicat, objecte):

(Institut_Sa_Palomera, ubicada_a, Blanes)
(Blanes, part_de, Girona)
(Girona, part_de, Catalunya)
(Institut_Sa_Palomera, imparteix, IABD)
(IABD, inclou_modul, Models_IA)

OWL (Web Ontology Language): permet definir classes, propietats, relacions i restriccions. Usat per a la web semàntica i en bioinformàtica (Gene Ontology, SNOMED CT).

SPARQL: llenguatge de consulta per a RDF, equivalent al SQL per a bases de dades relacionals.

# Consulta SPARQL: quin cursos imparteix l'Institut Sa Palomera?
PREFIX ex: <http://example.org/isap/>

SELECT ?curs ?nom
WHERE {
    ex:Institut_Sa_Palomera ex:imparteix ?curs .
    ?curs ex:nom ?nom .
}

3.3. Knowledge Graphs en IA moderna

El 2025, els KGs s'usen com a font de coneixement estructurat per augmentar les capacitats dels LLMs:

Knowledge-Enhanced LLMs (KE-LLMs): models que consulten un KG per a fets específics i eviten al·lucinació. En lloc de dependre de la memòria paramètrica del model, recuperen fets verificats del KG.

Graph Neural Networks (GNNs): xarxes neuronals que operen directament sobre grafs. Útils per a predicció de links (descobrir relacions no explícites en el KG), detecció de comunitats i recomanació de productes.


4. Comparativa: Sistemes Experts vs. ML vs. LLMs

4.1. Criteris de comparació

Una de les decisions de disseny més importants en un projecte d'IA és triar l'aproximació correcta. Cada paradigma té els seus punts forts i dèbils:

Criteri Sistemes Experts Machine Learning LLMs
Necessitat de dades Mínima (regles manuals) Alta (centenars-milers d'exemples) Massiva (corpus bilions de paraules)
Explicabilitat Total (traces de regles) Limitada (caixa gris/negra) Molt limitada (caixa negra)
Manteniment Alt (actualització manual) Moderat (re-entrenament periòdic) Moderat (fine-tuning o prompts)
Robustesa Fràgil (no tolera casos no previstos) Bona (generalitza) Excel·lent (generalitza molt bé)
Cost de desplegament Baix Moderat Alt (GPU, API)
Velocitat d'inferència Molt alta Alta Variable (lenta per a models grans)
Domini requirit Molt específic Específic General o específic
Incertesa Fets de certesa/lògica difusa Probabilitats Probabilístic (però opac)
Auditoria/regulació Fàcil Moderada Difícil

4.2. Quan usar cada aproximació

Usa sistemes experts quan: - Les regles del domini són conegudes, estables i ben documentades - La traçabilitat i explicabilitat de les decisions és obligatòria (per regulació) - Les dades d'entrenament escassegen - El cost computacional ha de ser mínim

Usa Machine Learning quan: - Tens moltes dades etiquetades - Les regles del domini són massa complexes per a codificar manualment - El rendiment (precisió) és prioritari sobre l'explicabilitat - El domini canvia amb el temps (el model pot re-entrenar-se)

Usa LLMs quan: - El problema involucra text lliure i variabilitat lingüística alta - Necessites generalitzar a dominis no vistos - La velocitat de prototipat és prioritària - El cost d'API és assumible

Usa una arquitectura híbrida quan: - Necessites precisió + explicabilitat (LLM per a comprensió, motor de regles per a decisió) - Vols LLM per a text + KG per a fets verificats (RAG estructurat) - Vols un sistema expert que aprengui i s'actualitzi automàticament (ML + regles)

Miniactivitat CA5.1 — Selecció de paradigma

Per a cadascun dels casos d'ús següents, justifica quin paradigma (sistema expert, ML, LLM o híbrid) usaries i per quin motiu:

  1. Classifiqueu les consultes d'atenció al client (motiu de la trucada) entre 15 categories predefinides
  2. Decidiu si una transacció bancària és fraudulenta (regulació bancària exigeix auditoria de cada decisió)
  3. Genereu respostes personalitzades als emails de queixes de clients
  4. Calculeu el preu d'una assegurança de cotxe basat en 20 factors de risc amb regles clares del regulador
  5. Detecteu cèl·lules cancerígenes en imatges de biòpsia

5. Sistema expert en Python: implementació completa

5.1. Motor d'inferència cap endavant (Forward Chaining)

Implementem un sistema expert de diagnòstic de problemes de xarxa des de zero en Python pur, sense biblioteques externes:

#!/usr/bin/env python3
"""
Sistema Expert de Diagnòstic de Problemes de Xarxa
Implementació de forward chaining en Python pur
"""

from dataclasses import dataclass, field
from typing import List, Dict, Set, Optional, Callable


@dataclass
class Regla:
    """Representa una regla IF-THEN del sistema expert."""
    id: str
    condicions: List[str]         # Llista de fets que han de ser certs (AND)
    conclusions: List[str]        # Fets derivats si es compleixen les condicions
    explicacio: str               # Text explicatiu per a la interfície
    prioritat: int = 1            # Per a resolució de conflictes


class MotorInferencia:
    """Motor d'inferència cap endavant (forward chaining) amb explicació."""

    def __init__(self):
        self.base_coneixement: List[Regla] = []
        self.memoria_treball: Set[str] = set()
        self.historial: List[Dict] = []    # Rastres d'execució per a explicació

    def afegir_regla(self, regla: Regla):
        """Afegeix una regla a la base de coneixement."""
        self.base_coneixement.append(regla)
        # Ordenem per prioritat (major prioritat primer)
        self.base_coneixement.sort(key=lambda r: -r.prioritat)

    def afegir_fet(self, fet: str):
        """Afegeix un fet a la memòria de treball."""
        self.memoria_treball.add(fet.lower().strip())

    def executar(self) -> List[str]:
        """
        Executa el motor d'inferència fins que no hi ha regles noves per disparar.
        Retorna la llista de conclusions finals.
        """
        continuar = True
        iteracio = 0

        while continuar:
            iteracio += 1
            continuar = False

            for regla in self.base_coneixement:
                # Comprovem si totes les condicions de la regla es compleixen
                condicions_complides = all(
                    cond.lower() in self.memoria_treball
                    for cond in regla.condicions
                )

                if condicions_complides:
                    # Comprovem si alguna conclusió és nova
                    conclusions_noves = [
                        c for c in regla.conclusions
                        if c.lower() not in self.memoria_treball
                    ]

                    if conclusions_noves:
                        # Disparem la regla: afegim les noves conclusions
                        for conclusio in conclusions_noves:
                            self.memoria_treball.add(conclusio.lower())

                        # Registrem l'execució per a l'explicació
                        self.historial.append({
                            'iteracio': iteracio,
                            'regla_id': regla.id,
                            'condicions': regla.condicions,
                            'conclusions_noves': conclusions_noves,
                            'explicacio': regla.explicacio
                        })

                        continuar = True

        return [f for f in self.memoria_treball
                if f.startswith('diagnosi:') or f.startswith('accio:')]

    def explicar(self):
        """Mostra el rastre de raonament del sistema."""
        print("\n" + "=" * 60)
        print("EXPLICACIÓ DEL RAONAMENT")
        print("=" * 60)
        for pas in self.historial:
            print(f"\nPas {pas['iteracio']} - Regla {pas['regla_id']}:")
            print(f"  Condicions: {', '.join(pas['condicions'])}")
            print(f"  Conclusions: {', '.join(pas['conclusions_noves'])}")
            print(f"  Perquè: {pas['explicacio']}")


class SistemaExpertXarxa:
    """Sistema expert complet per al diagnòstic de problemes de xarxa."""

    def __init__(self):
        self.motor = MotorInferencia()
        self._carregar_regles()

    def _carregar_regles(self):
        """Carrega la base de coneixement: regles de diagnòstic de xarxa."""
        regles = [
            # --- Detecció de problemes de connectivitat física ---
            Regla(
                id="R01",
                condicions=["cable_desconnectat"],
                conclusions=["diagnosi:problema_capa_fisica",
                             "accio:connectar_cable_xarxa"],
                explicacio="Un cable desconnectat implica que no hi ha comunicació física possible.",
                prioritat=10
            ),
            Regla(
                id="R02",
                condicions=["led_switch_apagat"],
                conclusions=["diagnosi:switch_sense_alimentacio",
                             "accio:revisar_alimentacio_switch"],
                explicacio="El LED apagat del switch indica que no té corrent elèctric.",
                prioritat=10
            ),
            # --- Diagnòstic de capa de xarxa (IP) ---
            Regla(
                id="R03",
                condicions=["ping_gateway_falla", "cable_connectat"],
                conclusions=["diagnosi:problema_configuracio_ip"],
                explicacio="Si el cable és correcte però no arriba al gateway, el problema és de config IP.",
                prioritat=8
            ),
            Regla(
                id="R04",
                condicions=["diagnosi:problema_configuracio_ip", "ip_169_254"],
                conclusions=["diagnosi:dhcp_no_funciona",
                             "accio:revisar_servidor_dhcp"],
                explicacio="Una IP 169.254.x.x indica que el client no ha rebut adreça DHCP.",
                prioritat=9
            ),
            Regla(
                id="R05",
                condicions=["diagnosi:problema_configuracio_ip", "ip_valida"],
                conclusions=["diagnosi:problema_routing",
                             "accio:comprovar_taula_routing"],
                explicacio="Amb IP vàlida però sense ping al gateway, el problema és de routing.",
                prioritat=8
            ),
            # --- Diagnòstic DNS ---
            Regla(
                id="R06",
                condicions=["ping_ip_funciona", "ping_nom_falla"],
                conclusions=["diagnosi:problema_dns",
                             "accio:comprovar_configuracio_dns",
                             "accio:provar_dns_alternatiu_8_8_8_8"],
                explicacio="Si ping per IP funciona però per nom falla, és un problema de resolució DNS.",
                prioritat=9
            ),
            # --- Diagnòstic de servei web ---
            Regla(
                id="R07",
                condicions=["dns_funciona", "navegador_error_connexio"],
                conclusions=["diagnosi:servidor_web_caigut",
                             "accio:comprovar_servei_apache_nginx"],
                explicacio="Si DNS funciona però el navegador falla, el servidor web pot estar aturat.",
                prioritat=7
            ),
            Regla(
                id="R08",
                condicions=["diagnosi:servidor_web_caigut", "port_80_tancat"],
                conclusions=["accio:reiniciar_servei_web",
                             "accio:comprovar_firewall_port_80"],
                explicacio="El port 80 tancat confirma que el servei web no està escoltant.",
                prioritat=8
            ),
            # --- Diagnòstic de rendiment lent ---
            Regla(
                id="R09",
                condicions=["xarxa_lenta", "cpu_alta"],
                conclusions=["diagnosi:problema_rendiment_servidor"],
                explicacio="Una CPU alta del servidor pot causar lentitud generalitzada.",
                prioritat=6
            ),
            Regla(
                id="R10",
                condicions=["xarxa_lenta", "cpu_normal", "molts_errors_col-lisio"],
                conclusions=["diagnosi:problema_duplex_mismatch",
                             "accio:revisar_configuracio_duplex_switch"],
                explicacio="Errors de col·lisió amb CPU normal suggereixen mismatch de duplex.",
                prioritat=7
            ),
        ]

        for regla in regles:
            self.motor.afegir_regla(regla)

    def diagnosticar(self, simptomes: List[str]) -> Dict:
        """
        Realitza el diagnòstic donat un conjunt de símptomes.

        Args:
            simptomes: Llista de fets observats sobre el problema de xarxa

        Returns:
            Diccionari amb diagnòstics, accions recomanades i explicació
        """
        # Reinicialitzem la memòria de treball
        self.motor.memoria_treball = set()
        self.motor.historial = []

        # Afegim els símptomes com a fets inicials
        for simptoma in simptomes:
            self.motor.afegir_fet(simptoma)

        # Executem el motor d'inferència
        conclusions = self.motor.executar()

        # Classifiquem les conclusions
        diagnostics = [c.replace('diagnosi:', '').replace('_', ' ').title()
                      for c in conclusions if c.startswith('diagnosi:')]
        accions = [c.replace('accio:', '').replace('_', ' ').capitalize()
                   for c in conclusions if c.startswith('accio:')]

        return {
            'simptomes': simptomes,
            'diagnostics': diagnostics,
            'accions': accions,
            'passos_raonament': len(self.motor.historial)
        }

    def executar_interactiu(self):
        """Interfície interactiva de text per al sistema expert."""
        print("=" * 60)
        print("SISTEMA EXPERT DE DIAGNÒSTIC DE XARXA")
        print("Institut Sa Palomera — Curs IABD")
        print("=" * 60)
        print("\nRespon SÍ o NO a les preguntes de diagnòstic:\n")

        preguntes = {
            "cable_connectat": "El cable de xarxa està connectat al dispositiu?",
            "cable_desconnectat": "El cable de xarxa NO està connectat (desconnectat)?",
            "led_switch_apagat": "El LED del switch on connectes el dispositiu està apagat?",
            "ping_gateway_falla": "El ping al gateway (router) falla?",
            "ip_169_254": "L'adreça IP del dispositiu comença per 169.254.x.x?",
            "ip_valida": "El dispositiu té una adreça IP vàlida (no 169.254)?",
            "ping_ip_funciona": "El ping a adreces IP externes (p.ex. 8.8.8.8) funciona?",
            "ping_nom_falla": "El ping per nom de domini (p.ex. google.com) falla?",
            "dns_funciona": "La resolució DNS funciona correctament?",
            "navegador_error_connexio": "El navegador mostra error de connexió refusada?",
            "port_80_tancat": "El port 80 del servidor web apareix tancat (nmap o telnet)?",
            "xarxa_lenta": "La xarxa funciona però molt lentament?",
            "cpu_alta": "La CPU del servidor o router està al 90-100%?",
            "cpu_normal": "La CPU del servidor o router és normal (< 70%)?",
            "molts_errors_col-lisio": "La interfície de xarxa mostra molts errors de col·lisió?"
        }

        simptomes = []
        for fet, pregunta in preguntes.items():
            resposta = input(f"  {pregunta} [s/N]: ").strip().lower()
            if resposta in ('s', 'si', 'sí', 'yes', 'y'):
                simptomes.append(fet)

        resultat = self.diagnosticar(simptomes)

        print("\n" + "=" * 60)
        print("RESULTATS DEL DIAGNÒSTIC")
        print("=" * 60)

        if resultat['diagnostics']:
            print("\nDIAGNÒSTICS:")
            for d in resultat['diagnostics']:
                print(f"  • {d}")
        else:
            print("\nNo s'ha pogut establir un diagnòstic clar.")
            print("Contacta amb el suport tècnic.")

        if resultat['accions']:
            print("\nACCIONS RECOMANADES:")
            for i, a in enumerate(resultat['accions'], 1):
                print(f"  {i}. {a}")

        self.motor.explicar()


# Exemple d'ús
if __name__ == '__main__':
    sistema = SistemaExpertXarxa()

    # Cas de prova automàtic
    simptomes_cas1 = [
        "cable_connectat",
        "ping_gateway_falla",
        "ip_169_254"
    ]

    print("CAS DE PROVA: Problema de DHCP")
    print(f"Símptomes: {simptomes_cas1}")
    resultat = sistema.diagnosticar(simptomes_cas1)
    print(f"Diagnòstics: {resultat['diagnostics']}")
    print(f"Accions: {resultat['accions']}")
    sistema.motor.explicar()

    print("\n" + "=" * 60)

    # Mode interactiu
    sistema2 = SistemaExpertXarxa()
    # sistema2.executar_interactiu()  # Descomentar per a mode interactiu

6. IA neuro-simbòlica: tendències 2025

6.1. Limitacions dels LLMs purs

Els LLMs (GPT-4, Claude, Gemini) tenen una limitació fonamental per a aplicacions d'alt risc: no garanteixen la consistència lògica de les seves respostes. Un LLM pot afirmar A i ¬A en moments diferents, no pot garantir que les seves inferències segueixen les regles de la lògica de primer ordre, i és difícil d'auditar.

6.2. Neuro-Symbolic AI

La IA neuro-simbòlica combina la capacitat de percepció i generalització de les xarxes neuronals amb la precisió i el raonament deductiu dels sistemes simbòlics.

Exemples actuals:

AlphaGeometry (DeepMind, 2024): sistema que resol problemes de geometria de nivell olimpíada. Combina un LLM (que genera conjectures geomètriques en llenguatge natural) amb un motor deductiu simbòlic (que verifica formalment si les conjectures són correctes). Va resoldre 25 de 30 problemes de l'Olimpíada Internacional de Matemàtiques de 2000-2023, equivalent al nivell d'un medallista d'or.

LLM + Knowledge Graphs: sistemes que consulten un KG estructurat per a fets precisos i usen el LLM per a la comprensió de l'intent i la generació de la resposta. Molt usats en assistents de sanitat (consulten SNOMED CT, ICD-11) i assistents financers (consulten bases de regulació SEC, EBA).

Program Synthesis: models que, donada una especificació en llenguatge natural, generen codi verificable formalment. GitHub Copilot + proves unitàries és una versió simplificada d'això.

Miniactivitat CA5.5 — Disseny neuro-simbòlic

Imagina que has de construir un sistema de recomanació de fàrmacs per a un hospital. Explica com combinaries:

  1. Un LLM (per a comprendre la descripció del pacient en text lliure)
  2. Una xarxa bayesiana (per a la probabilitat diagnòstica)
  3. Un sistema basat en regles (per a les contraindicacions legals del medicament)
  4. Un Knowledge Graph (SNOMED CT, interaccions fàrmac-fàrmac)

Dibuixa un diagrama de l'arquitectura i explica el flux de dades.


7. Exercici pràctic del tema

PR5071/02 — Sistema expert de diagnòstic en Python

Implementa un sistema expert de diagnòstic del domini de la teva elecció (problemes de Python, diagnòstic de plantes, tria de dieta, diagnòstic de cotxes, etc.) que compleixi:

  1. Base de coneixement: mínim 15 regles IF-THEN
  2. Motor d'inferència: forward chaining implementat des de zero (pots basar-te en el codi d'exemple)
  3. Interfície interactiva: el sistema fa preguntes a l'usuari i mostra les conclusions
  4. Explicació del raonament: el sistema mostra quines regles s'han disparat
  5. Incertesa (opcional +1 punt): implementa factors de certesa o una xarxa bayesiana senzilla

Execució amb Docker:

docker run --rm -it \
  --name expert-joan-garcia \
  -v $(pwd):/app \
  -w /app \
  python:3.11-slim \
  python sistema_expert_joan_garcia.py

Format d'entrega: fitxer Python sistema_expert_nom_cognom.py + informe PDF (400 paraules) a l'aula virtual.

Vegeu la rúbrica completa: Rúbrica PR5071/02