programa
Cuando pruebo un modelo nuevo con una llamada a la API, la primera respuesta me dice muy poco. Mi primera ejecución con Fable 5.1 devolvió una estructura válida y un plan genérico. Quería saber qué pasa cuando la conversación crece: ¿puede la aplicación mantener el historial intacto, inspeccionar archivos sin leer fuera del proyecto, informar del progreso y mostrar de dónde viene el coste?
Nuestro resumen de Claude Fable 5.1 cubre el lanzamiento, los benchmarks y comparaciones más amplias. Aquí empezaremos con una pequeña llamada en Python y construiremos a su alrededor el bucle del agente. El agente final recibe una petición de funcionalidad, lee un proyecto Flask y devuelve un plan vinculado a archivos que realmente inspeccionó.
Veremos cómo:
- Hacer una llamada a la API de Claude Fable 5.1 y leer bloques de contenido con seguridad
- Fijar el esfuerzo de razonamiento y cambiarlo a mitad de conversación (beta)
- Limitar una instrucción de sistema a un único turno (beta)
- Devolver un plan estructurado con Pydantic
- Añadir herramientas de repositorio de solo lectura con límite en la raíz del proyecto
- Ejecutar un bucle de herramientas de varios turnos
- Leer las actualizaciones de progreso del agente entre llamadas a herramientas (beta)
- Mantener válidos los bloques de pensamiento con historial solo-append
- Cachear contexto repetido y estimar el coste de la petición con tarifas publicadas
- Gestionar negativas y exponer el agente con FastAPI
Las funciones beta usan cabeceras con fecha; compruébalas en la documentación de Anthropic antes de desplegar.
Introducción a los modelos Claude
¿Cuánto cuesta ejecutar Claude Fable 5.1 en un bucle de agente?
Un agente reenvía el mismo prompt de sistema, las definiciones de herramientas y el contexto del repositorio en cada turno, así que la tarifa que determina tu factura es la de lectura de caché, no la de entrada.
Fable 5.1 cuesta 10 $ por millón de tokens de entrada y 50 $ por millón de tokens de salida, sin cambios respecto a Fable 5. Las lecturas de caché cuestan 0,25 $ por millón (antes 1 $) y las escrituras de caché de cinco minutos se mantienen en 12,50 $ por millón. Nuestra guía de Claude Fable 5.1 incluye la tabla completa de tarifas y las estimaciones de ahorro de Anthropic.
Leer un prefijo en caché es barato. Escribirlo no: cuesta 50 veces más que leer, así que el bucle solo compensa cuando un prefijo se lee varias veces. Más adelante verás el desglose de costes de una ejecución real y qué categoría dominó.
El techo de tokens lo marca el modelo, no tu presupuesto. Fable 5.1 ofrece una ventana de contexto de 1 M de tokens con hasta 128 K de tokens de salida por respuesta, y max_tokens es un límite duro que suma pensamiento y texto de respuesta. A alto esfuerzo necesitas espacio para ambos; por eso el bucle de agente de abajo fija 16.000 en lugar de algo más "redondo".
Retención de datos, nivel de prioridad y marca de agua
Hay algunos detalles de acceso que importan antes de escribir código. Dos de ellos bloquean directamente tus peticiones:
-
Fable 5.1 requiere retención de datos de 30 días y no está disponible con retención cero salvo autorización de Anthropic. Una petición desde un espacio de trabajo incompatible devuelve un 400
invalid_request_errorsin más pistas. -
El modelo no es compatible con Priority Tier. Fable 5 sí lo es, así que esto pilla a quienes migran.
-
La salida de texto de Fable 5.1 lleva la marca de agua de texto de Anthropic. No añade tokens y no requiere cambios en la petición.
Usa Claude Fable 5.1 por API para crear un agente de desarrollo con contexto de repositorio
Nuestro flujo tiene dos etapas:
- Un bucle de inspección acotado lee archivos permitidos del proyecto.
- Una petición final con salidas estructuradas convierte ese contexto en un plan.
El proyecto de ejemplo es una pequeña API JSON en Flask para guardar y buscar marcadores, con una factoría de app, tres blueprints, un módulo de config, modelos y una suite de pytest. Uso la limitación de peticiones como tarea de hilo conductor porque el agente debe inspeccionar la configuración de la app, las rutas, la config y los tests antes de identificar los archivos y pruebas necesarios. El código completo y el proyecto de ejemplo están disponibles en el repositorio de GitHub.

