Prompt Engineering Avanzado para LLMs¶
Tiempo de lectura: 40 minutos | Dificultad: Intermedia | Categoría: Inteligencia Artificial
Resumen¶
El prompt engineering es el arte y ciencia de diseñar instrucciones efectivas para LLMs. Esta guía cubre técnicas profesionales desde zero-shot hasta chain-of-thought, con ejemplos prácticos y frameworks de evaluación para modelos locales.
🎯 Por Qué Import¶
a el Prompt Engineering
Impacto del Prompt en Resultados¶
# Prompt mal diseñado
prompt_malo = "dame info sobre kubernetes"
# Resultado: vago, poco útil, sin estructura
# Prompt bien diseñado
prompt_bueno = """
Actúa como un experto en Kubernetes. Explica los conceptos de Pods, Deployments y Services en el contexto de una aplicación web de 3 capas (frontend, backend, database).
Requisitos:
- Audiencia: Desarrolladores con conocimiento básico de Docker
- Longitud: 300-400 palabras
- Incluir: 1 ejemplo de YAML por concepto
- Formato: Markdown con secciones H2
Estructura:
1. Pods - Qué son y cuándo usarlos
2. Deployments - Gestión de réplicas
3. Services - Exponer aplicaciones
"""
# Resultado: estructurado, relevante, accionable
Beneficios de Buenos Prompts¶
- ✅ Reducción de iteraciones: Resultado correcto en primer intento
- ✅ Consistencia: Outputs predecibles y reproducibles
- ✅ Calidad superior: Respuestas más precisas y útiles
- ✅ Ahorro de tokens: Menos correcciones = menos costo
- ✅ Automatización efectiva: Integrable en pipelines
📋 Anatomía de un Buen Prompt¶
Componentes Fundamentales¶
class PromptTemplate:
def __init__(
self,
role: str, # Personalidad/expertise del LLM
task: str, # Qué debe hacer
context: str, # Información de fondo
constraints: list, # Limitaciones y requisitos
output_format: str, # Formato deseado
examples: list = None # Ejemplos (few-shot)
):
self.role = role
self.task = task
self.context = context
self.constraints = constraints
self.output_format = output_format
self.examples = examples or []
def build(self) -> str:
"""Construye el prompt completo."""
prompt_parts = []
# 1. Role/Persona
if self.role:
prompt_parts.append(f"Rol: {self.role}\n")
# 2. Contexto
if self.context:
prompt_parts.append(f"Contexto:\n{self.context}\n")
# 3. Ejemplos (few-shot)
if self.examples:
prompt_parts.append("Ejemplos:")
for i, example in enumerate(self.examples, 1):
prompt_parts.append(f"\nEjemplo {i}:")
prompt_parts.append(f"Input: {example['input']}")
prompt_parts.append(f"Output: {example['output']}\n")
# 4. Tarea principal
prompt_parts.append(f"Tarea:\n{self.task}\n")
# 5. Constraints
if self.constraints:
prompt_parts.append("Requisitos:")
for constraint in self.constraints:
prompt_parts.append(f"- {constraint}")
prompt_parts.append("")
# 6. Formato de salida
if self.output_format:
prompt_parts.append(f"Formato de salida:\n{self.output_format}\n")
return "\n".join(prompt_parts)
# Ejemplo de uso
template = PromptTemplate(
role="Experto en seguridad de contenedores Docker",
task="Audita este Dockerfile y sugiere mejoras de seguridad",
context="""
Dockerfile actual:
FROM ubuntu:latest
RUN apt-get update && apt-get install -y python3
COPY . /app
WORKDIR /app
CMD ["python3", "app.py"]
""",
constraints=[
"Priorizar imágenes oficiales y slim",
"Usuario no-root obligatorio",
"Multi-stage build si es posible",
"Minimizar layers"
],
output_format="""
JSON con:
{
"issues": ["..."],
"improvements": ["..."],
"dockerfile_improved": "..."
}
"""
)
prompt = template.build()
print(prompt)
🎓 Técnica 1: Zero-Shot Prompting¶
Definición¶
Dar instrucciones claras sin ejemplos previos. El modelo debe inferir qué hacer solo por la descripción.
Cuándo Usar¶
- Tareas simples y bien definidas
- Modelos grandes (13B+) con buena comprensión
- Cuando no hay ejemplos disponibles
Ejemplo Práctico¶
def zero_shot_classification(text: str, categories: list) -> str:
"""Clasifica texto en categorías sin ejemplos previos."""
prompt = f"""
Clasifica el siguiente texto en UNA de estas categorías: {', '.join(categories)}
Texto: "{text}"
Responde SOLO con el nombre de la categoría, sin explicaciones.
Categoría:"""
response = requests.post("http://localhost:11434/api/generate", json={
"model": "llama2:13b-chat-q4_0",
"prompt": prompt,
"temperature": 0.1,
"stream": False
})
return response.json()["response"].strip()
# Uso
categories = ["Bug", "Feature Request", "Documentation", "Question"]
issue_text = "The application crashes when clicking the submit button"
category = zero_shot_classification(issue_text, categories)
print(f"Categoría: {category}") # Output: Bug
Mejores Prácticas Zero-Shot¶
# ❌ Prompt vago
prompt_malo = "Clasifica esto: 'app crashes'"
# ✅ Prompt claro y específico
prompt_bueno = """
Tarea: Clasificación de tickets de soporte
Categorías válidas:
1. BUG - Error funcional en la aplicación
2. FEATURE - Solicitud de nueva funcionalidad
3. DOCS - Problema con documentación
4. QUESTION - Consulta de usuario
Texto a clasificar: "The application crashes when clicking the submit button"
Instrucciones:
- Responde SOLO con el nombre de la categoría
- Si no estás seguro, elige la más probable
- Formato: Una palabra en mayúsculas
Categoría:"""
🎯 Técnica 2: Few-Shot Prompting¶
Definición¶
Proporcionar ejemplos de entrada-salida antes de la tarea real para guiar al modelo.
Cuándo Usar¶
- Tareas complejas con formato específico
- Modelos medianos (7B-13B) que necesitan guía
- Cuando necesitas salidas consistentes
Ejemplo Práctico¶
def few_shot_entity_extraction(text: str) -> dict:
"""Extrae entidades usando few-shot learning."""
prompt = f"""
Extrae entidades técnicas de descripciones de incidentes.
Ejemplo 1:
Texto: "PostgreSQL database on srv-db-01 is experiencing high CPU usage"
Entidades: {"technology": "PostgreSQL", "resource": "database", "server": "srv-db-01", "metric": "CPU usage", "status": "high"}
Ejemplo 2:
Texto: "Nginx reverse proxy returning 502 errors for api.example.com"
Entidades: {"technology": "Nginx", "resource": "reverse proxy", "error": "502", "domain": "api.example.com"}
Ejemplo 3:
Texto: "Kubernetes pod web-frontend-abc123 is in CrashLoopBackOff state"
Entidades: {"technology": "Kubernetes", "resource": "pod", "name": "web-frontend-abc123", "status": "CrashLoopBackOff"}
Ahora extrae entidades de este texto:
Texto: "{text}"
Entidades:"""
response = requests.post("http://localhost:11434/api/generate", json={
"model": "llama2:13b-chat-q4_0",
"prompt": prompt,
"temperature": 0.2,
"stream": False,
"format": "json"
})
import json
return json.loads(response.json()["response"])
# Uso
incident = "Redis cache cluster on redis-prod-cluster-01 showing memory leak"
entities = few_shot_entity_extraction(incident)
print(entities)
# Output: {"technology": "Redis", "resource": "cache cluster", "name": "redis-prod-cluster-01", "issue": "memory leak"}
Optimización de Few-Shot¶
class FewShotOptimizer:
def __init__(self, model: str = "llama2:13b-chat-q4_0"):
self.model = model
self.ollama_url = "http://localhost:11434/api/generate"
def find_optimal_examples(
self,
task_description: str,
candidate_examples: list,
test_cases: list,
max_examples: int = 5
) -> list:
"""
Encuentra el número y selección óptima de ejemplos.
Args:
task_description: Descripción de la tarea
candidate_examples: Pool de ejemplos posibles
test_cases: Casos de prueba para evaluación
max_examples: Máximo número de ejemplos a probar
Returns:
Lista de ejemplos óptimos
"""
best_score = 0
best_examples = []
# Probar diferentes combinaciones
from itertools import combinations
for n in range(1, min(max_examples + 1, len(candidate_examples) + 1)):
for example_combo in combinations(candidate_examples, n):
# Probar con estos ejemplos
score = self.evaluate_examples(
task_description,
list(example_combo),
test_cases
)
if score > best_score:
best_score = score
best_examples = list(example_combo)
return best_examples
def evaluate_examples(
self,
task: str,
examples: list,
test_cases: list
) -> float:
"""Evalúa calidad de ejemplos en casos de prueba."""
correct = 0
for test_case in test_cases:
# Construir prompt con ejemplos
prompt = self.build_few_shot_prompt(task, examples, test_case["input"])
# Obtener respuesta
response = requests.post(self.ollama_url, json={
"model": self.model,
"prompt": prompt,
"temperature": 0.1,
"stream": False
})
output = response.json()["response"].strip()
# Comparar con respuesta esperada
if output == test_case["expected_output"]:
correct += 1
return correct / len(test_cases) if test_cases else 0
def build_few_shot_prompt(self, task: str, examples: list, input_text: str) -> str:
"""Construye prompt con ejemplos."""
prompt_parts = [task, ""]
for i, example in enumerate(examples, 1):
prompt_parts.append(f"Ejemplo {i}:")
prompt_parts.append(f"Input: {example['input']}")
prompt_parts.append(f"Output: {example['output']}")
prompt_parts.append("")
prompt_parts.append("Ahora tu turno:")
prompt_parts.append(f"Input: {input_text}")
prompt_parts.append("Output:")
return "\n".join(prompt_parts)
🧠 Técnica 3: Chain-of-Thought (CoT)¶
Definición¶
Instruir al modelo para que muestre su razonamiento paso a paso antes de dar la respuesta final.
Cuándo Usar¶
- Problemas complejos que requieren múltiples pasos
- Debugging y troubleshooting
- Análisis y diagnóstico técnico
Ejemplo Práctico¶
def chain_of_thought_debug(error_log: str, context: str = "") -> dict:
"""Usa CoT para debugging complejo."""
prompt = f"""
Actúa como un experto debugger. Analiza este error usando razonamiento paso a paso.
Error:
{error_log}
Contexto:
{context}
Piensa en voz alta, paso a paso:
Paso 1 - Identificar el tipo de error:
[Tu razonamiento aquí]
Paso 2 - Analizar el stack trace:
[Tu razonamiento aquí]
Paso 3 - Identificar variables/estado relevante:
[Tu razonamiento aquí]
Paso 4 - Hipótesis de causa raíz:
[Tu razonamiento aquí]
Paso 5 - Conclusión y solución:
[Tu razonamiento aquí]
Formato final en JSON:
{
"error_type": "...",
"root_cause": "...",
"reasoning_steps": ["paso 1", "paso 2", ...],
"solution": "...",
"confidence": 0.0-1.0
}
"""
response = requests.post("http://localhost:11434/api/generate", json={
"model": "llama2:13b-chat-q4_0",
"prompt": prompt,
"temperature": 0.3,
"stream": False
})
# Extraer JSON del final de la respuesta
full_response = response.json()["response"]
# Parsear el JSON
import json
import re
json_match = re.search(r'\{.*\}', full_response, re.DOTALL)
if json_match:
return json.loads(json_match.group())
return {"error": "No se pudo parsear respuesta"}
# Uso
error = """
TypeError: Cannot read property 'id' of undefined
at getUserProfile (app/controllers/user.js:42:18)
at Router.handle (node_modules/express/lib/router/index.js:284:7)
"""
context = """
Endpoint: GET /api/users/:id/profile
Request: user_id=12345
Database query returned empty result
"""
debug_result = chain_of_thought_debug(error, context)
print("Razonamiento:")
for step in debug_result["reasoning_steps"]:
print(f"- {step}")
print(f"\nSolución: {debug_result['solution']}")
print(f"Confianza: {debug_result['confidence']}")
CoT con Auto-Consistency¶
def chain_of_thought_with_consistency(
question: str,
num_samples: int = 5
) -> dict:
"""
Genera múltiples razonamientos CoT y selecciona el más consistente.
"""
prompt_template = f"""
Resuelve este problema paso a paso:
{question}
Razonamiento paso a paso:
1. [Primer paso]
2. [Segundo paso]
3. [Tercer paso]
...
Respuesta final: [Tu respuesta]
"""
responses = []
# Generar múltiples respuestas con temperatura alta
for _ in range(num_samples):
response = requests.post("http://localhost:11434/api/generate", json={
"model": "llama2:13b-chat-q4_0",
"prompt": prompt_template,
"temperature": 0.7, # Mayor variación
"stream": False
})
responses.append(response.json()["response"])
# Extraer respuestas finales
final_answers = []
for resp in responses:
# Buscar "Respuesta final:" en la respuesta
import re
match = re.search(r'Respuesta final:\s*(.+)', resp, re.IGNORECASE)
if match:
final_answers.append(match.group(1).strip())
# Encontrar respuesta más común (voting)
from collections import Counter
answer_counts = Counter(final_answers)
most_common_answer, count = answer_counts.most_common(1)[0]
return {
"answer": most_common_answer,
"confidence": count / num_samples,
"all_responses": responses,
"answer_distribution": dict(answer_counts)
}
# Uso
question = """
Un pod de Kubernetes está consumiendo 800MB de memoria pero su límite es 512MB.
El pod no se reinicia pero las nuevas solicitudes fallan.
¿Por qué está sucediendo esto y cómo se soluciona?
"""
result = chain_of_thought_with_consistency(question, num_samples=5)
print(f"Respuesta consensuada: {result['answer']}")
print(f"Confianza: {result['confidence']:.1%}")
print(f"Distribución: {result['answer_distribution']}")
🎨 Técnica 4: Role Prompting¶
Definición¶
Asignar un rol o personalidad específica al modelo para obtener respuestas más apropiadas.
Ejemplo Práctico¶
class RoleBasedPrompt:
ROLES = {
"devops_engineer": """
Eres un Senior DevOps Engineer con 10+ años de experiencia en:
- Kubernetes, Docker, Terraform
- AWS, GCP, Azure
- CI/CD (Jenkins, GitLab, GitHub Actions)
- Observabilidad (Prometheus, Grafana, ELK)
Tu estilo:
- Pragmático y orientado a soluciones
- Enfocado en automatización y escalabilidad
- Prefieres código sobre explicaciones largas
- Consideras seguridad y costos en tus recomendaciones
""",
"security_expert": """
Eres un Security Architect especializado en:
- Application Security (OWASP Top 10)
- Cloud Security (CIS Benchmarks)
- Container Security (trivy, falco)
- Compliance (SOC2, ISO 27001, GDPR)
Tu estilo:
- Seguridad primero, siempre
- Asumes breach (zero trust)
- Proporcionas evidencia y referencias
- Balanceas seguridad con usabilidad
""",
"sre": """
Eres un Site Reliability Engineer enfocado en:
- Disponibilidad y confiabilidad (SLIs, SLOs, SLAs)
- Incident Management y Postmortems
- Capacity Planning
- Chaos Engineering
Tu estilo:
- Basado en datos y métricas
- Proactivo en prevención
- Automatizas toil sin piedad
- Documentas todo para futura referencia
"""
}
def __init__(self, role: str, model: str = "llama2:13b-chat-q4_0"):
self.role = self.ROLES.get(role, "")
self.model = model
self.ollama_url = "http://localhost:11434/api/generate"
def ask(self, question: str, context: str = "") -> str:
"""Hace una pregunta con el rol asignado."""
prompt = f"""
{self.role}
Contexto adicional:
{context}
Pregunta:
{question}
Tu respuesta (mantén el rol y estilo):
"""
response = requests.post(self.ollama_url, json={
"model": self.model,
"prompt": prompt,
"temperature": 0.4,
"stream": False
})
return response.json()["response"]
# Uso comparativo
question = "¿Cómo desplegar una aplicación Node.js en Kubernetes?"
# Perspectiva DevOps
devops = RoleBasedPrompt("devops_engineer")
devops_answer = devops.ask(question)
print("DevOps Engineer:")
print(devops_answer)
print("\n" + "="*80 + "\n")
# Perspectiva Security
security = RoleBasedPrompt("security_expert")
security_answer = security.ask(question)
print("Security Expert:")
print(security_answer)
print("\n" + "="*80 + "\n")
# Perspectiva SRE
sre = RoleBasedPrompt("sre")
sre_answer = sre.ask(question)
print("SRE:")
print(sre_answer)
📊 Evaluación de Prompts¶
Framework de Evaluación¶
from dataclasses import dataclass
from typing import Callable
import statistics
@dataclass
class PromptMetrics:
relevance: float # 0-1
accuracy: float # 0-1
completeness: float # 0-1
consistency: float # 0-1
tokens_used: int
latency_ms: float
class PromptEvaluator:
def __init__(self, model: str = "llama2:13b-chat-q4_0"):
self.model = model
self.ollama_url = "http://localhost:11434/api/generate"
def evaluate_prompt(
self,
prompt: str,
test_cases: list,
evaluation_criteria: dict
) -> PromptMetrics:
"""
Evalúa un prompt en múltiples dimensiones.
Args:
prompt: Template de prompt a evaluar
test_cases: Lista de casos de prueba
evaluation_criteria: Criterios de evaluación personalizados
Returns:
Métricas agregadas del prompt
"""
import time
results = []
total_tokens = 0
latencies = []
for test_case in test_cases:
# Ejecutar prompt
full_prompt = prompt.format(**test_case["variables"])
start_time = time.time()
response = requests.post(self.ollama_url, json={
"model": self.model,
"prompt": full_prompt,
"temperature": 0.2,
"stream": False
})
latency = (time.time() - start_time) * 1000
output = response.json()["response"]
# Evaluar respuesta
relevance = self._evaluate_relevance(output, test_case["expected_topics"])
accuracy = self._evaluate_accuracy(output, test_case["ground_truth"])
completeness = self._evaluate_completeness(output, test_case["required_elements"])
results.append({
"relevance": relevance,
"accuracy": accuracy,
"completeness": completeness
})
# Contar tokens (aproximado)
total_tokens += len(full_prompt.split()) + len(output.split())
latencies.append(latency)
# Calcular métricas agregadas
return PromptMetrics(
relevance=statistics.mean([r["relevance"] for r in results]),
accuracy=statistics.mean([r["accuracy"] for r in results]),
completeness=statistics.mean([r["completeness"] for r in results]),
consistency=1.0 - statistics.stdev([r["accuracy"] for r in results]) if len(results) > 1 else 1.0,
tokens_used=total_tokens,
latency_ms=statistics.mean(latencies)
)
def _evaluate_relevance(self, output: str, expected_topics: list) -> float:
"""Evalúa si la respuesta es relevante a los tópicos esperados."""
output_lower = output.lower()
matches = sum(1 for topic in expected_topics if topic.lower() in output_lower)
return matches / len(expected_topics) if expected_topics else 0.0
def _evaluate_accuracy(self, output: str, ground_truth: str) -> float:
"""Evalúa precisión comparando con ground truth."""
# Usar otro LLM para evaluar (LLM-as-Judge)
eval_prompt = f"""
Evalúa la precisión de esta respuesta en una escala de 0.0 a 1.0.
Respuesta correcta (ground truth):
{ground_truth}
Respuesta a evaluar:
{output}
Criterios:
- 1.0: Completamente correcta
- 0.8: Mayormente correcta con errores menores
- 0.6: Parcialmente correcta
- 0.4: Incorrecta pero relacionada
- 0.0: Completamente incorrecta
Responde SOLO con un número de 0.0 a 1.0:
"""
response = requests.post(self.ollama_url, json={
"model": self.model,
"prompt": eval_prompt,
"temperature": 0.1,
"stream": False
})
try:
score = float(response.json()["response"].strip())
return max(0.0, min(1.0, score))
except:
return 0.5 # Default si no se puede parsear
def _evaluate_completeness(self, output: str, required_elements: list) -> float:
"""Evalúa si la respuesta incluye todos los elementos requeridos."""
output_lower = output.lower()
present = sum(1 for elem in required_elements if elem.lower() in output_lower)
return present / len(required_elements) if required_elements else 1.0
def compare_prompts(self, prompts: dict, test_cases: list) -> dict:
"""Compara múltiples variantes de prompts."""
results = {}
for name, prompt in prompts.items():
print(f"Evaluando prompt: {name}...")
metrics = self.evaluate_prompt(prompt, test_cases, {})
results[name] = metrics
# Generar reporte comparativo
return self._generate_comparison_report(results)
def _generate_comparison_report(self, results: dict) -> dict:
"""Genera reporte comparativo de prompts."""
# Encontrar el mejor en cada métrica
best = {
"relevance": max(results.items(), key=lambda x: x[1].relevance),
"accuracy": max(results.items(), key=lambda x: x[1].accuracy),
"completeness": max(results.items(), key=lambda x: x[1].completeness),
"consistency": max(results.items(), key=lambda x: x[1].consistency),
"efficiency": min(results.items(), key=lambda x: x[1].tokens_used),
"speed": min(results.items(), key=lambda x: x[1].latency_ms)
}
return {
"all_results": results,
"best_per_metric": best,
"recommendation": self._recommend_best_prompt(results)
}
def _recommend_best_prompt(self, results: dict) -> str:
"""Recomienda el mejor prompt overall."""
# Scoring ponderado
scores = {}
for name, metrics in results.items():
score = (
metrics.relevance * 0.3 +
metrics.accuracy * 0.4 +
metrics.completeness * 0.2 +
metrics.consistency * 0.1
)
scores[name] = score
best_name = max(scores.items(), key=lambda x: x[1])[0]
return best_name
# Uso
evaluator = PromptEvaluator()
# Definir prompts a comparar
prompts = {
"simple": """
Explica qué es Kubernetes.
""",
"structured": """
Explica qué es Kubernetes.
Audiencia: Desarrolladores backend con experiencia en Docker
Longitud: 200-300 palabras
Incluir: Conceptos principales, beneficios, cuándo usar
Formato:
1. Definición breve
2. Conceptos clave
3. Beneficios
4. Cuándo usar vs Docker Compose
""",
"role_based": """
Eres un Senior Platform Engineer explicando a tu equipo.
Explica qué es Kubernetes de forma práctica y clara.
Requisitos:
- Audiencia: Developers que usan Docker
- Enfoque: Pragmático, no teórico
- Ejemplos: Casos de uso reales
- Longitud: 250 palabras
"""
}
# Casos de prueba
test_cases = [
{
"variables": {},
"expected_topics": ["containers", "orchestration", "pods", "clusters"],
"ground_truth": "Kubernetes is a container orchestration platform...",
"required_elements": ["pods", "services", "deployments"]
}
]
# Comparar
comparison = evaluator.compare_prompts(prompts, test_cases)
print("\n📊 Resultados de Evaluación:\n")
for name, metrics in comparison["all_results"].items():
print(f"{name}:")
print(f" Relevancia: {metrics.relevance:.2f}")
print(f" Precisión: {metrics.accuracy:.2f}")
print(f" Completitud: {metrics.completeness:.2f}")
print(f" Tokens: {metrics.tokens_used}")
print(f" Latencia: {metrics.latency_ms:.0f}ms\n")
print(f"🏆 Recomendación: {comparison['recommendation']}")
🔧 Técnicas Avanzadas¶
1. Self-Consistency con Voting¶
Ya cubierto en Chain-of-Thought, pero aquí la implementación completa:
def self_consistency_voting(
prompt: str,
num_samples: int = 7,
temperature: float = 0.8
) -> dict:
"""
Genera múltiples respuestas y usa voting para determinar consenso.
"""
responses = []
for i in range(num_samples):
response = requests.post("http://localhost:11434/api/generate", json={
"model": "llama2:13b-chat-q4_0",
"prompt": prompt,
"temperature": temperature,
"stream": False
})
responses.append(response.json()["response"])
# Usar LLM para determinar consenso
consensus_prompt = f"""
Estas son {num_samples} respuestas diferentes a la misma pregunta:
{chr(10).join([f"{i+1}. {r}" for i, r in enumerate(responses)])}
Analiza las respuestas y determina:
1. Puntos de consenso (qué dicen todas o la mayoría)
2. Puntos de divergencia (dónde difieren)
3. Respuesta final sintetizada (combina lo mejor de todas)
Formato JSON:
{
"consensus_points": ["..."],
"divergence_points": ["..."],
"final_answer": "...",
"confidence": 0.0-1.0
}
"""
consensus_response = requests.post("http://localhost:11434/api/generate", json={
"model": "llama2:13b-chat-q4_0",
"prompt": consensus_prompt,
"temperature": 0.2,
"stream": False,
"format": "json"
})
import json
return json.loads(consensus_response.json()["response"])
2. Prompt Chaining¶
class PromptChain:
def __init__(self, model: str = "llama2:13b-chat-q4_0"):
self.model = model
self.ollama_url = "http://localhost:11434/api/generate"
self.chain_history = []
def add_step(self, prompt_template: str, use_previous: bool = True):
"""Añade un paso a la cadena."""
def step_function(input_data: dict) -> str:
# Construir prompt con datos previos si es necesario
if use_previous and self.chain_history:
previous_output = self.chain_history[-1]["output"]
input_data["previous_output"] = previous_output
prompt = prompt_template.format(**input_data)
response = requests.post(self.ollama_url, json={
"model": self.model,
"prompt": prompt,
"temperature": 0.3,
"stream": False
})
output = response.json()["response"]
self.chain_history.append({
"prompt": prompt,
"output": output,
"input_data": input_data
})
return output
return step_function
def execute(self, initial_data: dict) -> dict:
"""Ejecuta toda la cadena."""
return {
"final_output": self.chain_history[-1]["output"] if self.chain_history else None,
"chain_history": self.chain_history
}
# Ejemplo: Pipeline de análisis de código
chain = PromptChain()
# Paso 1: Analizar código
analyze_step = chain.add_step("""
Analiza este código y identifica:
1. Funcionalidad principal
2. Posibles bugs
3. Mejoras de rendimiento
Código:
{code}
Análisis:
""", use_previous=False)
# Paso 2: Generar refactor
refactor_step = chain.add_step("""
Basándote en este análisis:
{previous_output}
Genera código refactorizado que implemente las mejoras sugeridas.
Código refactorizado:
""")
# Paso 3: Documentar
document_step = chain.add_step("""
Genera documentación completa para este código refactorizado:
{previous_output}
Incluye:
- Docstring de función
- Comentarios inline
- Ejemplos de uso
Documentación:
""")
# Ejecutar cadena
code_to_analyze = """
def process_data(data):
result = []
for item in data:
if item > 0:
result.append(item * 2)
return result
"""
analyze_step({"code": code_to_analyze})
refactor_step({})
document_step({})
final_result = chain.execute({})
print(final_result["final_output"])
📚 Mejores Prácticas y Anti-Patrones¶
✅ DO's¶
-
Sé específico y claro
# ✅ Bueno prompt = "Genera una función Python que calcule el factorial de un número usando recursión. Incluye manejo de errores para inputs negativos y tipo de retorno anotado." -
Usa delimitadores claros
# ✅ Bueno prompt = """ Texto a analizar: ''' {user_input} ''' Análisis: """ -
Especifica formato de salida
# ✅ Bueno prompt = "Responde en formato JSON con estas keys: {status, message, data}" -
Proporciona contexto relevante
# ✅ Bueno prompt = f"Contexto: Aplicación web de e-commerce con 1M usuarios/día\nPregunta: {question}"
❌ DON'Ts¶
-
Ambigüedad
# ❌ Malo prompt = "dame info sobre kubernetes" -
Prompts demasiado largos
# ❌ Malo (>4000 palabras de contexto innecesario) prompt = f"{entire_documentation}\nAhora responde: {simple_question}" -
Asumir conocimiento implícito
# ❌ Malo prompt = "Explica cómo funciona eso" # ¿Qué es "eso"? -
No validar outputs
# ❌ Malo response = llm.generate(prompt) use_directly(response) # Sin validación
🔗 Recursos Adicionales¶
📚 Próximos Pasos¶
Después de dominar prompt engineering, considera:
- Fine-tuning Básico - Personalizar modelos para tu dominio
- Evaluación de Modelos - Métricas y benchmarks
- LLMs en Producción - Despliegue a escala
¿Has desarrollado técnicas de prompting efectivas? Comparte tus estrategias y aprendizajes en los comentarios.