📋 Fitxa de la Pràctica
⏱️ Durada: 4 hores
🎯 RA: RA2 (CA 2.2, CA 2.5, CA 2.6)
📊 Pes: 25% de la nota pràctica
🐍 Python 3.11+ · LangChain 0.3
🎯 Objectius
💻 Eines a Implementar
Eina 1: Ping
from langchain.tools import tool
import subprocess
import platform
import re
@tool
def ping_host(hostname: str, count: int = 4) -> str:
"""
Fa ping a un hostname o IP i retorna estadístiques de connectivitat.
Útil per verificar si un host és accessible a la xarxa.
Args:
hostname: Hostname o adreça IP (p.ex. '8.8.8.8', 'www.google.com')
count: Nombre de paquets a enviar (1-10, per defecte 4)
"""
hostname = hostname.strip()
count = max(1, min(count, 10)) # Limitar entre 1 i 10
# Validació bàsica
if not re.match(r'^[a-zA-Z0-9._-]+$', hostname):
return "Error: hostname invàlid"
try:
if platform.system() == "Windows":
cmd = ["ping", "-n", str(count), hostname]
else:
cmd = ["ping", "-c", str(count), "-W", "3", hostname]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode == 0:
# Extreure estadístiques
output = result.stdout
return f"Host {hostname} ACCESSIBLE:\n{output[-500:]}" # Últims 500 chars
else:
return f"Host {hostname} NO ACCESSIBLE (timeout o host inexistent)"
except subprocess.TimeoutExpired:
return f"Timeout: {hostname} no ha respost en 30 segons"
except Exception as e:
return f"Error executant ping: {str(e)}"
Eina 2: Comprovació de Ports
@tool
def comprovar_port(hostname: str, port: int, timeout: float = 5.0) -> str:
"""
Comprova si un port TCP específic està obert en un host.
Args:
hostname: Hostname o IP
port: Número de port (1-65535)
timeout: Temps d'espera en segons (1-30)
"""
import socket
port = max(1, min(port, 65535))
timeout = max(1.0, min(timeout, 30.0))
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
result = sock.connect_ex((hostname, port))
sock.close()
PORTS_CONEGUTS = {
21: "FTP", 22: "SSH", 23: "Telnet", 25: "SMTP",
53: "DNS", 80: "HTTP", 110: "POP3", 143: "IMAP",
443: "HTTPS", 3306: "MySQL", 5432: "PostgreSQL",
6379: "Redis", 27017: "MongoDB", 8080: "HTTP-Alt"
}
servei = PORTS_CONEGUTS.get(port, "Desconegut")
if result == 0:
return f"Port {port}/{servei} a {hostname}: OBERT ✅"
else:
return f"Port {port}/{servei} a {hostname}: TANCAT ❌"
except socket.gaierror:
return f"Error: No s'ha pogut resoldre '{hostname}'"
except Exception as e:
return f"Error: {str(e)}"
@tool
def info_sistema(component: str = "tot") -> str:
"""
Retorna informació sobre el sistema local.
Args:
component: Quin component consultar:
- "cpu": Ús de CPU
- "memoria": RAM disponible/usada
- "disc": Espai de disc
- "xarxa": Interfícies de xarxa
- "tot": Tot el resum
"""
import psutil
import platform
result = []
if component in ("cpu", "tot"):
cpu = psutil.cpu_percent(interval=1)
result.append(f"CPU: {cpu}% d'ús ({psutil.cpu_count()} nuclis)")
if component in ("memoria", "tot"):
mem = psutil.virtual_memory()
result.append(
f"RAM: {mem.used/1e9:.1f}GB / {mem.total/1e9:.1f}GB "
f"({mem.percent}% usat)"
)
if component in ("disc", "tot"):
disc = psutil.disk_usage('/')
result.append(
f"Disc (/): {disc.used/1e9:.1f}GB / {disc.total/1e9:.1f}GB "
f"({disc.percent}% usat)"
)
if component in ("xarxa", "tot"):
interficies = psutil.net_if_addrs()
for nom, addrs in list(interficies.items())[:3]: # Limitar a 3
for addr in addrs:
if addr.family == 2: # AF_INET (IPv4)
result.append(f"Xarxa {nom}: {addr.address}")
if component == "tot":
result.insert(0, f"Sistema: {platform.system()} {platform.release()}")
return "\n".join(result) if result else f"Component '{component}' no reconegut"
Eina 4: Generació de Configuració
@tool
def generar_config_network(
tipus: str,
parametres: str
) -> str:
"""
Genera plantilles de configuració de xarxa.
Args:
tipus: Tipus de configuració: "vlan", "dhcp", "firewall", "nginx", "apache"
parametres: Paràmetres en format "clau=valor,clau=valor"
Per a vlan: "id=100,nom=VENDES,ip=192.168.100.0,mascara=24"
Per a dhcp: "xarxa=192.168.1.0,mascara=24,gateway=192.168.1.1,dns=8.8.8.8"
"""
# Parsejar paràmetres
params = {}
try:
for item in parametres.split(","):
k, v = item.strip().split("=")
params[k.strip()] = v.strip()
except ValueError:
return "Error: format de paràmetres incorrecte. Usa 'clau=valor,clau=valor'"
templates = {
"vlan": """
! Configuració VLAN {id} — {nom}
! Cisco IOS
vlan {id}
name {nom}
!
interface vlan {id}
ip address {ip} {mascara_decimal}
no shutdown
!
""",
"dhcp": """
# Configuració ISC DHCP Server
# /etc/dhcp/dhcpd.conf
subnet {xarxa} netmask {mascara_decimal} {{
range {primer_host} {ultim_host};
option routers {gateway};
option domain-name-servers {dns};
option domain-name "local.lan";
default-lease-time 86400;
max-lease-time 172800;
}}
""",
}
if tipus not in templates:
return f"Tipus '{tipus}' no suportat. Opcions: {list(templates.keys())}"
# Calcular màscares i rangs si cal
try:
import ipaddress
if "mascara" in params and "ip" in params:
net = ipaddress.IPv4Network(f"{params['ip']}/{params['mascara']}", strict=False)
params["mascara_decimal"] = str(net.netmask)
hosts = list(net.hosts())
if hosts:
params["primer_host"] = str(hosts[0])
params["ultim_host"] = str(hosts[-1])
except Exception:
pass
try:
return templates[tipus].format(**params)
except KeyError as e:
return f"Falta el paràmetre: {e}"
Eina 5: Cerca Web Tècnica
from langchain_community.tools import DuckDuckGoSearchResults
search_tool = DuckDuckGoSearchResults(
name="cerca_documentacio_tecnica",
description=(
"Cerca documentació tècnica, manuals, RFC, CVEs o solucions "
"a problemes d'administració de sistemes i xarxes. "
"Usa termes tècnics precisos per millors resultats."
),
num_results=4
)
💻 Integrar-ho Tot: L'Agent Complet
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from dotenv import load_dotenv
load_dotenv()
# Requereix psutil: pip install psutil
llm = ChatOpenAI(model="gpt-4o", temperature=0)
tools = [
ping_host,
comprovar_port,
info_sistema,
generar_config_network,
search_tool,
]
prompt = ChatPromptTemplate.from_messages([
("system", """Ets un expert en administració de sistemes i xarxes (ASIX).
Tens eines per diagnosticar la xarxa, comprovar ports, generar configuracions
i buscar documentació tècnica. Usa les eines adequades per a cada tasca.
Respon en català amb detall tècnic."""),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(
agent=agent, tools=tools,
verbose=True, max_iterations=8,
handle_parsing_errors=True
)
# ── TASQUES DE PROVA ──────────────────────────────────────────
tasques = [
"Comprova si els serveis HTTP i HTTPS de google.com estan actius",
"Genera la configuració VLAN per a la VLAN 200 de nom ALUMNES a la xarxa 10.200.0.0/24",
"Quin percentatge de RAM i CPU té el sistema ara mateix?",
"Busca la documentació oficial de com configurar SSH sense contrasenya (only key-based)",
]
for t in tasques:
print(f"\n{'='*60}\n📝 {t}\n{'='*60}")
r = executor.invoke({"input": t})
print(f"\n✅ {r['output']}")
📊 Criteris d'Avaluació
| Criteri |
Pes |
Indicadors |
| ≥5 eines implementades i funcionals |
30% |
Totes sense errors en casos normals |
| Gestió d'errors robusta |
25% |
IPs inexistents, ports tancats, timeouts |
| Intel·ligència de selecció d'eines |
25% |
L'agent tria l'eina correcta per a cada tasca |
| Qualitat del codi |
20% |
Docstrings, comentaris, estructura |