Las peticiones alcanzan los archivos a través de un único límite. Imagen del autor.
El agente solo puede usar tres herramientas: list_project_files, read_project_file y get_project_metadata. Claude nunca accede al sistema de archivos directamente. Pide una ruta y tu código decide si está permitida.
Configurar la API de Claude Fable 5.1 en Python
Empieza con un entorno de Python aislado y guarda la clave de API en el servidor.
Requisitos previos
Necesitas Python 3.10 o superior y una clave de la API de Anthropic con acceso a claude-fable-5-1.
Para crear una clave, inicia sesión en la Claude Console, abre la página de claves, haz clic en Create key y copia la clave. Es buena práctica ponerle un nombre descriptivo, elegir fecha de caducidad y guardarla de forma segura.
Instala el SDK y añade la clave de API
Crea un entorno virtual e instala los paquetes:
python -m venv .venv
source .venv/bin/activate # macOS o Linux
.venv\Scripts\Activate.ps1 # Windows PowerShell
pip install anthropic==1.3.0 pydantic fastapi uvicorn python-dotenv
Mantén el SDK fijado porque las funciones beta cambian con frecuencia. Las actualizaciones de progreso requieren al menos la 1.1.0 y los ejemplos usan 1.3.0.
Pon la clave en un .env y añade .env a .gitignore antes del primer commit. Debe vivir en un servidor bajo tu control, nunca en el navegador ni en un repositorio accesible. Exponerla puede permitir uso no autorizado de la API y cargos por entrada, salida y caché.
ANTHROPIC_API_KEY=sk-ant-your-key-here
Con eso, el cliente encuentra la clave por sí solo.
Haz tu primera llamada a la API de Claude Fable 5.1 en Python
Envía la petición de API más pequeña posible antes de construir nada encima.
Envía la primera petición
Inicializa el cliente, envía un mensaje de usuario y muestra la metainformación de la respuesta:
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv()
client = Anthropic()
MODEL = "claude-fable-5-1"
response = client.messages.create(
model=MODEL,
max_tokens=512,
messages=[{"role": "user", "content": "Reply in one sentence to confirm the API connection is working."}],
)
text = next((b.text for b in response.content if b.type == "text"), None)
print(text if text is not None else f"No text returned ({response.stop_reason})")
print(f"Model: {response.model}")
print(f"Stop reason: {response.stop_reason}")
print(f"Input tokens: {response.usage.input_tokens}")
print(f"Output tokens: {response.usage.output_tokens}")
print(f"Request ID: {response._request_id}")

