Cómo usar Ollama para respuestas en streaming y llamadas a herramientas

Mark Ponomarev

Mark Ponomarev

29 May 2025

Cómo usar Ollama para respuestas en streaming y llamadas a herramientas

Esta guía te explicará cómo usar una de las nuevas y potentes características de Ollama: la capacidad de transmitir respuestas (streaming) y llamar a herramientas (como funciones o APIs) en tiempo real. Esto cambia las reglas del juego para construir aplicaciones de chat que se sientan vivas y puedan interactuar con el mundo que las rodea.

Lo que aprenderás en este tutorial:

💡
¿Quieres una gran herramienta de prueba de API que genere hermosa Documentación de API?

¿Quieres una plataforma integrada y todo en uno para que tu Equipo de Desarrolladores trabaje con máxima productividad?

¡Apidog satisface todas tus demandas y reemplaza a Postman a un precio mucho más asequible!
button

Primeros Pasos: Lo Que Necesitarás

Para seguir esta guía, necesitarás algunas cosas:

Comprendiendo Ideas Clave: Streaming y Llamadas a Herramientas

Desglosemos qué queremos decir con "transmisión de respuestas" (streaming) y "llamada a herramientas".

¿Qué es la Transmisión de Respuestas (Response Streaming)?

Imagina que estás chateando con una IA. En lugar de esperar a que piense y escriba su respuesta completa antes de que veas algo, el streaming significa que la IA te envía su respuesta pieza por pieza, palabra por palabra, a medida que la genera. Esto hace que la interacción se sienta mucho más rápida y natural, como una conversación real.

Con Ollama, cuando habilitas el streaming ("stream": true), obtienes estas actualizaciones incrementales.

¿Cómo Funciona la Llamada a Herramientas?

La llamada a herramientas permite que tus modelos de IA hagan más que simplemente generar texto. Puedes definir "herramientas" – que son esencialmente funciones o APIs externas – que la IA puede decidir usar para obtener información o realizar acciones.

Por ejemplo, una herramienta podría ser:

Describes estas herramientas a Ollama, y cuando la IA determina que usar una herramienta ayudaría a responder la consulta del usuario, señala su intención de llamar a esa herramienta con argumentos específicos. Tu aplicación luego ejecuta la herramienta y puede enviar los resultados de vuelta a la IA para continuar la conversación.

¿Por Qué Combinar Streaming con Llamada a Herramientas?

La gran mejora de Ollama es que ahora puede manejar la llamada a herramientas mientras transmite respuestas. Esto significa que tu aplicación puede:

  1. Recibir texto inicial del modelo (transmitido).
  2. De repente, la transmisión puede indicar que se necesita una llamada a una herramienta.
  3. Tu aplicación procesa la llamada a la herramienta.
  4. Mientras tanto, el modelo incluso podría transmitir más texto (por ejemplo, "Okay, buscaré el clima para ti...").
  5. Una vez que tu aplicación obtiene el resultado de la herramienta, puedes enviarlo de vuelta al modelo, y este continuará transmitiendo su respuesta, ahora informada por la salida de la herramienta.

Esto crea aplicaciones de IA altamente responsivas y capaces.

¿Qué Modelos Soportan Estas Características?

Ollama ha habilitado esto para varios modelos populares, incluyendo:

Cómo Hacer Tu Primera Llamada a Herramienta con Streaming Usando cURL

cURL es una excelente manera de probar rápidamente la API de Ollama. Pidamos el clima en Toronto.

Paso 1: Conceptualizando Tu Herramienta

Nuestra herramienta será get_current_weather. Necesita:

Paso 2: Construyendo el Comando cURL

Abre tu terminal y prepara el siguiente comando. Lo desglosaremos:

