Skip to content

Latest commit

 

History

History
1461 lines (1210 loc) · 50.1 KB

File metadata and controls

1461 lines (1210 loc) · 50.1 KB

Section02 : Funktionskald i Small Language Models (SLMs)

Indholdsfortegnelse

  1. Hvad er funktionskald?
  2. Hvordan funktionskald fungerer
  3. Anvendelsesscenarier
  4. Opsætning af funktionskald med Phi-4-mini og Ollama
  5. Arbejdet med Qwen3 funktionskald
  6. Foundry Local-integration
  7. Bedste praksis og fejlfinding
  8. Avancerede eksempler

Hvad er funktionskald?

Funktionskald er en kraftfuld funktionalitet, der gør det muligt for Small Language Models (SLMs) at interagere med eksterne værktøjer, API'er og tjenester. I stedet for kun at være begrænset til deres træningsdata, kan SLM'er nu:

  • Forbinde til eksterne API'er (vejrtjenester, databaser, søgemaskiner)
  • Udføre specifikke funktioner baseret på brugerforespørgsler
  • Hente realtidsinformation fra forskellige kilder
  • Udføre beregningsopgaver via specialiserede værktøjer
  • Kæde flere operationer sammen for komplekse arbejdsgange

Denne funktionalitet forvandler SLM'er fra statiske tekstgeneratorer til dynamiske AI-agenter, der kan udføre opgaver i den virkelige verden.

Hvordan funktionskald fungerer

Processen med funktionskald følger en systematisk arbejdsgang:

1. Værktøjsintegration

  • Eksterne værktøjer: SLM'er kan forbinde til vejrtjenester, databaser, webtjenester og andre eksterne systemer
  • Funktionsdefinitioner: Hvert værktøj defineres med specifikke parametre, input/output-formater og beskrivelser
  • API-kompatibilitet: Værktøjer integreres via standardiserede grænseflader (REST API'er, SDK'er osv.)

2. Funktionsdefinition

Funktioner defineres med tre nøglekomponenter:

{
  "name": "function_name",
  "description": "Clear description of what the function does",
  "parameters": {
    "parameter_name": {
      "description": "What this parameter represents",
      "type": "data_type",
      "default": "default_value"
    }
  }
}

3. Intentionsgenkendelse

  • Naturlig sprogbehandling: SLM'en analyserer brugerinput for at forstå intentionen
  • Funktionsmatching: Bestemmer, hvilke funktion(er) der er nødvendige for at opfylde forespørgslen
  • Parameterudtræk: Identificerer og udtrækker nødvendige parametre fra brugerens besked

4. Generering af JSON-output

SLM'en genererer struktureret JSON, der indeholder:

  • Funktionsnavn, der skal kaldes
  • Nødvendige parametre med passende værdier
  • Eksekveringskontekst og metadata

5. Ekstern eksekvering

  • Parametervalidering: Sikrer, at alle nødvendige parametre er til stede og korrekt formateret
  • Funktionsudførelse: Applikationen udfører den specificerede funktion med de angivne parametre
  • Fejlhåndtering: Håndterer fejl, tidsoverskridelser og ugyldige svar

6. Responsintegration

  • Resultatbehandling: Funktionsoutput returneres til SLM'en
  • Kontekstintegration: SLM'en inkorporerer resultaterne i sit svar
  • Brugerkommunikation: Præsenterer informationen i et naturligt, samtalebaseret format

Anvendelsesscenarier

Datahentning

Konverter naturlige sprogforespørgsler til strukturerede API-kald:

  • "Vis mine seneste ordrer" → Databaseforespørgsel med bruger-ID og datofiltre
  • "Hvordan er vejret i Tokyo?" → Vejr-API-kald med lokalitetsparameter
  • "Find e-mails fra John i sidste uge" → E-mailtjenesteforespørgsel med afsender- og datofiltre

Udførelse af operationer

Transformér brugerforespørgsler til specifikke funktionskald:

  • "Planlæg et møde i morgen kl. 14" → Kalender-API-integration
  • "Send en besked til teamet" → Kommunikationsplatform-API
  • "Lav en sikkerhedskopi af mine filer" → Filhåndteringsoperation

Beregningsopgaver

Håndter komplekse matematiske eller logiske operationer:

  • "Beregn renters rente på $10.000 ved 5% i 10 år" → Finansiel beregningsfunktion
  • "Analyser dette datasæt for tendenser" → Statistiske analyseværktøjer
  • "Optimer denne rute til levering" → Ruteoptimeringsalgoritmer

Databehandlingsarbejdsgange

Kæd flere funktionskald sammen for komplekse operationer:

  1. Hent data fra flere kilder
  2. Parse og valider informationen
  3. Transformer data til det ønskede format
  4. Gem resultater i passende systemer
  5. Generér rapporter eller visualiseringer

UI/UX-integration

Muliggør dynamiske interfaceopdateringer:

  • "Vis salgsdata på dashboardet" → Diagramgenerering og visning
  • "Opdater kortet med nye lokationer" → Geospatiale data
  • "Opdater lageroversigten" → Realtidsdatasynkronisering

Opsætning af funktionskald med Phi-4-mini og Ollama

Microsofts Phi-4-mini understøtter både enkelt- og parallelle funktionskald via Ollama. Sådan opsættes det:

Forudsætninger

  • Ollama version 0.5.13 eller højere
  • Phi-4-mini model (anbefalet: phi4-mini:3.8b-fp16)

Installationsvejledning

1. Installer og kør Phi-4-mini

# Download the model (if not already present)
ollama run phi4-mini:3.8b-fp16

# Verify the model is available
ollama list

2. Opret brugerdefineret ModelFile-skabelon

På grund af nuværende begrænsninger i Ollamas standard-skabeloner skal du oprette en brugerdefineret ModelFile med følgende skabelon:

TEMPLATE """
{{- if .Messages }}
{{- if or .System .Tools }}<|system|>
{{ if .System }}{{ .System }}
{{- end }}
In addition to plain text responses, you can chose to call one or more of the provided functions.
Use the following rule to decide when to call a function:
* if the response can be generated from your internal knowledge (e.g., as in the case of queries like "What is the capital of Poland?"), do so
* if you need external information that can be obtained by calling one or more of the provided functions, generate a function calls
If you decide to call functions:
* prefix function calls with functools marker (no closing marker required)
* all function calls should be generated in a single JSON list formatted as functools[{"name": [function name], "arguments": [function arguments as JSON]}, ...]
* follow the provided JSON schema. Do not hallucinate arguments or values. Do to blindly copy values from the provided samples
* respect the argument type formatting. E.g., if the type if number and format is float, write value 7 as 7.0
* make sure you pick the right functions that match the user intent
Available functions as JSON spec:
{{- if .Tools }}
{{ .Tools }}
{{- end }}<|end|>
{{- end }}
{{- range .Messages }}
{{- if ne .Role "system" }}<|{{ .Role }}|>
{{- if and .Content (eq .Role "tools") }}
{"result": {{ .Content }}}
{{- else if .Content }}
{{ .Content }}
{{- else if .ToolCalls }}
functools[
{{- range .ToolCalls }}{{ "{" }}"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}{{ "}" }}
{{- end }}]
{{- end }}<|end|>
{{- end }}
{{- end }}<|assistant|>
{{ else }}
{{- if .System }}<|system|>
{{ .System }}<|end|>{{ end }}{{ if .Prompt }}<|user|>
{{ .Prompt }}<|end|>{{ end }}<|assistant|>
{{ end }}{{ .Response }}{{ if .Response }}<|user|>{{ end }}
"""

3. Opret den brugerdefinerede model

# Save the template above as 'Modelfile' and run:
ollama create phi4-mini-fc:3.8b-fp16 -f ./Modelfile

Eksempel på enkelt funktionskald

import json
import requests

# Define the tool/function
tools = [
    {
        "name": "get_weather",
        "description": "Get current weather information for a location",
        "parameters": {
            "location": {
                "description": "The city or location name",
                "type": "str",
                "default": "New York"
            },
            "units": {
                "description": "Temperature units (celsius or fahrenheit)",
                "type": "str",
                "default": "celsius"
            }
        }
    }
]

# Create the message with system prompt including tools
messages = [
    {
        "role": "system",
        "content": "You are a helpful weather assistant",
        "tools": json.dumps(tools)
    },
    {
        "role": "user",
        "content": "What's the weather like in London today?"
    }
]

# Make request to Ollama API
response = requests.post(
    "http://localhost:11434/api/chat",
    json={
        "model": "phi4-mini-fc:3.8b-fp16",
        "messages": messages,
        "stream": False
    }
)

print(response.json())

Eksempel på parallelle funktionskald

import json
import requests

# Define multiple tools for parallel execution
AGENT_TOOLS = {
    "booking_flight": {
        "name": "booking_flight",
        "description": "Book a flight ticket",
        "parameters": {
            "departure": {
                "description": "Departure airport code",
                "type": "str"
            },
            "destination": {
                "description": "Destination airport code", 
                "type": "str"
            },
            "outbound_date": {
                "description": "Departure date (YYYY-MM-DD)",
                "type": "str"
            },
            "return_date": {
                "description": "Return date (YYYY-MM-DD)",
                "type": "str"
            }
        }
    },
    "booking_hotel": {
        "name": "booking_hotel",
        "description": "Book a hotel room",
        "parameters": {
            "city": {
                "description": "City name for hotel booking",
                "type": "str"
            },
            "check_in_date": {
                "description": "Check-in date (YYYY-MM-DD)",
                "type": "str"
            },
            "check_out_date": {
                "description": "Check-out date (YYYY-MM-DD)",
                "type": "str"
            }
        }
    }
}

SYSTEM_PROMPT = """
You are my travel agent with some tools available.
"""

messages = [
    {
        "role": "system",
        "content": SYSTEM_PROMPT,
        "tools": json.dumps(AGENT_TOOLS)
    },
    {
        "role": "user", 
        "content": "I need to travel from London to New York from March 21 2025 to March 27 2025. Please book both flight and hotel."
    }
]

# The model will generate parallel function calls
response = requests.post(
    "http://localhost:11434/api/chat",
    json={
        "model": "phi4-mini-fc:3.8b-fp16",
        "messages": messages,
        "stream": False
    }
)

print(response.json())

Arbejdet med Qwen3 funktionskald

Qwen3 tilbyder avancerede funktionskald med fremragende ydeevne og fleksibilitet. Sådan implementeres det:

Brug af Qwen-Agent Framework

Qwen-Agent giver en høj-niveau ramme, der forenkler implementeringen af funktionskald:

Installation

pip install -U "qwen-agent[gui,rag,code_interpreter,mcp]"

Grundopsætning

import os
from qwen_agent.agents import Assistant

# Configure the LLM
llm_cfg = {
    'model': 'Qwen3-8B',
    # Option 1: Use Alibaba Model Studio
    'model_type': 'qwen_dashscope',
    'api_key': os.getenv('DASHSCOPE_API_KEY'),
    
    # Option 2: Use local deployment
    # 'model_server': 'http://localhost:8000/v1',
    # 'api_key': 'EMPTY',
    
    # Optional configuration for thinking mode
    'generate_cfg': {
        'thought_in_content': True,  # Include reasoning in response
    }
}

