Notebooks
T
Together
Function Calling Finetuning

🛠️ Function Calling Fine-tuning Guide

Open In Colab

Function calling fine-tuning teaches models to reliably invoke tools and structured functions in response to user queries. This is essential for building agents that can interact with APIs, query databases, and perform actions in the real world.

This notebook provides a step-by-step guide to fine-tuning a model for function calling using the Together AI platform. We'll use a subset of the glaive-function-calling-v2 dataset to train a model that can accurately select and call the right tools with correct arguments.

We will cover:

  1. Dataset Exploration: Loading and understanding the function calling dataset.
  2. Data Transformation: Converting the dataset to Together AI's function calling fine-tuning format.
  3. Data Upload: Validating and uploading the prepared dataset to Together AI.
  4. Fine-tuning Job Launch: Configuring and starting a LoRA fine-tuning job.
  5. Job Monitoring: Checking the status and progress of your fine-tuning job.
  6. Inference: Comparing the base model vs. fine-tuned model on function calling tasks.

By following this guide, you'll learn how to create models that reliably call the right tools with correct arguments using Together AI.

Setup and Installation


First, install the necessary Python libraries:

  • together: The official Together AI Python client for interacting with the API.
  • datasets: A library from Hugging Face for easily downloading and manipulating datasets.
  • tqdm: For progress bars.
[1]
[2]

1. Dataset Exploration


We'll use the glaive-function-calling-v2-formatted dataset, a curated collection of function calling conversations. The dataset contains:

  • 112,000 training examples of conversations that include function calling
  • 1,000 test examples

Each example includes a conversation with a system prompt, user query, and assistant responses that invoke functions, along with the available tool definitions.

[3]
README.md:   0%|          | 0.00/662 [00:00<?, ?B/s]
data/train-00000-of-00002.parquet:   0%|          | 0.00/99.6M [00:00<?, ?B/s]
data/train-00001-of-00002.parquet:   0%|          | 0.00/99.3M [00:00<?, ?B/s]
data/test-00000-of-00001.parquet:   0%|          | 0.00/1.66M [00:00<?, ?B/s]
Generating train split:   0%|          | 0/111944 [00:00<?, ? examples/s]
Generating test split:   0%|          | 0/1000 [00:00<?, ? examples/s]
DatasetDict({
    train: Dataset({
        features: ['text', 'messages', 'tools'],
        num_rows: 111944
    })
    test: Dataset({
        features: ['text', 'messages', 'tools'],
        num_rows: 1000
    })
})
[10]
Keys in each sample: ['text', 'messages', 'tools']

Number of messages: 5
Number of tools: 1
[11]
[system]: You are a helpful assistant with access to the following functions. Use them if required -
[
  {
    "type": "function",
    "function": {
      "name": "send_email",
      "description": "Send an ema...

[user]: I need to send an email to my boss. Can you help me with that?

[assistant]: Of course, I can help you with that. Could you please provide me with the recipient's email address, the subject of the email, and the message you want to send?

[user]: Sure, the recipient's email is boss@company.com. The subject is "Project Update" and the message is "Dear Boss, I have completed the project as per the given deadline. I have attached the final report for your review. Regards, [User's Name]".

[assistant]: [
  {
    "name": "send_email",
    "arguments": {
      "recipient": "boss@company.com",
      "subject": "Project Update",
      "message": "Dear Boss, I have completed the project as per the given deadline. I have attached the final report for your review. Regards, [User's Name]"
    }
  }
]

[12]
Tool definitions:
[
  {
    "type": "function",
    "function": {
      "name": "send_email",
      "description": "Send an email to a recipient",
      "parameters": {
        "type": "object",
        "properties": {
          "recipient": {
            "type": "string",
            "description": "The email address of the recipient"
          },
          "subject": {
            "type": "string",
            "description": "The subject of the email"
          },
          "message": {
            "type": "string",
            "description": "The body of the email message"
          }
        },
        "required": [
          "recipient",
          "subject",
          "message"
        ]
      }
    }
  }
]

Notice the current format of this dataset:

  • The system message contains the tool definitions embedded in the text content
  • The assistant messages contain function calls as JSON strings in the content field
  • The tools are stored as a separate column with proper OpenAI-style schemas

For Together AI's function calling fine-tuning format, we need to restructure this so that:

  • Assistant messages use the tool_calls field (not content) for function invocations
  • Tool results use the tool role
  • The tools field is at the top level of each example alongside messages

2. Data Transformation to Function Calling Format


Together AI's function calling fine-tuning requires a specific format where tool invocations use the tool_calls field in assistant messages.

Required Format

{
  "messages": [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "What's the weather in SF?"},
    {"role": "assistant", "tool_calls": [
      {
        "id": "call_abc123",
        "type": "function",
        "function": {
          "name": "getCurrentWeather",
          "arguments": "{\"location\": \"San Francisco, CA\"}"
        }
      }
    ]},
    {"role": "tool", "content": "{\"temperature\": \"65\", \"unit\": \"fahrenheit\"}"}
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "getCurrentWeather",
        "description": "Get the current weather in a given location",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {"type": "string", "description": "The city and state"}
          },
          "required": ["location"]
        }
      }
    }
  ]
}

Key Requirements:

  • Assistant messages with function calls use tool_calls (not content)
  • Each tool call needs an id, type: "function", and function with name and arguments (as a JSON string)
  • Tool responses use the tool role with the result in content

We'll also subsample 200 training and 50 validation examples since this is a demo. For production use cases, you'd use significantly more data.

