You need to enable JavaScript to run this app.
导航
Function Calling 使用说明
最近更新时间:2025.04.03 15:47:55首次发布时间:2024.05.20 14:03:52
我的收藏
有用
有用
无用
无用

说明

  • 本文档通过FC(分支)模型对 Function Calling 的使用进行介绍。
    • FC分支模型:指模型版本名称包含Function Calling的版本

介绍

Function Calling 是一种将大模型与外部工具和API相连的新功能,助力大模型向实际产业落地迈进。Function Calling 允许开发者更可靠的从模型中获得结构化数据,无需用户输入复杂的Prompt。

在使用时,您可以用自然语言向模型描述一组 Function 的功能和定义; 在对话过程中,当大模型觉得需要使用某函数时会智能地选择该 Function,并返回调用所需参数,来满足用户的特定需求; 其他情况,大模型不会返回 Function,而是继续对话。大模型不会直接调用 Function,而是返回其对应的入参,您可自行调用该函数/API接口。

简单来说,Function Calling 是介于自然语言和信息接口的「翻译官」

  • 把自然语言翻译成所需使用函数、参数,返回给大模型调用方
  • 大模型调用方执行函数后把结果返回给大模型,大模型能总结为自然语言,或继续规划子任务

示例: 北京天气怎么样?把结果发给alan

========== round 1 ==========
user提问:北京天气怎么样?把结果发给alan
[发起请求1]
assistant 思考:根据FunctionList,先调用WeatherPlugin插件获得天气。 
assistant 返回:WeatherPlugin, {"city":"北京"}

========== round 2 ==========
user调用WeatherPlugin函数,传入参数,得到结果:6月12日,以晴为主,16°C~30°C, 降水概率:0%;湿度:27%;风速:21 公里/时
结果作为role=tool回填到req.messages中
[发起请求2]
assistant 思考:第1步已经获得北京天气,现在需要调用SendMessage插件发送消息给alan 
assistant 返回:SendMessage, {"receiver":"alan", "content": "北京晴,微风"}

========== round 3 ==========
user调用SendMessage函数,得到结果:发送成功
结果作为role=tool回填到req.messages中
[发起请求3]
assistant 总结:已告知alan今天北京晴,微风

其中,user调用xxxxx函数 由客户自行完成, 用户只需要把函数执行结果回填到messages内。
时序图如下:

这种能力带来了潜在风险。建议调用者在执行任何操作之前,严谨地确认和校验模型的生成结果。

说明

tools 会以特定的格式输入到模型,它同样会计入模型的输入长度限制,并计费。

支持范围

拥有Function Calling能力的模型列表如下:

注意

如果在应用中使用了 联网插件知识库插件 ,通过 Bot API 方式调用以下模型时,Function Calling 能力不生效

模型代系

模型系列

模型名称

模型版本

备注

doubao-1.5
(推荐)

pro

doubao-1.5-pro-32k

250115

效果好,速度快

lite

doubao-1.5-lite-32k

250115

适用于对延迟敏感、Function calling效果要求相对宽松的场景

vision-pro

doubao-1.5-vision-pro-32K

250115

doubao

pro

doubao-pro-32k

functioncall-preview

functioncall-241028

效果好, 并且推理速度快;支持FC SFT精调

functioncall-240815

支持FC SFT精调

241215

效果好,速度一般;支持FC SFT精调

240828

支持FC SFT精调

240615

character-240528

doubao-pro-128k

240515

240628

lite

doubao-lite-32k

240828

适用于对延迟敏感、Function calling效果要求相对宽松的场景

240628

适用于对延迟敏感、Function calling效果要求相对宽松的场景

240428

适用于对延迟敏感、Function calling效果要求相对宽松的场景

doubao-lite-128k

240828

适用于对延迟敏感、Function calling效果要求相对宽松的场景

deepseek

DeepSeek-R1

250120

DeepSeek-V3

250324

环境准备

Golang

可通过Github链接直接下载:https://github.com/volcengine/volcengine-go-sdk

Python

请按照如下方式安装最新SDK

pip install 'volcengine-python-sdk[ark]'

Java

请使用版本

<dependency>
  <groupId>com.volcengine</groupId>
  <artifactId>volcengine-java-sdk-ark-runtime</artifactId>
  <version>LATEST</version>
</dependency>

推理步骤

说明

从Doubao-1.5系列开始支持流式输出,其他模型不支持。

使用Function Calling 的基本步骤如下:

  1. 在模型请求基础上,新增 tools 字段调用模型,类似OpenAI。
    1. 参数类型支持json类型, 支持enum。 支持array、object等嵌套数据格式
  2. 模型可以选择调用一个 function;这样的话,返回的内容是一个符合规格说明的 JSON 对象(注意:模型可能生成无效的 JSON 或虚构参数)。
  3. 用户将返回的参数解析为 JSON,并调用对应 function
  4. function 的响应(或报错)作为新的 message(role=tool) 告知模型,并让模型总结结果并回复给用户。