# Define tools using MCP (Model Context Protocol)
tools = [
    {
        'mcpServers': {
            'time': {
                'command': 'uvx',
                'args': ['mcp-server-time', '--local-timezone=Asia/Shanghai']
            },
            'fetch': {
                'command': 'uvx', 
                'args': ['mcp-server-fetch']
            }
        }
    },
    'code_interpreter',  # Built-in code execution tool
]

# Create the assistant
bot = Assistant(llm=llm_cfg, function_list=tools)

# Example usage
messages = [
    {
        'role': 'user', 
        'content': 'What time is it now? Also, fetch the latest news from https://example.com/news'
    }
]

# Generate response with function calling
for response in bot.run(messages=messages):
    print(response)

Brugerdefineret funktionsimplementering

Du kan også definere brugerdefinerede funktioner til Qwen3:

import json
from qwen_agent.tools.base import BaseTool

class WeatherTool(BaseTool):
    description = 'Get weather information for a specific location'
    parameters = [
        {
            'name': 'location',
            'type': 'string', 
            'description': 'City or location name',
            'required': True
        },
        {
            'name': 'units',
            'type': 'string',
            'description': 'Temperature units (celsius or fahrenheit)',
            'required': False,
            'default': 'celsius'
        }
    ]
    
    def call(self, params: str, **kwargs) -> str:
        """Execute the weather lookup"""
        params_dict = json.loads(params)
        location = params_dict.get('location')
        units = params_dict.get('units', 'celsius')
        
        # Simulate weather API call
        weather_data = {
            'location': location,
            'temperature': '22°C' if units == 'celsius' else '72°F',
            'condition': 'Partly cloudy',
            'humidity': '65%'
        }
        
        return json.dumps(weather_data)

# Use the custom tool
tools = [WeatherTool()]
bot = Assistant(llm=llm_cfg, function_list=tools)

messages = [{'role': 'user', 'content': 'What\'s the weather in Tokyo?'}]
response = bot.run(messages=messages)
print(list(response)[-1])

Avancerede Qwen3-funktioner

Kontrol af tænkemodus

Qwen3 understøtter dynamisk skift mellem tænke- og ikke-tænkemodus:

# Enable thinking mode for complex reasoning
messages = [
    {
        'role': 'user',
        'content': '/think Solve this complex math problem: If a train travels 120 km in 1.5 hours, and another train travels 200 km in 2.5 hours, which train is faster and by how much?'
    }
]

# Disable thinking mode for simple queries
messages = [
    {
        'role': 'user', 
        'content': '/no_think What is the capital of France?'
    }
]

Multi-step funktionskald

Qwen3 er fremragende til at kæde flere funktionskald sammen:

# Complex workflow example
messages = [
    {
        'role': 'user',
        'content': '''
        I need to prepare for a business meeting:
        1. Check my calendar for conflicts tomorrow
        2. Get weather forecast for the meeting location (San Francisco)
        3. Find recent news about the client company (TechCorp)
        4. Calculate travel time from my office to their headquarters
        '''
    }
]

# Qwen3 will automatically determine the sequence of function calls needed

Foundry Local-integration

Microsofts Foundry Local tilbyder en OpenAI-kompatibel API til at køre modeller lokalt med forbedret privatliv og ydeevne.

Opsætning og installation

Windows

Download installationsprogrammet fra Foundry Local releases side og følg installationsvejledningen.

macOS

brew tap microsoft/foundrylocal
brew install foundrylocal

Grundlæggende brug

import openai
from foundry_local import FoundryLocalManager

# Initialize with model alias
alias = "phi-3.5-mini"  # Or any supported model
manager = FoundryLocalManager(alias)

# Create OpenAI client pointing to local endpoint
client = openai.OpenAI(
    base_url=manager.endpoint,
    api_key=manager.api_key
)

# Define functions for the model
functions = [
    {
        "name": "calculate_tax",
        "description": "Calculate tax amount based on income and rate",
        "parameters": {
            "type": "object",
            "properties": {
                "income": {
                    "type": "number",
                    "description": "Annual income amount"
                },
                "tax_rate": {
                    "type": "number", 
                    "description": "Tax rate as decimal (e.g., 0.25 for 25%)"
                }
            },
            "required": ["income", "tax_rate"]
        }
    }
]

# Make function calling request
response = client.chat.completions.create(
    model=manager.model_info.id,
    messages=[
        {
            "role": "user",
            "content": "Calculate the tax for someone earning $75,000 with a 22% tax rate"
        }
    ],
    functions=functions,
    function_call="auto"
)

print(response.choices[0].message.content)

Avancerede Foundry Local-funktioner

Modelhåndtering

# List available models
foundry model list

# Download specific model
foundry model download phi-3.5-mini

# Run model interactively
foundry model run phi-3.5-mini

# Remove model from cache
foundry model remove phi-3.5-mini

# Delete all cached models
foundry model remove "*"

Ydelsesoptimering

Foundry Local vælger automatisk den bedste modelvariant til din hardware:

  • CUDA GPU: Downloader GPU-optimerede modeller
  • Qualcomm NPU: Bruger NPU-accelererede varianter
  • Kun CPU: Vælger CPU-optimerede modeller

Bedste praksis og fejlfinding

Bedste praksis for funktionsdefinition

1. Klare og beskrivende navne

# Good
{
    "name": "get_stock_price",
    "description": "Retrieve current stock price for a given symbol"
}

# Avoid
{
    "name": "get_data", 
    "description": "Gets data"
}

2. Omfattende parameterdefinitioner

{
    "name": "send_email",
    "description": "Send an email message to specified recipients",
    "parameters": {
        "to": {
            "type": "array",
            "items": {"type": "string"},
            "description": "List of recipient email addresses",
            "required": True
        },
        "subject": {
            "type": "string",
            "description": "Email subject line",
            "required": True
        },
        "body": {
            "type": "string", 
            "description": "Email message content",
            "required": True
        },
        "priority": {
            "type": "string",
            "enum": ["low", "normal", "high"],
            "description": "Email priority level",
            "default": "normal",
            "required": False
        }
    }
}

