You need to enable JavaScript to run this app.
导航
Function calling
最近更新时间:2024.12.17 17:20:59首次发布时间:2024.11.05 11:56:09

在实时对话式 AI场景下,你可能需要 Function calling 功能将大模型与外部工具和 API 相连,助力大模型向实际产业落地迈进。在使用火山方舟大模型时你可以使用此功能。关于 Function calling 的详细介绍,请参看 Function calling 功能介绍

你可通过以下步骤在该场景下使用 Function calling 功能:

步骤 1: 配置 Function calling 函数工具。
步骤 2: 接收工具调用信息。
步骤 3: 将工具调用信息传回 RTC 服务端。
步骤 4: 收到 Function calling 音频回复。
其中步骤 2 和步骤 3 支持多轮 function call 调用。当 Function Calling 的流程结束后,用户会收到房间内智能体的音频回复消息。

步骤 1:配置 Function calling 函数工具

你可参看 startVoiceChat 接口LLMConfig.Tools 字段进行 Function calling 函数工具配置。
你可以参考以下示例代码进行请求:

POST https://rtc.volcengineapi.com?Action=StartVoiceChat&Version=2024-06-01
{
    "AppId": "661e****543cf",
    "RoomId": "Room1",
    "UserId": "User1",
    "Config": {
        "BotName": "audiobot_user1_1611736812853",
        "ASRConfig": {
            "AppId": "93****21",
            "Cluster": "volcengine_streaming_common"
        },
        "TTSConfig": {
            "AppId": "94****11",
            "Cluster": "volcano_tts",
            "VoiceType": "BV002_streaming"
        },
        "LLMConfig": {
            "Mode": "ArkV3",
            "EndPointId": "epid****212",
            "MaxTokens": 1024,
            "Temperature": 0,
            "TopP": 0.3,
            "SystemMessages": [
                "你是小宁,性格幽默又善解人意。你在表达时需简明扼要,有自己的观点。"
            ],
            "UserMessages": [
                "user:\"你是谁\"",
                "assistant:\"我是问答助手\"",
                "user:\"你能干什么\"",
                "assistant:\"我能回答问题\""
            ],
            "HistoryLength": 3,
            "WelcomeSpeech": "Hello",
            "Tools": [
                {
                    "Type": "function",
                    "function": {
                        "name": "get_current_weather",
                        "description": "获取给定地点的天气",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "location": {
                                    "type": "string",
                                    "description": "地理位置,比如北京市"
                                },
                                "unit": {
                                    "type": "string",
                                    "description": "",
                                    "enum": [
                                        "摄氏度",
                                        "华氏度"
                                    ]
                                }
                            },
                            "required": [
                                "location"
                            ]
                        }
                    }
                }
            ]
            
        }
    }
}

步骤 2:接收工具调用信息指令

当用户的问题触发 Function calling 时,你可以使用onRoomBinaryMessageReceived客户端接口接收本次函数工具调用的信息指令。

消息指令格式

消息指令的格式为二进制,格式如下:
alt

参数名类型描述
magic numberbinary消息格式,固定为 tool
lengthbinary信息指令长度,单位为 bytes。存放方式为大端序。
Tool_Callsbinary信息指令详细信息。格式参看Tool_Calls

Tool_Calls

参数名类型是否必填描述
subscriber_user_idString用户 UserId。
tool_callsArray of tool_call工具调用信息列表。
idString本次工具调用的唯一标识 ID。
typeString工具类型。

tool_call

参数名类型是否必填描述
functionString方法参数。
nameString方法名称。

消息指令解析示例

你可参看以下示例代码对消息指令进行解析。

//定义结构体
struct function {
    std::string arguments;
    std::string name;
};

struct ToolCall {
    std::string id;
    std::string type;
    function func;
};

struct ToolCallsMsgData {
    std::string subscribe_user_id;
    std::vector<ToolCall> tool_calls
};

//回调事件
void onRoomBinaryMessageReceived(const char* uid, int size, const uint8_t* message) {
    std::string tool_calls;
    bool ret = Unpack(message, size, tool_calls);
    if(ret) {
        ParseData(tool_calls);
    }
}

//拆包校验
bool Unpack(const uint8_t *message, int size, std::string& tool_calls_msg) {
    int kToolCallsHeaderSize = 8;
    if(size < kToolCallsHeaderSize) { 
        return false;
    }
    // magic number "tool"
    if(static_cast<uint32_t>((static_cast<uint32_t>(message[0]) << 24) 
           | (static_cast<uint32_t>(message[1]) << 16) 
           | (static_cast<uint32_t>(message[2]) << 8) 
           | static_cast<uint32_t>(message[3])) != 0x746F6F6CU) {
        return false;
    }
    
    uint32_t length = static_cast<uint32_t>((static_cast<uint32_t>(message[4]) << 24) 
           | (static_cast<uint32_t>(message[5]) << 16) 
           | (static_cast<uint32_t>(message[6]) << 8) 
           | static_cast<uint32_t>(message[7]));
           
    if(size - kToolCallsHeaderSize != length) {
        return false;
    }

    if(length) {
        tool_calls_msg.assign((char*)message + kToolCallsHeaderSize, length);
    } else {
        tool_calls_msg = "";
    }
    return true;
}