单轮Function Calling代码示例

Golang

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "os"

    "github.com/volcengine/volcengine-go-sdk/service/arkruntime"
    "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
    "github.com/volcengine/volcengine-go-sdk/volcengine"
)

func main() {
    client := arkruntime.NewClientWithApiKey(
       os.Getenv("ARK_API_KEY"),
       arkruntime.WithBaseUrl("${BASE_URL}"),
    )
    
    ctx := context.Background()

    fmt.Println("----- function call request -----")
    req := model.CreateChatCompletionRequest{
       Model: "${YOUR_ENDPOINT_ID}",
       Messages: []*model.ChatCompletionMessage{
          {
             Role: model.ChatMessageRoleUser,
             Content: &model.ChatCompletionMessageContent{
                StringValue: volcengine.String("北京今天天气如何?"),
             },
          },
       },
       Tools: []*model.Tool{
          {
             Type: model.ToolTypeFunction,
             Function: &model.FunctionDefinition{
                Name:        "get_current_weather",
                Description: "获取给定地点的天气",
                Parameters: map[string]interface{}{
                   "type": "object",
                   "properties": map[string]interface{}{
                      "location": map[string]interface{}{
                         "type":        "string",
                         "description": "地点的位置信息,比如北京",
                      },
                      "unit": map[string]interface{}{
                         "type": "string",
                         "enum": []string{"摄氏度", "华氏度"},
                      },
                   },
                   "required": []string{"location"},
                },
             },
          },
       },
    }

    resp, err := client.CreateChatCompletion(ctx, req)
    if err != nil {
       fmt.Printf("standard chat error: %v\n", err)
       return
    }

    s, _ := json.Marshal(resp)
    fmt.Println(string(s))

}

Python

from volcenginesdkarkruntime import Ark

client = Ark(
    base_url="${BASE_URL}",
)

print("----- function call request -----")
completion = client.chat.completions.create(
    model="${YOUR_ENDPOINT_ID}",
    messages = [
        {"role": "user", "content": "北京今天天气如何?"},
    ],
    tools=[
        {
            "type": "function",
            "function": {
                "name": "get_current_weather",
                "description": "获取给定地点的天气",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "地点的位置信息,比如北京"
                        },
                        "unit": {
                            "type": "string",
                            "enum": [
                                "摄氏度",
                                "华氏度"
                            ]
                        }
                    },
                    "required": [
                        "location"
                    ]
                }
            }
        }
    ]
)
print(completion.choices[0])

Java

package com.volcengine.ark.runtime;


import com.volcengine.ark.runtime.model.completion.chat.*;
import com.volcengine.ark.runtime.service.ArkService;

import java.util.*;

public class FunctionCallChatCompletionsExample {
    public static void main(String[] args) {
        String apiKey = System.getenv("ARK_API_KEY");
        ArkService service = ArkService.builder().apiKey(apiKey).baseUrl("${BASE_URL}").build();

        System.out.println("\n----- function call request -----");
        final List<ChatMessage> messages = new ArrayList<>();
        final ChatMessage userMessage = ChatMessage.builder().role(ChatMessageRole.USER).content("北京今天天气如何?").build();
        messages.add(userMessage);

        final List<ChatTool> tools = Arrays.asList(
                new ChatTool(
                        "function",
                        new ChatFunction.Builder()
                                .name("get_current_weather")
                                .description("获取给定地点的天气")
                                .parameters(new Weather(
                                        "object",
                                        new HashMap<String, Object>() {{
                                            put("location", new HashMap<String, String>() {{
                                                put("type", "string");
                                                put("description", "T地点的位置信息,比如北京");
                                            }});
                                            put("unit", new HashMap<String, Object>() {{
                                                put("type", "string");
                                                put("description", "枚举值有celsius、fahrenheit");
                                               }});
                                        }},
                                        Collections.singletonList("location")
                                ))
                                .build()
                        )
        );


        ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
                .model("${Model_ID}")
                .messages(messages)
                .tools(tools)
                .build();

        service.createChatCompletion(chatCompletionRequest).getChoices().forEach(System.out::println);

        // shutdown service
        service.shutdownExecutor();
    }

    public static class Weather {
        public String type;
        public Map<String, Object> properties;
        public List<String> required;

        public Weather(String type, Map<String, Object> properties, List<String> required) {
            this.type = type;
            this.properties = properties;
            this.required = required;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public Map<String, Object> getProperties() {
            return properties;
        }

        public void setProperties(Map<String, Object> properties) {
            this.properties = properties;
        }

        public List<String> getRequired() {
            return required;
        }

        public void setRequired(List<String> required) {
            this.required = required;
        }
    }

}

cURL示例