3. Inputvalidering og fejlhåndtering

def execute_function(function_name, parameters):
    try:
        # Validate required parameters
        if function_name == "send_email":
            if not parameters.get("to") or not parameters.get("subject"):
                return {"error": "Missing required parameters: to, subject"}
            
            # Validate email format
            email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
            for email in parameters["to"]:
                if not re.match(email_pattern, email):
                    return {"error": f"Invalid email format: {email}"}
        
        # Execute function logic
        result = perform_actual_function(function_name, parameters)
        return {"success": True, "data": result}
        
    except Exception as e:
        return {"error": str(e)}

Almindelige problemer og løsninger

Problem 1: Funktion bliver ikke kaldt

Symptomer: Modellen svarer med tekst i stedet for at kalde funktionen

Løsninger:

  1. Tjek funktionsbeskrivelsen: Sørg for, at den klart matcher brugerens intention
  2. Bekræft parameterdefinitioner: Sørg for, at alle nødvendige parametre er korrekt defineret
  3. Gennemgå systemprompten: Inkluder klare instruktioner om, hvornår funktioner skal bruges
  4. Test med eksplicitte forespørgsler: Prøv "Brug venligst vejrfunktionen til at hente data for London"

Problem 2: Forkerte parametre

Symptomer: Funktion kaldt med forkerte eller manglende parametre

Løsninger:

  1. Tilføj parameter-eksempler: Inkluder prøveværdier i parameterbeskrivelser
  2. Brug enum-begrænsninger: Begræns parameterværdier til specifikke muligheder, hvor det er muligt
  3. Implementer fallback-værdier: Giv fornuftige standardværdier for valgfrie parametre
{
    "name": "book_restaurant",
    "parameters": {
        "cuisine": {
            "type": "string",
            "enum": ["italian", "chinese", "mexican", "american", "french"],
            "description": "Type of cuisine (example: 'italian' for Italian food)"
        },
        "party_size": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "description": "Number of people (example: 4 for a family of four)"
        }
    }
}

Problem 3: Fejl i parallelle funktionskald

Symptomer: Kun én funktion udføres, når flere burde køre

Løsninger:

  1. Tjek modelsupport: Sørg for, at din model understøtter parallelle funktionskald
  2. Opdater systemprompten: Inkluder "nogle værktøjer" eller "flere værktøjer" i systembeskeden
  3. Brug passende modelversioner: Phi-4-mini:3.8b-fp16 anbefales til Ollama

Problem 4: Skabelonproblemer med Ollama

Symptomer: Funktionskald fungerer ikke med Ollamas standardopsætning

Løsninger:

  1. Brug brugerdefineret ModelFile: Anvend den korrigerede skabelon, der er angivet i denne vejledning
  2. Opdater Ollama: Sørg for, at du bruger version 0.5.13 eller højere
  3. Tjek modelkvantisering: Højere kvantiseringsniveauer (Q8_0, fp16) fungerer bedre end stærkt kvantiserede versioner

Ydelsesoptimering

1. Effektiv funktionsdesign

  • Hold funktioner fokuserede: Hver funktion bør have et enkelt, klart formål
  • Minimer eksterne afhængigheder: Reducer API-kald og netværksanmodninger, hvor det er muligt
  • Cache resultater: Gem ofte forespurgte data for at forbedre svartider

2. Batch- og asynkrone operationer

import asyncio
import aiohttp

async def batch_function_calls(function_calls):
    """Execute multiple function calls concurrently"""
    async with aiohttp.ClientSession() as session:
        tasks = []
        for call in function_calls:
            if call["name"] == "fetch_url":
                task = fetch_url_async(session, call["parameters"]["url"])
                tasks.append(task)
        
        results = await asyncio.gather(*tasks)
        return results

async def fetch_url_async(session, url):
    async with session.get(url) as response:
        return await response.text()

3. Ressourcestyring

  • Forbindelsespuljer: Genbrug database- og API-forbindelser
  • Hastighedsbegrænsning: Implementer korrekt hastighedsbegrænsning for eksterne API'er
  • Timeout-håndtering: Indstil rimelige tidsgrænser for alle eksterne kald

Avancerede eksempler

Multi-agent samarbejdssystem

import json
from typing import List, Dict
from qwen_agent.agents import Assistant

