在对话式 AI 实时交互场景下,你可能需要 Function calling 功能将大模型与外部工具和 API 相连,助力大模型向实际产业落地迈进。在使用火山方舟大模型时你可以使用此功能。关于 Function calling 的详细介绍,请参看 Function calling 功能介绍。
你可通过以下步骤在该场景下使用 Function calling 功能:
步骤 1: 配置 Function calling 函数工具。
步骤 2: 接收工具调用信息。
步骤 3: 将工具调用信息传回 RTC-AIGC 服务端。
步骤 4: 收到 Function calling 音频回复。
其中步骤 2 和步骤 3 支持多轮 function call 调用。当 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" ] } } } ] } } }
当用户的问题触发 Function calling 时,你可以使用onRoomBinaryMessageReceived客户端接口接收本次函数工具调用的信息指令。
消息指令的格式为二进制,格式如下:
参数名 | 类型 | 描述 |
---|---|---|
magic number | binary | 消息格式,固定为 tool 。 |
length | binary | 信息指令长度,单位为 bytes。存放方式为大端序。 |
Tool_Calls | binary | 信息指令详细信息。格式参看Tool_Calls。 |
Tool_Calls
参数名 | 类型 | 是否必填 | 描述 |
---|---|---|---|
subscriber_user_id | String | 否 | 用户 UserId。 |
tool_calls | Array of tool_call | 是 | 工具调用信息列表。 |
id | String | 是 | 本次工具调用的唯一标识 ID。 |
type | String | 是 | 工具类型。 |
tool_call
参数名 | 类型 | 是否必填 | 描述 |
---|---|---|---|
function | String | 是 | 方法参数。 |
name | String | 是 | 方法名称。 |
你可参看以下示例代码对消息指令进行解析。
//定义结构体 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); } }
在使用 onRoomBinaryMessageReceived
获得消息指令后,你需要将该消息指令传给 RTC-AIGC 服务端。你可以使用以下方式传回:
UpdateVoiceChat
服务端接口传回;SendUserBinaryMessage
客户端接口传回。你可参看 UpdateVoiceChat 接口使用 Command
和 Message
字段将信息传回。
可参考以下示例:
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
接口将工具调用指令信息按照二进制格式传回。
二进制消息格式如下:
参数名 | 类型 | 描述 |
---|---|---|
magic number | binary | 消息格式,固定为 func 。 |
length | binary | 信息指令长度,单位为 bytes。存放方式为大端序。 |
Function Response | binary | 信息指令详细信息。 |
你可参看以下示例代码传回工具调用指令信息。
/** * @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);
当 Function Calling 的流程结束后,用户会收到房间内智能体的音频回复消息。