Tool calling
Senast uppdaterad:
På denna sida
staik stödjer OpenAI-kompatibel tool calling. Skicka tools och tool_choice
i din request — modellen svarar med tool_calls i exakt samma format som
OpenAI. Alla chat-modeller stödjer tool calling; gemma4:31b och
qwen3.6:35b-a3b har bäst tool-following.
Definiera ett verktyg
curl https://api.staik.se/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-st-your-key" \
-d '{
"model": "gemma4:31b",
"messages": [
{"role": "user", "content": "Vad är vädret i Stockholm?"}
],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Hämta aktuellt väder för en stad",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}],
"tool_choice": "auto"
}'Modellen svarar med tool_calls i stället för text när den vill kalla ett verktyg:
{
"choices": [{
"message": {
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"Stockholm\", \"unit\": \"celsius\"}"
}
}]
},
"finish_reason": "tool_calls"
}]
}Komplett tool-loop
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.staik.se/v1", api_key="sk-st-your-key")
def get_weather(city: str, unit: str = "celsius") -> dict:
# Anropa din riktiga väder-API här
return {"city": city, "temp": 4, "unit": unit, "conditions": "molnigt"}
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Hämta aktuellt väder för en stad",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["city"],
},
},
}]
messages = [{"role": "user", "content": "Vad är vädret i Stockholm?"}]
# 1. Modellen bestämmer att den vill kalla verktyget
response = client.chat.completions.create(
model="gemma4:31b", messages=messages, tools=tools,
)
msg = response.choices[0].message
messages.append(msg)
# 2. Kör varje tool call och skicka tillbaka resultatet
for tc in msg.tool_calls or []:
args = json.loads(tc.function.arguments)
result = get_weather(**args)
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps(result),
})
# 3. Modellen formulerar slutsvaret baserat på tool-resultatet
final = client.chat.completions.create(model="gemma4:31b", messages=messages)
print(final.choices[0].message.content)Sätt tool_choice: "required" för att tvinga fram ett verktygsanrop, eller
{"type": "function", "function": {"name": "..."}} för ett specifikt verktyg.
Streaming stöds — tool_calls kommer i delta-fältet med samma index-format
som OpenAI.
Web search (inbyggt verktyg)
Slipp bygga in egna sök-beroenden i varje agent. Skicka "web_search": true
(eller lägg {"type": "web_search"} i tools) så kör staik sökningen åt
modellen och loopar tillbaka resultatet automatiskt:
- Modellen får tillgång till verktyget
web_search(query, max_results) - Behöver den aktuell information kallar den verktyget
- staik kör sökningen och matar tillbaka träffarna
- Modellen svarar — eller söker igen (upp till en intern gräns)
Tokens för alla rundor summeras i usage, precis som ett vanligt anrop. Dina
egna verktyg i samma request exekveras inte server-side — bara web_search
körs av staik.
curl https://api.staik.se/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-st-your-key" \
-d '{
"model": "qwen3.6:35b-a3b",
"messages": [
{"role": "user", "content": "Vad hände senast inom svensk AI-politik?"}
],
"web_search": true
}'Klienter som inte tillåter extra body-fält kan sätta headern
X-Staik-Web-Search: 1 i stället — den motsvarar "web_search": true.
Vill du bara ha råa sökträffar (utan modell), t.ex. för en egen RAG-pipeline:
curl https://api.staik.se/v1/tools/web-search \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-st-your-key" \
-d '{"query": "svensk LLM API", "max_results": 5}'
# => {"results": [{"title": "...", "url": "...", "content": "..."}, ...]}Integritet
Sökningen går via en self-hostad sökmotor på staiks egen hårdvara — ingen tredjeparts-sök-API. Sökfrågan lämnar aldrig infrastrukturen.
Strukturerad JSON-output
Behöver du garanterat parse-bar JSON? Skicka response_format så slås
grammatik-tvingad avkodning (guided decoding) på server-side — modellen kan
inte lägga in markdown-staket eller kommentarer.
{"type": "json_object"} garanterar giltig JSON men inte vilka fält. Vill du
tvinga en exakt struktur, använd json_schema:
curl https://api.staik.se/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-st-your-key" \
-d '{
"model": "gemma4:31b",
"messages": [
{"role": "user", "content": "Extrahera namn och ålder: Anna är 30."}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "person",
"schema": {
"type": "object",
"properties": {"name": {"type": "string"}, "age": {"type": "integer"}},
"required": ["name", "age"]
}
}
}
}'Bra att veta om gemma4:31b
Vid strikt structured output kan gemma4:31b i sällsynta fall fastna i en
whitespace-loop efter giltig JSON tills max_tokens är slut
(finish_reason: "length"). Skicka presence_penalty mellan 0.3 och 0.5 så
bryts loopen. Behöver du maximal robusthet är qwen3.6:35b-a3b det mer
förutsägbara valet. Och notera: temperature: 0 är greedy per anrop men inte
bitvis reproducerbart mellan anrop (continuous batching, MoE-routing,
prefix-caching).