CULTIVA IA

Tool Use / Function Calling con LLMs

Agente de soporte veterinario — NutriPaw SaaS · Python + Anthropic SDK

IA-Ingenieria-MLOps · Nivel Avanzado
4
Herramientas definidas
3
Tool calls en ejecución
2
Iteraciones del loop
Acciones encadenables
Herramientas Definidas
buscar_dieta
Busca dietas por especie, condición o ingrediente en catálogo NutriPaw
especie* condicion* ingrediente_excluir
calcular_calorias
Calcula RER y MER diario según peso, especie y nivel de actividad
peso_kg* especie* nivel_actividad
crear_ticket
Abre ticket en sistema de soporte con prioridad y descripción
titulo* descripcion* prioridad*
enviar_alerta_email
Notifica al equipo veterinario de casos urgentes via email
destinatario* asunto* cuerpo*
Ciclo del Agente (Bucle)
👤
1 · Input usuario
Mensaje + definiciones de herramientas enviados al LLM. messages=[{role:"user",...}]
🧠
2 · LLM decide
El modelo responde con stop_reason="tool_use" y uno o varios bloques tool_use
⚙️
3 · Tu código ejecuta
Dispatcher llama la función real. Errores devueltos como {"error":"..."} — nunca excepciones
📤
4 · Resultado → LLM
Appends tool_result al historial y vuelve al paso 2 hasta stop_reason="end_turn"
Traza de Conversación — Caso Real: Labrador con ERC estadio 2
👤 veterinario · turno 1
Tengo un labrador de 32 kg con enfermedad renal crónica estadio 2. ¿Qué dieta recomiendas y cuántas calorías necesita? Si no hay opciones disponibles, abre un ticket urgente.
🧠 claude-3-5-sonnet · stop_reason = tool_use · 2 llamadas paralelas
→ buscar_dieta({
  "especie": "perro",
  "condicion": "enfermedad_renal_cronica",
  "ingrediente_excluir": "fosforo_alto"
})

→ calcular_calorias({
  "peso_kg": 32,
  "especie": "perro",
  "nivel_actividad": "moderado"
})
⚙️ tool_result · buscar_dieta
{
  "resultados": [{
    "id": "NP-RENAL-K9-002",
    "nombre": "NutriPaw Renal Support Canine",
    "fosforo_g_1000kcal": 0.87,
    "proteina_pct": 14.2,
    "disponible": true,
    "stock": "disponible · entrega 24h"
  }]
}
⚙️ tool_result · calcular_calorias
{
  "peso_kg": 32,
  "RER_kcal_dia": 876,
  "MER_kcal_dia": 1401,
  "factor_actividad": 1.6,
  "nota": "Para ERC estadio 2 se recomienda reducir 10-15% del MER",
  "MER_ajustado_kcal_dia": 1190
}
🤖 claude-3-5-sonnet · stop_reason = end_turn
Para tu labrador de 32 kg con ERC estadio 2 tengo buenas noticias:

Dieta recomendada: NutriPaw Renal Support Canine (ref. NP-RENAL-K9-002)
· Fósforo restringido: 0,87 g/1.000 kcal (por debajo del límite WSAVA)
· Proteína moderada: 14,2% — adecuado para ERC estadio 2
· Disponibilidad: en stock, entrega en 24h

Calorías diarias: ~1.190 kcal/día (MER ajustado −15% por patología renal; RER basal 876 kcal)

No fue necesario abrir ticket — se encontró solución en catálogo.
Implementación Python (Anthropic SDK)
import anthropic, json

client = anthropic.Anthropic()

tools = [{
  "name": "buscar_dieta",
  "description": "Busca dietas por especie y condicion",
  "input_schema": {
    "type": "object",
    "properties": {
      "especie": {"type": "string"},
      "condicion": {"type": "string"}
    },
    "required": ["especie", "condicion"]
  }
}]

def run_agent(user_msg):
  messages = [{"role": "user", "content": user_msg}]

  while True:
    response = client.messages.create(
      model="claude-3-5-sonnet-20241022",
      max_tokens=4096,
      tools=tools, messages=messages
    )
    messages.append({
      "role": "assistant",
      "content": response.content
    })

    if response.stop_reason == "end_turn":
      return next(
        b.text for b in response.content
        if hasattr(b, "text")
      )

    # stop_reason == "tool_use"
    tool_results = []
    for block in response.content:
      if block.type == "tool_use":
        result = execute_tool(block.name, block.input)
        tool_results.append({
          "type": "tool_result",
          "tool_use_id": block.id,
          "content": result
        })
    messages.append({
      "role": "user",
      "content": tool_results
    })
Buenas Prácticas Aplicadas
📝
Descripciones accionables "Usa buscar_dieta cuando el veterinario consulte por alimentación. NO para cálculos calóricos." El modelo elige herramientas correctamente.
Llamadas paralelas Claude lanzó buscar_dieta + calcular_calorias simultáneamente en 1 turno → latencia ÷2 vs secuencial.
🔒
Errores como JSON execute_tool() devuelve {"error":"..."} nunca lanza excepciones. El agente puede razonar sobre el fallo.
🔁
Cap de iteraciones max_iterations=10 evita bucles infinitos y gasto descontrolado de tokens en producción.
🧾
Log completo Cada tool call + resultado se persiste en historial → auditoría, debugging y trazabilidad clínica.
Confirmación en acciones destructivas enviar_alerta_email requiere dry_run=False explícito para evitar spam accidental durante desarrollo.