  • 请求示例
curl https://ark.cn-beijing.volces.com/api/v3/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer {YOUR_API_KEY}" \
  -d '{
    "model": "ep-xxxxxxxxxx",
    "messages": [
        {
            "role": "system",
            "content": "你是豆包AI助手"
        },
        {
            "role": "user",
            "content": "上海天气怎么样?"
        }
    ],   
    "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_current_weather",
        "description": "查询天气",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "需要查询的城市"
            },
            "unit": {
              "type": "string",
              "enum": ["摄氏度", "华氏度"]
            }
          },
          "required": ["location"]
        }
      }
    }
  ]
}'
  • 响应示例
{"choices":[{"finish_reason":"tool_calls","index":0,"logprobs":null,"message":{"content":"好的,正在为您查询上海天气","role":"assistant","tool_calls":[{"function":{"arguments":"{\"location\": \"上海\", \"unit\": \"celsius\"}","name":"get_current_weather"},"id":"call_2d13sqcanleeezy62as2cshm","type":"function"}]}}],"created":1734349923,"id":"021734349921275679fc877cfa440c1efdd7643d16d14ef2bd444","model":"doubao-pro-4k-functioncall-240615","object":"chat.completion","usage":{"completion_tokens":67,"prompt_tokens":106,"total_tokens":173,"prompt_tokens_details":{"cached_tokens":0}}}

多轮Function Calling代码示例

说明

多轮Function Calling:指用户query需要多次调用工具和大模型才能完成的情况, 是多轮对话的子集。

调用Response细节图:
Image

Golang

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "os"
    "strings"

    "github.com/volcengine/volcengine-go-sdk/service/arkruntime"
    "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
    "github.com/volcengine/volcengine-go-sdk/volcengine"
)

func main() {
    client := arkruntime.NewClientWithApiKey(
       os.Getenv("ARK_API_KEY"),
       arkruntime.WithBaseUrl("${BASE_URL}"),
    )

    fmt.Println("----- function call mulstiple rounds request -----")
    ctx := context.Background()
    // Step 1: send the conversation and available functions to the model
    req := model.CreateChatCompletionRequest{
       Model: "${Model_ID}",
       Messages: []*model.ChatCompletionMessage{
          {
             Role: model.ChatMessageRoleSystem,
             Content: &model.ChatCompletionMessageContent{
                StringValue: volcengine.String("你是豆包,是由字节跳动开发的 AI 人工智能助手"),
             },
          },
          {
             Role: model.ChatMessageRoleUser,
             Content: &model.ChatCompletionMessageContent{
                StringValue: volcengine.String("上海的天气怎么样?"),
             },
          },
       },
       Tools: []*model.Tool{
          {
             Type: model.ToolTypeFunction,
             Function: &model.FunctionDefinition{
                Name:        "get_current_weather",
                Description: "Get the current weather in a given location",
                Parameters: map[string]interface{}{
                   "type": "object",
                   "properties": map[string]interface{}{
                      "location": map[string]interface{}{
                         "type":        "string",
                         "description": "The city and state, e.g. Beijing",
                      },
                      "unit": map[string]interface{}{
                         "type":        "string",
                         "description": "枚举值有celsius、fahrenheit",
                      },
                   },
                   "required": []string{
                      "location",
                   },
                },
             },
          },
       },
    }
    resp, err := client.CreateChatCompletion(ctx, req)
    if err != nil {
       fmt.Printf("chat error: %v\n", err)
       return
    }
    // extend conversation with assistant's reply
    req.Messages = append(req.Messages, &resp.Choices[0].Message)
    
    // Step 2: check if the model wanted to call a function.
    // The model can choose to call one or more functions; if so,
    // the content will be a stringified JSON object adhering to
    // your custom schema (note: the model may hallucinate parameters).
    for _, toolCall := range resp.Choices[0].Message.ToolCalls {
       fmt.Println("calling function")
       fmt.Println("    id:", toolCall.ID)
       fmt.Println("    name:", toolCall.Function.Name)
       fmt.Println("    argument:", toolCall.Function.Arguments)
       functionResponse, err := CallAvailableFunctions(toolCall.Function.Name, toolCall.Function.Arguments)
       if err != nil {
          functionResponse = err.Error()
       }
       // extend conversation with function response
       req.Messages = append(req.Messages,
          &model.ChatCompletionMessage{
             Role:       model.ChatMessageRoleTool,
             ToolCallID: toolCall.ID,
             Content: &model.ChatCompletionMessageContent{
                StringValue: &functionResponse,
             },
          },
       )
    }
    // get a new response from the model where it can see the function response
    secondResp, err := client.CreateChatCompletion(ctx, req)
    if err != nil {
       fmt.Printf("second chat error: %v\n", err)
       return
    }
    fmt.Println("conversation", MustMarshal(req.Messages))
    fmt.Println("new message", MustMarshal(secondResp.Choices[0].Message))
}
func CallAvailableFunctions(name, arguments string) (string, error) {
    if name == "get_current_weather" {
       params := struct {
          Location string `json:"location"`
          Unit     string `json:"unit"`
       }{}
       if err := json.Unmarshal([]byte(arguments), &params); err != nil {
          return "", fmt.Errorf("failed to parse function call name=%s arguments=%s", name, arguments)
       }
       return GetCurrentWeather(params.Location, params.Unit), nil
    } else {
       return "", fmt.Errorf("got unavailable function name=%s arguments=%s", name, arguments)
    }
}

