En esta práctica vamos a construir un agente de IA capaz de analizar archivos.
El objetivo inicial será trabajar con archivos de texto y logs de sistemas. Posteriormente podremos ampliar el agente para trabajar con PDF, imágenes, capturas de pantalla y otros formatos.
La arquitectura será:
USUARIO
│
▼
"Analiza este archivo"
│
▼
GEMINI
│
▼
analizar_archivo()
│
▼
PYTHON
│
┌────────────┼────────────┐
▼ ▼ ▼
TXT LOG PDF
│ │ │
└────────────┼────────────┘
▼
INFORMACIÓN
│
▼
GEMINI
│
▼
INFORME FINAL
¿Qué vamos a aprender?
Con este ejercicio vamos a trabajar varios conceptos importantes en el desarrollo de agentes:
- Function calling.
- Herramientas ejecutadas por Python.
- Acceso a archivos locales.
- Análisis de logs.
- Separación entre herramienta e inteligencia artificial.
- Análisis automático de información.
- Preparación de agentes multimodales.
Una idea importante es que Gemini no tiene por qué acceder directamente al sistema de archivos.
Podemos crear una herramienta en Python que se encargue de abrir el archivo y entregar su contenido al modelo.
De esta forma:
Archivo
↓
Python
↓
Contenido
↓
Gemini
↓
Interpretación
1. Crear el proyecto
Creamos un nuevo archivo:
touch analizar_archivos.py
También podemos crear un archivo de prueba:
nano servidor.log
Introducimos algunos eventos:
2026-09-23 20:01:12 INFO Usuario admin inició sesión
2026-09-23 20:02:44 INFO Consulta realizada
2026-09-23 20:05:01 WARNING Intento de acceso fallido para usuario root
2026-09-23 20:05:04 WARNING Intento de acceso fallido para usuario root
2026-09-23 20:05:08 WARNING Intento de acceso fallido para usuario root
2026-09-23 20:06:12 ERROR Acceso bloqueado por exceso de intentos
2026-09-23 20:10:32 INFO Usuario admin cerró sesión
Tenemos así un pequeño log que podremos utilizar para probar nuestro agente.
2. Crear la herramienta analizar_archivo()
La función será ejecutada por Python.
def analizar_archivo(ruta: str) -> dict:
print("\n🔧 PYTHON → analizar_archivo()")
print(f"📄 Archivo: {ruta}")
if not os.path.exists(ruta):
return {
"error": "El archivo no existe"
}
extension = os.path.splitext(ruta)[1].lower()
if extension not in [".txt", ".log"]:
return {
"error": f"Tipo de archivo todavía no soportado: {extension}"
}
try:
with open(
ruta,
"r",
encoding="utf-8",
errors="replace"
) as archivo:
contenido = archivo.read()
return {
"archivo": ruta,
"tipo": extension,
"tamaño": len(contenido),
"contenido": contenido
}
except Exception as e:
return {
"error": str(e)
}
La función realiza varias operaciones.
Primero comprueba si existe el archivo:
os.path.exists(ruta)
Después obtiene la extensión:
extension = os.path.splitext(ruta)[1].lower()
Y solamente permite inicialmente:
.txt
.log
Finalmente Python abre el archivo y devuelve su contenido.
3. Definir la herramienta para Gemini
Ahora tenemos que explicar a Gemini que existe nuestra función.
herramienta_archivo = {
"name": "analizar_archivo",
"description": "Lee un archivo de texto o log y devuelve su contenido para poder analizarlo.",
"parameters": {
"type": "object",
"properties": {
"ruta": {
"type": "string",
"description": "Ruta del archivo que se quiere analizar."
}
},
"required": ["ruta"]
}
}
La descripción es importante.
Estamos diciendo al modelo:
Existe una herramienta llamada
analizar_archivoy necesita un parámetro llamadoruta.
Por ejemplo, Gemini puede decidir realizar:
analizar_archivo(
ruta="servidor.log"
)
4. Configurar Gemini
Utilizamos el SDK de Google:
from google import genai
from google.genai import types
import os
Creamos el cliente:
client = genai.Client(
api_key=os.environ["GEMINI_API_KEY"]
)
Y establecemos el modelo:
MODELO = "gemini-3.5-flash-lite"
Creamos la herramienta:
tools = types.Tool(
function_declarations=[
herramienta_archivo
]
)
5. Dar instrucciones al agente
Ahora podemos definir el comportamiento del agente:
config = types.GenerateContentConfig(
tools=[tools],
system_instruction="""
Eres un agente de ciberseguridad defensiva.
Puedes utilizar la herramienta analizar_archivo()
para leer archivos TXT y LOG.
Cuando el usuario proporcione una ruta de archivo,
utiliza la herramienta para obtener su contenido.
Después analiza el contenido y busca:
- errores
- intentos de acceso
- autenticaciones
- actividades sospechosas
- patrones repetitivos
- posibles incidentes de seguridad
No inventes información que no aparezca en el archivo.
Explica claramente las evidencias encontradas.
"""
)
Aquí aparece otra característica importante de los agentes.
No solamente proporcionamos una herramienta.
También proporcionamos reglas de comportamiento.
Por ejemplo:
No inventes información.
y:
Explica claramente las evidencias encontradas.
Esto ayuda a orientar el comportamiento del agente.
6. Enviar la petición
Podemos pedir al usuario una pregunta:
pregunta = input("\n🧑 Usuario: ")
Y crear el historial:
historial = [
types.Content(
role="user",
parts=[
types.Part.from_text(
text=pregunta
)
]
)
]
Ahora Gemini recibe la petición.
7. El agente decide utilizar la herramienta
La parte interesante está aquí:
respuesta = client.models.generate_content(
model=MODELO,
contents=historial,
config=config
)
Después comprobamos si Gemini ha solicitado una función:
llamadas = respuesta.function_calls
Si no hay llamadas:
if not llamadas:
break
Pero si Gemini considera que necesita analizar un archivo, tendremos una llamada como:
Función: analizar_archivo
Argumentos:
{
"ruta": "servidor.log"
}
8. Python ejecuta la función
Ahora nuestro programa recupera el nombre de la función:
nombre = llamada.name
Y sus argumentos:
argumentos = dict(llamada.args)
Podemos mostrarlo por pantalla:
print("\n🤖 GEMINI HA DECIDIDO:")
print(f" Función: {nombre}")
print(f" Argumentos: {argumentos}")
Y ejecutar Python:
if nombre == "analizar_archivo":
resultado = analizar_archivo(
**argumentos
)
Aquí se produce una separación muy importante:
GEMINI
│
│ decide
▼
analizar_archivo("servidor.log")
│
│ ejecuta
▼
PYTHON
│
│ lee
▼
servidor.log
Gemini decide qué herramienta utilizar. Python ejecuta realmente la operación.
9. Devolver el resultado a Gemini
Una vez leído el archivo, tenemos que devolver la información al modelo:
resultados.append(
types.Part.from_function_response(
name=nombre,
response=resultado
)
)
Después añadimos el resultado al historial:
historial.append(
types.Content(
role="user",
parts=resultados
)
)
Y volvemos a llamar a Gemini.
Ahora el modelo ya tiene el contenido del archivo y puede analizarlo.
10. Código completo
El agente completo queda así:
from google import genai
from google.genai import types
import os
client = genai.Client(
api_key=os.environ["GEMINI_API_KEY"]
)
MODELO = "gemini-3.5-flash-lite"
def analizar_archivo(ruta: str) -> dict:
print("\n🔧 PYTHON → analizar_archivo()")
print(f"📄 Archivo: {ruta}")
if not os.path.exists(ruta):
return {
"error": "El archivo no existe"
}
extension = os.path.splitext(ruta)[1].lower()
if extension not in [".txt", ".log"]:
return {
"error": f"Tipo de archivo todavía no soportado: {extension}"
}
try:
with open(
ruta,
"r",
encoding="utf-8",
errors="replace"
) as archivo:
contenido = archivo.read()
return {
"archivo": ruta,
"tipo": extension,
"tamaño": len(contenido),
"contenido": contenido
}
except Exception as e:
return {
"error": str(e)
}
herramienta_archivo = {
"name": "analizar_archivo",
"description": "Lee un archivo de texto o log y devuelve su contenido para poder analizarlo.",
"parameters": {
"type": "object",
"properties": {
"ruta": {
"type": "string",
"description": "Ruta del archivo que se quiere analizar."
}
},
"required": ["ruta"]
}
}
tools = types.Tool(
function_declarations=[
herramienta_archivo
]
)
config = types.GenerateContentConfig(
tools=[tools],
system_instruction="""
Eres un agente de ciberseguridad defensiva.
Puedes utilizar la herramienta analizar_archivo()
para leer archivos TXT y LOG.
Cuando el usuario proporcione una ruta de archivo,
utiliza la herramienta para obtener su contenido.
Después analiza el contenido y busca:
- errores
- intentos de acceso
- autenticaciones
- actividades sospechosas
- patrones repetitivos
- posibles incidentes de seguridad
No inventes información que no aparezca en el archivo.
Explica claramente las evidencias encontradas.
"""
)
pregunta = input("\n🧑 Usuario: ")
historial = [
types.Content(
role="user",
parts=[
types.Part.from_text(
text=pregunta
)
]
)
]
while True:
respuesta = client.models.generate_content(
model=MODELO,
contents=historial,
config=config
)
historial.append(
respuesta.candidates[0].content
)
llamadas = respuesta.function_calls
if not llamadas:
break
resultados = []
for llamada in llamadas:
nombre = llamada.name
argumentos = dict(llamada.args)
print("\n🤖 GEMINI HA DECIDIDO:")
print(f" Función: {nombre}")
print(f" Argumentos: {argumentos}")
if nombre == "analizar_archivo":
resultado = analizar_archivo(
**argumentos
)
else:
resultado = {
"error": "Herramienta desconocida"
}
print("\n📦 RESULTADO:")
print(resultado)
resultados.append(
types.Part.from_function_response(
name=nombre,
response=resultado
)
)
historial.append(
types.Content(
role="user",
parts=resultados
)
)
print("\n" + "=" * 60)
print("📋 INFORME DEL AGENTE")
print("=" * 60)
print(respuesta.text)
11. Probando el agente
Ejecutamos:
python3.12 analizar_archivos.py
Y podemos introducir:
Analiza servidor.log y dime qué eventos de seguridad encuentras.
El flujo será aproximadamente:
🧑 Usuario
│
▼
Analiza servidor.log
│
▼
🤖 GEMINI
│
│ decide utilizar
▼
🔧 analizar_archivo()
│
▼
🖥️ PYTHON
│
▼
📄 servidor.log
│
▼
📦 contenido
│
▼
🤖 GEMINI
│
▼
📋 INFORME
12. ¿Puede detectar un ataque?
Podemos plantearle una pregunta más interesante:
Analiza servidor.log y dime si observas algún patrón compatible con intentos de fuerza bruta.
Gemini recibirá el contenido:
WARNING Intento de acceso fallido para usuario root
WARNING Intento de acceso fallido para usuario root
WARNING Intento de acceso fallido para usuario root
ERROR Acceso bloqueado por exceso de intentos
Y podrá relacionar esos eventos y explicar la evidencia encontrada.
Aquí aparece una diferencia importante:
Python obtiene los datos.
Gemini interpreta los datos.
Por tanto:
PYTHON
│
extracción
│
▼
DATOS
│
▼
GEMINI
│
interpretación
│
▼
INFORME
Esta separación permite construir agentes bastante más complejos.