Llamadas a funciones
Describe tus funciones en la solicitud y el modelo responde con las llamadas que quiere que se hagan. Tu código las ejecuta y envía los resultados de vuelta: la API devuelve llamadas a herramientas y no ejecuta ninguna herramienta por sí misma.
POST https://api.shannon-ai.com/v1/chat/completions
Los ejemplos de esta página usan /v1/chat/completions. El mismo bucle funciona en /v1/responses y /v1/messages; donde un formato escribe algo de otra manera, las secciones siguientes muestran los tres.
import json
from openai import OpenAI
client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.shannon-ai.com/v1")
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, for example Paris"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["city"],
},
},
}]
def get_weather(city, unit="celsius"):
# Your code: ask a weather service here.
return {"city": city, "temperature": 18, "unit": unit, "conditions": "cloudy"}
messages = [{"role": "user", "content": "What is the weather in Paris right now?"}]
answer = None
for _ in range(5): # the API sets no limit on rounds: set your own
response = client.chat.completions.create(
model="Kimi-K3-3BIT-REAP",
messages=messages,
tools=tools,
)
choice = response.choices[0]
if choice.finish_reason != "tool_calls":
answer = choice.message.content
break
messages.append(choice.message) # the assistant turn with its tool_calls
for call in choice.message.tool_calls:
args = json.loads(call.function.arguments) # arguments is a JSON string
result = get_weather(**args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
})
print(answer) import OpenAI from "openai";
const client = new OpenAI({ apiKey: "YOUR_API_KEY", baseURL: "https://api.shannon-ai.com/v1" });
const tools = [{
type: "function",
function: {
name: "get_weather",
description: "Get the current weather for a city.",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "City name, for example Paris" },
unit: { type: "string", enum: ["celsius", "fahrenheit"] },
},
required: ["city"],
},
},
}];
// Your code: ask a weather service here.
function getWeather({ city, unit = "celsius" }) {
return { city, temperature: 18, unit, conditions: "cloudy" };
}
const messages = [{ role: "user", content: "What is the weather in Paris right now?" }];
let answer = null;
// The API sets no limit on rounds: set your own.
for (let round = 0; round < 5; round++) {
const response = await client.chat.completions.create({
model: "Kimi-K3-3BIT-REAP",
messages,
tools,
});
const choice = response.choices[0];
if (choice.finish_reason !== "tool_calls") {
answer = choice.message.content;
break;
}
messages.push(choice.message); // the assistant turn with its tool_calls
for (const call of choice.message.tool_calls) {
const args = JSON.parse(call.function.arguments); // arguments is a JSON string
messages.push({
role: "tool",
tool_call_id: call.id,
content: JSON.stringify(getWeather(args)),
});
}
}
console.log(answer); # 1. Send the conversation with the tools.
curl https://api.shannon-ai.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "Kimi-K3-3BIT-REAP",
"messages": [
{"role": "user", "content": "What is the weather in Paris right now?"}
],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, for example Paris"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}]
}'
# 2. Run the function yourself, then send the assistant turn and the result.
# Use the id of the call you received.
curl https://api.shannon-ai.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "Kimi-K3-3BIT-REAP",
"messages": [
{"role": "user", "content": "What is the weather in Paris right now?"},
{"role": "assistant", "content": null, "tool_calls": [{
"id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"type": "function",
"function": {"name": "get_weather", "arguments": "{\"city\":\"Paris\"}"}
}]},
{"role": "tool", "tool_call_id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"content": "{\"city\":\"Paris\",\"temperature\":18,\"unit\":\"celsius\",\"conditions\":\"cloudy\"}"}
],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, for example Paris"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}]
}' La primera respuesta no contesta la pregunta. Pide una llamada:
{
"id": "chatcmpl-5b0c1d8e7f6a4b3c9d2e1f0a8b7c6d5e",
"object": "chat.completion",
"created": 1791590400,
"model": "Kimi-K3-3BIT-REAP",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"reasoning_content": "The user asks for the current weather in Paris. I should call get_weather.",
"tool_calls": [
{
"id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
},
"finish_reason": "tool_calls"
}
],
"usage": {
"prompt_tokens": 312,
"completion_tokens": 58,
"total_tokens": 370,
"prompt_tokens_details": {"cached_tokens": 0},
"completion_tokens_details": {"reasoning_tokens": 31}
}
} Después de enviar el resultado, la siguiente respuesta es una respuesta normal: message.content contiene el texto y finish_reason es stop.
El bucle, paso a paso
| Paso | Quién | Qué ocurre |
|---|---|---|
| 1 | Tú | Envía messages junto con tools, la lista de funciones que el modelo puede pedir. |
| 2 | Modelo | La respuesta lleva tool_calls y finish_reason: "tool_calls". Cada llamada nombra una función y da sus argumentos. |
| 3 | Tú | Analiza los arguments de cada llamada y ejecuta tu función. |
| 4 | Tú | Añade a messages el mensaje del asistente y un mensaje tool por cada llamada, y luego envía la solicitud de nuevo con los mismos tools. |
| 5 | Modelo | El modelo responde con texto y finish_reason: "stop", o pide más llamadas. En ese caso vuelve al paso 3. |
Una solicitud es un turno del modelo. La API no guarda ninguna conversación entre solicitudes ni fija un número de rondas: tu código conserva messages y decide cuándo parar.
Definir herramientas
tools es un array con una entrada por función. Todos los modelos del catálogo lo aceptan; GET /v1/models informa capabilities.tools: true para cada id.
| Campo | Tipo | Descripción |
|---|---|---|
type | string | Siempre function. |
function.name | string | El nombre que usa el modelo cuando pide esta función. Obligatorio. |
function.description | string | Qué hace la función, en lenguaje claro. |
function.parameters | object | Los argumentos como un JSON Schema con type: "object" en el nivel superior. Consulta Palabras clave del esquema más abajo. |
Cada endpoint tiene su propia forma de escribir una herramienta. Envía la que corresponde al endpoint al que llamas:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, for example Paris"
},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
} {
"type": "function",
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, for example Paris"
},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
} {
"name": "get_weather",
"description": "Get the current weather for a city.",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, for example Paris"
},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
} tool_choice
tool_choice indica si el modelo puede, o debe, llamar a una herramienta. Solo se lee junto con tools.
| Valor | Descripción | Aplicado por |
|---|---|---|
"auto" | El modelo decide si responder con texto o llamar a una herramienta. Es el valor predeterminado. | Todos los modelos |
"required" | La respuesta llama al menos a una herramienta. | Modelos de pesos abiertos alojados |
{"type": "function", "function": {"name": "get_weather"}} | La respuesta llama a esta función. El nombre debe ser el de una de las funciones de tools. Las llamadas a otras funciones se omiten de la respuesta. | Modelos de pesos abiertos alojados |
Los modelos Shannon (shannon-*) deciden en cada solicitud, como con auto.
Con required o una función con nombre, lee finish_reason antes de leer tool_calls. Cuando el modelo no produce ninguna llamada, la respuesta es una respuesta de texto con finish_reason: "stop".
| Significado | /v1/chat/completions | /v1/responses | /v1/messages |
|---|---|---|---|
| El modelo decide | "auto" | "auto" | {"type": "auto"} |
| Llamar al menos a una herramienta | "required" | "required" | {"type": "any"} |
| Llamar a una función con nombre | {"type": "function", "function": {"name": "get_weather"}} | {"type": "function", "name": "get_weather"} | {"type": "tool", "name": "get_weather"} |
Se aceptan por compatibilidad: tool_choice: "none" y parallel_tool_calls. Para un turno sin llamadas a herramientas, envía la solicitud sin tools.
Llamadas a herramientas en la respuesta
| Campo | Tipo | Descripción |
|---|---|---|
finish_reason | string | tool_calls cuando la respuesta lleva al menos una llamada. |
message.tool_calls | array | Una entrada por llamada. El campo está presente solo cuando el modelo pidió una llamada. |
tool_calls[].id | string | call_ seguido de 32 caracteres hexadecimales. Envíalo de vuelta como tool_call_id con el resultado. |
tool_calls[].type | string | Siempre function. |
tool_calls[].function.name | string | La función que se debe ejecutar. |
tool_calls[].function.arguments | string | Los argumentos como una cadena JSON, "{}" cuando no hay ninguno. Analízala antes de usarla. |
message.content | string | null | null cuando la respuesta contiene solo llamadas. Los modelos de pesos abiertos alojados pueden devolver texto junto a las llamadas; los modelos Shannon devuelven null. |
La misma llamada en cada formato. En /v1/responses es un elemento function_call de output, con arguments como cadena JSON. En /v1/messages es un bloque de contenido tool_use cuyo input es un objeto JSON, su id empieza por toolu_ y stop_reason es tool_use.
{
"finish_reason": "tool_calls",
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
}
} {
"output": [
{
"id": "fc_3d2c1b0a9f8e4d7c6b5a4f3e2d1c0b9a",
"type": "function_call",
"call_id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}",
"status": "completed"
}
]
} {
"stop_reason": "tool_use",
"content": [
{
"type": "tool_use",
"id": "toolu_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"name": "get_weather",
"input": {"city": "Paris"}
}
]
} Varias llamadas en un turno
Una respuesta puede llevar varias llamadas. Ejecuta todas y envía un resultado por llamada, cada uno con su propio tool_call_id, en la siguiente solicitud.
Los modelos de pesos abiertos alojados devuelven cada llamada distinta una sola vez: dos llamadas con el mismo nombre y los mismos argumentos llegan como una.
Llamadas a herramientas en streaming
Con stream: true, una llamada llega de una sola pieza: el fragmento contiene la cadena arguments completa, así que no hay fragmentos que unir. index cuenta las llamadas del turno desde 0.
{
"id": "chatcmpl-5b0c1d8e7f6a4b3c9d2e1f0a8b7c6d5e",
"object": "chat.completion.chunk",
"created": 1791590400,
"model": "Kimi-K3-3BIT-REAP",
"choices": [
{
"index": 0,
"delta": {
"tool_calls": [
{
"index": 0,
"id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
},
"finish_reason": null
}
]
} | Endpoint | Qué llega |
|---|---|
/v1/chat/completions | Un chat.completion.chunk por llamada, con la llamada en delta.tool_calls[0]. El último fragmento tiene finish_reason: "tool_calls" y usage. |
/v1/responses | Por llamada: response.output_item.added (un elemento function_call con arguments vacío), response.function_call_arguments.delta (la cadena completa), response.function_call_arguments.done, response.output_item.done. |
/v1/messages | Por llamada: content_block_start (un bloque tool_use con input vacío), un content_block_delta cuyo input_json_delta.partial_json es el texto completo de los argumentos, content_block_stop. Luego message_delta lleva stop_reason: "tool_use". |
En shannon-1.6-lite, shannon-1.6-pro, shannon-2-lite, shannon-2-pro y shannon-coder-1, una solicitud con streaming que lleva tools entrega el texto de su respuesta en un solo delta de content al final del turno. Streaming
Enviar los resultados de vuelta
La segunda solicitud repite la conversación y añade dos cosas: el turno del asistente que pidió las llamadas y un resultado por llamada.
{
"messages": [
{"role": "user", "content": "What is the weather in Paris right now?"},
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"content": "{\"city\":\"Paris\",\"temperature\":18,\"unit\":\"celsius\",\"conditions\":\"cloudy\"}"
}
]
} {
"input": [
{"role": "user", "content": "What is the weather in Paris right now?"},
{
"type": "function_call",
"call_id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
},
{
"type": "function_call_output",
"call_id": "call_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"output": "{\"city\":\"Paris\",\"temperature\":18,\"unit\":\"celsius\",\"conditions\":\"cloudy\"}"
}
]
} {
"messages": [
{"role": "user", "content": "What is the weather in Paris right now?"},
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"name": "get_weather",
"input": {"city": "Paris"}
}
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_9f1c2ab34d5e4f60a7b8c9d0e1f2a3b4",
"content": "{\"city\":\"Paris\",\"temperature\":18,\"unit\":\"celsius\",\"conditions\":\"cloudy\"}"
}
]
}
]
} - Envía de vuelta el turno del asistente tal como lo recibiste, antes de los resultados. Cada resultado se asocia a su llamada por id:
tool_call_iden/v1/chat/completions,call_iden/v1/responses,tool_use_iden/v1/messages. - Un resultado es texto. Serializa un objeto a una cadena JSON para
content(outputen/v1/responses). En/v1/messages, elcontentde un bloquetool_resultes una cadena o una lista de bloques de texto. - En el turno del asistente,
function.argumentssigue siendo una cadena JSON, exactamente como llegó. - La conversación puede terminar con un resultado de herramienta. No hace falta un mensaje de usuario nuevo: el modelo continúa a partir del resultado.
- Cuando tu función falla, envía el fallo como texto del resultado. El modelo puede entonces informar de él o pedir otra llamada.
Palabras clave del esquema
parameters (input_schema en /v1/messages) es un JSON Schema. La familia Shannon 3 (shannon-3, shannon-3-pro, shannon-3.1, shannon-3.1-pro) y los modelos de pesos abiertos alojados lo reciben tal como lo envías.
shannon-1.6-lite, shannon-1.6-pro, shannon-2-lite, shannon-2-pro y shannon-coder-1 reciben este subconjunto, en los tres endpoints:
| Tratamiento | Palabras clave |
|---|---|
| Se lee tal como se envía | Toda palabra clave que no se nombra en las filas siguientes, por ejemplo type, properties, required, items, enum, description, minimum, maximum, anyOf y allOf, en todos los niveles del esquema. |
format | Se lee para los valores int32, int64, float, double, byte, date, date-time y enum. |
oneOf | Se lee como anyOf. |
| No forma parte del esquema que reciben estos modelos | $schema, $id, $ref, $defs, $comment, definitions, additionalProperties, unevaluatedProperties, patternProperties, propertyNames, examples, default, title, const, not, if, then, else, contains, minContains, maxContains, dependentRequired, dependentSchemas, dependencies, readOnly, writeOnly, deprecated, contentEncoding, contentMediaType, contentSchema, multipleOf, uniqueItems; exclusiveMinimum y exclusiveMaximum cuando son números; otros valores de format. |
Comportamiento y límites
- La API no fija un número máximo de herramientas ni un número máximo de rondas.
- El cuerpo de una solicitud puede tener hasta 32 MiB, y la conversación con sus resultados debe caber en la ventana de contexto del modelo.
- Cuando una solicitud lleva
tools,shannon-1.6-lite,shannon-1.6-pro,shannon-2-liteyshannon-2-prousan unatemperaturepredeterminada de 0.2. Envíatemperaturepara establecer otro valor. - Cada ronda es una solicitud propia: toma de tu saldo y cuenta para la protección contra flood como cualquier otra solicitud. Límites y saldo
Errores
Una llamada a herramienta es una respuesta normal con estado 200. Estos errores se refieren a la solicitud en sí:
| Estado | Tipo | Mensaje | Cuándo |
|---|---|---|---|
400 | invalid_request_error | unknown model: <id> | model no es un id de la lista publicada. |
422 | invalid_request_error | Failed to deserialize the JSON body into the target type: … | tools no es un array, o otro campo tiene un tipo JSON incorrecto. |
413 | invalid_request_error | Failed to buffer the request body: … | El cuerpo de la solicitud es mayor que 32 MiB. El error lleva code: "request_too_large". |
El modelo escribe los argumentos. Compruébalos con tu esquema antes de ejecutar la función, como comprobarías cualquier entrada externa.
Los errores de autenticación, saldo y servidor son los mismos que para cualquier solicitud. Manejo de errores