// GetCurrentWeather get the current weather in a given location.
// Example dummy function hard coded to return the same weather.
// In production, this could be your backend API or an external API
func GetCurrentWeather(location, unit string) string {
    if unit == "" {
       unit = "celsius"
    }
    switch strings.ToLower(location) {
    case "beijing":
       return `{"location": "Beijing", "temperature": "10", "unit": unit}`
    case "北京":
       return `{"location": "Beijing", "temperature": "10", "unit": unit}`
    case "shanghai":
       return `{"location": "Shanghai", "temperature": "23", "unit": unit})`
    case "上海":
       return `{"location": "Shanghai", "temperature": "23", "unit": unit})`
    default:
       return fmt.Sprintf(`{"location": %s, "temperature": "unknown"}`, location)
    }
}
func MustMarshal(v interface{}) string {
    b, _ := json.Marshal(v)
    return string(b)
}

Python

from volcenginesdkarkruntime import Ark
import time
client = Ark(
    base_url="${BASE_URL}",
)

print("----- function call mulstiple rounds request -----")
messages = [
    {
        "role": "system",
        "content": "你是豆包,是由字节跳动开发的 AI 人工智能助手",
    },
    {
        "role": "user",
        "content": "北京今天的天气",
    },
]
req = {
    "model": "${YOUR_ENDPOINT_ID}",
    "messages": messages,
    "temperature": 0.8,
    "tools": [
        {
            "type": "function",
            "function": {
                "name": "MusicPlayer",
                "description": """歌曲查询Plugin,当用户需要搜索某个歌手或者歌曲时使用此plugin,给定歌手,歌名等特征返回相关音乐。\n 例子1:query=想听孙燕姿的遇见, 输出{"artist":"孙燕姿","song_name":"遇见","description":""}""",
                "parameters": {
                    "properties": {
                        "artist": {"description": "表示歌手名字", "type": "string"},
                        "description": {
                            "description": "表示描述信息",
                            "type": "string",
                        },
                        "song_name": {
                            "description": "表示歌曲名字",
                            "type": "string",
                        },
                    },
                    "required": [],
                    "type": "object",
                },
            },
        },
        {
            "type": "function",
            "function": {
                "name": "get_current_weather",
                "description": "",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "地理位置,比如北京市",
                        },
                        "unit": {"type": "string", "description": "枚举值 [摄氏度,华氏度]"},
                    },
                    "required": ["location"],
                },
            },
        },
    ],
}

ts = time.time()
completion = client.chat.completions.create(**req)
if completion.choices[0].message.tool_calls:
    print(
        f"Bot [{time.time() - ts:.3f} s][Use FC]: ",
        completion.choices[0].message.tool_calls[0],
    )
    # ========== 补充函数调用的结果 =========
    req["messages"].extend(
        [
            completion.choices[0].message.dict(),
             {
                "role": "tool",
                "tool_call_id": completion.choices[0].message.tool_calls[0].id,
                "content": "北京天气晴,24~30度",  # 根据实际调用函数结果填写,最好用自然语言。
                "name": completion.choices[0].message.tool_calls[0].function.name,
            },
        ]
    )
    # 再请求一次模型,获得总结。 如不需要,也可以省略
    ts = time.time()
    completion = client.chat.completions.create(**req)
    print(
        f"Bot [{time.time() - ts:.3f} s][FC Summary]: ",
        completion.choices[0].message.content,
    )
  • 响应示例
========== Round 1 ==========
user: 先查询北京的天气,如果是晴天微信发给Alan,否则发给Peter...

assistant [FC Response]:
name=GetCurrentWeather, args={"location": "\u5317\u4eac"} 
[elpase=2.607 s]
========== Round 2 ==========
tool: 北京今天20~24度,天气:阵雨。...

assistant [FC Response]:
name=SendMessage, args={"content": "\u4eca\u5929\u5317\u4eac\u7684\u5929\u6c14", "receiver": "Peter"} 
[elpase=3.492 s]
========== Round 3 ==========
tool: 成功发送微信消息至Peter...

assistant [Final Answer]:
好的,请问还有什么可以帮助您? 
[elpase=0.659 s]

Java

package com.volcengine.ark.runtime;

import com.volcengine.ark.runtime.model.completion.chat.*;
import com.volcengine.ark.runtime.service.ArkService;
import okhttp3.ConnectionPool;
import okhttp3.Dispatcher;

import java.util.*;
import java.util.concurrent.TimeUnit;

public class FunctionCallChatCompletionsExample {
    static String apiKey = System.getenv("ARK_API_KEY");
    static ConnectionPool connectionPool = new ConnectionPool(5, 1, TimeUnit.SECONDS);
    static Dispatcher dispatcher = new Dispatcher();
    static ArkService service = ArkService.builder().dispatcher(dispatcher).connectionPool(connectionPool).baseUrl("${BASE_URL}").apiKey(apiKey).build();

