AWS Builder Center
Strands harness: tu agente ya viene armado, en una línea

Strands harness: tu agente ya viene armado, en una línea

Si ya construiste tu primer agente, seguro conoces este momento: en la demo funcionó precioso, pero cuando quisiste que hiciera trabajo de verdad empezó la cuesta arriba. Hoy quiero contarte cómo me salté esa cuesta con Strands harness, y cómo armé un agente que revisa los issues de mi repositorio open source empezando por una sola línea de código.

Si ya construiste tu primer agente, seguro conoces este momento: en la demo funcionó precioso, pero cuando quisiste que hiciera trabajo de verdad empezó la cuesta arriba. Ahí es donde toca conectar las herramientas, cuidar que el contexto no se dispare y te cueste una fortuna, darle memoria, y encima dejarlo corriendo solo sin estar tú ahí mirándolo. Y es ahí, en todo ese trabajo de plomería, donde muchos proyectos se quedan a medias.
Hoy quiero contarte cómo me salté esa cuesta con Strands harness, y cómo armé un agente que revisa los issues de mi repositorio open source empezando por una sola línea de código. Al final lo dejé corriendo solo cada vez que alguien abre un issue. También quiero contarte lo que se me rompió en el camino, porque de ahí salió la mejor lección.
Si todavía no has construido tu primer agente, te recomiendo pasar antes por Tu primer agente de IA con Strands Agents . Aquí doy por hecho que ya sabes qué es un agente y el loop; vamos a hablar de qué pasa después.

Qué es un harness (y por qué te importa)

Un harness  es todo lo que rodea al modelo para que pueda hacer trabajo de verdad: el loop, las herramientas, el manejo del contexto, la memoria y las sesiones. Si el modelo es el cerebro, el harness es el cuerpo que le pones alrededor.
Un detalle importante: Strands harness no es un asistente para programar de esos que ya usas en tu día a día. Es de propósito general, pensado para esas tareas largas y repetitivas que normalmente le tocarían a una persona. Revisar los issues de un repo es justo eso.
Ese cuerpo lo puedes armar de dos maneras. Una es a mano, pieza por pieza, con el SDK de Strands: empiezas con el modelo y su loop, y le vas agregando las herramientas, el manejo de contexto, la memoria y un ayudante para las partes grandes. Cada pieza es una decisión que tomas y afinas tú. Es totalmente válido, y para eso existe el SDK.
La otra manera es el harness: todas esas piezas ya vienen puestas y afinadas desde el primer momento, en una línea. No es que una forma esté bien y la otra mal. Es que puedes empezar con todo puesto, y bajar a armar a mano solo el día que lo necesites. Hoy vamos por el camino corto.

El ejemplo: un agente que revisa issues

Mantengo proyectos open source, y la montaña de issues sin revisar es un dolor que viví de verdad. Cada issue hay que abrirlo, leerlo, entender si es un error o una idea, buscar si alguien ya lo reportó, y ponerle etiquetas. Y mientras haces eso, siguen entrando más.
Mi agente hace ese primer pase por mí. Cuando entra un issue nuevo, lo lee, revisa el código relacionado, busca reportes parecidos, decide si es un bug, una idea o un duplicado, y deja un comentario con su análisis y las etiquetas que sugiere. Después yo apruebo. Todo el código vive en mi repo splice , en examples/issue-reviewer.

La línea que arma el agente

Esta es la parte central del agente, tal cual corre en mi repo:
1
2
3
4
5
6
7
8
9
from strands_harness import create_harness