curl <http://localhost:11434/api/chat> -d '{
  "model": "qwen3",
  "messages": [
    {
      "role": "user",
      "content": "What is the weather today in Toronto?"
    }
  ],
  "stream": true,
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_current_weather",
        "description": "Get the current weather for a location",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "The location to get the weather for, e.g. San Francisco, CA"
            },
            "format": {
              "type": "string",
              "description": "The format to return the weather in, e.g. \\\\\\\\'celsius\\\\\\\\' or \\\\\\\\'fahrenheit\\\\\\\\'",
              "enum": ["celsius", "fahrenheit"]
            }
          },
          "required": ["location", "format"]
        }
      }
    }
  ]
}'

Desglose:

Paso 3: Ejecuta y Observa la Salida

Presiona Enter. Verás una serie de objetos JSON aparecer uno tras otro. ¡Esto es la transmisión (stream)!

Fragmentos de ejemplo de la transmisión:

{
  "model": "qwen3", "created_at": "...",
  "message": { "role": "assistant", "content": "Okay, " }, "done": false
}

{
  "model": "qwen3", "created_at": "...",
  "message": { "role": "assistant", "content": "I will " }, "done": false
}

{
  "model": "qwen3", "created_at": "...",
  "message": { "role": "assistant", "content": "try to get that for you." }, "done": false
}

(El modelo podría generar algunos tokens de "pensamiento" como <think>...celsius...</think> dependiendo de su proceso interno, estos también son parte de la transmisión)

Luego, de manera crucial, podrías ver algo como esto:

{
  "model": "qwen3",
  "created_at": "2025-05-27T22:54:58.100509Z",
  "message": {
    "role": "assistant",
    "content": "", // El contenido puede estar vacío cuando se realiza una llamada a herramienta
    "tool_calls": [
      {
        "function": {
          "name": "get_current_weather",
          "arguments": { // ¡Los argumentos que el modelo decidió!
            "format": "celsius",
            "location": "Toronto"
          }
        }
      }
    ]
  },
  "done": false // Todavía no terminado, esperando el resultado de la herramienta
}

Qué Observar:

En una aplicación real, cuando veas un fragmento tool_calls, tu código debería:

  1. Pausar el procesamiento de la transmisión (o manejarlo de forma asíncrona).
  2. Ejecutar la función/API real get_current_weather con "Toronto" y "celsius".
  3. Obtener el resultado (por ejemplo, "20 grados Celsius").
  4. Enviar este resultado de vuelta a Ollama en un nuevo mensaje con role: "tool".
  5. El modelo luego usará esta información para continuar generando su respuesta, también transmitida.

Cómo Transmitir Llamadas a Herramientas Usando Python

Implementemos una idea similar en Python usando la librería oficial de Ollama.

Paso 1: Instalando la Librería de Ollama para Python

Si aún no lo has hecho, instala o actualiza la librería:

pip install -U ollama

Paso 2: Definiendo Tu Herramienta y Codificando en Python

El SDK de Ollama para Python permite inteligentemente pasar funciones de Python directamente como herramientas. Inspecciona la firma de la función y el docstring para crear el esquema para la IA.

Creemos un ejemplo simple de herramienta matemática (la entrada usa add_two_numbers pero el ejemplo de salida muestra subtract_two_numbers siendo llamado por el modelo. Nos ceñiremos a add_two_numbers proporcionado para la definición y dejaremos que el modelo decida qué hacer basándose en la instrucción.)

import ollama

# Define the python function that can be used as a tool
def add_two_numbers(a: int, b: int) -> int:
  """
  Add two numbers.

  Args:
    a (int): The first number as an int.
    b (int): The second number as an int.

  Returns:
    int: The sum of the two numbers.
  """
  print(f"--- Tool 'add_two_numbers' called with a={a}, b={b} ---")
  return a + b

# --- Main conversation logic ---
messages = [{'role': 'user', 'content': 'What is three plus one?'}]
# Or, for the subtraction example in the original output:
# messages = [{'role': 'user', 'content': 'what is three minus one?'}]

print(f"User: {messages[0]['content']}")