    public static void main(String[] args) {
        System.out.println("\n----- function call mulstiple rounds request -----");
        final List<ChatMessage> messages = new ArrayList<>();
        final ChatMessage userMessage = ChatMessage.builder().role(ChatMessageRole.USER).content("北京今天天气如何?").build();
        messages.add(userMessage);

        final List<ChatTool> tools = Arrays.asList(
                new ChatTool(
                        "function",
                        new ChatFunction.Builder()
                                .name("get_current_weather")
                                .description("获取给定地点的天气")
                                .parameters(new Weather(
                                        "object",
                                        new HashMap<String, Object>() {{
                                            put("location", new HashMap<String, String>() {{
                                                put("type", "string");
                                                put("description", "T地点的位置信息,比如北京");
                                            }});
                                            put("unit", new HashMap<String, Object>() {{
                                                put("type", "string");
                                                put("description", "枚举值有celsius、fahrenheit");
                                            }});
                                        }},
                                        Collections.singletonList("location")
                                ))
                                .build()
                )
        );

        ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
                .model("${YOUR_ENDPOINT_ID}")
                .messages(messages)
                .tools(tools)
                .build();

        ChatCompletionChoice choice = service.createChatCompletion(chatCompletionRequest).getChoices().get(0);
        messages.add(choice.getMessage());
        choice.getMessage().getToolCalls().forEach(
                toolCall -> {
                messages.add(ChatMessage.builder().role(ChatMessageRole.TOOL).toolCallId(toolCall.getId()).content("北京天气晴,24~30度").name(toolCall.getFunction().getName()).build());
        });
        ChatCompletionRequest chatCompletionRequest2 = ChatCompletionRequest.builder()
                .model("${YOUR_ENDPOINT_ID}")
                .messages(messages)
                .build();

        service.createChatCompletion(chatCompletionRequest2).getChoices().forEach(System.out::println);

        // shutdown service
        service.shutdownExecutor();
    }

    public static class Weather {
        public String type;
        public Map<String, Object> properties;
        public List<String> required;

        public Weather(String type, Map<String, Object> properties, List<String> required) {
            this.type = type;
            this.properties = properties;
            this.required = required;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public Map<String, Object> getProperties() {
            return properties;
        }

        public void setProperties(Map<String, Object> properties) {
            this.properties = properties;
        }

        public List<String> getRequired() {
            return required;
        }

        public void setRequired(List<String> required) {
            this.required = required;
        }
    }

}

参数类型说明

说明

类型

例子

基本类型

string
integer
number (浮点数)
boolean

复杂类型(含嵌套结构)

object (对象)

  • description:简要说明
  • properties 描述该对象所有属性
  • required 描述必填属性
  • 示例1: 查询特点用户画像(根据年龄、性别、婚姻状况等)
"person": {
    "type": "object",
    "description": "个人特征",
    "items": {
        "age": {"type": "integer", "description": "年龄"},
        "gender": {"type": "string", "description": "性别"},
        "married": {"type": "boolean", "description": "是否已婚"}
    },
    "required": ["age"],
}

array (列表)

