Casos de Uso en Producción
Mapa de Casos de Uso
┌─────────────────────────────────────────────────┐
│ CASOS DE USO LLM EN PRODUCCIÓN │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Chatbot │ │ Búsqueda │ │ Generación │ │
│ │ Soporte │ │ Semántica│ │ de Contenido │ │
│ └──────────┘ └──────────┘ └──────────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Resumen │ │Extracción│ │ Asistente │ │
│ │Documentos│ │ de Datos │ │ de Código │ │
│ └──────────┘ └──────────┘ └──────────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │Traducción│ │Clasificac│ │ Agente │ │
│ │ Técnica │ │ ión │ │ Autónomo │ │
│ └──────────┘ └──────────┘ └──────────────┘ │
└─────────────────────────────────────────────────┘
Chatbot de Soporte con RAG
from openai import OpenAI
import chromadb
client = OpenAI()
chroma = chromadb.HttpClient()
collection = chroma.get_collection("knowledge_base")
class SupportChatbot:
def __init__(self):
self.system_prompt = """Eres un agente de soporte técnico.
Responde SOLO basándote en el contexto proporcionado.
Si no encuentras la respuesta en el contexto, di:
"No tengo información sobre eso. ¿Puedo conectarte con un agente humano?"
Formato:
- Sé conciso y directo
- Usa listas para pasos
- Incluye enlaces relevantes del contexto"""
def get_context(self, query: str, n_results: int = 5) -> str:
results = collection.query(
query_texts=[query],
n_results=n_results,
)
context_parts = []
for doc, metadata in zip(results["documents"][0], results["metadatas"][0]):
source = metadata.get("source", "desconocido")
context_parts.append(f"[Fuente: {source}]\n{doc}")
return "\n\n---\n\n".join(context_parts)
def chat(self, user_message: str, history: list = None) -> dict:
context = self.get_context(user_message)
messages = [{"role": "system", "content": self.system_prompt}]
# Añadir historial (últimos 5 turnos)
if history:
messages.extend(history[-10:])
messages.append({
"role": "user",
"content": f"""Contexto relevante:
{context}
Pregunta del usuario: {user_message}"""
})
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
temperature=0.3,
max_tokens=500,
)
answer = response.choices[0].message.content
# Detectar si necesita escalación
needs_human = any(phrase in answer.lower() for phrase in [
"agente humano", "no tengo información", "no puedo ayudar"
])
return {
"answer": answer,
"needs_escalation": needs_human,
"sources": [m.get("source") for m in results["metadatas"][0]],
"tokens_used": response.usage.total_tokens,
}
Búsqueda Semántica
class SemanticSearch:
"""Motor de búsqueda semántica sobre documentos."""
def __init__(self):
self.collection = chroma.get_or_create_collection(
"documents",
metadata={"hnsw:space": "cosine"},
)
def index_documents(self, documents: list[dict]):
"""Indexar documentos con chunking."""
for doc in documents:
chunks = self.chunk_document(doc["content"])
for i, chunk in enumerate(chunks):
self.collection.add(
documents=[chunk],
ids=[f"{doc['id']}_chunk_{i}"],
metadatas=[{
"doc_id": doc["id"],
"title": doc["title"],
"chunk_index": i,
"total_chunks": len(chunks),
}],
)
def search(self, query: str, n_results: int = 10) -> list:
results = self.collection.query(
query_texts=[query],
n_results=n_results,
)
# Agrupar por documento y re-rankear
doc_scores = {}
for doc, metadata, distance in zip(
results["documents"][0],
results["metadatas"][0],
results["distances"][0],
):
doc_id = metadata["doc_id"]
if doc_id not in doc_scores:
doc_scores[doc_id] = {
"title": metadata["title"],
"best_chunk": doc,
"score": 1 - distance,
"chunks": [],
}
doc_scores[doc_id]["chunks"].append(doc)
doc_scores[doc_id]["score"] = max(
doc_scores[doc_id]["score"], 1 - distance
)
# Re-rankear con LLM
ranked = self.rerank_with_llm(query, doc_scores)
return ranked
def rerank_with_llm(self, query: str, candidates: dict) -> list:
prompt = f"Consulta: {query}\n\nDocumentos:\n"
for i, (doc_id, info) in enumerate(candidates.items()):
prompt += f"{i+1}. [{info['title']}]: {info['best_chunk'][:200]}\n"
prompt += "\nOrdena del más al menos relevante (solo números):"
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
temperature=0,
max_tokens=50,
)
return list(candidates.values())
def chunk_document(self, text: str, chunk_size: int = 500, overlap: int = 50) -> list:
words = text.split()
chunks = []
for i in range(0, len(words), chunk_size - overlap):
chunk = " ".join(words[i:i + chunk_size])
if chunk:
chunks.append(chunk)
return chunks
Extracción Estructurada de Datos
from pydantic import BaseModel, Field
class Invoice(BaseModel):
invoice_number: str = Field(description="Número de factura")
date: str = Field(description="Fecha de emisión (YYYY-MM-DD)")
vendor: str = Field(description="Nombre del proveedor")
total: float = Field(description="Monto total")
currency: str = Field(description="Moneda (USD, EUR, MXN)")
line_items: list[dict] = Field(description="Lista de items")
class DataExtractor:
def extract_invoice(self, text: str) -> Invoice:
response = client.beta.chat.completions.parse(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Extrae datos de facturas."},
{"role": "user", "content": f"Extrae los datos:\n\n{text}"},
],
response_format=Invoice,
)
return response.choices[0].message.parsed
def extract_batch(self, documents: list[str]) -> list[Invoice]:
results = []
for doc in documents:
try:
invoice = self.extract_invoice(doc)
results.append({"status": "ok", "data": invoice})
except Exception as e:
results.append({"status": "error", "error": str(e)})
return results
# Uso
extractor = DataExtractor()
invoice = extractor.extract_invoice("""
FACTURA #INV-2024-001
Fecha: 15 de Enero, 2024
Proveedor: Tech Solutions SA
1x Licencia Software: $500.00
2x Consultoría (hora): $300.00
Total: $800.00 USD
""")
print(invoice.model_dump_json(indent=2))
Generación de Código
class CodeAssistant:
SYSTEM = """Eres un asistente de programación experto.
Genera código limpio, documentado y con manejo de errores.
Sigue las mejores prácticas del lenguaje especificado."""
def generate(self, description: str, language: str = "python") -> dict:
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": self.SYSTEM},
{"role": "user", "content": f"""
Lenguaje: {language}
Requerimiento: {description}
Responde con:
1. Código completo
2. Explicación breve
3. Tests unitarios
"""}
],
temperature=0.2,
)
return {"code": response.choices[0].message.content}
def review(self, code: str) -> dict:
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "Revisa este código. Identifica bugs, mejoras de rendimiento, y problemas de seguridad."},
{"role": "user", "content": code},
],
temperature=0.3,
)
return {"review": response.choices[0].message.content}
def explain(self, code: str) -> str:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Explica este código de forma clara y concisa."},
{"role": "user", "content": code},
],
temperature=0.3,
)
return response.choices[0].message.content
Resumen de Documentos
class DocumentSummarizer:
"""Resumir documentos largos con estrategia map-reduce."""
def summarize(self, text: str, max_length: int = 500) -> str:
# Si cabe en contexto, resumir directo
if len(text.split()) < 3000:
return self._direct_summary(text, max_length)
# Map-reduce para documentos largos
return self._map_reduce_summary(text, max_length)
def _direct_summary(self, text: str, max_length: int) -> str:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": f"Resume en máximo {max_length} palabras. Mantén los puntos clave."},
{"role": "user", "content": text},
],
temperature=0.3,
)
return response.choices[0].message.content
def _map_reduce_summary(self, text: str, max_length: int) -> str:
# Map: resumir cada chunk
chunks = self._chunk_text(text, chunk_size=2000)
chunk_summaries = []
for chunk in chunks:
summary = self._direct_summary(chunk, max_length=200)
chunk_summaries.append(summary)
# Reduce: combinar resúmenes
combined = "\n\n".join(chunk_summaries)
return self._direct_summary(combined, max_length)
def _chunk_text(self, text: str, chunk_size: int = 2000) -> list:
words = text.split()
return [
" ".join(words[i:i + chunk_size])
for i in range(0, len(words), chunk_size)
]
# Uso
summarizer = DocumentSummarizer()
resumen = summarizer.summarize(documento_largo)
Patrones Comunes en Producción
# Patrón: Clasificador + Especialista
class SmartRouter:
"""Clasifica la intención y rutea al handler apropiado."""
HANDLERS = {
"soporte_tecnico": SupportChatbot(),
"ventas": SalesAgent(),
"facturacion": BillingAssistant(),
"general": GeneralAssistant(),
}
def classify(self, message: str) -> str:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{
"role": "user",
"content": f"Clasifica esta consulta: '{message}'\nCategorías: {list(self.HANDLERS.keys())}\nResponde SOLO la categoría."
}],
temperature=0,
max_tokens=20,
)
category = response.choices[0].message.content.strip().lower()
return category if category in self.HANDLERS else "general"
def handle(self, message: str) -> str:
category = self.classify(message)
handler = self.HANDLERS[category]
return handler.process(message)
Resumen
Los casos de uso más comunes de LLMs en producción incluyen: chatbots con RAG para soporte, búsqueda semántica, extracción estructurada de datos, generación y revisión de código, resumen de documentos, y clasificación inteligente. Cada caso requiere patrones específicos de prompt engineering, manejo de errores, y evaluación continua.