# Make the chat request with streaming and the tool
# Note: ChatResponse type hint might be ollama.ChatResponse or similar depending on library version
response_stream = ollama.chat(
  model='qwen3', # Or another capable model
  messages=messages,
  tools=[
      { # You can also define the tool explicitly if needed, or pass the function directly
          'type': 'function',
          'function': {
              'name': 'add_two_numbers', # Must match the Python function name if you want it to be called directly by your code later
              'description': 'Add two integer numbers together.',
              'parameters': {
                  'type': 'object',
                  'properties': {
                      'a': {'type': 'integer', 'description': 'The first number'},
                      'b': {'type': 'integer', 'description': 'The second number'}
                  },
                  'required': ['a', 'b']
              }
          }
      }
      # Simpler way for Python: pass the function directly if the library supports easy schema generation from it
      # tools=[add_two_numbers] # The SDK can often create the schema from this
  ],
  stream=True
)

print("Assistant (streaming):")
full_response_content = ""
tool_call_info = None

for chunk in response_stream:
  # Print the streamed content part
  if chunk['message']['content']:
    print(chunk['message']['content'], end='', flush=True)
    full_response_content += chunk['message']['content']

  # Check for tool calls in the chunk
  if 'tool_calls' in chunk['message'] and chunk['message']['tool_calls']:
    tool_call_info = chunk['message']['tool_calls'][0] # Assuming one tool call for simplicity
    print(f"\\\\n--- Detected Tool Call: {tool_call_info['function']['name']} ---")
    break # Stop processing stream for now, handle tool call

  if chunk.get('done'):
      print("\\\\n--- Stream finished ---")
      if not tool_call_info:
          print("No tool call was made.")

# --- If a tool call was detected, handle it ---
if tool_call_info:
  tool_name = tool_call_info['function']['name']
  tool_args = tool_call_info['function']['arguments']

  print(f"Arguments for the tool: {tool_args}")

  # Here, you'd actually call your Python tool function
  if tool_name == "add_two_numbers":
    # For safety, ensure arguments are of correct type if necessary
    try:
        arg_a = int(tool_args.get('a'))
        arg_b = int(tool_args.get('b'))
        tool_result = add_two_numbers(a=arg_a, b=arg_b)
        print(f"--- Tool execution result: {tool_result} ---")

        # Now, send this result back to Ollama to continue the conversation
        messages.append({'role': 'assistant', 'content': full_response_content, 'tool_calls': [tool_call_info]})
        messages.append({
            'role': 'tool',
            'content': str(tool_result), # Result must be a string
            'tool_call_id': tool_call_info.get('id', '') # If your library/model provides a tool_call_id
        })

        print("\\\\n--- Sending tool result back to model ---")

        follow_up_response_stream = ollama.chat(
            model='qwen3',
            messages=messages,
            stream=True
            # No tools needed here unless you expect another tool call
        )

        print("Assistant (after tool call):")
        for follow_up_chunk in follow_up_response_stream:
            if follow_up_chunk['message']['content']:
                print(follow_up_chunk['message']['content'], end='', flush=True)
            if follow_up_chunk.get('done'):
                print("\\\\n--- Follow-up stream finished ---")
                break
    except ValueError:
        print("Error: Could not parse tool arguments as integers.")
    except Exception as e:
        print(f"An error occurred during tool execution or follow-up: {e}")
  else:
    print(f"Error: Unknown tool '{tool_name}' requested by the model.")

Explicación del Código Python:

  1. Importa ollama.
  2. Función add_two_numbers: Esta es nuestra herramienta. El docstring y las sugerencias de tipo (type hints) ayudan a Ollama a entender su propósito y parámetros.
  3. messages: Iniciamos la conversación con la consulta del usuario.
  4. ollama.chat(...):
  1. Iterando a través de response_stream:
  1. Manejo de la Llamada a la Herramienta: Similar a Python, si tool_call_info está establecido:

Flujo de Salida Esperado:Verás la pregunta inicial del usuario, luego la respuesta del asistente transmitiéndose. Si decide llamar a add_two_numbers (o a subtract_two_numbers como en la salida de ejemplo del material original si la instrucción fue para una resta), verás el mensaje "Detected Tool Call", los argumentos, el resultado de tu función Python, y luego el asistente continuando su respuesta usando ese resultado.

(La salida de ejemplo original mostraba:

<think>
Okay, the user is asking ...
</think>

[ToolCall(function=Function(name='subtract_two_numbers', arguments={'a': 3, 'b': 1}))]

Esto indica el proceso de "pensamiento" interno de la IA y luego el objeto de llamada a herramienta estructurado que proporciona el SDK de Python.)

Cómo Transmitir Llamadas a Herramientas Usando JavaScript (Node.js)

Ahora, hagamos lo mismo con JavaScript, típicamente para un backend Node.js o una aplicación web.

Paso 1: Instalando la Librería de Ollama para JavaScript

En el directorio de tu proyecto, ejecuta:

npm i ollama

Paso 2: Definiendo el Esquema de la Herramienta y Codificando en JavaScript

En JavaScript, usualmente defines el esquema de la herramienta como un objeto JSON.

import ollama from 'ollama';

// Describe the tool schema (e.g., for adding two numbers)
const addTool = {
    type: 'function',
    function: {
        name: 'addTwoNumbers',
        description: 'Add two numbers together',
        parameters: {
            type: 'object',
            required: ['a', 'b'],
            properties: {
                a: { type: 'number', description: 'The first number' },
                b: { type: 'number', description: 'The second number' }
            }
        }
    }
};

// Your actual JavaScript function that implements the tool
function executeAddTwoNumbers(a, b) {
    console.log(`--- Tool 'addTwoNumbers' called with a=${a}, b=${b} ---`);
    return a + b;
}

async function main() {
    const messages = [{ role: 'user', content: 'What is 2 plus 3?' }];
    console.log('User:', messages[0].content);

    console.log('Assistant (streaming):');
    let assistantResponseContent = "";
    let toolToCallInfo = null;

    try {
        const responseStream = await ollama.chat({
            model: 'qwen3', // Or another capable model
            messages: messages,
            tools: [addTool],
            stream: true
        });

        for await (const chunk of responseStream) {
            if (chunk.message.content) {
                process.stdout.write(chunk.message.content);
                assistantResponseContent += chunk.message.content;
            }
            if (chunk.message.tool_calls && chunk.message.tool_calls.length > 0) {
                toolToCallInfo = chunk.message.tool_calls[0]; // Assuming one tool call
                process.stdout.write(`\\\\n--- Detected Tool Call: ${toolToCallInfo.function.name} ---\\\\n`);
                break; // Stop processing stream to handle tool call
            }
            if (chunk.done) {
                process.stdout.write('\\\\n--- Stream finished ---\\\\n');
                if (!toolToCallInfo) {
                    console.log("No tool call was made.");
                }
                break;
            }
        }

        // --- If a tool call was detected, handle it ---
        if (toolToCallInfo) {
            const toolName = toolToCallInfo.function.name;
            const toolArgs = toolToCallInfo.function.arguments;

            console.log(`Arguments for the tool:`, toolArgs);

            let toolResult;
            if (toolName === 'addTwoNumbers') {
                toolResult = executeAddTwoNumbers(toolArgs.a, toolArgs.b);
                console.log(`--- Tool execution result: ${toolResult} ---`);

                // Append assistant's partial message and the tool message
                messages.push({
                    role: 'assistant',
                    content: assistantResponseContent, // Include content leading up to tool call
                    tool_calls: [toolToCallInfo]
                });
                messages.push({
                    role: 'tool',
                    content: toolResult.toString(), // Result must be a string
                    // tool_call_id: toolToCallInfo.id // If available and needed
                });

                console.log("\\\\n--- Sending tool result back to model ---");
                const followUpStream = await ollama.chat({
                    model: 'qwen3',
                    messages: messages,
                    stream: true
                });

                console.log("Assistant (after tool call):");
                for await (const followUpChunk of followUpStream) {
                    if (followUpChunk.message.content) {
                        process.stdout.write(followUpChunk.message.content);
                    }
                     if (followUpChunk.done) {
                        process.stdout.write('\\\\n--- Follow-up stream finished ---\\\\n');
                        break;
                    }
                }
            } else {
                console.error(`Error: Unknown tool '${toolName}' requested.`);
            }
        }

    } catch (error) {
        console.error('Error during Ollama chat:', error);
    }
}

