Proyectos de agentes del mundo real
Aplica las técnicas de IA agéntica a la generación de código, la revisión de código, la refactorización, las pruebas, la documentación y la automatización de DevOps.
Resumen
Esta lección final sintetiza todo lo que has aprendido en proyectos prácticos y del mundo real. Cada proyecto demuestra cómo se aplican las técnicas de IA agéntica a las tareas habituales de ingeniería de software.
Estos proyectos están diseñados para ejecutarse con OpenCode o cualquier framework de agentes. Cada proyecto incluye la configuración, las definiciones de skills y las salidas esperadas necesarias para una ejecución correcta.
Proyecto 1: Generación automatizada de código
Genera un módulo de API REST completo a partir de una especificación usando un agente.
# project-configs/blog-api/skill.yaml
name: blog-api-generator
description: Generate a complete blog REST API from specifications
version: "1.0.0"
instructions:
- path: instructions/generate-models.md
- path: instructions/generate-routes.md
- path: instructions/generate-tests.md
tools:
required:
- read
- write
- glob
- bash
resources:
- path: templates/fastapi-base.py.hbs
- path: templates/sqlalchemy-model.py.hbs
- path: templates/pytest-fixtures.py.hbs
autoload:
enabled: false# Example: Agent generating a FastAPI blog module
GENERATED_MODULE = """
# models.py
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey
from sqlalchemy.sql import func
from database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True, nullable=False)
email = Column(String(120), unique=True, nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(200), nullable=False)
content = Column(Text, nullable=False)
author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# schemas.py
from pydantic import BaseModel, EmailStr
from datetime import datetime
from typing import Optional
class UserCreate(BaseModel):
username: str
email: EmailStr
class UserResponse(BaseModel):
id: int
username: str
email: str
created_at: datetime
class PostCreate(BaseModel):
title: str
content: str
class PostResponse(BaseModel):
id: int
title: str
content: str
author_id: int
created_at: datetime
updated_at: Optional[datetime] = None
# routes.py
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from . import models, schemas
from .database import get_db
router = APIRouter(prefix="/api/v1", tags=["blog"])
@router.post("/users/", response_model=schemas.UserResponse, status_code=201)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = models.User(**user.model_dump())
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
@router.get("/posts/", response_model=list[schemas.PostResponse])
def list_posts(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
return db.query(models.Post).offset(skip).limit(limit).all()
@router.post("/posts/", response_model=schemas.PostResponse, status_code=201)
def create_post(post: schemas.PostCreate, db: Session = Depends(get_db)):
db_post = models.Post(**post.model_dump())
db.add(db_post)
db.commit()
db.refresh(db_post)
return db_post
@router.get("/posts/{post_id}", response_model=schemas.PostResponse)
def get_post(post_id: int, db: Session = Depends(get_db)):
post = db.query(models.Post).filter(models.Post.id == post_id).first()
if not post:
raise HTTPException(status_code=404, detail="Post not found")
return post
"""Proyecto 2: Revisión inteligente de código
Un agente que revisa pull requests en busca de errores, problemas de seguridad y violaciones de estilo.
{
"skills": {
"pr-reviewer": {
"manifest": "skills/pr-reviewer/skill.yaml",
"autoLoad": true,
"matchPattern": "review pull request|review PR|#\\d+"
}
},
"agents": {
"reviewer": {
"model": "claude-sonnet-4-20250514",
"description": "Code review specialist",
"prompt": "You are a senior code reviewer. Review code for: security vulnerabilities, performance issues, maintainability problems, and style violations.",
"constraints": {
"allowedTools": ["read", "grep", "glob", "bash"],
"deniedTools": ["write", "edit"]
}
}
}
}class CodeReviewAgent:
def __init__(self, agent):
self.agent = agent
self.findings = []
async def review_pr(self, pr_number, repo_path):
# Get the diff
diff = await self.agent.bash(f"cd {repo_path} && git diff main...HEAD")
changed_files = await self.agent.bash(
f"cd {repo_path} && git diff --name-only main...HEAD"
)
files = changed_files.strip().split("\n")
for file in files[:10]:
if file.endswith((".py", ".ts", ".js", ".tsx", ".jsx")):
content = await self.agent.read(f"{repo_path}/{file}")
file_review = await self._review_file(file, content)
self.findings.extend(file_review)
return self._generate_report()
async def _review_file(self, file_path, content):
findings = []
lines = content.split("\n")
for i, line in enumerate(lines, 1):
# Security checks
if "eval(" in line or "exec(" in line:
findings.append({
"severity": "critical",
"file": file_path,
"line": i,
"type": "code_injection",
"description": f"Dangerous function usage at line {i}"
})
# Performance checks
if "for " in line and any(
nested_line.strip().startswith("for ")
for nested_line in lines[i:i+5]
):
findings.append({
"severity": "major",
"file": file_path,
"line": i,
"type": "nested_loop",
"description": "Potential O(n^2) nested loop detected"
})
# Hardcoded secrets
if "password" in line.lower() and "=" in line:
findings.append({
"severity": "critical",
"file": file_path,
"line": i,
"type": "hardcoded_secret",
"description": "Possible hardcoded credential"
})
# TODO/FIXME tracking
if "TODO" in line or "FIXME" in line:
findings.append({
"severity": "info",
"file": file_path,
"line": i,
"type": "todo",
"description": line.strip()
})
return findings
def _generate_report(self):
by_severity = {}
for f in self.findings:
sev = f["severity"]
if sev not in by_severity:
by_severity[sev] = []
by_severity[sev].append(f)
report = [
"# Code Review Report",
f"**Total Findings**: {len(self.findings)}",
"",
"## Summary by Severity",
]
for sev in ["critical", "major", "minor", "info"]:
count = len(by_severity.get(sev, []))
report.append(f"- **{sev.capitalize()}**: {count}")
report.append("")
for sev in ["critical", "major", "minor", "info"]:
items = by_severity.get(sev, [])
if items:
report.append(f"## {sev.capitalize()} Issues")
for item in items[:5]:
report.append(
f"- [{item['type']}] {item['file']}:{item['line']}"
)
report.append(f" {item['description']}")
report.append("")
return "\n".join(report)Proyecto 3: Refactorización automatizada
Un agente que reestructura el código de forma segura conservando el comportamiento.
class RefactoringAgent:
def __init__(self, agent):
self.agent = agent
self.changes = []
async def refactor(self, file_path, strategy):
content = await self.agent.read(file_path)
if strategy == "extract_function":
result = await self._extract_function(content, file_path)
elif strategy == "rename_variable":
result = await self._rename_variable(content, file_path)
elif strategy == "add_type_hints":
result = await self._add_type_hints(content, file_path)
else:
return {"status": "error", "message": f"Unknown strategy: {strategy}"}
return result
async def _extract_function(self, content, file_path):
lines = content.split("\n")
# Find long functions (simplified heuristic)
changes = []
i = 0
while i < len(lines):
if lines[i].startswith("def ") and i + 30 < len(lines):
# Function is long, suggest extraction
func_name = lines[i][4:].split("(")[0]
changes.append({
"type": "extract_function",
"function": func_name,
"start_line": i + 1,
"length": "30+ lines",
"suggestion": f"Extract helper functions from {func_name}"
})
i += 30
i += 1
return {"status": "analyzed", "changes": changes}
async def _rename_variable(self, content, file_path):
# Find poorly named variables
import re
lines = content.split("\n")
changes = []
bad_names = re.findall(r'\b([a-z]{1,2})\s*=', content)
for name in set(bad_names):
if name not in ["id", "ok", "no"]:
changes.append({
"type": "rename_variable",
"old_name": name,
"suggestion": f"Rename '{name}' to a more descriptive name"
})
return {"status": "analyzed", "changes": changes}
async def _add_type_hints(self, content, file_path):
lines = content.split("\n")
changes = []
for i, line in enumerate(lines, 1):
if line.startswith("def ") and "->" not in line:
func = line[4:].split("(")[0]
changes.append({
"type": "add_return_type",
"function": func,
"line": i,
"suggestion": f"Add return type hint to {func}"
})
return {"status": "analyzed", "changes": changes}Al refactorizar con agentes, usa siempre un sistema de control de versiones. Crea una rama, aplica los cambios de forma incremental y ejecuta las pruebas después de cada cambio. Si una refactorización rompe las pruebas, el agente puede analizar el fallo y autocorregirse.
Proyecto 4: Generación de pruebas
Un agente que crea suites de pruebas completas a partir del análisis del código fuente.
# skills/test-generator/skill.yaml
name: test-generator
description: Generate comprehensive test suites from source code analysis
version: "2.0.0"
instructions:
- path: instructions/analyze-source.md
- path: instructions/generate-tests.md
- path: instructions/validate-tests.md
tools:
required:
- read
- write
- bash
optional:
- grep
resources:
- path: templates/pytest-test.py.hbs
- path: templates/jest-test.ts.hbs
autoload:
enabled: true
matchPattern: "generate tests|create tests|add test coverage"class TestGenerator:
def __init__(self, agent):
self.agent = agent
async def generate_tests(self, source_path):
content = await self.agent.read(source_path)
tree = self._parse_functions(content)
test_file = self._build_test_file(source_path, tree)
test_path = source_path.replace("src/", "tests/").replace(".py", "_test.py")
await self.agent.write(test_path, test_file)
return {"test_file": test_path, "test_count": len(tree)}
def _parse_functions(self, content):
import ast
tree = ast.parse(content)
functions = []
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef):
functions.append({
"name": node.name,
"args": [a.arg for a in node.args.args],
"decorators": [
d.id for d in node.decorator_list
if isinstance(d, ast.Name)
]
})
return functions
def _build_test_file(self, source_path, functions):
module_name = source_path.split("/")[-1].replace(".py", "")
lines = [
f'"""Tests for {module_name} module."""',
"import pytest",
f"from {module_name} import (",
]
for func in functions:
lines.append(f" {func['name']},")
lines.append(")")
lines.append("")
for func in functions:
lines.extend(self._generate_test_case(func))
return "\n".join(lines)
def _generate_test_case(self, func):
name = func["name"]
params = func["args"]
test_cases = [
f"def test_{name}_basic():",
f' """Test basic {name} functionality."""',
f" # TODO: Implement test for {name}",
f" # result = {name}({', '.join(params)})",
f" # assert result is not None",
f" pass",
"",
f"def test_{name}_edge_cases():",
f' """Test {name} with edge case inputs."""',
f" # TODO: Add edge case tests",
f" pass",
"",
]
return test_casesProyecto 5: Generación de documentación
class DocGenerator:
def __init__(self, agent):
self.agent = agent
async def generate_docs(self, project_root):
files = await self.agent.glob(f"{project_root}/**/*.py")
docs = []
for file in files[:20]:
content = await self.agent.read(file)
module_doc = self._extract_module_doc(content)
functions = self._parse_functions(content)
doc_section = [
f"## Module: {file}",
"",
module_doc or "No module documentation.",
"",
"### Functions",
"",
]
for func in functions:
doc_section.extend([
f"#### `{func['name']}({', '.join(func['args'])})`",
"",
func["docstring"] or "No documentation.",
"",
])
docs.append("\n".join(doc_section))
return "# API Documentation\n\n" + "\n---\n".join(docs)
def _extract_module_doc(self, content):
import ast
try:
tree = ast.parse(content)
return ast.get_docstring(tree)
except SyntaxError:
return None
def _parse_functions(self, content):
import ast
try:
tree = ast.parse(content)
functions = []
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef) and not node.name.startswith("_"):
functions.append({
"name": node.name,
"args": [a.arg for a in node.args.args],
"docstring": ast.get_docstring(node)
})
return functions
except SyntaxError:
return []Ejercicios de práctica
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
¿Cuál es el enfoque recomendado cuando una refactorización de un agente rompe las pruebas existentes?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
En el proyecto de revisión de código, ¿qué problema de seguridad comprueba el agente?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
¿Qué herramienta usa el generador de pruebas para analizar el código fuente e identificar funciones?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
¿Cuál es el propósito de usar una rama para la refactorización dirigida por un agente?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
En el proyecto de generación de la API de blogs, ¿qué framework se usa para la API generada?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
¿Qué convención de nombres de archivos usa el generador de pruebas al crear los archivos de prueba?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
¿Qué información extrae el generador de documentación del código fuente?
Cómo responder
Pregunta de opción múltiple con 4 opciones. Selecciona una opción y elige Comprobar respuesta.
¿Por qué el agente de revisión de código usa 'deniedTools: [write, edit]'?
- Los proyectos de agentes del mundo real abarcan generación de código, revisión, refactorización, pruebas y documentación
- Los agentes de generación de código crean módulos completos a partir de especificaciones, con modelos, rutas y pruebas
- Los agentes de revisión de código deben ser de solo lectura e inspeccionar el código en busca de errores, problemas de seguridad y fallos de estilo
- Los agentes de refactorización deben usar ramas de control de versiones para cambios seguros y reversibles
- Los agentes de generación de pruebas crean suites completas analizando la estructura del código fuente
- Los agentes de documentación extraen automáticamente la documentación de módulos y funciones del código fuente
- Cada proyecto combina varias skills: uso de herramientas, planificación, gestión del contexto y patrones de seguridad
- Los agentes reales gestionan los errores, adaptan los planes y aprenden de los resultados intermedios
- La lección final demuestra todo el poder de la IA agéntica: ingeniería de software autónoma y multietapa