class MultiAgentSystem:
    def __init__(self):
        # Research Agent
        self.research_agent = Assistant(
            llm={'model': 'Qwen3-8B', 'model_server': 'http://localhost:8000/v1'},
            function_list=[
                {'mcpServers': {'search': {'command': 'uvx', 'args': ['mcp-server-search']}}},
                {'mcpServers': {'fetch': {'command': 'uvx', 'args': ['mcp-server-fetch']}}}
            ]
        )
        
        # Analysis Agent
        self.analysis_agent = Assistant(
            llm={'model': 'Qwen3-8B', 'model_server': 'http://localhost:8000/v1'},
            function_list=['code_interpreter']
        )
        
        # Communication Agent
        self.comm_agent = Assistant(
            llm={'model': 'Qwen3-8B', 'model_server': 'http://localhost:8000/v1'},
            function_list=[self.create_email_tool(), self.create_slack_tool()]
        )
    
    def create_email_tool(self):
        """Custom email sending tool"""
        class EmailTool:
            name = "send_email"
            description = "Send email to specified recipients"
            parameters = {
                "to": {"type": "string", "description": "Recipient email"},
                "subject": {"type": "string", "description": "Email subject"},
                "body": {"type": "string", "description": "Email content"}
            }
            
            def call(self, params):
                # Implement actual email sending logic
                return f"Email sent successfully to {params['to']}"
        
        return EmailTool()
    
    def create_slack_tool(self):
        """Custom Slack messaging tool"""  
        class SlackTool:
            name = "send_slack"
            description = "Send message to Slack channel"
            parameters = {
                "channel": {"type": "string", "description": "Slack channel"},
                "message": {"type": "string", "description": "Message content"}
            }
            
            def call(self, params):
                # Implement actual Slack API call
                return f"Message sent to {params['channel']}"
        
        return SlackTool()
    
    async def process_complex_request(self, user_request: str):
        """Process complex multi-step requests using multiple agents"""
        
        # Step 1: Research phase
        research_prompt = f"Research the following topic and gather relevant information: {user_request}"
        research_results = []
        for response in self.research_agent.run([{'role': 'user', 'content': research_prompt}]):
            research_results.append(response)
        
        # Step 2: Analysis phase
        analysis_prompt = f"Analyze the following research data and provide insights: {research_results[-1]}"
        analysis_results = []
        for response in self.analysis_agent.run([{'role': 'user', 'content': analysis_prompt}]):
            analysis_results.append(response)
        
        # Step 3: Communication phase
        comm_prompt = f"Create a summary report and send it via email: {analysis_results[-1]}"
        comm_results = []
        for response in self.comm_agent.run([{'role': 'user', 'content': comm_prompt}]):
            comm_results.append(response)
        
        return {
            'research': research_results[-1],
            'analysis': analysis_results[-1], 
            'communication': comm_results[-1]
        }

# Usage example
async def main():
    system = MultiAgentSystem()
    
    request = """
    Analyze the impact of remote work on productivity in tech companies. 
    Research recent studies, analyze the data, and send a summary to our team.
    """
    
    results = await system.process_complex_request(request)
    print("Multi-agent processing complete:", results)

# Run the example
# asyncio.run(main())

Dynamisk værktøjsvalgssystem

class DynamicToolSelector:
    def __init__(self):
        self.available_tools = {
            'weather': {
                'description': 'Get weather information',
                'domains': ['weather', 'temperature', 'forecast', 'climate'],
                'function': self.get_weather
            },
            'calculator': {
                'description': 'Perform mathematical calculations',
                'domains': ['math', 'calculate', 'compute', 'arithmetic'],
                'function': self.calculate
            },
            'web_search': {
                'description': 'Search the internet for information',
                'domains': ['search', 'find', 'lookup', 'research'],
                'function': self.web_search
            },
            'file_manager': {
                'description': 'Manage files and directories',
                'domains': ['file', 'directory', 'save', 'load', 'delete'],
                'function': self.manage_files
            }
        }
    
    def analyze_intent(self, user_input: str) -> List[str]:
        """Analyze user input to determine which tools might be needed"""
        user_words = user_input.lower().split()
        relevant_tools = []
        
        for tool_name, tool_info in self.available_tools.items():
            for domain in tool_info['domains']:
                if domain in user_words:
                    relevant_tools.append(tool_name)
                    break
        
        return relevant_tools
    
    def get_tool_definitions(self, tool_names: List[str]) -> List[Dict]:
        """Generate function definitions for selected tools"""
        definitions = []
        
        for tool_name in tool_names:
            if tool_name == 'weather':
                definitions.append({
                    'name': 'get_weather',
                    'description': 'Get current weather information',
                    'parameters': {
                        'location': {'type': 'string', 'description': 'City or location name'},
                        'units': {'type': 'string', 'enum': ['celsius', 'fahrenheit'], 'default': 'celsius'}
                    }
                })
            elif tool_name == 'calculator':
                definitions.append({
                    'name': 'calculate',
                    'description': 'Perform mathematical calculations',
                    'parameters': {
                        'expression': {'type': 'string', 'description': 'Mathematical expression to evaluate'},
                        'precision': {'type': 'integer', 'default': 2, 'description': 'Decimal places for result'}
                    }
                })
            # Add more tool definitions as needed
        
        return definitions
    
    def get_weather(self, location: str, units: str = 'celsius') -> Dict:
        """Mock weather function"""
        return {
            'location': location,
            'temperature': '22°C' if units == 'celsius' else '72°F',
            'condition': 'Sunny',
            'humidity': '60%'
        }
    
    def calculate(self, expression: str, precision: int = 2) -> Dict:
        """Safe mathematical calculation"""
        try:
            # Simple evaluation for demo - in production, use a proper math parser
            import math
            allowed_names = {
                k: v for k, v in math.__dict__.items() if not k.startswith("__")
            }
            allowed_names.update({"abs": abs, "round": round})
            
            result = eval(expression, {"__builtins__": {}}, allowed_names)
            return {
                'expression': expression,
                'result': round(float(result), precision),
                'success': True
            }
        except Exception as e:
            return {
                'expression': expression,
                'error': str(e),
                'success': False
            }
    
    def web_search(self, query: str, max_results: int = 5) -> Dict:
        """Mock web search function"""
        return {
            'query': query,
            'results': [
                {'title': f'Result {i+1} for {query}', 'url': f'https://example{i+1}.com'}
                for i in range(max_results)
            ]
        }
    
    def manage_files(self, action: str, file_path: str, content: str = None) -> Dict:
        """Mock file management function"""
        return {
            'action': action,
            'file_path': file_path,
            'success': True,
            'message': f'Successfully {action}ed file: {file_path}'
        }

# Usage example
def smart_assistant_with_dynamic_tools():
    selector = DynamicToolSelector()
    
    user_requests = [
        "What's the weather like in New York and calculate 15% tip on $50?",
        "Search for recent AI developments and save the results to a file",
        "Calculate the area of a circle with radius 10 and check weather in Tokyo"
    ]
    
    for request in user_requests:
        print(f"\nUser Request: {request}")
        
        # Analyze which tools might be needed
        relevant_tools = selector.analyze_intent(request)
        print(f"Relevant Tools: {relevant_tools}")
        
        # Get function definitions for the LLM
        tool_definitions = selector.get_tool_definitions(relevant_tools)
        print(f"Tool Definitions: {len(tool_definitions)} functions available")
        
        # In a real implementation, you would pass these to your LLM
        # The LLM would then decide which functions to call and with what parameters