La primera llamada devuelve texto y metadatos. Imagen del autor.
La llamada a next(...) selecciona el primer bloque de texto. El pensamiento adaptativo está siempre activo y no puede deshabilitarse, así que una respuesta puede empezar con un bloque de pensamiento; si envías thinking: {"type": "disabled"} obtendrás un 400 en lugar de apagarlo. Cuando el primer bloque es de pensamiento, response.content[0].text lanza una excepción.
La solución es filtrar por tipo de bloque en vez de asumir una posición fija. Registra también response._request_id, ya que soporte de Anthropic lo usa para rastrear la petición.
Esta es la petición que usé en los ejemplos de planificación y esfuerzo. Obliga al agente a inspeccionar varios archivos:
feature_request = (
"Add rate limiting to the public API endpoints so one client cannot exhaust "
"the search endpoint or brute force the token endpoint."
)
Mantén ese texto sin cambios al comparar niveles de esfuerzo y recuentos de tokens. Así los resultados describen la configuración de la API y no un prompt distinto.
Fija el esfuerzo de razonamiento con output_config
Configura el esfuerzo a través de output_config. Acepta low, medium, high, xhigh y max. El valor por defecto de la API es high.
response = client.messages.create(
model=MODEL,
max_tokens=8192,
output_config={"effort": "high"},
messages=[{"role": "user", "content": feature_request}],
)
El esfuerzo puede afectar al uso de tokens, al comportamiento de las herramientas y a la latencia. Ejecuté la misma petición tres veces en cuatro niveles de esfuerzo; la tabla muestra las medias:
|
Esfuerzo |
Segundos |
Tokens de pensamiento |
Tokens de salida totales |
Coste |
|---|---|---|---|---|
|
|
7,7 |
111 |
173 |
$0.0093 |
|
|
8,1 |
129 |
186 |
$0.0099 |
|
|
7,9 |
136 |
199 |
$0.0106 |
|
|
20,0 |
151 |
1.764 |
$0.0888 |
Los tokens de pensamiento están incluidos en los tokens totales de salida, así que no sumes ambas columnas. En estas ejecuciones, low, medium y high se mantuvieron muy cerca en latencia y coste.
xhigh tardó dos veces y media más, generó casi nueve veces los tokens de salida y costó ocho veces más.
Conclusión: Empieza en high, baja a medium para pasos rutinarios y usa niveles más altos solo cuando tus pruebas muestren mejoras medibles. Con low el modelo puede responder de memoria en vez de llamar a una herramienta de recuperación. Si un turno necesita información fresca, dilo explícitamente o sube el nivel.
Restringe el alcance del agente con un prompt de sistema
El prompt de sistema define el comportamiento del agente:
SYSTEM_PROMPT = """You are a senior engineer who turns feature requests into implementation plans for an existing codebase.
Stay inside the requested feature. Do not propose unrelated refactors, dependency upgrades, or style changes.
If a file or dependency you need does not exist, say so plainly instead of inventing it.
Write in plain sentences and do not use em dashes.
Finish with concrete guidance: what changes, where, in what order, what could break, and which tests to add."""
Las recomendaciones de prompting de Anthropic señalan que el modelo puede ampliar la tarea o cortar demasiado pronto. El prompt le pide ceñirse al alcance y terminar con pautas concretas. Más adelante, un esquema controla el formato de salida.
Devuelve un plan estructurado con Pydantic
Define el plan con Pydantic para que tu aplicación pueda validarlo y pasarlo a otro código:
from pydantic import BaseModel, Field
class FeaturePlan(BaseModel):
summary: str = Field(description="One or two sentences on what will be built.")
implementation_steps: list[str]
files_to_modify: list[str]
risks: list[str]
tests: list[str]
response = client.messages.parse(
model=MODEL,
max_tokens=8192,
system=SYSTEM_PROMPT,
messages=[{"role": "user", "content": feature_request}],
output_format=FeaturePlan,
)
if response.stop_reason == "refusal":
category = (
response.stop_details.category
if response.stop_details and response.stop_details.category
else "unspecified"
)
print(f"Declined: {category}")
elif response.parsed_output is None:
print(f"No plan. Stop reason: {response.stop_reason}")
else:
print(response.parsed_output.summary)
messages.parse() convierte el modelo de Pydantic en un esquema JSON, lo envía, valida la respuesta y devuelve un objeto tipado en parsed_output. Las salidas estructuradas están en disponibilidad general, así que no hace falta cabecera beta. Revisa antes stop_reason porque una negativa, que cubrimos más adelante, se salta el esquema y te deja sin nada que parsear.
Ese resultado genérico de la introducción sí hizo algo bien: no nombró archivos que no podía ver. Un esquema valida estructura, no el anclaje factual.
Claude Fable 5.1 vs. Fable 5: cambios de migración en la API
Antes de añadir herramientas, ten en cuenta las restricciones de forzar herramientas, la compatibilidad de bloques de pensamiento y el historial solo-append.
-
Fable 5.1 rechaza la selección forzada de herramientas. La sección del bucle de herramientas más abajo muestra el error y la configuración
autousada en su lugar. -
Los bloques de pensamiento son compatibles en una sola dirección. Fable 5.1 lee bloques de modelos Claude anteriores, pero ningún modelo anterior puede leer sus bloques.
Cuando un router o un fallback mueve la conversación a un modelo más antiguo, la API elimina los bloques incompatibles antes de que el modelo de destino los vea. El resto del historial se mantiene, pero el modelo antiguo tiene que planificar sin esos bloques.
Editar turnos anteriores invalida los bloques de pensamiento que vinieron después. Esto puede romper el recorte de historial y la resumidera del lado del cliente.
La guía de migración cubre todos los cambios.
Añade herramientas de repositorio de solo lectura
Ahora dale al modelo contexto del repositorio con herramientas de solo lectura.
Define las herramientas de solo lectura
La capa de herramientas tiene dos partes: las funciones de Python que aplican las reglas de acceso y los esquemas que Claude puede invocar.
Restringe las rutas a la raíz del proyecto
Solo lectura no significa seguro. Un modelo puede pedir ../../.env tan fácil como config.py, así que el guardián debe estar en tu código, no en el prompt:
def _resolve(self, relative_path: str) -> Path:
relative = Path(relative_path)
if relative.is_absolute() or relative.drive:
raise ToolError(f"path is outside the project root: {relative_path}")
cursor = self.root
for part in relative.parts:
cursor /= part
if cursor.is_symlink():
raise ToolError(f"symlinks are not followed: {relative_path}")
candidate = (self.root / relative).resolve()
# After resolving "..", the path still has to sit under the allowed root.
if candidate != self.root and self.root not in candidate.parents:
raise ToolError(f"path is outside the project root: {relative_path}")
if candidate.name in DENY_NAMES:
raise ToolError(f"reading {candidate.name} is not allowed")
return candidate
Rechaza rutas absolutas y componentes que sean symlink, luego resuelve la ruta y confirma que permanece bajo la raíz del proyecto. Pedir ../.env devuelve "path is outside the project root". El error devuelto por la herramienta permite que el agente continúe con archivos permitidos.
Define esquemas estrictos para las herramientas
La clase lectora controla qué puede abrir Python. Claude también necesita esquemas JSON que describan las tres acciones que puede solicitar:
EMPTY_SCHEMA = {
"type": "object",
"properties": {},
"additionalProperties": False,
}
TOOLS = [
{
"name": "list_project_files",
"description": "List readable text files in the project.",
"input_schema": EMPTY_SCHEMA,
"strict": True,
},
{
"name": "read_project_file",
"description": "Read one text file relative to the project root.",
"input_schema": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
"additionalProperties": False,
},
"strict": True,
},
{
"name": "get_project_metadata",
"description": "Read project metadata and dependency manifests.",
"input_schema": EMPTY_SCHEMA,
"strict": True,
},
]
strict valida los argumentos cuando el modelo elige una herramienta. No fuerza una llamada a herramienta, lo cual es relevante en Fable 5.1.
Ejecuta el bucle de herramientas de varios turnos
Empieza con el bucle base: envía las herramientas, inspecciona stop_reason, ejecuta lo solicitado, añade los resultados y repite.
MAX_AGENT_TURNS = 8
reader = ProjectReader("sample_project")
messages = [{"role": "user", "content": feature_request}]
for turn in range(1, MAX_AGENT_TURNS + 1):
response = client.messages.create(
model=MODEL,
max_tokens=16000,
system=SYSTEM_PROMPT,
tools=TOOLS,
messages=messages,
)
if response.stop_reason == "refusal":
return declined(response.stop_details.category)
if response.stop_reason == "max_tokens":
return cutoff()
if response.stop_reason != "tool_use":
messages.append({"role": "assistant", "content": response.content})
break
messages.append({"role": "assistant", "content": response.content})
results = []
for block in response.content:
if block.type != "tool_use":
continue
output, is_error = reader.run(block.name, block.input)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
"is_error": is_error,
})
messages.append({"role": "user", "content": results})
else:
return turn_limit()
MAX_AGENT_TURNS limita las solicitudes del modelo, no el gasto; aplica un tope de coste aparte si te hace falta. El bucle gestiona refusal, max_tokens y tool_use de forma explícita; otros motivos de parada terminan la fase de inspección. El campo is_error le indica al modelo que una ruta fue rechazada, para que elija otra acción.
Por qué forzar la elección de herramienta devuelve un 400
En Fable 5 se podía forzar la primera llamada con tool_choice: {"type": "any"}. Fable 5.1 devuelve este error antes de ejecutar la petición:
tool_choice: type "tool" and "any" are not supported for this model.
Las llamadas forzadas saltarían el pensamiento siempre activo. Deja tool_choice en auto, usa los esquemas estrictos anteriores y nombra las herramientas en el prompt cuando un paso necesite una.
Fable 5.1 a veces emite una llamada de herramienta por turno, mientras que Fable 5 las agrupaba. Eso añade idas y vueltas. Añade esta línea al prompt: «Solicita archivos independientes en el mismo turno en lugar de uno por turno». En una ejecución de ejemplo agrupó nueve lecturas independientes, aunque el número varía.
Haz streaming de respuestas y actualizaciones de progreso de Claude Fable 5.1
El streaming de texto emite contenido conforme se genera; las actualizaciones de progreso cubren las pausas entre llamadas a herramientas.
Haz streaming de respuestas de texto
El proyecto completo usa context_system() para combinar SYSTEM_PROMPT con un resumen del proyecto antes de iniciar el stream:
with client.messages.stream(
model=MODEL,
max_tokens=8192,
system=context_system(),
messages=[{"role": "user", "content": feature_request}],
) as stream:
for chunk in stream.text_stream:
print(chunk, end="", flush=True)
final = stream.get_final_message()
print(f"\nOutput tokens: {final.usage.output_tokens}")
get_final_message() te devuelve el mensaje ensamblado con uso y motivo de parada cuando el stream termina. Los fragmentos en streaming no garantizan JSON completo, así que espera al mensaje final antes de parsear.
Muestra el progreso entre llamadas a herramientas
El streaming de texto no cubre las demoras durante llamadas a herramientas. Fable 5.1 puede escribir breves actualizaciones de progreso antes de llamar a herramientas. Con el valor por defecto de thinking.display, "omitted", los bloques de pensamiento específicos de progreso quedan vacíos, aunque el modelo puede producir un texto de entrada normal.
Con display: "updates" y la cabecera beta thinking-display-updates-2026-08-18 , la documentación de la API define una actualización legible como un bloque thinking no vacío mientras el razonamiento permanece oculto. En las ejecuciones en vivo de este proyecto, el campo thinking quedó vacío y el estado legible llegó como un bloque text justo antes de tool_use. Por ello, el helper comprueba ambos tipos de bloque, y el bucle lo llama solo en turnos que acaban en tool_use:
PROGRESS_BETA = "thinking-display-updates-2026-08-18"
response = client.beta.messages.create(
model=MODEL,
max_tokens=16000,
betas=[PROGRESS_BETA],
thinking={"type": "adaptive", "display": "updates"},
system=SYSTEM_PROMPT,
tools=TOOLS,
messages=messages,
)
def status_lines(response) -> list[str]:
lines = []
for block in response.content:
if block.type == "thinking":
text = (block.thinking or "").strip()
elif block.type == "text":
text = (block.text or "").strip()
else:
continue
if text:
lines.append(text)
return lines
Los mensajes de progreso describen los archivos que el modelo planea leer: «Leeré el cableado de la app, la config, las extensiones, las rutas públicas y de auth y los tests existentes, que es donde se engancharía el rate limiting». Muestra esos mensajes e ignora bloques vacíos.