//解析
void ParseData(const std::string& msg) {
    // 解析 JSON 字符串
    nlohmann::json json_data = nlohmann::json::parse(msg);
    ToolCallsMsgData toolcalls_data;
    // 存储解析后的数据
    toolcalls_data.subscribe_user_id = json_data["subscribe_user_id"];
    // 遍历 JSON 数据并填充结构体
    for (const auto& item : json_data["tool_calls"]) {
        ToolCall tool_call;
        tool_call.id = item["id"];
        tool_call.type = item["type"];
        auto fun_json = item["function"];
        tool_call.func.arguments = fun_json["arguments"];
        tool_call.func.name = fun_json["name"];
        toolcalls_data.push_back(tool_call);
    }
}

步骤 3:将工具调用信息指令传回 RTC 服务端

在使用 onRoomBinaryMessageReceived 获得消息指令后,你需要将该消息指令传给 RTC 服务端。你可以使用以下方式传回:

  • 调用 UpdateVoiceChat 服务端接口传回;
  • 调用 SendUserBinaryMessage 客户端接口传回。

调用 UpdateVoiceChat 服务端接口传回

你可参看 UpdateVoiceChat 接口使用 CommandMessage字段将信息传回。

可参考以下示例:

POST https://rtc.volcengineapi.com?Action=UpdateVoiceChat&Version=2024-06-01
{
    "AppId": "661e****543cf",
    "RoomId": "Room1",
    "UserId": "User1",
    "Command": "function",
    "Message":"{\"ToolCallID\":\"call_cx\",\"Content\":\"上海天气是台风\"}"
}

调用 SendUserBinaryMessage 客户端接口传回

你可调用SendUserBinaryMessage接口将工具调用指令信息按照二进制格式传回。

二进制消息格式

二进制消息格式如下:
alt

参数名类型描述
magic numberbinary消息格式,固定为 func
lengthbinary信息指令长度,单位为 bytes。存放方式为大端序。
Function Responsebinary信息指令详细信息。

传回指令信息示例

你可参看以下示例代码传回工具调用指令信息。

/**
 * @brief 将字符串包装成 TLV
 */
function stringToTLV(inputString: string) {
  const type = 'func';
  const typeBuffer = new Uint8Array(4);

  for (let i = 0; i < type.length; i++) {
    typeBuffer[i] = type.charCodeAt(i);
  }

  const lengthBuffer = new Uint32Array(1);
  const valueBuffer = new TextEncoder().encode(inputString);

  lengthBuffer[0] = valueBuffer.length;

  const tlvBuffer = new Uint8Array(typeBuffer.length + 4 + valueBuffer.length);

  tlvBuffer.set(typeBuffer, 0);

  tlvBuffer[4] = (lengthBuffer[0] >> 24) & 0xff;
  tlvBuffer[5] = (lengthBuffer[0] >> 16) & 0xff;
  tlvBuffer[6] = (lengthBuffer[0] >> 8) & 0xff;
  tlvBuffer[7] = lengthBuffer[0] & 0xff;

  tlvBuffer.set(valueBuffer, 8);

  return tlvBuffer.buffer;
}

/**
 * @brief 通过 onRoomBinaryMessageReceived 接收 toolcall
 *        通过 sendUserBinaryMessage 发送 response
 */
const handleRoomBinaryMessageReceived = (event: { userId: string; message: ArrayBuffer }) => {
  const { message } = event;
  const decoder = new TextDecoder('utf-8');
  const str = decoder.decode(message);
  const start = str.indexOf('{');
  const context = JSON.parse(str.substring(start, str.length)) || {};
  if (context?.tool_calls?.length) {
    const roomId = sessionStorage.getItem('roomId');
    const userId = sessionStorage.getItem('username');
    const name: string = context?.tool_calls?.[0]?.function?.name;
    const map: Record<string, string> = {
      getcurrentweather: '今天下雪, 最低气温零下10度',
      musicplayer: '查询到李四的歌曲, 名称是千里之内',
      sendmessage: '发送成功',
    };
    RtcClient.engine.sendUserBinaryMessage(
      'RobotMan_',
      stringToTLV(
        JSON.stringify({
          ToolCallID: context?.tool_calls?.[0]?.id,
          Content: map[name.toLocaleLowerCase().replaceAll('_', '')],
        })
      )
    );
  }
};

this.engine.on(VERTC.events.onRoomBinaryMessageReceived, handleRoomBinaryMessageReceived);

步骤 4:获取 Function calling 最终答复

当 Function Calling 的流程结束后,用户会收到房间内智能体的音频回复消息。