Crida de funcions
Descriu les teves funcions a la sol·licitud i el model respon amb les crides que vol que es facin. El teu codi les executa i n'envia els resultats: l'API retorna crides d'eina i no executa cap eina per si mateixa.
POST https://api.shannon-ai.com/v1/chat/completions
Els exemples d'aquesta pàgina fan servir /v1/chat/completions. El mateix bucle funciona a /v1/responses i a /v1/messages; quan un format escriu alguna cosa de manera diferent, les seccions de sota mostren els 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 resposta no contesta la pregunta. Demana una crida:
{
"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}
}
} Després d'enviar el resultat, la resposta següent és una resposta normal: message.content conté el text i finish_reason és stop.
El bucle, pas a pas
| Pas | Qui | Què passa |
|---|---|---|
| 1 | Tu | Envia messages juntament amb tools, la llista de funcions que el model pot demanar. |
| 2 | Model | La resposta porta tool_calls i finish_reason: "tool_calls". Cada crida indica una funció i en dona els arguments. |
| 3 | Tu | Analitza els arguments de cada crida i executa la teva funció. |
| 4 | Tu | Afegeix a messages el missatge de l'assistent i un missatge tool per crida, i torna a enviar la sol·licitud amb els mateixos tools. |
| 5 | Model | El model respon amb text i finish_reason: "stop", o demana més crides. En aquest cas, torna al pas 3. |
Una sol·licitud és un torn del model. L'API no conserva cap conversa entre sol·licituds i no fixa cap nombre de rondes: el teu codi guarda messages i decideix quan aturar-se.
Definició d'eines
tools és una matriu amb una entrada per funció. Tots els models del catàleg l'accepten; GET /v1/models informa capabilities.tools: true per a cada id.
| Camp | Tipus | Descripció |
|---|---|---|
type | string | Sempre function. |
function.name | string | El nom que el model fa servir quan demana aquesta funció. Obligatori. |
function.description | string | Què fa la funció, en llenguatge planer. |
function.parameters | object | Els arguments com a JSON Schema amb type: "object" a dalt de tot. Vegeu Paraules clau de l'esquema més avall. |
Cada endpoint té la seva manera d'escriure una eina. Envia la que correspon a l'endpoint que crides:
{
"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 model pot, o ha de, cridar una eina. Només es llegeix juntament amb tools.
| Valor | Descripció | S'aplica a |
|---|---|---|
"auto" | El model decideix si respon amb text o crida una eina. Aquest és el valor per defecte. | Tots els models |
"required" | La resposta crida almenys una eina. | Models open-weight hostejats |
{"type": "function", "function": {"name": "get_weather"}} | La resposta crida aquesta funció. El nom ha de ser el d'una de les funcions de tools. Les crides a altres funcions no apareixen a la resposta. | Models open-weight hostejats |
Els models Shannon (shannon-*) decideixen a cada sol·licitud, com amb auto.
Amb required o una funció amb nom, llegeix finish_reason abans de llegir tool_calls. Quan el model no produeix cap crida, la resposta és un text amb finish_reason: "stop".
| Significat | /v1/chat/completions | /v1/responses | /v1/messages |
|---|---|---|---|
| El model decideix | "auto" | "auto" | {"type": "auto"} |
| Crida almenys una eina | "required" | "required" | {"type": "any"} |
| Crida una funció concreta | {"type": "function", "function": {"name": "get_weather"}} | {"type": "function", "name": "get_weather"} | {"type": "tool", "name": "get_weather"} |
S'accepten per compatibilitat: tool_choice: "none" i parallel_tool_calls. Per a un torn sense crides d'eina, envia la sol·licitud sense tools.
Crides d'eina a la resposta
| Camp | Tipus | Descripció |
|---|---|---|
finish_reason | string | tool_calls quan la resposta porta almenys una crida. |
message.tool_calls | array | Una entrada per crida. El camp només hi és quan el model ha demanat una crida. |
tool_calls[].id | string | call_ seguit de 32 caràcters hexadecimals. Torna'l com a tool_call_id amb el resultat. |
tool_calls[].type | string | Sempre function. |
tool_calls[].function.name | string | La funció que cal executar. |
tool_calls[].function.arguments | string | Els arguments com a cadena JSON, "{}" quan no n'hi ha. Analitza-la abans de fer-la servir. |
message.content | string | null | null quan la resposta conté només crides. Els models open-weight hostejats poden retornar text al costat de les crides; els models Shannon retornen null. |
La mateixa crida en cada format. A /v1/responses és un element function_call d'output, amb arguments com a cadena JSON. A /v1/messages és un bloc de contingut tool_use amb input com a objecte JSON, l'id comença per toolu_ i stop_reason és 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"}
}
]
} Diverses crides en un torn
Una resposta pot portar diverses crides. Executa-les totes i envia un resultat per crida, cadascun amb el seu tool_call_id, a la sol·licitud següent.
Els models open-weight hostejats retornen cada crida diferent una sola vegada: dues crides amb el mateix nom i els mateixos arguments arriben com una.
Crides d'eina en streaming
Amb stream: true, una crida arriba d'una sola peça: el fragment conté la cadena arguments completa, així que no cal unir fragments. index compta les crides del torn des de 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è arriba |
|---|---|
/v1/chat/completions | Un chat.completion.chunk per crida, amb la crida a delta.tool_calls[0]. L'últim fragment té finish_reason: "tool_calls" i usage. |
/v1/responses | Per crida: response.output_item.added (un element function_call amb arguments buit), response.function_call_arguments.delta (la cadena sencera), response.function_call_arguments.done, response.output_item.done. |
/v1/messages | Per crida: content_block_start (un bloc tool_use amb input buit), un content_block_delta on input_json_delta.partial_json és tot el text dels arguments, content_block_stop. Després message_delta porta stop_reason: "tool_use". |
A shannon-1.6-lite, shannon-1.6-pro, shannon-2-lite, shannon-2-pro i shannon-coder-1, una sol·licitud en streaming que porta tools lliura el text de la resposta en un sol delta de content al final del torn. Streaming
Enviament dels resultats
La segona sol·licitud repeteix la conversa i hi afegeix dues coses: el torn de l'assistent que ha demanat les crides i un resultat per crida.
{
"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\"}"
}
]
}
]
} - Torna el torn de l'assistent tal com l'has rebut, abans dels resultats. Cada resultat s'associa a la seva crida per l'id:
tool_call_ida/v1/chat/completions,call_ida/v1/responses,tool_use_ida/v1/messages. - Un resultat és text. Serialitza un objecte a una cadena JSON per a
content(outputa/v1/responses). A/v1/messages, elcontentd'un bloctool_resultés una cadena o una llista de blocs de text. - Al torn de l'assistent,
function.argumentscontinua sent una cadena JSON, tal com va arribar. - La conversa pot acabar amb un resultat d'eina. No cal cap missatge nou de l'usuari: el model continua a partir del resultat.
- Quan la teva funció falla, envia el fracàs com a text del resultat. El model pot llavors informar-ne o demanar una altra crida.
Paraules clau de l'esquema
parameters (input_schema a /v1/messages) és un JSON Schema. La família Shannon 3 (shannon-3, shannon-3-pro, shannon-3.1, shannon-3.1-pro) i els models open-weight hostejats el reben tal com l'envies.
shannon-1.6-lite, shannon-1.6-pro, shannon-2-lite, shannon-2-pro i shannon-coder-1 reben aquest subconjunt, als tres endpoints:
| Tractament | Paraules clau |
|---|---|
| Es llegeix tal com s'envia | Totes les paraules clau que no es nomenen a les files de sota, per exemple type, properties, required, items, enum, description, minimum, maximum, anyOf i allOf, a tots els nivells de l'esquema. |
format | Es llegeix per als valors int32, int64, float, double, byte, date, date-time i enum. |
oneOf | Es llegeix com a anyOf. |
| No forma part de l'esquema que reben aquests models | $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 i exclusiveMaximum quan són nombres; altres valors de format. |
Comportament i límits
- L'API no fixa cap màxim d'eines ni cap màxim de rondes.
- El cos d'una sol·licitud pot ser de fins a 32 MiB, i la conversa amb els seus resultats ha de cabre a la finestra de context del model.
- Quan una sol·licitud porta
tools,shannon-1.6-lite,shannon-1.6-pro,shannon-2-liteishannon-2-profan servir unatemperatureper defecte de 0.2. Enviatemperatureper fixar-ne un altre valor. - Cada ronda és una sol·licitud pròpia: descompta del teu saldo i compta per a la protecció contra inundació com qualsevol altra sol·licitud. Límits i saldo
Errors
Una crida d'eina és una resposta normal amb estat 200. Aquests errors afecten la sol·licitud en si:
| Estat | Tipus | Missatge | Quan |
|---|---|---|---|
400 | invalid_request_error | unknown model: <id> | model no és un id de la llista publicada. |
422 | invalid_request_error | Failed to deserialize the JSON body into the target type: … | tools no és una matriu, o un altre camp té un tipus JSON incorrecte. |
413 | invalid_request_error | Failed to buffer the request body: … | El cos de la sol·licitud és més gran que 32 MiB. L'error porta code: "request_too_large". |
El model escriu els arguments. Comprova'ls amb el teu esquema abans d'executar la funció, com faries amb qualsevol entrada externa.
Els errors d'autenticació, de saldo i del servidor són els mateixos que a qualsevol sol·licitud. Gestió d’errors