El agente lee archivos mientras informa del progreso. Imagen del autor.
Fable 5.1 escribe menos de estos mensajes que Fable 5, especialmente a mayor esfuerzo. Si tu interfaz necesita actualizaciones regulares, pide una línea de apertura, mensajes de progreso y un cierre con recap.
Cambia el esfuerzo de Claude Fable 5.1 a mitad de conversación
La siguiente función es muy útil. Como sabemos, el agente de repositorio no necesita la misma profundidad de razonamiento en cada turno.
Cambia el esfuerzo entre turnos
En un bucle de agente, baja el esfuerzo en turnos rutinarios de recuperación y súbelo de nuevo para el turno final de planificación.
Con la cabecera beta mid-conversation-output-config-2026-07-01 puedes añadir un mensaje de sistema que solo cambie el nivel de esfuerzo:
EFFORT_BETA = "mid-conversation-output-config-2026-07-01"
messages.append({"role": "system", "content": [], "output_config": {"effort": "low"}})
messages.append({"role": "user", "content": "Summarize the repository evidence in five words."})
response = client.beta.messages.create(
model=MODEL,
max_tokens=4096,
betas=[EFFORT_BETA],
output_config={"effort": "high"},
messages=messages,
)
El nuevo nivel se aplica desde el siguiente turno de usuario, no a mitad del actual, y no invalida la caché de prompts. Cambiar output_config.effort a nivel de petición sí la invalida.
El agente mantiene el ajuste superior en high, añade una directiva por mensaje a medium antes de la recuperación rutinaria y una a high antes del plan final. En una prueba emparejada usó 18 tokens de salida con menor esfuerzo frente a 76 con el ajuste anterior. Tómalo como ejemplo, no como reducción esperada.
Aplica una instrucción de sistema a un único turno
Usa una instrucción con alcance de turno para bloquear lecturas adicionales durante la planificación final.
Configura clear_at: "next_user_message" en un mensaje de sistema con la cabecera beta mid-conversation-system-clear-at-2026-08-21 . La API trata su texto como instrucción de sistema para el turno actual y deja de renderizarlo tras el siguiente mensaje del usuario. Permanece en messages, así que el historial previo no cambia, la caché sigue coincidiendo y el mensaje despejado no cuesta tokens de entrada.
SCOPED_SYSTEM_BETA = "mid-conversation-system-clear-at-2026-08-21"
messages.append({"role": "system", "content": [], "output_config": {"effort": "high"}})
messages.append({"role": "user", "content": "Write the implementation plan now."})
messages.append({
"role": "system",
"content": (
"For this turn only: do not request more files. Base the plan on what "
"you have already read, and name only paths you actually opened."
),
"clear_at": "next_user_message",
})
response = client.beta.messages.create(
model=MODEL,
max_tokens=16000,
betas=[EFFORT_BETA, SCOPED_SYSTEM_BETA],
tool_choice={"type": "none"},
output_config={"format": {"type": "json_schema", "schema": plan_schema()}},
system=agent_system(),
tools=TOOLS,
messages=messages,
)
tool_choice={"type": "none"} evita que la petición final llame otra herramienta. La instrucción acotada limita el plan a archivos que el agente ya inspeccionó. No añadas recordatorios y los borres en la siguiente petición: esa edición invalida bloques de pensamiento posteriores.
Corrige errores 400 de bloques de pensamiento en Claude Fable 5.1
Un error The block is bound to a different conversation significa que el historial anterior a un bloque de pensamiento cambió. Cada bloque de pensamiento en Fable 5.1 está ligado exactamente al prompt de sistema, definiciones de herramientas y mensajes previos.
El resultado depende de cuándo se creó tu cuenta.
-
Las cuentas creadas a partir del 31 de agosto de 2026 reciben un 400 indicando que el bloque está ligado a otra conversación.
-
Para cuentas anteriores, la API registra el desajuste pero solo actúa si la petición fija
thinking.block_binding.prefix_mismatch_behavior.
Puedes detectarlo con la cabecera beta thinking-binding-controls-2026-08-01, thinking.block_binding.prefix_mismatch_behavior a "drop_block" y el array input_transformations. Un historial editado aparece como reason: "prefix_binding_mismatch". Ejecuta esta comprobación una vez en tu integración.
Las siguientes operaciones provocan el desajuste:
-
Editar, reordenar o eliminar un turno anterior manteniendo los posteriores
-
Inyectar texto por petición en un turno anterior y retirarlo en la siguiente
-
Cambiar el contenido u orden del
systemsuperior o del array detoolsa mitad de conversación -
Servir bytes distintos desde una URL de imagen o documento en una petición posterior
Cada caso tiene un sustituto que mantiene los vínculos intactos:
-
Añade instrucciones con mensajes de sistema a mitad de conversación en lugar de editar
system. -
Cambia herramientas con cambios de herramientas a mitad de conversación en vez de tocar el array superior.
-
Recorta historial con edición o compactación de contexto del servidor, que no cuentan como ediciones.
-
Pasa de vuelta los bloques de pensamiento sin alterarlos.
Mover los marcadores de cache_control y cambiar el esfuerzo a nivel de petición es seguro y no invalida los vínculos de bloques de pensamiento. Sin embargo, cambiar el esfuerzo superior reinicia el caché de prompts; usa esfuerzo por mensaje cuando quieras conservar el prefijo en caché.
Caché de prompts y coste de la API de Claude Fable 5.1
La siguiente ejecución separa los costes de entrada nueva, escrituras de caché, lecturas de caché y salida.
Añade caché automático de prompts
El caché de prompts reduce el coste del contexto que se repite entre turnos. El historial creciente cambia dónde debería estar el punto de corte, así que aquí encaja mejor el caché automático.
Un campo superior cache_control mueve el punto de corte al último bloque cacheable en cada petición:
response = client.beta.messages.create(
model=MODEL,
cache_control={"type": "ephemeral"},
system=system,
tools=TOOLS,
messages=messages,
# Other request fields...
)
Un prefijo cacheable de menos de 512 tokens no se cachea en Fable 5.1 aunque lo marques con cache_control. La API lo procesa normalmente y devuelve cero en ambos contadores de caché. Escribir un prefijo de 583 tokens costó 0,0073 $; leerlo en el siguiente turno costó 0,00015 $. El segundo turno aún tuvo que escribir su parte nueva en la caché, así que un acierto de caché no elimina todos los costes de entrada.
Estima el coste de la API teniendo en cuenta la caché
response.usage informa por separado de entrada nueva, creación de caché, lecturas de caché y salida. Valora los cuatro contadores por su tarifa; sumar solo entrada y salida oculta el coste de escritura en caché y sobrestima el precio de los aciertos.
Aquí está el desglose de costes de una ejecución completa que leyó 12 archivos en tres turnos y produjo un plan final:
|
Partida |
Tokens |
Coste estimado |
Porcentaje |
|---|---|---|---|
|
Salida |
5.713 |
$0.2857 |
59,4% |
|
Escrituras de caché |
15.426 |
$0.1928 |
40,1% |
|
Entrada nueva |
50 |
$0.0005 |
0,1% |
|
Lecturas de caché |
6.549 |
$0.0016 |
0,3% |
|
Total |
27.738 |
$0.4806 |
100% |
Las lecturas de caché fueron menos de medio punto porcentual de esta estimación. Con la tarifa antigua de Fable 5, la ejecución habría costado unos 0,4855 $ frente a 0,4806 $. El ahorro crece cuando cada turno reutiliza mucho más contexto.
En esta ejecución, la salida supuso casi el 60% del coste estimado, y las escrituras de caché, alrededor del 40%. A la tarifa de cinco minutos usada aquí, un token de escritura en caché cuesta 50 veces más que uno de lectura. Con una hora de caché, cuesta 80 veces más.
Gestiona negativas y fallbacks en Claude Fable 5.1
Una negativa y una petición fallida requieren comportamientos distintos en tu aplicación.
Detecta negativas antes de parsear la salida
Una negativa previa a la salida llega como HTTP 200 con stop_reason: "refusal", contenido vacío y stop_details. Su categoría puede ser nula. Una negativa posterior en un stream puede llegar tras salida parcial, que la aplicación debe descartar. Un try/except alrededor de la llamada no captura ninguno de los casos.
response = client.messages.create(model=MODEL, max_tokens=8192, messages=messages)
if response.stop_reason == "refusal":
category = (
response.stop_details.category
if response.stop_details and response.stop_details.category
else "unspecified"
)
return f"This request was declined ({category})."
Trátalo como estado de la aplicación. Si una petición permitida es ambigua, reescríbela con más precisión. No crees lógica de reintento cuyo fin sea sortear el clasificador.