### Enterprise Integration Example

```python
import asyncio
import json
from typing import Dict, List, Any
from dataclasses import dataclass
from datetime import datetime

@dataclass
class FunctionResult:
    """Standardresultatformat for alle funktionskald"""
    success: bool
    data: Any = None
    error: str = None
    execution_time: float = 0.0
    timestamp: datetime = None

class EnterpriseAIAgent:
    """Produktionsklar AI-agent med omfattende funktionskaldsfunktioner"""
    
    def __init__(self, config: Dict):
        self.config = config
        self.functions = {}
        self.audit_log = []
        self.rate_limiters = {}
        
        # Initialiser kerneforretningsfunktioner
        self._register_core_functions()
    
    def _register_core_functions(self):
        """Registrer alle tilgængelige forretningsfunktioner"""
        
        # CRM-funktioner
        self.register_function(
            name="get_customer_info",
            description="Hent kundeinformation fra CRM",
            parameters={
                "customer_id": {"type": "string", "required": True},
                "include_history": {"type": "boolean", "default": False}
            },
            handler=self._get_customer_info,
            rate_limit=100  # kald pr. minut
        )
        
        # Salgsfunktioner
        self.register_function(
            name="create_sales_opportunity",
            description="Opret en ny salgsmulighed",
            parameters={
                "customer_id": {"type": "string", "required": True},
                "product_id": {"type": "string", "required": True},
                "estimated_value": {"type": "number", "required": True},
                "expected_close_date": {"type": "string", "required": True}
            },
            handler=self._create_sales_opportunity,
            rate_limit=50
        )
        
        # Analysefunktioner
        self.register_function(
            name="generate_sales_report",
            description="Generer salgspræstationsrapport",
            parameters={
                "period": {"type": "string", "enum": ["daily", "weekly", "monthly", "quarterly"]},
                "region": {"type": "string", "required": False},
                "product_category": {"type": "string", "required": False}
            },
            handler=self._generate_sales_report,
            rate_limit=10
        )
        
        # Notifikationsfunktioner
        self.register_function(
            name="send_notification",
            description="Send notifikation til teammedlemmer",
            parameters={
                "recipients": {"type": "array", "items": {"type": "string"}},
                "message": {"type": "string", "required": True},
                "priority": {"type": "string", "enum": ["low", "medium", "high"], "default": "medium"},
                "channel": {"type": "string", "enum": ["email", "slack", "teams"], "default": "email"}
            },
            handler=self._send_notification,
            rate_limit=200
        )
    
    def register_function(self, name: str, description: str, parameters: Dict, 
                         handler: callable, rate_limit: int = 60):
        """Registrer en ny funktion med agenten"""
        self.functions[name] = {
            'description': description,
            'parameters': parameters,
            'handler': handler,
            'rate_limit': rate_limit,
            'call_count': 0,
            'last_reset': datetime.now()
        }
    
    async def execute_function(self, function_name: str, parameters: Dict) -
Could you please provide the markdown file content that needs to be translated?
"""Udfør en funktion med omfattende fejlhåndtering og logning"""
start_time = datetime.now()

try:
    # Valider, at funktionen eksisterer
    if function_name not in self.functions:
        return FunctionResult(
            success=False,
            error=f"Funktionen '{function_name}' blev ikke fundet",
            timestamp=start_time
        )
    
    # Tjek hastighedsbegrænsninger
    if not self._check_rate_limit(function_name):
        return FunctionResult(
            success=False,
            error=f"Hastighedsbegrænsning overskredet for funktionen '{function_name}'",
            timestamp=start_time
        )
    
    # Valider parametre
    validation_result = self._validate_parameters(function_name, parameters)
    if not validation_result.success:
        return validation_result
    
    # Udfør funktionen
    func_info = self.functions[function_name]
    handler = func_info['handler']
    
    if asyncio.iscoroutinefunction(handler):
        result_data = await handler(**parameters)
    else:
        result_data = handler(**parameters)
    
    execution_time = (datetime.now() - start_time).total_seconds()
    
    result = FunctionResult(
        success=True,
        data=result_data,
        execution_time=execution_time,
        timestamp=start_time
    )
    
    # Log vellykket udførelse
    self._log_function_call(function_name, parameters, result)
    
    return result
    
except Exception as e:
    execution_time = (datetime.now() - start_time).total_seconds()
    result = FunctionResult(
        success=False,
        error=str(e),
        execution_time=execution_time,
        timestamp=start_time
    )
    
    # Log mislykket udførelse
    self._log_function_call(function_name, parameters, result)
    
    return result

def _check_rate_limit(self, function_name: str) -> bool:
    """Tjek om funktionens kald er inden for hastighedsbegrænsninger"""
    func_info = self.functions[function_name]
    now = datetime.now()
    
    # Nulstil tæller, hvis et minut er gået
    if (now - func_info['last_reset']).seconds >= 60:
        func_info['call_count'] = 0
        func_info['last_reset'] = now
    
    # Tjek om grænsen er overskredet
    if func_info['call_count'] >= func_info['rate_limit']:
        return False
    
    func_info['call_count'] += 1
    return True

def _validate_parameters(self, function_name: str, parameters: Dict) -> FunctionResult:
    """Valider funktionens parametre"""
    func_params = self.functions[function_name]['parameters']
    
    # Tjek påkrævede parametre
    for param_name, param_info in func_params.items():
        if param_info.get('required', False) and param_name not in parameters:
            return FunctionResult(
                success=False,
                error=f"Mangler påkrævet parameter: {param_name}"
            )
    
    # Valider parameter typer og begrænsninger
    for param_name, value in parameters.items():
        if param_name in func_params:
            param_info = func_params[param_name]
            
            # Typevalidering
            expected_type = param_info.get('type')
            if expected_type == 'string' and not isinstance(value, str):
                return FunctionResult(
                    success=False,
                    error=f"Parameter '{param_name}' skal være en streng"
                )
            elif expected_type == 'number' and not isinstance(value, (int, float)):
                return FunctionResult(
                    success=False,
                    error=f"Parameter '{param_name}' skal være et tal"
                )
            elif expected_type == 'boolean' and not isinstance(value, bool):
                return FunctionResult(
                    success=False,
                    error=f"Parameter '{param_name}' skal være en boolsk værdi"
                )
            
            # Enum-validering
            if 'enum' in param_info and value not in param_info['enum']:
                return FunctionResult(
                    success=False,
                    error=f"Parameter '{param_name}' skal være en af følgende: {param_info['enum']}"
                )
    
    return FunctionResult(success=True)

def _log_function_call(self, function_name: str, parameters: Dict, result: FunctionResult):
    """Log funktionens kald til revisionsformål"""
    log_entry = {
        'timestamp': result.timestamp.isoformat(),
        'function_name': function_name,
        'parameters': parameters,
        'success': result.success,
        'execution_time': result.execution_time,
        'error': result.error if not result.success else None
    }
    
    self.audit_log.append(log_entry)
    
    # Valgfrit: skriv til eksternt logningssystem
    if self.config.get('enable_external_logging', False):
        self._write_to_external_log(log_entry)

def _write_to_external_log(self, log_entry: Dict):
    """Skriv logindgang til eksternt logningssystem"""
    # Implementeringen afhænger af dit logningsinfrastruktur
    # f.eks. send til ELK stack, CloudWatch osv.
    pass

# Implementeringer af forretningsfunktioner
async def _get_customer_info(self, customer_id: str, include_history: bool = False) -> Dict:
    """Hent kundeinformation fra CRM-systemet"""
    # Simuler database-/API-kald
    await asyncio.sleep(0.1)  # Simuler netværksforsinkelse
    
    customer_data = {
        'customer_id': customer_id,
        'name': 'John Doe',
        'email': 'john.doe@example.com',
        'phone': '+1-555-0123',
        'status': 'active',
        'tier': 'premium'
    }
    
    if include_history:
        customer_data['purchase_history'] = [
            {'date': '2024-01-15', 'product': 'Produkt A', 'amount': 1500},
            {'date': '2024-03-22', 'product': 'Produkt B', 'amount': 2300}
        ]
    
    return customer_data

async def _create_sales_opportunity(self, customer_id: str, product_id: str, 
                                  estimated_value: float, expected_close_date: str) -> Dict:
    """Opret en ny salgschance"""
    # Simuler CRM API-kald
    await asyncio.sleep(0.2)
    
    opportunity_id = f"OPP-{datetime.now().strftime('%Y%m%d%H%M%S')}"
    
    return {
        'opportunity_id': opportunity_id,
        'customer_id': customer_id,
        'product_id': product_id,
        'estimated_value': estimated_value,
        'expected_close_date': expected_close_date,
        'status': 'open',
        'created_date': datetime.now().isoformat()
    }

async def _generate_sales_report(self, period: str, region: str = None, 
                               product_category: str = None) -> Dict:
    """Generer omfattende salgsrapport"""
    # Simuler dataaggregering
    await asyncio.sleep(0.5)
    
    return {
        'report_id': f"RPT-{datetime.now().strftime('%Y%m%d%H%M%S')}",
        'period': period,
        'region': region,
        'product_category': product_category,
        'total_sales': 125000.00,
        'total_opportunities': 45,
        'conversion_rate': 0.67,
        'top_products': [
            {'product_id': 'PROD-001', 'sales': 45000},
            {'product_id': 'PROD-002', 'sales': 32000}
        ],
        'generated_at': datetime.now().isoformat()
    }

async def _send_notification(self, recipients: List[str], message: str, 
                           priority: str = 'medium', channel: str = 'email') -> Dict:
    """Send notifikation via angivet kanal"""
    # Simuler notifikationstjeneste-kald
    await asyncio.sleep(0.1)
    
    notification_id = f"NOTIF-{datetime.now().strftime('%Y%m%d%H%M%S')}"
    
    return {
        'notification_id': notification_id,
        'recipients': recipients,
        'channel': channel,
        'priority': priority,
        'status': 'sent',
        'sent_at': datetime.now().isoformat()
    }

def get_function_definitions(self) -> List[Dict]:
    """Hent OpenAI-kompatible funktionsdefinitioner for alle registrerede funktioner"""
    definitions = []
    
    for func_name, func_info in self.functions.items():
        definition = {
            'name': func_name,
            'description': func_info['description'],
            'parameters': {
                'type': 'object',
                'properties': {},
                'required': []
            }
        }
        
        for param_name, param_info in func_info['parameters'].items():
            definition['parameters']['properties'][param_name] = {
                'type': param_info['type'],
                'description': param_info.get('description', '')
            }
            
            if 'enum' in param_info:
                definition['parameters']['properties'][param_name]['enum'] = param_info['enum']
            
            if 'default' in param_info:
                definition['parameters']['properties'][param_name]['default'] = param_info['default']
            
            if param_info.get('required', False):
                definition['parameters']['required'].append(param_name)
        
        definitions.append(definition)
    
    return definitions

# Eksempel på brug til virksomhedsintegration
async def enterprise_demo():
    """Demonstrer virksomhedens AI-agent kapaciteter"""
    
    config = {
        'enable_external_logging': True,
        'max_concurrent_functions': 10,
        'default_timeout': 30
    }
    
    agent = EnterpriseAIAgent(config)
    
    # Eksempel 1: Behandling af kundeforespørgsler
    print("=== Behandling af kundeforespørgsler ===")
    
    # Hent kundeinformation
    result = await agent.execute_function(
        'get_customer_info',
        {'customer_id': 'CUST-12345', 'include_history': True}
    )
    
    if result.success:
        print(f"Kundeinformation hentet: {result.data['name']}")
        print(f"Udførelsestid: {result.execution_time:.3f}s")
    
    # Eksempel 2: Oprettelse af salgschance
    print("\n=== Oprettelse af salgschance ===")
    
    result = await agent.execute_function(
        'create_sales_opportunity',
        {
            'customer_id': 'CUST-12345',
            'product_id': 'PROD-001',
            'estimated_value': 15000.0,
            'expected_close_date': '2025-09-30'
        }
    )
    
    if result.success:
        print(f"Salgschance oprettet: {result.data['opportunity_id']}")
    
    # Eksempel 3: Batch-operationer
    print("\n=== Batch-operationer ===")
    
    tasks = [
        agent.execute_function('generate_sales_report', {'period': 'monthly'}),
        agent.execute_function('send_notification', {
            'recipients': ['manager@company.com'],
            'message': 'Ny salgschance oprettet',
            'priority': 'high',
            'channel': 'email'
        })
    ]
    
    results = await asyncio.gather(*tasks)
    
    for i, result in enumerate(results):
        if result.success:
            print(f"Task {i+1} fuldført med succes")
        else:
            print(f"Task {i+1} mislykkedes: {result.error}")
    
    # Vis revisionslog
    print(f"\n=== Revisionslog ({len(agent.audit_log)} poster) ===")
    for entry in agent.audit_log[-3:]:  # Vis de sidste 3 poster
        print(f"{entry['timestamp']}: {entry['function_name']} - {'SUCCESS' if entry['success'] else 'FAILED'}")

# Kør virksomhedens demo
# asyncio.run(enterprise_demo())

## Konklusion

Funktionskald i Small Language Models repræsenterer et paradigmeskift fra statiske AI-assistenter til dynamiske, kapable agenter, der kan interagere med den virkelige verden. Denne vejledning har dækket:

### Vigtige pointer

1. **Grundlæggende forståelse**: Funktionskald gør det muligt for SLM'er at  ud over deres træningsdata ved at forbinde til eksterne værktøjer og tjenester.

2. **Fleksibilitet i implementering**: Der findes flere tilgange, fra lav-niveau implementeringer med brugerdefinerede skabeloner til høj-niveau frameworks som Qwen-Agent og Foundry Local.

3. **Overvejelser for produktion**: Virksomhedsudrulninger kræver opmærksomhed  fejlhåndtering, hastighedsbegrænsning, sikkerhed og revisionslogning.

4. **Optimering af ydeevne**: Korrekt funktionsdesign, effektiv udførelse og smart caching kan markant forbedre svartider.

### Fremtidige retninger

Efterhånden som SLM-teknologien fortsætter med at udvikle sig, kan vi forvente:

- **Forbedret nøjagtighed i funktionskald**: Bedre identifikation af intentioner og parameterudtrækning
- **Avanceret parallel behandling**: Mere sofistikeret orkestrering af flere funktioner
- **Bedre integrationsstandarder**: Standardiserede protokoller for værktøjsintegration
- **Avancerede sikkerhedsfunktioner**: Forbedret autentificering og autorisationsmekanismer
- **Udvidet økosystem**: Voksende bibliotek af forudbyggede funktioner og integrationer

### Kom godt i gang

For at begynde implementeringen af funktionskald i dine projekter:

1. **Start simpelt**: Begynd med grundlæggende scenarier med enkeltfunktioner
2. **Vælg dit framework**: Vælg mellem direkte implementering (Ollama/Phi-4) eller framework-assisteret (Qwen-Agent)
3. **Design funktioner omhyggeligt**: Fokusér  klare, veldokumenterede funktionsdefinitioner
4. **Implementér fejlhåndtering**: Byg robust fejlhåndtering fra starten
5. **Skalér gradvist**:  fra simple til komplekse scenarier, efterhånden som du får erfaring

Funktionskald transformerer SLM'er fra imponerende tekstgeneratorer til praktiske AI-agenter, der kan løse virkelige problemer. Ved at følge mønstrene og praksisserne i denne vejledning kan du bygge kraftfulde, pålidelige AI-systemer, der går langt ud over traditionelle chatgrænseflader.

### Ressourcer og referencer
- **Phi-4 Modeller**: [Hugging Face Collection](https://huggingface.co/collections/microsoft/phi-4-677e9380e514feb5577a40e4)
- **Qwen3 Dokumentation**: [Officiel Qwen Dokumentation](https://qwen.readthedocs.io/)
- **Ollama**: [Officiel Hjemmeside](https://ollama.com/)
- **Foundry Local**: [GitHub Repository](https://github.com/microsoft/Foundry-Local)
- **Bedste Fremgangsmåder for Funktionskald**: [Hugging Face Guide](https://huggingface.co/docs/hugs/en/guides/function-calling)

Husk, at funktionskald er et område i konstant udvikling, og det at holde sig opdateret med de nyeste fremskridt inden for dine valgte frameworks og modeller vil hjælpe dig med at bygge mere effektive AI-agenter.


## ➡️ Hvad er næste skridt

- [03: Model Context Protocol (MCP) Integration](./03.IntroduceMCP.md)

---

**Ansvarsfraskrivelse**:  
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os  nøjagtighed, skal du være opmærksom , at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument  dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.