
Ett modellsvar med ett funktionsnamn är ett förslag. I det här exemplet bestämmer ditt Python-program om förslaget får leda till ett uppslag. Du kan följa samma påhittade fråga genom fyra steg: modellförslag, validering, lokalt resultat och modellens formulering av resultatet. Ingen verklig inventering ligger bakom siffrorna.
Vem gör vad i loopen?
Du skickar verktygets deklaration tillsammans med frågan. Ollamas dokumentation visar hur ett assistant-meddelande med tool_calls följs av programmets funktionskörning och ett meddelande med rollen tool. Därefter får modellen fortsätta samtalet. Se Ollamas exempel på verktygsanrop.
I vår konstruktion finns tre skilda ansvar. Modellen föreslår ett namn och argument. Kontrollkoden accepterar eller avvisar förslaget. Uppslagsfunktionen läser ett värde ur en ordbok i minnet. Ett snyggt formulerat svar räcker alltså inte som bevis för att uppslaget skedde: beviset är den separata raden med det validerade resultatet.
Har du inte använt endpointen tidigare ger guiden till Ollamas API transportgrunderna. Här använder vi chat-endpointens model, messages och valfria tools, med stream: false så att varje svar hanteras samlat. Fälten beskrivs i API-referensen för chat.
Välj en bestämd lokal modellvariant
Koden anger qwen3:8b. Den exakta variantsidan hos Ollama är vid kontrollen den 15 oktober 2026 märkt tools och listar kvantiseringen Q4_K_M. Använd en redan hämtad lokal variant och kontrollera namnet med ollama list innan du kör. Spara modellens lokala id och serverversion tillsammans med dina utfall.
Märkningen är ett underlag för modellvalet. Den säger inte hur ofta just din modell väljer rätt skåp eller lämnar ett begripligt svenskt slutsvar. Byter du variant behöver du göra om provet. Om servern avvisar verktygsfältet eller modellen saknar stöd har du inte fått ett resultat om träffsäkerheten; du har hittat ett hinder i kombinationen av modell och runtime.
Ett uppslag i två påhittade skåp
Skåp A1 innehåller 7 block och B2 innehåller 0. Det är exempeldata som vi själva har valt. Verktyget heter hamta_block och tar exakt ett fält, skap. Lägg märke till att själva uppslaget saknar filåtkomst, nätverk och kommandokörning. Koden som gör HTTP-anropen hör till samtalet med den lokala Ollama-servern och ligger utanför uppslagsfunktionen.
Kör exemplet med Python och en startad lokal Ollama-server. Det använder standardbiblioteket. Spara det som ett eget skript; ändra frågan längst ned när du vill pröva B2 eller en vanlig språkfråga. Timeouten är satt till 120 sekunder per begäran och tokenbudgeten till 2 048. De är valda stoppgränser i exemplet, inga löften om att din dator eller modell hinner svara.
import json
import urllib.request
MODEL = "qwen3:8b"
DATA = {"A1": 7, "B2": 0} # helt påhittade skåp
TOOL = {"type": "function", "function": {
"name": "hamta_block",
"description": "Hämta antal block i ett påhittat skåp.",
"parameters": {"type": "object",
"properties": {"skap": {"type": "string", "enum": ["A1", "B2"]}},
"required": ["skap"], "additionalProperties": False}}}
def uppslag(call):
if not isinstance(call, dict):
raise ValueError("Fel anropsform")
fn = call.get("function")
if not isinstance(fn, dict) or fn.get("name") != "hamta_block":
raise ValueError("Okänt verktyg")
args = fn.get("arguments")
if not isinstance(args, dict) or set(args) != {"skap"}:
raise ValueError("Fel argumentfält")
skap = args["skap"]
if not isinstance(skap, str) or skap not in DATA:
raise ValueError("Ogiltig skåpkod")
return {"skap": skap, "antal_block": DATA[skap]}
def chat(messages, tools=None):
body = {"model": MODEL, "messages": messages, "stream": False,
"options": {"temperature": 0, "num_predict": 2048}}
if tools is not None:
body["tools"] = tools
req = urllib.request.Request(
"http://localhost:11434/api/chat",
json.dumps(body).encode("utf-8"),
{"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=120) as response:
data = json.load(response)
if not data.get("done") or data.get("done_reason") != "stop":
raise ValueError("Ofullständigt modellsvar: " + str(data.get("done_reason")))
msg = data.get("message")
if not isinstance(msg, dict) or msg.get("role") != "assistant":
raise ValueError("Fel meddelandeform")
return msg
def kor(fraga):
messages = [
{"role": "system", "content":
"Använd hamta_block när frågan gäller antal block i ett skåp. "
"Gissa inte antal. För andra frågor: svara utan verktyg."},
{"role": "user", "content": fraga}]
first = chat(messages, [TOOL])
print("MODELLFÖRSLAG", json.dumps(first, ensure_ascii=False))
calls = first.get("tool_calls", [])
if calls == []:
print("TEXTSVAR UTAN UPPSLAG", first.get("content", ""))
return
if not isinstance(calls, list) or len(calls) != 1:
raise ValueError("Kräver exakt ett anrop")
result = uppslag(calls[0])
print("VALIDERAT UPPSLAG", result)
messages.append(first)
messages.append({"role": "tool", "tool_name": "hamta_block",
"content": json.dumps(result, ensure_ascii=False)})
final = chat(messages) # inga verktyg erbjuds i sista steget
if final.get("tool_calls"):
raise ValueError("Nytt anrop efter gränsen")
print("SLUTSVAR", final.get("content", ""))
if __name__ == "__main__":
try:
kor("Hur många block finns i skåp A1?")
except (ValueError, OSError) as error:
print("AVVISAT ELLER AVBRUTET", error)
Vi skriver ut hela modellförslaget före kontrollen. Vid ett godkänt anrop kommer sedan VALIDERAT UPPSLAG med skåpkoden och antalet. Assistant-meddelandet bevaras i historiken och resultatet läggs till som ett separat tool-meddelande. Det gör att du kan skilja modellens föreslagna argument från de data som programmet faktiskt återförde.
Avvisa namn och argument före uppslaget
Schemafältet beskriver det avsedda gränssnittet. Den egna kontrollen är det som avgör vad programmet accepterar. Ett förslag med namnet radera_skap avvisas. Detsamma gäller {"skap": 1}, en okänd kod som C3 och ett extra fält som {"skap": "A1", "fil": "lager.txt"}. Inget av dessa fall ska nå ordboksuppslaget.
Det är frestande att bara ta första posten i listan, men då döljer du vad som hände med resten. Dokumentationen visar även flera anrop i samma svar. Vårt program godtar därför exakt ett anrop och avvisar hela förslaget om antalet avviker. Avvisningen avslutar försöket; programmet ber inte modellen reparera argumenten och lägger inte till nya möjligheter på vägen.
Du kan kontrollera skyddet separat genom att anropa uppslag med handskrivna ordböcker. Ett sådant prov visar hur din validering beter sig. Det visar inte att modellen producerar samma fel, eller att den klarar uppgiften när den får välja själv.
Mät verktygsval och svar var för sig
Använd tre frågor med samma systeminstruktion och verktygsdeklaration. Fråga först efter antalet i A1, sedan efter antalet i B2 och slutligen ”Skriv en kort hälsning på svenska”. För de två första är facit ett anrop till hamta_block med respektive skåpkod. För hälsningen är facit ett vanligt textsvar utan uppslag. Detta är förväntningar för provet, inte observerade modellsvar.
Räkna rätt verktygsval separat från godkända argument och korrekt slutsvar. Ett giltigt A1-anrop på B2-frågan är ett felval trots att valideringen släpper igenom det. Ett direkt svar med rätt siffra utan uppslag är också ett misslyckande för uppslagsfrågan: modellen följde inte den beställda metoden. En hälsning utan verktyg är däremot avsedd framgång.
Anteckna även avvisade anrop och avbrutna körningar. Ett timeoutfel eller en annan avslutsorsak än stop får en egen rad i protokollet och ska inte räknas som ett korrekt textsvar. Skriv exempelvis ”rätt verktygsval 2 av 3 frågor” först när du verkligen har kört frågorna och har loggar som stöder täljaren. Läs gärna provet för svenska lokala modeller för ett bredare språkfacit.
Håll loopen kort och slutsvaret granskningsbart
Vår gräns är två modellbegäranden och högst ett uppslag. När resultatet återförs erbjuds inga verktyg, och ett nytt anropsförslag leder till stopp. Slutsvaret jämför du med den utskrivna ordboken: A1 ska återges som 7 block, B2 som 0. Ett godkänt uppslag gör inte modellens efterföljande formulering korrekt av sig självt.
Behöver du ett schema för det färdiga svaret är det nästa fråga. Artikeln om strukturerad utdata behandlar svarens form. Börja här med att få ansvarskedjan synlig: vad föreslogs, vad accepterades och vilket resultat kom tillbaka? Behåll den kedjan även om du senare bygger ut funktionen.
Källor och metod
- Ollama: Tool calling – deklaration, anropsförslag och återfört verktygsresultat.
- Ollama: Chat API – fält och svar.
- Ollama: Qwen3 8B – den angivna modellvarianten.
Källorna ovan kontrollerades 15 oktober 2026: Ollamas verktygsdokumentation stöder meddelandeflödet, chat-referensen stöder API-fälten och variantsidan stöder det angivna modellvalet. Skåpen, valideringsreglerna och stoppgränserna är artikelns eget undervisningsexempel. Inga Ollama-anrop eller modellmätningar har utförts för artikeln. Förväntat facit ska hållas skilt från dina kommande körloggar.