Multi-agente con CrewAI: investigador, redactor, revisor
Tres agentes con roles distintos (investigador, redactor, revisor) coordinados por CrewAI. Aprendes qué resuelve un framework de orquestación cuando ya entiendes a mano lo que automatiza.
Costo estimado: free
Aparece en: Agentic AI desde cero para Backend y Cloud Developers
Por qué este lab existe
El TechnicalExplainerAgent del Lab 3 funciona para tareas individuales. Probemos algo que mezcle varias responsabilidades en una sola llamada:
from agent import TechnicalExplainerAgent
agent = TechnicalExplainerAgent()
respuesta = agent.run(
"Investiga qué es AWS EventBridge. "
"Después redacta un post de 300 palabras para backend devs. "
"Después revisa el post y arregla cualquier afirmación dudosa. "
"Devuelve la versión final lista para publicar."
)
print(respuesta)
Lo que sale es un texto mediocre: el modelo intenta hacer las tres cosas a la vez sin separar dónde investiga, dónde redacta y dónde revisa. Las instrucciones del system prompt del Lab 3 están escritas para un explainer técnico, no para un revisor crítico ni para un copywriter. Cuando le pides tres roles a la vez, te entrega el mínimo común denominador.
El problema no es el modelo. Es que un solo agente tiene una sola identidad. Para un flujo de tres pasos con criterios distintos en cada uno, necesitas tres agentes con sus propios roles y un orquestador que les pase la salida de uno como entrada del siguiente.
CrewAI es un framework que da las primitivas para hacer eso: Agent (rol + objetivo + backstory), Task (descripción + output esperado + agente asignado) y Crew (la ejecución coordinada).
Duración: ~75 min · Nivel: Intermediate · Costo: gratis (free tier de Groq).
Lo que vas a tener al terminar
- Un
crew_setup.pycon tres agentes definidos: un Researcher, un Writer y un Reviewer, cada uno con su propio role, goal y backstory. - Tres
Taskentasks.pyque se encadenan: research, write, review. - Un
main.pyreescrito que ejecuta elCrewy produce un post técnico final. - Criterio claro de cuándo conviene un framework de orquestación frente a hacerlo a mano (como en Labs 2 y 3).
Qué vas a aprender
- Definir un
Agenten CrewAI con role, goal y backstory. - Encadenar
Taskpasando contexto entre etapas conProcess.sequential. - Configurar un LLM de Groq compatible con CrewAI vía litellm.
- Leer el output final y el consumo de tokens agregado de un Crew.
Conceptos clave
Crew como orquestador
Qué es: un Crew (también llamado equipo en español; los dos nombres refieren al mismo objeto de CrewAI) es la entidad que coordina la ejecución de varios agentes. Tú declaras los agentes, declaras las tasks (cada task asignada a un agente) y el Crew las ejecuta en el orden definido por su process. La salida de cada task queda disponible como contexto para las siguientes, sin que tengas que pasársela a mano.
Mini-ejemplo:
from crewai import Crew, Process
crew = Crew(
agents=[researcher, writer, reviewer],
tasks=[research_task, write_task, review_task],
process=Process.sequential,
)
result = crew.kickoff(inputs={"topic": "AWS EventBridge"})
Agent y Task: la separación de roles
Qué es: un Agent en CrewAI es un AI Agent (mismo concepto que en Labs 2 y 3) con tres campos distintivos: role, goal y backstory. El backstory es texto extra que enriquece el system prompt con contexto sobre quién es el agente, qué experiencia tiene y qué actitud asumir. Una Task (también tarea en español) es una unidad de trabajo: una description de qué hacer, un expected_output que orienta el formato de salida y un agent asignado. La separación importa porque el framework genera un system prompt distinto por agente y le pasa contexto controlado, no un historial mezclado.
Mini-ejemplo:
researcher = Agent(
role="Investigador técnico cloud",
goal="Reunir hechos verificables sobre el tema solicitado",
backstory="Cloud architect con 10 años en AWS, prefiere fuentes oficiales.",
llm=llm,
)
research_task = Task(
description="Investiga qué es {topic} y cuándo conviene usarlo.",
expected_output="3-5 viñetas con hechos verificables.",
agent=researcher,
)
Para profundizar: la diferencia entre
Process.sequentialyProcess.hierarchicales central en CrewAI. En el primero, las tasks corren en el orden del array. En el segundo, un manager-agent extra decide a qué agente asignarle cada task y en qué orden. Para este lab usamos sequential porque el flujo research → write → review es estrictamente lineal. Ref: CrewAI · Processes.
Mapa visual
main.py
│
│ crew.kickoff(inputs={"topic": "AWS EventBridge"})
│
▼
┌─ Researcher ──────────────────────────────────┐
│ role: "Investigador técnico cloud" │
│ task: "investiga {topic}" │
│ output: "EventBridge es un bus de eventos..."│
└───────────────────────────────────────────────┘
│
│ context = output del Researcher
▼
┌─ Writer ──────────────────────────────────────┐
│ role: "Redactor técnico" │
│ task: "redacta un post de 300 palabras" │
│ output: "# AWS EventBridge para backend..." │
└───────────────────────────────────────────────┘
│
│ context = outputs del Researcher + Writer
▼
┌─ Reviewer ────────────────────────────────────┐
│ role: "Editor crítico" │
│ task: "revisa y arregla afirmaciones..." │
│ output: "# AWS EventBridge (versión final)" │
└───────────────────────────────────────────────┘
│
▼
crew.kickoff() retorna el output del último task
Cada agente tiene su propio system prompt construido a partir de role + goal + backstory. CrewAI le pasa al Writer el output del Researcher como contexto inicial; al Reviewer le pasa los outputs de Researcher y Writer. Ese paso de contexto entre agentes es lo que el framework te ahorra escribir a mano.
Punto de partida
Antes de instalar CrewAI, verifica que el Lab 3 esté funcionando.
cd agentic-ai-lab
source .venv/bin/activate
python main.py
Respuesta esperada: ves la respuesta del agente con las dos tools invocadas y summary.md generado o actualizado.
Si el script falla, regresa al Lab 3 y verifica que tools.py, agent.py y main.py estén correctos.
Sobre las versiones y los modelos
Igual que en los labs anteriores, no pineamos versiones en el pip install. Cuando corras el comando, instalas lo último que haya en PyPI ese día. Por si necesitas reproducir la versión validada (al updatedAt: 2026-05-16):
crewai: validado con0.95.x. Última en pypi.org/project/crewai o el changelog en GitHub.- Modelo de Groq: este lab sigue usando
llama-3.3-70b-versatile. Lista vigente en console.groq.com/docs/models.
CrewAI sigue versionado 0.x, lo que en la convención de SemVer admite breaking changes entre minors. Si entre la versión validada y la que descargues hubo un salto de minor (por ejemplo, 0.95.x → 0.110.x), revisa el changelog antes de seguir.
Paso 1. Instalar CrewAI
pip install crewai
Probar:
python -c "import crewai; print(crewai.__version__)"
Respuesta esperada: un número de versión, por ejemplo:
0.95.0
CrewAI trae como dependencia transitiva litellm, la librería que abstrae más de 100 proveedores de LLM detrás de la misma interfaz. Por eso un modelo de Groq se referencia como groq/llama-3.3-70b-versatile: el prefix le indica a litellm cuál proveedor usar. Esto va a ser central en el Lab 5.
Paso 2. Definir tres agentes con roles distintos
Archivo: crew_setup.py (NUEVO)
import os
from dotenv import load_dotenv
from crewai import Agent, LLM
load_dotenv()
llm = LLM(
model="groq/llama-3.3-70b-versatile",
api_key=os.environ["GROQ_API_KEY"],
temperature=0.3,
)
researcher = Agent(
role="Investigador técnico cloud",
goal="Reunir hechos verificables sobre el tema solicitado, sin opiniones ni embellecimiento",
backstory=(
"Eres un cloud architect con 10 años de experiencia en AWS. "
"Prefieres citar documentación oficial antes que generalidades. "
"Si un hecho no estás seguro de él, lo marcas como '(verificar)'."
),
llm=llm,
verbose=True,
)
writer = Agent(
role="Redactor técnico para backend developers",
goal="Convertir hechos en un post claro de 300 palabras dirigido a devs con experiencia",
backstory=(
"Has escrito durante 5 años para blogs técnicos. "
"Tono directo, sin marketing, con ejemplos concretos. "
"Estructuras los posts en: contexto, qué resuelve, cuándo usarlo, cuándo no."
),
llm=llm,
verbose=True,
)
reviewer = Agent(
role="Editor crítico de contenido técnico",
goal="Detectar afirmaciones vagas, marketing implícito y errores fácticos; reescribir donde haga falta",
backstory=(
"Has revisado más de 1000 posts técnicos. "
"Aplicas claridad sobre fluidez: si una frase no aporta, la cortas. "
"Si una afirmación no se verifica con los hechos del Researcher, la marcas."
),
llm=llm,
verbose=True,
)
Tres cosas para notar:
- Cada
Agenttiene su propiorole,goalybackstory. CrewAI compone con esos campos el system prompt antes de cada llamada al modelo. verbose=Truehace que CrewAI imprima en consola el output intermedio de cada agente. Útil mientras desarrollas; en producción se baja aFalse.- El mismo
llmse reutiliza entre los tres. No hay razón en este lab para usar modelos distintos por rol, aunque CrewAI lo permite.
Probar:
python -c "from crew_setup import researcher, writer, reviewer; print(researcher.role)"
Respuesta esperada:
Investigador técnico cloud
Paso 3. Definir las tasks y enlazarlas
Archivo: tasks.py (NUEVO)
from crewai import Task
from crew_setup import researcher, writer, reviewer
research_task = Task(
description=(
"Investiga qué es {topic}. "
"Reúne entre 4 y 6 hechos verificables: definición, caso de uso típico, "
"comparación con alternativas conocidas y limitaciones documentadas. "
"Cita fuentes oficiales cuando puedas (docs.aws.amazon.com, etc)."
),
expected_output=(
"Una lista de viñetas con los hechos. Si alguno requiere verificación, "
"marca con '(verificar)' al final de la línea."
),
agent=researcher,
)
write_task = Task(
description=(
"Convierte los hechos del Researcher en un post de 300 palabras dirigido a backend developers. "
"Estructura: contexto del problema, qué resuelve {topic}, cuándo conviene usarlo, "
"cuándo NO conviene. Sin marketing, sin frases vacías como 'solución poderosa'."
),
expected_output="Un post en Markdown de ~300 palabras, sin código (eso lo agrega otro lab).",
agent=writer,
)
review_task = Task(
description=(
"Lee el post del Writer y verifica que: "
"1) las afirmaciones técnicas sean correctas comparándolas con los hechos del Researcher, "
"2) no haya frases vacías ni marketing implícito, "
"3) el tono sea directo y conciso. "
"Reescribe solo lo que haga falta; preserva el resto literal. "
"Devuelve la versión final lista para publicar."
),
expected_output="El post final en Markdown, listo para publicar.",
agent=reviewer,
)
Notas:
\{topic\}es un placeholder; CrewAI lo sustituye con losinputsque le pases alkickoff(). Si olvidas pasarlo, el texto literal\{topic\}viaja al modelo.- Las tasks no se enlazan explícitamente entre sí. CrewAI infiere el contexto por el orden del array cuando
process=Process.sequential. - Cada
Taskdeclaraexpected_output. Eso le dice al modelo cómo estructurar la salida. Sin esa pista, el modelo improvisa el formato y el siguiente agente recibe algo más difícil de procesar.
Probar:
python -c "from tasks import research_task, write_task, review_task; print('OK')"
Respuesta esperada:
OK
Paso 4. Ejecutar el crew y observar el flujo
Archivo: main.py (REESCRITO)
from crewai import Crew, Process
from crew_setup import researcher, writer, reviewer
from tasks import research_task, write_task, review_task
crew = Crew(
agents=[researcher, writer, reviewer],
tasks=[research_task, write_task, review_task],
process=Process.sequential,
verbose=True,
)
resultado = crew.kickoff(inputs={"topic": "AWS EventBridge"})
print("\n" + "=" * 60)
print("POST FINAL")
print("=" * 60)
print(resultado.raw)
print("\n" + "=" * 60)
print("CONSUMO DE TOKENS (agregado del Crew)")
print("=" * 60)
print(resultado.token_usage)
Probar:
python main.py
Respuesta esperada (recortada; con verbose=True cada agente imprime su sección):
[INFO]: Working Agent: Investigador técnico cloud
[INFO]: Starting Task: Investiga qué es AWS EventBridge...
...
[INFO]: Working Agent: Redactor técnico para backend developers
[INFO]: Starting Task: Convierte los hechos del Researcher...
...
[INFO]: Working Agent: Editor crítico de contenido técnico
[INFO]: Starting Task: Lee el post del Writer y verifica...
...
============================================================
POST FINAL
============================================================
# AWS EventBridge para backend developers
AWS EventBridge es un bus de eventos serverless...
[post de ~300 palabras]
============================================================
CONSUMO DE TOKENS (agregado del Crew)
============================================================
total_tokens=4523 prompt_tokens=3201 completion_tokens=1322 ...
Lo que pasa paso a paso:
- El Researcher produce viñetas con hechos.
- El Writer recibe esas viñetas como contexto y redacta el post.
- El Reviewer recibe ambos outputs y devuelve la versión final.
El resultado.token_usage agrega el consumo de las llamadas a Groq que hicieron los tres agentes en suma. Ese número es lo que vas a ver crecer rápido cuando agregues tools, contextos largos o más agentes a un crew.
Errores comunes
Error 1. LLM no resuelve el proveedor (404 not_found)
Síntoma: al correr python main.py te sale litellm.exceptions.NotFoundError: ... llama-3.3-70b-versatile.
Causa raíz: sin el prefix groq/, litellm asume OpenAI por defecto y le pide al endpoint de OpenAI un modelo de Llama que ahí no existe.
Corrección: asegúrate de instanciar el LLM con el prefix del proveedor:
llm = LLM(model="groq/llama-3.3-70b-versatile", api_key=...)
Error 2. El placeholder \{topic\} queda literal en el prompt
Síntoma: los agentes investigan el tema \{topic\} literal en vez de AWS EventBridge.
Causa raíz: olvidaste pasar inputs=\{"topic": "AWS EventBridge"\} en crew.kickoff(). CrewAI no falla; deja el placeholder sin reemplazar.
Corrección:
resultado = crew.kickoff(inputs={"topic": "AWS EventBridge"})
Error 3. El Reviewer regenera todo el post desde cero
Síntoma: la versión final del post se parece muy poco al post del Writer; el Reviewer reescribió todo.
Causa raíz: la description del review_task es demasiado abierta. Cuando le pides “revisa”, el modelo siente libertad para reescribir entero.
Corrección: sé explícito sobre qué debe cambiar y qué debe preservar:
description=(
"Revisa el post sin reescribirlo entero. "
"Solo cambia las frases donde detectes errores técnicos o marketing implícito. "
"Preserva el resto literal."
),
Buenas prácticas
- Un rol por agente. No fusiones “Writer + Reviewer” en un mismo agente para ahorrar tokens. La separación de roles le da al modelo un system prompt enfocado por etapa y produce mejor calidad que un agente único pidiéndose autorrevisión. Ref: principio SRP (Martin, Clean Code, capítulo sobre funciones y responsabilidad única), aplicado a agentes.
expected_outputsiempre presente. Una task sinexpected_outputproduce formato inestable: a veces JSON, a veces prosa, a veces viñetas. El siguiente agente recibe algo distinto cada corrida y la coordinación se vuelve frágil. Trataexpected_outputcomo el contrato de salida del agente. Ref: principio de Tell Don’t Ask (Hunt & Thomas, The Pragmatic Programmer).- Empezar con sequential, no hierarchical.
Process.hierarchicalagrega un manager-agent que decide a qué agente asignar cada task. Es más flexible pero más caro en tokens y más difícil de debuggear. Empieza siempre por sequential; pasa a hierarchical solo cuando el orden de ejecución sea realmente dinámico. Ref: CrewAI · Sequential vs Hierarchical.
Estado del proyecto al finalizar
agentic-ai-lab/
├── .venv/
├── .env
├── .gitignore
├── call_llm.py ← del Lab 1, sin cambios
├── tools.py ← del Lab 3, sin cambios
├── agent.py ← del Lab 3, sin cambios
├── crew_setup.py ← NUEVO (researcher, writer, reviewer)
├── tasks.py ← NUEVO (research_task, write_task, review_task)
├── main.py ← REESCRITO (kickoff del Crew)
└── summary.md ← del Lab 3, sin cambios
Checklist:
-
python main.pycorre los tres agentes en orden y termina con un post final coherente. - El output de
verbose=Truemuestra cada agente actuando con su rol propio. -
resultado.rawcontiene el post final;resultado.token_usageel consumo agregado. - El
\{topic\}se reemplaza al invocarcrew.kickoff(inputs=...).
¿Qué viene en el Lab 5?
El crew corre con Groq porque así lo dejamos hardcodeado en crew_setup.py. En la práctica, cambiar de proveedor es algo que vas a querer hacer: por costos, por privacidad (datos que no salen de tu VPC), por probar otro modelo. En el Lab 5 vas a desacoplar el proveedor del crew y comparar la misma corrida contra OpenAI, Anthropic, Amazon Bedrock y Ollama local. Vas a confirmar de manera práctica que el agente es independiente del proveedor.
Referencias
Documentación oficial
Libros canónicos
- Martin, R. C. Clean Code, capítulo sobre funciones y responsabilidad única (SRP aplicado a separar roles entre agentes en vez de mezclarlos).
- Hunt, A. & Thomas, D. The Pragmatic Programmer, sección sobre Tell Don’t Ask aplicada al diseño del contrato de salida de cada task.
Repositorios y librerías
- crewAIInc/crewAI: repo oficial del framework.
- BerriAI/litellm: la librería que CrewAI usa por debajo para abstraer proveedores; explica por qué
groq/es el prefix correcto y prepara el terreno del Lab 5.