Portar un bucle de chat de OpenAI a Claude en Python: Una página de cambios
Mueva un bucle de mensajes sin estado a la API de Mensajes de Anthropic: el parámetro del sistema de nivel superior, los tokens máximos requeridos, los bloques de contenido y la alternancia estricta de roles.
Fuentes: Part 3 — Call the Claude API in Python (same messages, system on the side)
Cualquiera que haya escrito un bucle de chat para OpenAI puede trasladarlo a Claude de Anthropic con aproximadamente una página de cambios. Ambos proveedores comparten la estructura básica: una lista messages etiquetada con un rol que se envía en su totalidad en cada llamada, a la cual se agrega la respuesta posteriormente. Solo un elemento cambia, y es útil saber exactamente cuál.
Misma lista, sistema externo
La API Messages de Claude acepta el formato de conversación que ya conoces: una lista de diccionarios con role y content, sin estado en el servidor, que se envía nuevamente en su totalidad cada vez. Los nombres y significados de los roles son idénticos: user y assistant.
La única diferencia estructural es que system no es un mensaje. OpenAI coloca las instrucciones en la fila cero de la lista. Anthropic las traslada a un parámetro de nivel superior system= que se encuentra junto a la lista. La lista contiene únicamente la conversación real, con turnos del usuario y el asistente que alternan estrictamente.
import anthropic
client = anthropic.Anthropic() # key from env
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=300,
system="You are a concise Python assistant.",
messages=[
{
"role": "user",
"content": "Why is system a parameter "
"here, not a message?",
},
],
)
print(resp.content[0].text)
Existen dos diferencias menores antes de que se ejecute cualquier proceso:
max_tokensdebe estar definido. OpenAI permite enviar una solicitud sin un límite de salida. Claude exige que se especifique en cada llamada, lo cual resulta un poco molesto pero ayuda a controlar los costos, ya que no se puede realizar una solicitud hasta decidir cuán larga será la respuesta.- Las respuestas vienen en bloques de contenido. En lugar de una sola cadena, se recibe una lista, y el texto plano se encuentra en
resp.content[0].text. Hoy en día parece un exceso, pero las llamadas a herramientas también llegan con esa misma estructura más adelante, por lo que tiene su justificación.
Configuración y un solo turno
Instale el SDK con pip install anthropic, luego exporte la clave desde la consola de Anthropic (que comienza con sk-ant-) como ANTHROPIC_API_KEY. El cliente la detectará automáticamente.
Si lo coloca junto a la llamada de OpenAI, toda la diferencia radica en cuatro modificaciones: el método client.chat.completions.create se convierte en client.messages.create, la fila del sistema pasa a estar dentro de system=, se vuelve obligatorio establecer un límite para la salida, y el texto se lee desde content[0].text en lugar de choices[0].message.content. Una nota sobre el SDK: el paquete anthropic actual no acepta el parámetro temperature= en messages.create, por lo que los fragmentos antiguos que lo incluyen generarán errores. Se aplica la muestra por defecto, y eso es suficiente para estos ejemplos.
Los recuentos de tokens se encuentran en resp.usage.input_tokens y resp.usage.output_tokens. Se trata del mismo medidor con nombres de campo ligeramente diferentes, y vale la pena registrarlos cada vez.
El bucle de historial en Claude
Aquí está la función ask() de la versión de OpenAI, adaptada. La lógica de agregar y volver a enviar no ha cambiado. Solo cambió la llamada que está en el medio.
msgs = []
def ask(text: str) -> str:
msgs.append(
{"role": "user", "content": text}
)
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=300,
system="You are a concise assistant.",
messages=msgs, # full history
)
reply = resp.content[0].text
msgs.append(
{"role": "assistant", "content": reply}
)
return reply
print(ask("Define a context window."))
print(ask("Now for a five-year-old."))
print(ask("Which answer was shorter?"))
La tercera pregunta en una conversación sigue demostrando el ciclo, ya que solo funciona si ambas respuestas anteriores se encuentran en msgs y se envían nuevamente. Se aplican las mismas consideraciones económicas. Los tokens de entrada aumentan con cada turno, por lo que las conversaciones largas cuestan más por llamada.
Claude también es más exigente con el orden: los mensajes deben alternar entre el usuario y el asistente. Dos mensajes del usuario seguidos, lo cual ocurre fácilmente cuando se vuelve a intentar una solicitud fallida, hacen que Claude rechace la llamada, mientras que OpenAI lo tolera. Considérelo una regla básica de verificación para cualquier sistema que gestione el historial.
Ejecutarlo
El script ejecuta un único turno y luego una conversación de tres turnos, registrando los tokens de entrada y salida después de cada llamada. Se detiene temprano con un mensaje claro si falta ANTHROPIC_API_KEY. También realiza solicitudes en tiempo real con costo, por lo que debe mantenerse fuera de cualquier ejecución de CI.
pip install -r requirements.txt
export ANTHROPIC_API_KEY="sk-ant..."
python examples/part03_claude.py
Por qué el puerto fue sencillo y qué se obtiene
Fue un cambio pequeño, ya que ambos proveedores exponen el mismo contrato: un endpoint sin estado que recibe toda la conversación en cada llamada y responde una sola vez. Lo que difiere es el dialecto, como la ubicación del prompt del sistema, el aspecto del objeto de respuesta y los parámetros requeridos. LangChain abstrae esa capa sencilla, y sus clases ChatOpenAI y ChatAnthropic existen para absorber esa diferencia de cuatro líneas que acabas de escribir manualmente. Saber qué esconde la abstracción marca la diferencia entre utilizarla y quedar atrapado por ella.
También existe una ventaja práctica. Tener a dos proveedores detrás de una misma estructura ask() permite realizar pruebas A/B de modelos, pasar a un sistema alternativo en caso de que la API funcione mal, o dirigir las preguntas simples a uno y las complejas al otro, todo sin tener que modificar el código de la aplicación. La API Gemini de Google sigue la misma estructura de conversación, pero con un único rol renombrado: el asistente ahora se denomina model.