  • description:简要说明
  • "items": {"type": ITEM_TYPE}来表达数组元素的数据类型
  • 示例1:文本数组,若干个网页链接
"url": {
    "type": "array",
    "description": "需要解析网页链接,最多3个",
    "items": {"type": "string"}
  • 示例2: 二维数组
"matrix": {
    "type": "array",
    "description": "需要计算的二维矩阵",
    "items": {"type": "array", "items": {"type": "number"}},
},
  • 示例3, 给多个年级布置任务
{
    "type": "object",
    "properties": {
        "grade": {
            "description": "年级, 支持多选",
            "type": "array",
            "items": {
                "type": "string",
                "description": """枚举值有
                    "一年级",
                    "二年级",
                    "三年级",
                    "四年级",
                    "五年级",
                    "六年级"。 """,
            },
        },
        "task": {
            "description": "任务",
            "type": "string",
            "description": """枚举值有["卫生", "黑板报", "才艺", "升旗"]""",
        },
    },
    "required": ["grade", "task"],
}

精调

如果Function Calling效果达不到目标,可以考虑精调(SFT)。以下需求建议精调:

  • 提高函数准确性(是否调用函数、调用哪个函数)
  • 提高参数准确性
  • 优化模型总结函数执行结果

为了最好的效果,建议精调样本与推理请求时的Function list(参数、介绍)一致

多轮精调

样本格式参考:模型精调数据集格式说明
针对Function Calling样本,有以下新增内容:

  1. 必填tools字段,格式与推理一致
  2. 可选parallel_tool_calls 字段,布尔型,默认值为True

注意

对于Deepseek R1模型,不支持 parallel_tool_calls 字段, 默认就是自动触发、并行调用。

  1. 含义与推理API参数一致,表达该样本是否为并行tool_calls样本。
  2. 如果设为false,但当前轮次label又出现了超过1个tool_calls,则会报错
  3. messages内,如果assistant触发fc,会有tool_calls字段
    1. 每个Function内必须包含namearguments
    2. arguments 是合法的json dump字符串
  4. messages内,会有role=tool的msg, loss_weight为0.0且不可修改

一个完整的(多轮、含并行)的FC精调样例:

{
  "messages": [
    {
      "role": "system",
      "content": "你是豆包"
    },
    {
      "role": "user",
      "content": "把北京上海的天气发给Alan"
    },
    {
      "role": "assistant",
      "content": "",
      "tool_calls": [
        {
          "type": "function",
          "function": {
            "name": "GetCurrentWeather",
            "arguments": "{\"location\": \"北京\"}"
          }
        },
        {
          "type": "function",
          "function": {
            "name": "GetCurrentWeather",
            "arguments": "{\"location\": \"上海\"}"
          }
        }
      ],
      "loss_weight": 1.0
    },
    {
      "role": "tool",
      "content": "北京天气晴,18~25°,微风"
    },
    {
      "role": "tool",
      "content": "上海天气阴,20~30°,大风"
    },
    {
      "role": "assistant",
      "content": "已经查到北京上海天气,需要发送给alan",
      "tool_calls": [
        {
          "type": "function",
          "function": {
            "name": "SendMessage",
            "arguments": "{\"receiver\": \"Alan\", \"content\": \"今天北京天气晴,18~25°,微风;上海天气阴,20~30°,大风\"}"
          }
        }
      ],
      "loss_weight": 1.0
    },
    {
      "role": "tool",
      "content": "发送成功"
    },
    {
      "role": "assistant",
      "content": "已经查到北京上海今日天气,发送给了Alan",
      "loss_weight": 1.0
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "MusicPlayer",
        "description": "歌曲查询Plugin,当用户需要搜索某个歌手或者歌曲时使用此plugin,给定歌手,歌名等特征返回相关音乐",
        "parameters": {
          "properties": {
            "artist": {
              "description": "表示歌手名字",
              "type": "string"
            },
            "description": {
              "description": "表示描述信息",
              "type": "string"
            },
            "song_name": {
              "description": "表示歌曲名字",
              "type": "string"
            }
          },
          "required": [],
          "type": "object"
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "GetCurrentWeather",
        "description": "查询当前的天气",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "地理位置,比如北京市"
            },
            "unit": {
              "type": "string",
              "description": "温度单位",
              "enum": [
                "celsius",
                "fahrenheit"
              ]
            }
          },
          "required": [
            "location"
          ]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "LinkReaderPlugin",
        "description": "当需要解析网页内容时,调用本插件",
        "parameters": {
          "type": "object",
          "properties": {
            "url": {
              "type": "string",
              "description": "需要解析网页链接,最多3个;"
            }
          },
          "required": [
            "url"
          ]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "WebSearchPlugin",
        "description": "当需要搜索互联网内容时,调用本插件",
        "parameters": {
          "type": "object",
          "properties": {
            "keywords": {
              "type": "string",
              "description": "需要搜索的内容"
            }
          },
          "required": [
            "keywords"
          ]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "SendMessage",
        "description": "需要发送微信消息时,使用本函数",
        "parameters": {
          "type": "object",
          "properties": {
            "receiver": {
              "type": "string",
              "description": "接受对象"
            },
            "content": {
              "type": "string",
              "description": "需要发送的内容"
            }
          },
          "required": [
            "receiver"
          ]
        }
      }
    }
  ]
}

实际在jsonl文件里需要写为一行:

{"messages":[{"role":"system","content":"你是豆包"},{"role":"user","content":"把北京的天气发给Alan"},{"role":"assistant","content":"","tool_calls":[{"type":"function","function":{"name":"GetCurrentWeather","arguments":"{\"location\": \"北京\"}"}}],"loss_weight":1.0},{"role":"tool","content":"北京天气晴,18~25°,微风"},{"role":"assistant","content":"已经查到北京天气,需要发送给alan","tool_calls":[{"type":"function","function":{"name":"SendMessage","arguments":"{\"receiver\": \"Alan\", \"content\": \"今天北京天气晴,18~25°,微风\"}"}}],"loss_weight":1.0},{"role":"tool","content":"发送成功"},{"role":"assistant","content":"已经查到北京今日天气,发送给了Alan","loss_weight":1.0}],"tools":[{"type":"function","function":{"name":"MusicPlayer","description":"歌曲查询Plugin,当用户需要搜索某个歌手或者歌曲时使用此plugin,给定歌手,歌名等特征返回相关音乐","parameters":{"properties":{"artist":{"description":"表示歌手名字","type":"string"},"description":{"description":"表示描述信息","type":"string"},"song_name":{"description":"表示歌曲名字","type":"string"}},"required":[],"type":"object"}}},{"type":"function","function":{"name":"GetCurrentWeather","description":"查询当前的天气","parameters":{"type":"object","properties":{"location":{"type":"string","description":"地理位置,比如北京市"},"unit":{"type":"string","description":"温度单位","enum":["celsius","fahrenheit"]}},"required":["location"]}}},{"type":"function","function":{"name":"LinkReaderPlugin","description":"当需要解析网页内容时,调用本插件","parameters":{"type":"object","properties":{"url":{"type":"string","description":"需要解析网页链接,最多3个;"}},"required":["url"]}}},{"type":"function","function":{"name":"WebSearchPlugin","description":"当需要搜索互联网内容时,调用本插件","parameters":{"type":"object","properties":{"keywords":{"type":"string","description":"需要搜索的内容"}},"required":["keywords"]}}},{"type":"function","function":{"name":"SendMessage","description":"需要发送微信消息时,使用本函数","parameters":{"type":"object","properties":{"receiver":{"type":"string","description":"接受对象"},"content":{"type":"string","description":"需要发送的内容"}},"required":["receiver"]}}}]}

注意

  • 如果tool_calls的长度为n ,则后面必须有 nrole=tool的msg,反之亦然
  • 遵循json规范,参数type必须是下列之一,否则会报错
    • string
    • number
    • boolean
    • integer
    • object
    • array
  • 如果希望模型在返回fc同时输出内容(如CoT),可以填在content字段内
  • 如果不需要完整的fc,可以视情况截断,以role=assistant消息结束

最佳实践

Function Calling适用场景

推荐程度

常见功能、用法

推荐

  • 调用本地工具/API ,如天气、翻译、发送邮件等,参考coze.cn插件列表。
  • 调用多个工具解决一个问题
    • 需保证单个Function是简单、明确的

一般

  • 简单的结构化输出
    • 输出时最好不要同时进行复杂任务。
    • 可以在复杂任务完成后再单独发起Function Calling请求进行结构化

不推荐

Function Calling并不会增强模型能力, 并且FC模型综合能力不如pro

  • 试图完成模型正常情况下做不到的事
    • 写代码、写sql、(无插件)解题等
  • 意图识别、分类、信息抽取、摘要等通用能力:
    • 主模型(pro、lite)的通用能力显著优于FC模型,不建议用Function Calling
  • 多角色扮演类任务
  • 文本生成任务
    • 建议用主模型根据prompt回答
  • 长文处理、长文生成任务
    • 建议由大模型直接完成

Tool如何填写

  1. Function简单清晰、互相之间无歧义无重叠,如查询天气、设置闹钟、发送邮件等
  2. Function参数含义清晰、无歧义,有足够的例子(prompt)约束模型输出
    1. 歧义,比如房产业务下用户说“100”可能代表100或100万,需要prompt告知模型或sft
    2. 时间相关需要明确格式,用户说10点, 模型可能返回 “10 AM”、“10点”、“10:00” 等
    3. 参数之间有重叠混淆,则模型有可能出错。

新业务接入

  1. 明确业务需求。
    1. 如果对Function Calling依赖较重(如流水线、插件较多)、需要串行调用等场景,建议用Function Calling分支模型。
    2. 对Function Calling依赖较轻、无串行调用,对模型通用能力更为依赖的业务,建议用pro即可。
  2. 准备评测集,在FC模型测试,看看准确率,以及业务预期上线准确率。
  3. 常规调优手段:
    1. Functions的params、description等字段准确填写。 System prompt中不用再重复介绍函数,(可选)可以描述在何种情况下调用某函数
    • 优化函数、参数的描述, 明确不同函数的边界情况; 避免歧义;增加示例等。
    • 对模型进行sft(建议至少50条数据,模型越多、参数越多、情况越多,则所需要的数据越多),见上文【精调功能】。
  4. 速度优化手段:
    • 对于简单无歧义的函数或参数,适当精简输入输出内容。
    • 对于system prompt、fc list固定的流量,可购买模型单元,并联系我们开启Cache。
    • 对于实时性要求较高的场景,可以人工拆分为2个阶段,1阶段调用LLM做函数选择,2阶段调用LLM做该函数内的参数提取。 可测试2个阶段分别用pro/lite的表现; 该方法可能会降低效果。

prompt最佳实践

原则: Treat LLM as a kid

  1. 能不用大模型完成的任务,就不要调用大模型,尽量代码完成。
  2. 和任务无关的信息,避免输入,避免信息干扰。

类别

问题

错误示例

改正后示例

函数

命名不规范、描述不规范

{
   "type": "function",
    "function": {
        "name": "GPT1",
        "description": "新建日程",
     }
}
{
   "type": "function",
    "function": {
        "name": "CreateEvent",
        "description": "当需要为用户新建日程时,此工具将创建日程,并返回日程ID",
     }
}

参数

避免不必要的复杂格式(或嵌套)

{
    "time": {
        "type": "object",
        "description": "事件时间",
        "properties": {
            "timestamp": {
                "description": "事件时间"
            }
        }
    }
}
{
    "time": {
        "type": "string",
        "description": "事件时间",
    }
}

避免固定值

{
    "time": {
        "type": "object",
        "description": "事件时间",
        "properties": {
            "timestamp": {
                "description": "固定传2024-01-01即可"
            }
        }
    }
}

既然参数值固定,删去该参数,由代码处理。

业务流程

尽量缩短LLM调用轮次

System prompt:

你正在与用户Alan沟通,你需要先查询用户ID,再通过ID创建日程……

System prompt:

你正在与用户Alan(ID=abc123)沟通,你可以通过ID创建日程……

歧义消解

System prompt:

可以通过ID查找用户,并获得用户的日程ID

这里两个ID未明确,模型可能会混用

System prompt:

每个用户具有唯一的用户ID;每个日程有对应的日常ID,两者独立的ID。
可以通过用户ID查找用户,并获得用户的所有日程ID

需求澄清(追问)

需求澄清(确认需求),不依赖与FC,可独立使用
System promp加入

如果用户没有提供足够的信息来调用函数,请继续提问以确保收集到了足够的信息。
在调用函数之前,你必须总结用户的描述并向用户提供总结,询问他们是否需要进行任何修改。
......

在函数的description中加入

函数参数除了提取a和b, 还应要求用户提供c、d、e、f和其他相关细节。

如果用户提供的信息缺少工具所需要的必填参数,你需要进一步追问让用户提供更多信息。

Function Calling流式输出适配

Doubao 1.5 代模型相比之前的模型,除了性能上提升之外,还升级了Function Calling的能力。详见Function Calling 流式输出适配

FAQ

Deepseek R1 模型常见问题

  1. 不支持parallel_tool_calls字段, 默认就是自动触发、并行调用。
  2. 存在一些参数幻觉问题。 如预期先调用get_location获得城市,再调用get_weather查询,R1模型有概率直接返回get_weather:{city: get_location()} 这种嵌套调用, 请在system prompt中进行干预解决,分步完成调用。

多轮FC不符合预期排查

  1. 先保留最后一轮(单轮),进行验证,是否触发fc。(如果不触发,请尝试sft,或通过产品控制台工单联系我们)
  2. 更换更好的模型,如最新的doubao pro模型。
  3. 删除历史轮次中的中间过程,并保证历史轮次内容无特殊符号。

chat v3接口 与open AI推理请求有何差异?

  1. 不支持message.name 字段,填入无效。
  2. 从Doubao-1.5系列开始支持流式输出,其他模型不支持。

支持Langchain吗

请检查langchain调用细节,已知的差异见前一小节

流式输出

从Doubao-1.5系列开始支持。 历史模型不支持。

与coze的关系

合作关系,在coze专业版里,可以使用账户内方舟里的模型(含精调)

返回arguments不合法处理

在大模型生成的fc json的时候,有可能出现json不合法的情况,此时需要客户端进行异常处理。
解法一:
尝试使用一些json修复库去解析,如 json_repair

# pip install json-repair
import json-repair
completion = completion = client.chat.completions.create(**req)
fc_dict = json_repair.loads(completion.choices[0].message.tool_calls[0].function.arguments)

解法二:
可以根据arguments内的内容,判断json在哪出错,进行PE,尝试解决该问题。
例子 , arguments:

[
{"name":"ABC","parameters":{"data":{"Column1":[1,2,3,4],"Column2":["A","B","C","D"],"Column3":[10.1,20.2,30.3,40.4]}}}]
]

排查发现是最后多了一个] 。因此,尝试在函数ABC的description 里加上注意,在}}}结尾之后只有一个] ,未解决。
继续尝试在System prompt里加上注意,在}}}结尾之后只有一个] ,解决。

报错自查

错误编号

示例

原因

建议

1709602

报错-4312/1709602 FunctionCallPostProcessError: model generated content is invalid, code=1709602'

  • 模型输出的内容格式无法解析为json,无法返回。
  • 如果稳定报错,建议对req进行消融。以下情况可能会导致报错:
    • system prompt太长(超2k)
    • 设置了repetition_penalty、presence penalty等采样参数
  • 可能是使用了不支持fc功能的模型,或V1/V2接口不支持该模型。
  • 切换 doubao1.5 模型
  • 如果稳定报错,请通过产品工单联系我们

1709808

code_n: 1709808
code: InvalidParameter
message: invoke with function call error: Function prompt must be followed by an AI response with Function Call instruction

  • 如果是使用v2接口,回填assistant FC结果时,应该用tool_calls,而不是v1的function_call
{
    "role": resp.choices[0].message.role,
    "content": resp.choices[0].message.content,
    "name": resp.choices[0].message.name,
    "tool_calls": [{
        "name": resp.choices[0].message.tool_calls[0].name,
        "arguments": resp.choices[0]
        .message.tool_calls[0]
        .arguments,
    }],
},