agent = create_harness(
model="openai/gpt-5.6-sol", # 1
effort="medium", # 2
instructions=INSTRUCTIONS, # 3
builtin_tools=READ_ONLY_TOOLS, # 4
callback_handler=None, # 5
)
Esa llamada te devuelve un agente completo: loop, manejo de contexto, memoria y sesiones ya puestos. Déjame explicarte cada línea, porque cada una fue una decisión:
  1. El modelo. Más adelante te cuento por qué terminé en OpenAI y no en el default de Amazon Bedrock, porque esa decisión tiene su historia.
  2. El esfuerzo de razonamiento. medium fue suficiente para clasificar issues. Con high el agente razonaba de más y se ponía a leer medio repo. Volveré a esto, porque fue parte de la lección.
  3. Las instrucciones. Un texto corto que se suma al system prompt y le dice al agente cuál es su trabajo.
  4. Las herramientas. Las acoté a solo lectura por una razón de seguridad que te explico abajo.
  5. El handler de salida. Lo apago porque el agente corre en un pipeline y su salida va directo al comentario. Si lo dejara encendido, se colaría todo el ruido del streaming.

Las herramientas de fábrica, y por qué las acoté

El harness trae de fábrica terminal, archivos y web, todas encendidas por default. Pero mi agente lo activa cualquiera: cualquier persona en internet puede abrir un issue en mi repo. Entonces no quiero que ese agente tenga permiso de escribir en el repositorio ni de tocar nada. El texto de un issue no debería poder convertirse en una acción que rompa mi proyecto.
Así que acoté las herramientas a solo lectura:
1
READ_ONLY_TOOLS = ["read", "web_fetch", "web_search"]
Con eso el agente puede leer archivos, abrir una URL que el issue mencione, y buscar en la web. Nada de write, edit ni shell. Su única salida es un comentario con etiquetas sugeridas que yo apruebo. El agente propone, la persona decide. No le doy las llaves de la casa, le doy una voz en la conversación.
Y esto no es una caja negra. Lo que create_harness() te devuelve es un agente de Strands normal, y acotar una herramienta es solo un ejemplo. Cualquier default que quieras cambiar, lo cambias.

El modelo: la decisión honesta

El default del harness es Amazon Bedrock. Mi plan era usar Amazon Nova , que es de bajo costo y rápido. Pero al intentarlo me topé con dos cosas que vale la pena que sepas antes de que te pasen a ti.
Primero, mi cuenta tiene el nuevo nivel gratuito "Starter". El modelo premium de Anthropic que viene por default sí aparece en Model access, pero al ser una cuenta del plan gratuito no me otorgaron permisos para usarlo. Lo único que podía usar era Amazon Nova.
Segundo: el web_search nativo del harness no es una herramienta suelta, es la búsqueda web del propio proveedor del modelo. Funciona con OpenAI, Anthropic y Google. Amazon Nova hace function calling perfecto, pero no trae esa búsqueda web nativa, y en Bedrock ningún modelo que yo tuviera disponible la exponía.
Para esta demo yo quería mostrar justo esa búsqueda nativa en una línea, así que me fui por OpenAI directo:
1
model="openai/gpt-5.6-sol"
El harness toma la llave de OPENAI_API_KEY del entorno y listo. Trade-off honesto: deja de ser 100% nivel gratuito de AWS, porque le pagas a OpenAI aparte. Si tu caso puede vivir sin búsqueda web nativa, tienes la opción de quedarte en Bedrock con Nova y bajar builtin_tools a ["read", "web_fetch"], que funcionan con cualquier modelo. Pasar de un proveedor a otro es cambiar el valor de model=, nada más.
Me gustó que el harness no me lo escondiera. Las capacidades viajan con el modelo, y el harness te lo dice en vez de fallar en silencio. Por eso "una línea" no es magia que no controlas.

Lo que se me rompió: de 48 llamadas a 9

