Los modelos de inteligencia artificial pueden hacer mucho más que responder preguntas. Mediante function calling, un modelo como Gemini puede decidir qué herramienta necesita utilizar, solicitar su ejecución y utilizar posteriormente el resultado para continuar con el análisis.
En este ejemplo se construye un pequeño agente de ciberseguridad defensiva con Gemini y Python.
El objetivo es comprender cómo funciona un agente desde dentro:
🛡️ CIBERAGENTE
│
▼
GEMINI
│
┌──────────────┼──────────────┐
▼ ▼ ▼
DNS lookup Analizar IP Normativa
│ │ │
└──────────────┼──────────────┘
▼
GEMINI
│
▼
INFORME FINAL
La característica importante es que Gemini decide qué función utilizar, mientras que Python ejecuta realmente la función en el ordenador.
¿Qué vamos a construir?
El agente tendrá tres herramientas:
dns_lookup()
analizar_ip()
consultar_normativa()
Con ellas podrá:
- Resolver un dominio mediante DNS.
- Analizar características básicas de una dirección IP.
- Consultar principios generales relacionados con protección de datos.
- Encadenar varias herramientas.
- Generar un informe final.
Por ejemplo, podremos escribir:
Analiza example.com, obtiene sus IP, analiza esas IP y dime qué aspectos de protección de datos debería tener en cuenta si registro esta información.
El agente podrá realizar una secuencia similar a:
Usuario
↓
Gemini
↓
dns_lookup()
↓
IPs
↓
analizar_ip()
↓
Características de las IP
↓
consultar_normativa()
↓
Principios de protección de datos
↓
Gemini
↓
Informe final
1. Requisitos
Necesitamos:
- Python 3.12.
- Una API key de Gemini.
- El paquete
google-genai.
Instalamos el SDK:
python3.12 -m pip install -U google-genai
La API key puede configurarse mediante una variable de entorno:
export GEMINI_API_KEY="TU_API_KEY"
Podemos comprobar que está disponible:
echo $GEMINI_API_KEY
2. Crear el agente
Creamos un archivo llamado:
ciberagente2.py
El código completo es el siguiente:
from google import genai
from google.genai import types
import os
import socket
import ipaddress
# ============================================================
# CONFIGURACIÓN
# ============================================================
client = genai.Client(
api_key=os.environ["GEMINI_API_KEY"]
)
MODELO = "gemini-3.5-flash-lite"
# ============================================================
# HERRAMIENTA 1
# DNS
# ============================================================
def dns_lookup(dominio: str) -> dict:
"""
Resuelve un dominio mediante DNS.
No realiza escaneo de puertos ni otras acciones intrusivas.
"""
print(f"\n🔧 EJECUTANDO EN TU MAC → dns_lookup('{dominio}')")
try:
resultados = socket.getaddrinfo(
dominio,
None
)
ips = sorted(
set(
resultado[4][0]
for resultado in resultados
)
)
return {
"dominio": dominio,
"ips": ips
}
except Exception as e:
return {
"error": str(e)
}
# ============================================================
# HERRAMIENTA 2
# ANÁLISIS DE IP
# ============================================================
def analizar_ip(ip: str) -> dict:
"""
Analiza características básicas de una IP.
No realiza escaneo de red.
"""
print(f"\n🔧 EJECUTANDO EN TU MAC → analizar_ip('{ip}')")
try:
direccion = ipaddress.ip_address(ip)
return {
"ip": ip,
"version": direccion.version,
"privada": direccion.is_private,
"publica": not direccion.is_private,
"loopback": direccion.is_loopback,
"link_local": direccion.is_link_local,
"multicast": direccion.is_multicast,
"reservada": direccion.is_reserved
}
except ValueError:
return {
"error": f"{ip} no es una IP válida"
}
# ============================================================
# HERRAMIENTA 3
# NORMATIVA
# ============================================================
def consultar_normativa(tema: str) -> dict:
"""
Proporciona principios generales relacionados con
protección de datos y RGPD.
"""
print(
f"\n🔧 EJECUTANDO EN TU MAC → "
f"consultar_normativa('{tema}')"
)
return {
"tema": tema,
"principios": [
"Minimización de datos",
"Limitación de la finalidad",
"Licitud del tratamiento",
"Transparencia",
"Exactitud",
"Limitación del plazo de conservación",
"Integridad y confidencialidad"
],
"referencias": [
"RGPD - artículo 5",
"RGPD - artículo 6",
"RGPD - artículo 13",
"RGPD - artículo 32"
],
"advertencia":
"La aplicación concreta depende del tratamiento "
"y de sus circunstancias."
}
# ============================================================
# DECLARACIÓN DE LAS HERRAMIENTAS
# ============================================================
dns_tool = {
"name": "dns_lookup",
"description": (
"Resuelve un dominio mediante DNS y devuelve "
"las direcciones IP obtenidas."
),
"parameters": {
"type": "object",
"properties": {
"dominio": {
"type": "string",
"description": "Nombre del dominio."
}
},
"required": [
"dominio"
]
}
}
ip_tool = {
"name": "analizar_ip",
"description": (
"Analiza las características básicas de una "
"dirección IP sin realizar escaneo de red."
),
"parameters": {
"type": "object",
"properties": {
"ip": {
"type": "string",
"description": "Dirección IPv4 o IPv6."
}
},
"required": [
"ip"
]
}
}
normativa_tool = {
"name": "consultar_normativa",
"description": (
"Consulta principios generales de protección "
"de datos aplicables al tratamiento de información."
),
"parameters": {
"type": "object",
"properties": {
"tema": {
"type": "string",
"description":
"Aspecto jurídico que se quiere analizar."
}
},
"required": [
"tema"
]
}
}
tools = types.Tool(
function_declarations=[
dns_tool,
ip_tool,
normativa_tool
]
)
# ============================================================
# MAPA DE FUNCIONES
# ============================================================
funciones = {
"dns_lookup":
dns_lookup,
"analizar_ip":
analizar_ip,
"consultar_normativa":
consultar_normativa
}
# ============================================================
# CONFIGURACIÓN DEL MODELO
# ============================================================
config = types.GenerateContentConfig(
tools=[
tools
],
system_instruction="""
Eres un agente de ciberseguridad defensiva.
Tienes varias herramientas disponibles.
Utiliza las herramientas cuando sean necesarias.
Cuando analices un dominio:
1. Puedes utilizar dns_lookup para obtener sus IP.
2. Puedes utilizar analizar_ip para analizar las IP obtenidas.
3. Si el usuario pregunta por protección de datos,
puedes utilizar consultar_normativa.
No inventes resultados de herramientas.
No realices acciones intrusivas.
Después de obtener la información necesaria,
genera un informe claro para el usuario.
"""
)
# ============================================================
# PREGUNTA
# ============================================================
print()
print(
"╔════════════════════════════════════════════╗"
)
print(
"║ 🛡️ CIBERAGENTE GEMINI ║"
)
print(
"╚════════════════════════════════════════════╝"
)
print()
pregunta = input(
"¿Qué quieres analizar?\n> "
)
# ============================================================
# HISTORIAL
# ============================================================
historial = [
types.Content(
role="user",
parts=[
types.Part.from_text(
text=pregunta
)
]
)
]
# ============================================================
# BUCLE DEL AGENTE
# ============================================================
while True:
print(
"\n🧠 GEMINI está tomando una decisión..."
)
respuesta = client.models.generate_content(
model=MODELO,
contents=historial,
config=config
)
# Añadimos la respuesta del modelo
# al historial
historial.append(
respuesta.candidates[0].content
)
# ¿Gemini ha solicitado herramientas?
llamadas = respuesta.function_calls
if not llamadas:
break
# ========================================================
# EJECUTAR LAS FUNCIONES
# ========================================================
resultados = []
for llamada in llamadas:
nombre = llamada.name
argumentos = dict(
llamada.args
)
print()
print(
"🤖 GEMINI HA DECIDIDO:"
)
print(
f" Función: {nombre}"
)
print(
f" Argumentos: {argumentos}"
)
funcion = funciones.get(
nombre
)
if funcion is None:
resultado = {
"error":
f"Herramienta desconocida: {nombre}"
}
else:
print(" ↓")
print(
" 🖥️ Python ejecutando la función..."
)
resultado = funcion(
**argumentos
)
print(" ↓")
print(
f" 📦 Resultado: {resultado}"
)
resultados.append(
types.Part.from_function_response(
name=nombre,
response=resultado
)
)
# ========================================================
# DEVOLVEMOS LOS RESULTADOS A GEMINI
# ========================================================
historial.append(
types.Content(
role="user",
parts=resultados
)
)
# ============================================================
# RESULTADO FINAL
# ============================================================
print()
print(
"════════════════════════════════════════════"
)
print(
"📋 INFORME FINAL"
)
print(
"════════════════════════════════════════════"
)
print()
print(
respuesta.text
)
print()
3. Ejecutar el agente
Ejecutamos:
python3.12 ciberagente2.py
El programa mostrará:
╔════════════════════════════════════════════╗
║ 🛡️ CIBERAGENTE GEMINI ║
╚════════════════════════════════════════════╝
¿Qué quieres analizar?
>
Podemos introducir:
Analiza example.com, obtiene sus IP, analiza esas IP y dime qué aspectos de protección de datos debería tener en cuenta si registro esta información.
4. ¿Qué está ocurriendo?
Esta es la parte más interesante del ejercicio.
Gemini recibe:
Analiza example.com...
y conoce las herramientas:
dns_lookup()
analizar_ip()
consultar_normativa()
El modelo puede decidir:
Quiero utilizar:
dns_lookup(
dominio="example.com"
)
Pero Gemini no ejecuta esa función.
La aplicación Python recibe la solicitud y ejecuta:
dns_lookup("example.com")
En nuestro caso:
socket.getaddrinfo(
dominio,
None
)
se ejecuta en el ordenador.
El resultado vuelve al modelo.
Gemini puede entonces decidir utilizar otra herramienta:
analizar_ip(
ip="..."
)
Python vuelve a ejecutar la función.
Y si la consulta requiere un análisis de protección de datos:
consultar_normativa(
tema="registro de direcciones IP"
)
Finalmente, Gemini recibe los resultados y genera el informe.
5. Arquitectura del agente
El flujo completo es:
USUARIO
│
▼
┌─────────┐
│ GEMINI │
└────┬────┘
│
DECIDE TOOL
│
┌─────────────┼─────────────┐
▼ ▼ ▼
dns_lookup() analizar_ip() consultar_normativa()
│ │ │
└─────────────┼─────────────┘
│
PYTHON/MAC
│
RESULTADOS
│
▼
┌─────────┐
│ GEMINI │
└────┬────┘
│
▼
INFORME FINAL
Por tanto:
La inteligencia artificial decide qué herramienta necesita utilizar, pero el programa que hemos desarrollado es quien ejecuta realmente esa herramienta.
Esta separación es fundamental para comprender el desarrollo de agentes.
6. Una diferencia importante respecto a un chatbot
Un chatbot tradicional funciona aproximadamente así:
Pregunta
↓
Modelo
↓
Respuesta
Un agente puede funcionar así:
Pregunta
↓
Modelo
↓
Decisión
↓
Herramienta
↓
Resultado
↓
Modelo
↓
Nueva decisión
↓
Otra herramienta
↓
Resultado
↓
Modelo
↓
Informe
El agente puede realizar, por tanto, varias operaciones antes de producir la respuesta final.
7. Ejemplo de cadena de herramientas
Una consulta podría generar una secuencia como:
Usuario:
Analiza example.com y dime qué implicaciones tendría
registrar las IP obtenidas.
↓
Gemini
↓
dns_lookup("example.com")
↓
Python obtiene las IP
↓
Gemini
↓
analizar_ip("93.184.216.34")
↓
Python analiza la IP
↓
Gemini
↓
consultar_normativa(
"registro de direcciones IP"
)
↓
Python devuelve principios jurídicos
↓
Gemini
↓
INFORME FINAL
No es necesario programar manualmente cada secuencia.
El modelo puede determinar qué herramienta necesita a partir de las descripciones que hemos proporcionado.
8. ¿Dónde se ejecutan las funciones?
Esta cuestión es especialmente importante desde el punto de vista de seguridad.
Gemini está en la nube:
☁️ Gemini
Mientras que nuestras herramientas están en el equipo:
💻 Mac
Por ejemplo:
def analizar_ip(ip):
direccion = ipaddress.ip_address(ip)
esa instrucción se ejecuta en Python, en nuestro ordenador.
Gemini solamente solicita:
analizar_ip("8.8.8.8")
La aplicación decide qué hacer con esa solicitud.
9. Seguridad
Este ejemplo utiliza herramientas de carácter pasivo:
- Resolución DNS.
- Análisis sintáctico de direcciones IP.
- Consulta de principios generales de protección de datos.
No incorpora:
- Escaneo de puertos.
- Explotación de vulnerabilidades.
- Ataques contra sistemas.
- Ejecución arbitraria de comandos.
- Acceso automático a sistemas externos.
Esto es deliberado.
Una de las cuestiones importantes al desarrollar agentes es que una herramienta puede proporcionar al modelo capacidad real de actuación.
Por ejemplo, una función como:
def ejecutar_comando(comando):
...
podría proporcionar al agente capacidad para ejecutar comandos en el sistema operativo.
Por ello, las herramientas deben diseñarse con permisos mínimos, validación de parámetros y límites claros.
Conclusión
Este pequeño proyecto permite observar uno de los mecanismos fundamentales de los agentes de inteligencia artificial.
Gemini no tiene por qué realizar directamente todas las operaciones. Puede actuar como orquestador, decidir qué herramienta necesita y proporcionar los argumentos necesarios.
Python mantiene el control sobre la ejecución:
GEMINI
│
DECISIÓN
│
▼
HERRAMIENTA
│
▼
PYTHON
│
▼
RESULTADO
│
▼
GEMINI
│
▼
RESPUESTA
La potencia del sistema aparece cuando aumentamos progresivamente el número de herramientas y permitimos que el agente combine sus resultados.
A partir de aquí, el siguiente salto consiste en conectar el agente con documentos, APIs, bases de datos y sistemas externos, utilizando mecanismos como RAG y MCP.