Una negativa llega como HTTP 200. Imagen del autor.
Configura el fallback del lado del servidor
El fallback del lado del servidor puede reintentar una petición rechazada en otro modelo usando fallbacks: "default" con la cabecera beta server-side-fallback-2026-07-01 . Los destinos permitidos para Fable 5.1 son Opus 4.8 y Opus 5.
El fallback por defecto solo se activa cuando la categoría de negativa tiene un destino recomendado. En una negativa de reasoning_extraction probada no se activó; inspecciona usage.iterations en lugar de asumir que toda negativa se reintenta. Como se mencionó, pasar a un modelo anterior también elimina los bloques de pensamiento de Fable 5.1.
Sirve el agente de Claude Fable 5.1 con FastAPI
Ahora el agente local puede exponer el mismo flujo a través de una API HTTP.
Crea el endpoint del plan
Si solo necesitas un script local, sáltate esta sección. Para un servicio web, usa FastAPI con AsyncAnthropic. Crea un cliente por proceso en un gestor de lifespan. Importa el esquema y los prompts del módulo del agente existente.
@asynccontextmanager
async def lifespan(_: FastAPI):
global client
client = AsyncAnthropic()
try:
yield
finally:
await client.close()
@app.post("/plan", response_model=PlanResponse)
async def create_plan(body: PlanRequest):
reader = resolve_project(body.project)
messages, totals, turns, tool_calls = await inspect(reader, body.feature_request)
plan, final_usage = await write_plan(messages)
totals.add(final_usage)
return PlanResponse(plan=plan, turns=turns, tool_calls=tool_calls, usage=as_usage(totals))
Fíjate en que el cliente envía un nombre de proyecto, no una ruta. resolve_project() lo mapea a una de varias raíces permitidas, de modo que la petición no puede pedir leer en ubicaciones arbitrarias. Este servicio mapea negativas a 422 como decisión de la aplicación. La API de Claude las devuelve como HTTP 200.
Ejecuta con uvicorn app:app --reload. La documentación interactiva está en http://localhost:8000/docs.
El endpoint devuelve un plan con coste estimado. Vídeo del autor.
El endpoint /plan/stream ejecuta la inspección en una tarea en segundo plano, pone eventos de progreso y herramientas en una asyncio.Queue y los emite mediante StreamingResponse. Cuando se cierra el stream, el generador cancela la tarea de fondo. La interfaz de Streamlit en el repositorio renderiza el mismo flujo de eventos.
Streamlit muestra el progreso en vivo del agente. Vídeo del autor.
Lista de comprobación para desplegar el agente Claude Fable 5.1
Los límites y comprobaciones que incorporaste antes siguen formando parte del servicio. Antes de desplegar, añade las piezas operativas que no se ven en local.
-
Revisa los dos reintentos por defecto del SDK para respuestas 429 y 5xx; ajusta
max_retriesy los timeouts al presupuesto de latencia del servicio -
Fija un timeout de petición y confirma que la cancelación de la tarea SSE existente detiene el trabajo pendiente si el cliente se desconecta
-
Registra el ID de modelo, la versión del SDK, el ID de petición, el motivo de parada y las cuatro categorías de tokens en cada ejecución
-
Activa alertas ante subidas en escrituras de caché, tokens de salida, negativas y ejecuciones que alcanzan el tope de turnos
-
Confirma que el ajuste de retención de la cuenta coincide con el requisito del modelo
-
Fija el SDK y vuelve a comprobar las cabeceras beta antes de cada release
Cuándo usar Claude Fable 5.1 en lugar de Opus 5 o Sonnet 5
- Anthropic recomienda Opus 5 como opción razonable por defecto.
- Prueba Fable 5.1 cuando Opus 5 se quede corto en análisis de repositorios largos, depuración difícil o tareas agenticas con gran contexto.
- Para trabajo en repositorios y tareas del día a día, compara Sonnet 5 y Opus 5 en calidad, latencia y coste.
- Para clasificación, extracción, respuestas cortas y solicitudes sencillas, Sonnet 5 es una buena base; para lo más simple, Haiku 4.5 también puede ser suficiente.
No elijas Fable 5.1 solo porque es más nuevo. Una petición única también puede usar esfuerzo y salidas estructuradas; el streaming funciona igual. No se beneficia del bucle ni del caché de prefijos repetidos que usamos aquí.
Reflexiones finales
El plan genérico de mi primera llamada solo fue útil después de que el agente leyera el repositorio. En la ejecución completa, inspeccionó 12 archivos en tres turnos, mientras que la salida y las escrituras de caché representaron el 99,5% del coste estimado. Mantendría el límite de rutas y el historial solo-append, y luego probaría si un menor esfuerzo reduce el coste sin hacer que el modelo se salte las herramientas del repositorio.
Si una sola respuesta resuelve la tarea, quédate en salidas estructuradas. Usa el bucle de herramientas cuando la respuesta deba depender de archivos del repositorio o haya que informar del progreso entre llamadas.
Para detalles de selección de modelos, te recomiendo nuestro curso Introduction to Claude Models. Para prompting y flujos de trabajo con agentes, consulta nuestro curso Software Development with Cursor.
FAQs
¿Claude Fable 5.1 puede leer imágenes además de código?
Sí. Acepta entrada de imagen y puede leer gráficos y PDFs. Dejé la visión fuera del ejemplo principal porque el plan del repositorio no la necesita. Si ampliara este agente para planificar un cambio de UI, enviaría la captura de pantalla actual junto con la petición de funcionalidad. Redúcela antes si los detalles visuales pequeños no afectan a la tarea.
¿Por qué mi agente se volvió más lento al pasar de Fable 5?
Revisa los resultados de las herramientas antes de culpar al modelo. Si ya incluiste la instrucción de agrupado mencionada antes, compara tanto su número como su tamaño. El lector actual limita cada archivo a 40.000 bytes. Si sigue siendo demasiado grande, añade argumentos de rango de líneas o de búsqueda para que la herramienta devuelva solo las secciones relevantes.
¿Por qué Claude Fable 5.1 devuelve un 400 invalid_request_error?
No reintentes de primeras. Un invalid_request_error suele apuntar a la forma de la petición o a un ajuste de cuenta que hay que cambiar. En este proyecto, las causas probables son tool_choice forzado, un ajuste de retención incompatible, un prefijo editado con pensamiento preservado o un campo beta enviado sin su cabecera. Corrige la causa indicada y vuelve a enviar.
¿Debería cachear los archivos fuente o un resumen?
Yo uso esta regla: cachea archivos fuente cuando el código exacto importa a lo largo de varios turnos. Si pasos posteriores solo necesitan la arquitectura o el mapa de archivos, cachea un resumen. El resumen cuesta menos tokens, pero puede omitir justo la línea que el plan final necesita.
¿La Batch API puede ejecutar este agente?
No por sí sola. La Batch API envía peticiones Messages individuales; no ejecuta este bucle de herramientas del lado cliente. La usaría para revisiones de repositorios autocontenidas cuando no haga falta progreso en vivo. Para ejecutar el bucle completo en lotes, tu propio código debe procesar las herramientas de un lote antes de enviar el siguiente.
Soy ingeniero de datos y creador de comunidades. Trabajo con canalizaciones de datos, nube y herramientas de IA, al tiempo que escribo tutoriales prácticos y de gran impacto para DataCamp y programadores emergentes.