No voy a mentir, la primera vez que corrí el agente se fue por las ramas. Llegó a 48 llamadas a herramientas, leyendo archivos y buscando en la web sin parar, sin llegar a una respuesta. Si te pasa, créeme, no estás solo, y lo mejor es que no era culpa del harness ni del modelo. Eran tres cosas mías combinadas.
La primera fue de lo más tonta: mi archivo .env tenía los valores sin comillas. Algo como ISSUE_TITLE=Setup tool crashes.... Al hacer source .env, el shell intentaba ejecutar esas palabras como comandos, las variables llegaban vacías, y el agente recibía una tarea sin contenido. Un agente autónomo con herramientas y sin objetivo claro no se detiene: se pone a explorar el repo entero buscando un issue que no existe.
El arreglo fue poner comillas en los valores, y de paso dejé un .env.example con una nota para que no te pase:
1
2
3
4
# Mantén las comillas: los valores tienen espacios, y sin comillas
# el shell intentaría ejecutar las palabras como comandos.
ISSUE_TITLE="Setup tool crashes with KeyError on fresh clone"
ISSUE_BODY="Running the splice setup fails with a KeyError when .splice/config.json is missing."
La segunda fue blindar la entrada. Si no hay título ni cuerpo, no hay nada que revisar, así que ahora el agente falla rápido y claro en vez de deambular:
1
2
3
4
if not title.strip() and not body.strip():
raise ValueError(
"No issue to review: both ISSUE_TITLE and ISSUE_BODY are empty."
)
La tercera fue el tuning. Aun con el issue bien formado, con effort="high" el agente investigaba de más. Bajé a effort="medium" y le di instrucciones más decisivas: lee pocos archivos, un fetch por URL, y cuando tengas suficiente, para y escribe. Con eso pasó de 48 llamadas a 9, con la respuesta correcta.
Esa es la lección que me llevo: un harness autónomo es potente porque es autónomo, y por eso mismo la entrada tiene que estar bien formada y el tuning importa. El harness te expone effort y las instrucciones como argumentos simples de una línea, así que afinar ese comportamiento no te obliga a reescribir el agente.

Que corra solo: el GitHub Action

Hasta aquí el agente corrió mientras yo lo miraba. El último paso fue dejarlo trabajando solo con un GitHub Action que se dispara cuando alguien abre un issue. El corazón del workflow es esto:
1
2
3
on:
issues:
types: [opened]
Cuando entra un issue, el Action instala el agente, lo corre con el título y el cuerpo del issue, y publica el resultado como comentario:
1
2
3
4
5
6
7
8
9
10
11
- name: Review the issue
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ github.event.issue.body }}
run: python examples/issue-reviewer/agent.py > comment.md

- name: Post the comment
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh issue comment "${{ github.event.issue.number }}" --body-file comment.md
Lo probé abriendo un issue de verdad y funcionó sin problema: el agente leyó, analizó y dejó su comentario solo. Eso sí, va un poco lento, porque está razonando y usando herramientas de verdad, no respondiendo de memoria. Para un primer pase de clasificación que antes hacía yo a mano, el cambio se agradece.
Fíjate en un detalle: el agente que corre ahí es el mismo que armé en una línea al principio. No hay una versión de laptop y otra de producción. Corre en tu máquina, en un pipeline o en la nube, y no necesita infraestructura de AWS para existir. Corre donde tú lo pongas.

Cierre

Miremos el camino: empezamos con una línea, de ahí salió un agente que usa herramientas, cuida su costo y se acuerda de lo que hizo, y terminamos con ese mismo agente corriendo solo cada vez que entra un issue. En el medio se me rompió un par de veces, y arreglarlo me enseñó más que si hubiera salido a la primera.
Hacer que un agente funcione ya dejó de ser el problema. Strands harness te da el punto de partida resuelto, con lo difícil afinado por dentro, y aun así te deja abrirlo y cambiar lo que necesites, desde el modelo hasta cada herramienta. Todo el código del agente y del Action está en mi repo splice , por si quieres clonarlo y hacerlo tuyo.
Gracias por llegar hasta el final. Cuéntame en los comentarios qué tarea repetitiva le pondrías tú a un agente así. Nos vemos en la nube ☁️
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article