[13]
[15]
[16]
Converted format:
{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant with access to tools. Use them when appropriate to answer user questions."
    },
    {
      "role": "user",
      "content": "I need to send an email to my boss. Can you help me with that?"
    },
    {
      "role": "assistant",
      "content": "Of course, I can help you with that. Could you please provide me with the recipient's email address, the subject of the email, and the message you want to send?"
    },
    {
      "role": "user",
      "content": "Sure, the recipient's email is boss@company.com. The subject is \"Project Update\" and the message is \"Dear Boss, I have completed the project as per the given deadline. I have attached the final report for your review. Regards, [User's Name]\"."
    },
    {
      "role": "assistant",
      "tool_calls": [
        {
          "id": "call_09c5c9e4",
          "type": "function",
          "function": {
            "name": "send_email",
            "arguments": "{\"recipient\": \"boss@company.com\", \"subject\": \"Project Update\", \"message\": \"Dear Boss, I have completed the project as per the given deadline. I have attached the final report for your review. Regards, [User's Name]\"}"
          }
        }
      ]
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "send_email",
        "description": "Send an email to a recipient",
        "parameters": {
          "type": "object",
          "properties": {
            "recipient": {
              "type": "string",
              "description": "The email address of the recipient"
            },
            "subject": {
              "type": "string",
              "description": "The subject of the email"
            },
            "message": {
              "type": "string",
              "description": "The body of the email message"
            }
          },
          "required": [
            "recipient",
            "subject",
            "message"
          ]
        }
      }
    }
  ]
}
[17]
Converting training examples...
  0%|          | 0/111944 [00:00<?, ?it/s]
Converted 200 training examples

Converting validation examples...
  0%|          | 0/1000 [00:00<?, ?it/s]
Converted 50 validation examples
[18]
Saved 200 examples to fc_train.jsonl
Saved 50 examples to fc_val.jsonl

3. Upload Data to Together AI


Now we'll validate and upload our prepared datasets to Together AI. The check=True parameter validates the file format before uploading.

[20]
Uploading training file...
Validating file: 200 lines [00:00, 69713.35 lines/s]
Uploading file fc_train.jsonl: 100%|██████████| 248k/248k [00:00<00:00, 814kB/s]
Training file ID: file-f253dccc-9d68-4d01-a9e3-efb02de74206
[21]
Uploading validation file...
Validating file: 50 lines [00:00, 63859.68 lines/s]
Uploading file fc_val.jsonl: 100%|██████████| 58.6k/58.6k [00:00<00:00, 281kB/s]
Validation file ID: file-655272d8-0ee4-4ece-9a3f-25a4776baf41

4. Launch Fine-tuning Job


Now we'll launch a LoRA fine-tuning job for function calling.

Key Parameters

ParameterDescriptionOur Value
modelBase model to fine-tuneQwen/Qwen3-8B
loraUse LoRA (recommended)True
n_epochsTraining epochs3
learning_rateWeight update rate1e-5
n_checkpointsCheckpoints to save1
suffixCustom model name suffixfc-demo

Note: These hyperparameters are tuned for this small demo dataset. For production, you'd use more data and may want to adjust epochs and learning rate accordingly.

🔗 See the Function Calling Fine-tuning documentation for the full list of supported models and parameters.

[22]
message="train_on_inputs is not set for SFT training, it will be set to 'auto'"
message="train_on_inputs is not set for SFT training, it will be set to 'auto'"
Fine-tuning job created!
Job ID: ft-05f3fbe3-bd07

5. Monitor Fine-tuning Job


You can monitor your fine-tuning job's progress using the Together AI API or the dashboard.

Your job will progress through several states: Pending → Queued → Running → Uploading → Completed.

Available methods:

  • client.fine_tuning.retrieve(id) — Get job status
  • client.fine_tuning.list_events(id=job_id) — Get job logs
  • client.fine_tuning.cancel(id=job_id) — Cancel a job
  • client.fine_tuning.list() — List all jobs
[23]
Job Status: pending
[24]
Fine-tuning request created
[26]
Status: completed

Final status: completed

🔗 You can also monitor your job on the Together AI Fine-tuning Dashboard and view WandB logs if you provided an API key.

6. Inference — Base Model vs. Fine-tuned Model


Once training is complete, let's compare the base model against our fine-tuned model on function calling tasks. We'll define a set of test tools and queries to see how each model performs.

[31]
Fine-tuned model: zainhas/Qwen3-8B-fc-demo-792ac632
[32]
[33]

Below we compare the fine-tuned model against the base model on our test queries. We expect the fine-tuned model to more reliably select the correct tool and provide well-formed arguments.

[ ]

Summary


In this notebook, we demonstrated how to:

  1. Load and explore a function calling dataset from Hugging Face
  2. Transform data into Together AI's function calling format with tool_calls, tool roles, and top-level tools
  3. Upload and validate the dataset using the Together AI Python client
  4. Launch a LoRA fine-tuning job on Qwen3-8B for function calling
  5. Monitor training progress via the API and dashboard
  6. Compare inference between the base model and fine-tuned model

Key Takeaways

  • Function calling fine-tuning requires assistant messages to use the tool_calls field with id, type, and function (name + arguments as JSON string)
  • LoRA fine-tuning is recommended for function calling — it's faster, cheaper, and enables serverless inference
  • Even a small subset of training data (200 examples) can improve a model's tool selection and argument formatting
  • The tools field at the top level of each example defines the available functions for that conversation

Next Steps

  • Scale up to more training data for improved reliability
  • Try DPO preference tuning to teach the model when not to call tools
  • Experiment with different base models (Qwen3-8B, Qwen2.5-72B-Instruct, etc.)
  • Deploy your fine-tuned model on a dedicated endpoint for production use

🔗 For more details, see the Function Calling Fine-tuning documentation.