El 30 de mayo de 2024, Amazon anunció el lanzamiento de la API de Bedrock Converse. Esta API está diseñada para proporcionar una experiencia consistente al "conversar" con los modelos de Amazon Bedrock.
La API admite:
En esta publicación, exploraremos cómo usar la API de Amazon Bedrock Converse con el modelo fundamental Claude Haiku.
Ten en cuenta que no todos los modelos fundamentales pueden admitir todas las características de la API Converse. Para más detalles por modelo, consulta la documentación de Amazon Bedrock.
Para empezar, instalemos el paquete boto3
.
pip install boto3
A continuación, crearemos un nuevo script en Python e importaremos las dependencias necesarias.
import boto3
client = boto3.client("bedrock-runtime")
Para empezar de manera simple, enviemos un solo mensaje a Claude.
import boto3
client = boto3.client("bedrock-runtime")
messages = [{"role": "user", "content": [{"text": "¿Cuál es tu nombre?"}]}]
response = client.converse(
modelId="anthropic.claude-3-haiku-20240307-v1:0",
messages=messages,
)
print(response)
La respuesta es un diccionario que contiene la respuesta y otra información de metadatos de la API. Para más información sobre la salida, consulta la documentación de Amazon Bedrock.
Para los propósitos de esta publicación, imprimiré la respuesta como JSON.
{
"ResponseMetadata": {
"RequestId": "6984dcf2-c6aa-4000-a3d6-22e34a43df12",
"HTTPStatusCode": 200,
"HTTPHeaders": {
"date": "Dom, 02 Jun 2024 14:54:08 GMT",
"content-type": "application/json",
"content-length": "222",
"connection": "keep-alive",
"x-amzn-requestid": "6984dcf2-c6aa-4000-a3d6-22e34a43df12"
},
"RetryAttempts": 0
},
"output": {
"message": {
"role": "assistant",
"content": [
{
"text": "Mi nombre es Claude. ¡Es un placer conocerte!"
}
]
}
},
"stopReason": "end_turn",
"usage": {
"inputTokens": 12,
"outputTokens": 15,
"totalTokens": 27
},
"metrics": {
"latencyMs": 560
}
}
Para recuperar el contenido del mensaje, podemos acceder a la clave output
en la respuesta.
import boto3
client = boto3.client("bedrock-runtime")
messages = [{"role": "user", "content": [{"text": "¿Cuál es tu nombre?"}]}]
response = client.converse(
modelId="anthropic.claude-3-haiku-20240307-v1:0",
messages=messages,
)
ai_message = response["output"]["message"]
output_text = ai_message["content"][0]["text"]
print(output_text)
Salida:
¡Mi nombre es Claude. Es un placer conocerte!
Continuemos la conversación añadiendo el mensaje de la IA a la lista original de mensajes. Esto nos permitirá tener una conversación de múltiples turnos.
import boto3
client = boto3.client("bedrock-runtime")
messages = [{"role": "user", "content": [{"text": "¿Cuál es tu nombre?"}]}]
response = client.converse(
modelId="anthropic.claude-3-haiku-20240307-v1:0",
messages=messages,
)
ai_message = response["output"]["message"]
messages.append(ai_message)
# Hagamos otra pregunta
messages.append({"role": "user", "content": [{"text": "¿Puedes ayudarme?"}]})
response = client.converse(
modelId="anthropic.claude-3-haiku-20240307-v1:0",
messages=messages,
)
print(response["output"]["message"]["content"][0]["text"])
Salida:
Sí, estaría encantado de intentar ayudarte con lo que necesites. ¿En qué puedo ayudarte?
La API de Amazon Bedrock Converse admite imágenes como entrada. Enviemos una imagen a Claude y veamos cómo responde. Descargaré una imagen de un gato de Wikipedia y se la enviaré a Claude.
Para este ejemplo, utilicé la biblioteca requests
para descargar la imagen. Si no la tienes instalada, puedes instalarla usando pip install requests
.
pip install requests
import boto3
import requests
client = boto3.client("bedrock-runtime")
messages = [{"role": "user", "content": [{"text": "¿Cuál es tu nombre?"}]}]
response = client.converse(
modelId="anthropic.claude-3-haiku-20240307-v1:0",
messages=messages,
)
ai_message = response["output"]["message"]
messages.append(ai_message)
messages.append({"role": "user", "content": [{"text": "¿Puedes ayudarme?"}]})
response = client.converse(
modelId="anthropic.claude-3-haiku-20240307-v1:0",
messages=messages,
)
ai_message = response["output"]["message"]
messages.append(ai_message)
image_bytes = requests.get(
"https://upload.wikimedia.org/wikipedia/commons/4/4d/Cat_November_2010-1a.jpg"
).content
messages.append(
{
"role": "user",
"content": [
{"text": "¿Qué hay en esta imagen?"},
{"image": {"format": "jpeg", "source": {"bytes": image_bytes}}},
],
}
)
response = client.converse(
modelId="anthropic.claude-3-haiku-20240307-v1:0",
messages=messages,
)
ai_message = response["output"]["message"]
print(ai_message)
Salida:
{
"role": "assistant",
"content": [
{
"text": "La imagen muestra un gato doméstico. El gato parece ser un gato atigrado con un patrón de pelaje rayado. El gato está sentado erguido y sus ojos verdes son claramente visibles, con una expresión enfocada y alerta. El fondo sugiere un entorno exterior nevado, con algunas ramas o vegetación borrosas visibles detrás del gato."
}
]
}
Como puedes ver, la IA pudo identificar la imagen como un gato y proporcionar una descripción detallada de la imagen dentro de un contexto conversacional.
Para esta sección, comencemos una nueva conversación con Claude y proporcionemos herramientas que pueda usar.
import boto3
client = boto3.client("bedrock-runtime")
tools = [
{
"toolSpec": {
"name": "get_weather",
"description": "Obtener el clima actual en una ubicación dada",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "La ciudad y el estado, por ejemplo, San Francisco, CA",
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "La unidad de temperatura, ya sea 'celsius' o 'fahrenheit'",
},
},
"required": ["location"],
}
},
}
},
]
messages = [
{
"role": "user",
"content": [{"text": "¿Cómo está el clima ahora en Nueva York?"}],
}
]
response = client.converse(
modelId="anthropic.claude-3-haiku-20240307-v1:0",
messages=messages,
toolConfig={"tools": tools},
)
print(response["output"])
Salida:
{
"message": {
"role": "assistant",
"content": [
{
"text": "Está bien, déjame verificar el clima actual en Nueva York:"
},
{
"toolUse": {
"toolUseId": "tooluse_rRwaOoldTeiRiDZhTadP0A",
"name": "get_weather",
"input": {
"location": "Nueva York, NY",
"unit": "fahrenheit"
}
}
}
]
}
}
La salida incluye un objeto toolUse
que indica a los desarroll
adores que la IA está usando la herramienta get_weather
para obtener el clima actual en Nueva York. Ahora debemos cumplir con la solicitud de la herramienta respondiendo con la información del clima.
Pero primero, construyamos un enrutador simplista que pueda manejar la solicitud de la herramienta y responder con la información del clima.
def get_weather(location: str, unit: str = "fahrenheit") -> dict:
return {"temperature": "78"}
def tool_router(tool_name, input):
match tool_name:
case "get_weather":
return get_weather(input["location"], input.get("unit", "fahrenheit"))
case _:
raise ValueError(f"Herramienta desconocida: {tool_name}")
Ahora, actualicemos el código para manejar la solicitud de la herramienta y responder con la información del clima.
import boto3
def get_weather(location: str, unit: str = "fahrenheit") -> dict:
return {"temperature": "78"}
def tool_router(tool_name, input):
match tool_name:
case "get_weather":
return get_weather(input["location"], input.get("unit", "fahrenheit"))
case _:
raise ValueError(f"Herramienta desconocida: {tool_name}")
client = boto3.client("bedrock-runtime")
tools = [
{
"toolSpec": {
"name": "get_weather",
"description": "Obtener el clima actual en una ubicación dada",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "La ciudad y el estado, por ejemplo, San Francisco, CA",
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "La unidad de temperatura, ya sea 'celsius' o 'fahrenheit'",
},
},
"required": ["location"],
}
},
}
},
]
messages = [
{
"role": "user",
"content": [{"text": "¿Cómo está el clima ahora en Nueva York?"}],
}
]
response = client.converse(
modelId="anthropic.claude-3-haiku-20240307-v1:0",
messages=messages,
toolConfig={"tools": tools},
)
ai_message = response["output"]["message"]
messages.append(ai_message)
if response["stopReason"] == "tool_use":
contents = response["output"]["message"]["content"]
for c in contents:
if "toolUse" not in c:
continue
tool_use = c["toolUse"]
tool_id = tool_use["toolUseId"]
tool_name = tool_use["name"]
input = tool_use["input"]
tool_result = {"toolUseId": tool_id}
try:
output = tool_router(tool_name, input)
if isinstance(output, dict):
tool_result["content"] = [{"json": output}]
elif isinstance(output, str):
tool_result["content"] = [{"text": output}]
# Añadir más casos, como imágenes, si es necesario
else:
raise ValueError(f"Tipo de salida no soportado: {type(output)}")
except Exception as e:
tool_result["content"] = [{"text": f"Ocurrió un error desconocido: {str(e)}"}]
tool_result["status"] = "error"
message = {"role": "user", "content": [{"toolResult": tool_result}]}
messages.append(message)
response = client.converse(
modelId="anthropic.claude-3-haiku-20240307-v1:0",
messages=messages,
toolConfig={"tools": tools},
)
print(response["output"])
Salida:
{
"message": {
"role": "assistant",
"content": [
{
"text": "Según los datos climáticos, la temperatura actual en Nueva York, NY es de 78 grados Fahrenheit."
}
]
}
}
¡Genial! Hemos respondido exitosamente a la solicitud de herramienta de la IA y proporcionado la información del clima para Nueva York.
Para referencia, estoy adaptando los ejemplos de herramientas de Anthropic AI de su documentación a la API de Bedrock Converse.
En el ejemplo anterior, solo usamos una herramienta para obtener la información del clima. Sin embargo, podemos usar múltiples herramientas en una sola conversación.
Agreguemos otra herramienta a la conversación para obtener la hora actual e introduzcamos un bucle para manejar múltiples solicitudes de herramientas.
import boto3
def get_weather(location: str, unit: str = "fahrenheit") -> dict:
return {"temperature": "78"}
def get_time(timezone: str) -> str:
return "12:00 PM"
def tool_router(tool_name, input):
match tool_name:
case "get_weather":
return get_weather(input["location"], input.get("unit", "fahrenheit"))
case "get_time":
return get_time(input["timezone"])
case _:
raise ValueError(f"Herramienta desconocida: {tool_name}")
client = boto3.client("bedrock-runtime")
tools = [
{
"toolSpec": {
"name": "get_weather",
"description": "Obtener el clima actual en una ubicación dada",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "La ciudad y el estado, por ejemplo, San Francisco, CA",
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "La unidad de temperatura, ya sea 'celsius' o 'fahrenheit'",
},
},
"required": ["location"],
}
},
}
},
{
"toolSpec": {
"name": "get_time",
"description": "Obtener la hora actual en una zona horaria dada",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "El nombre de la zona horaria IANA, por ejemplo, America/Los_Angeles",
}
},
"required": ["timezone"],
}
},
}
},
]
messages = [
{
"role": "user",
"content": [
{
"text": "¿Cómo está el clima ahora en Nueva York y qué hora es allí?"
}
],
}
]
response = client.converse(
modelId="anthropic.claude-3-haiku-20240307-v1:0",
messages=messages,
toolConfig={"tools": tools},
)
ai_message = response["output"]["message"]
messages.append(ai_message)
tool_use_count = 0
while response["stopReason"] == "tool_use":
if response["stopReason"] == "tool_use":
contents = response["output"]["message"]["content"]
for c in contents:
if "toolUse" not in c:
continue
tool_use = c["toolUse"]
tool_id = tool_use["toolUseId"]
tool_name = tool_use["name"]
input = tool_use["input"]
tool_result = {"toolUseId": tool_id}
try:
output = tool_router(tool_name, input)
if isinstance(output, dict):
tool_result["content"] = [{"json": output}]
elif isinstance(output, str):
tool_result["content"] = [{"text": output}]
# Añadir más casos como imágenes si es necesario
else:
raise ValueError(f"Tipo de salida no soportado: {type(output)}")
except Exception as e:
tool_result["content"] = [
{"text": f"Ocurrió un error desconocido: {str(e)}"}
]
tool_result["status"] = "error"
message = {"role": "user", "content": [{"toolResult": tool_result}]}
messages.append(message)
response = client.converse(
modelId="anthropic.claude-3-haiku-20240307-v1:0",
messages=messages,
toolConfig={"tools": tools},
)
ai_message = response["output"]["message"]
messages.append(ai_message)
tool_use_count += 1
print(tool_use_count)
print(response["output"])
Salida:
{
"message": {
"role": "assistant",
"content": [
{
"text": "La hora actual en Nueva York es 12:00 PM.\n\nAsí que, en resumen, el clima en Nueva York ahora es de 78 grados Fahrenheit, y la hora es 12:00 PM."
}
]
}
}
Conteo de uso de herramientas: 2
Hemos respondido exitosamente a las solicitudes de herramientas de la IA y proporcionado la información del clima y la hora para Nueva York.
En esta publicación, aprendimos a usar la API de Amazon Bedrock Converse para enriquecer conversaciones con modelos de IA. No solo aprovechamos texto e imágenes, sino que también utilizamos herramientas para simular la obtención de datos externos.
¡La API de Amazon Bedrock Converse es una herramienta poderosa que se puede utilizar para construir aplicaciones de IA conversacional! 😎
Jorge García
Fullstack developer