main().catch(console.error);

Explicación del Código JavaScript:

  1. Importa ollama.
  2. Objeto addTool: Este es el esquema JSON que describe nuestra herramienta a Ollama.
  3. Función executeAddTwoNumbers: Nuestra función de JavaScript real para la herramienta.
  4. Función asíncrona main:
  1. Manejo de la Llamada a la Herramienta: Similar a Python, si toolToCallInfo está establecido:

Flujo de Salida Esperado (similar a los ejemplos de cURL y Python):Verás la pregunta del usuario, luego la respuesta del asistente transmitiéndose. Cuando decida llamar a addTwoNumbers, imprimirá la información de la llamada a la herramienta, el resultado de tu función JavaScript, y luego continuará transmitiendo la respuesta de la IA basándose en ese resultado.

La salida de ejemplo original para JS se veía así:

Question: What is 2 plus 3?
<think>
Okay, the user is asking...
</think>
Tool call: {
  function: {
    name: "addTwoNumbers",
    arguments: { a: 2, b: 3 },
  },
}

Cómo Ollama Maneja el Análisis de Herramientas Durante el Streaming

Quizás te preguntes cómo Ollama logra transmitir texto e identificar llamadas a herramientas de manera tan fluida. Utiliza un nuevo y astuto analizador incremental.

¿Por qué es mejor esto?

Consejo: Mejorando el Rendimiento con la Ventana de Contexto

Para interacciones más complejas, especialmente con la llamada a herramientas, el tamaño de la "ventana de contexto" que usa el modelo puede importar. Una ventana de contexto más grande significa que el modelo recuerda más de la conversación actual.

Ejemplo: Estableciendo la Ventana de Contexto con cURL(Usa un modelo que soporte contextos más grandes, como llama3.1 o llama4 como se sugiere en el material original - aunque el ejemplo usa llama3.2)

curl -X POST "<http://localhost:11434/api/chat>" -d '{
  "model": "llama3.1",
  "messages": [
    {
      "role": "user",
      "content": "why is the sky blue?"
    }
  ],
  "options": {
    "num_ctx": 32000
  }
}'

Experimenta con esta configuración si encuentras que la llamada a herramientas no es tan fiable como te gustaría.

¿Hacia Dónde Ir Desde Aquí?

¡Ahora tienes los fundamentos para construir aplicaciones de IA sofisticadas y en tiempo real con Ollama usando transmisión de respuestas y llamada a herramientas!

Ideas para explorar:

Consulta la documentación oficial de Ollama y su repositorio en GitHub (incluyendo la "Solicitud de Extracción de Streaming de Herramientas" mencionada en el material fuente original para inmersiones técnicas más profundas) para las últimas actualizaciones y ejemplos más avanzados.

💡
¿Quieres una gran herramienta de prueba de API que genere hermosa Documentación de API?

¿Quieres una plataforma integrada y todo en uno para que tu Equipo de Desarrolladores trabaje con máxima productividad?

¡Apidog satisface todas tus demandas y reemplaza a Postman a un precio mucho más asequible!
button

Practica el diseño de API en Apidog

Descubre una forma más fácil de construir y usar APIs