百度智能云,流式请求示例
前端
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Sample</title> </head> <body> <label for="textInput">Prompt:</label> <input type="textarea" id="textInput" placeholder="您有什么问题"> <button onclick="run_prompt()">执行prompt</button> <p><textarea id="answer" rows="10" cols="50" readonly></textarea></p> <script> current_text = document.getElementById('answer'); text = ""; char_index = 0 function run_prompt() { var inputValue = document.getElementById('textInput').value; document.getElementById('answer').value = ""; // 调用服务端的流式接口, 修改为自己的服务器地址和端口号 fetch('http://<server address>:8000/eb_stream', { method: 'post', headers: {'Content-Type': 'text/plain'}, body: JSON.stringify({'prompt': inputValue}) }) .then(response => { return response.body; }) .then(body => { const reader = body.getReader(); const decoder = new TextDecoder(); function read() { return reader.read().then(({ done, value }) => { if (done) { // 读取完成 return; } data = decoder.decode(value, { stream: true }); text += JSON.parse(data).result; type(); // 打字机效果输出 return read(); }); } return read(); }) .catch(error => { console.error('发生错误:', error); }); } function type() { let enableCursor = true; // 启用光标效果 if (char_index < text.length) { let txt = document.getElementById('answer').value; let cursor = enableCursor ? "|" : ""; if (enableCursor && txt.endsWith("|")) { txt = txt.slice(0, -1); } document.getElementById('answer').value = txt + text.charAt(char_index) + cursor; char_index++; setTimeout(type, 1000/5); // 打字机速度控制, 每秒5个字 } } </script> </body> </html>
后端
<?php /* 启动方式: php server.php */ // 运行端口号 $server_port = "0.0.0.0:8000"; // 服务无连接时的超时时间(s), 0表示永不超时 $server_timeout = 0; // 千帆应用 AK/SK $ak = "24.9d0562891315801c0f14cef19b39f7b0.2592000.1719019992.282335-73805369"; $sk = ""; // 鉴权接口URL $token_url = 'https://aip.baidubce.com/oauth/2.0/token'; // 大模型接口URL $eb_url = 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant'; $source = "&sourceVer=0.0.1&source=app_center&appName=streamDemo"; // 创建 socket 服务 $server = stream_socket_server("tcp://$server_port", $errno, $errstr); if (!$server) { die("Failed to create server: $errstr"); } // 持续等待客户端请求 $start_time = time(); while (true) { // 检查是否有客户端连接 $read = array($server); $write = null; $except = null; if (stream_select($read, $write, $except, 0)) { $client = stream_socket_accept($server); // 获取客户端发来的数据 $data = fread($client, 1024); $postData = ''; if (preg_match("/\r\n\r\n(.*)/s", $data, $match)) { $postData = $match[1]; } // 解析 POST 参数, 请求大模型服务 $prompt = json_decode($postData); $responseBody = get_info_from_llm($prompt->prompt); // 返回响应头 $responseHeaders = [ 'Content-Type' => 'text/plain', 'Access-Control-Allow-Origin' => '*', ]; fwrite($client, "HTTP/1.1 200 OK\r\n"); foreach ($responseHeaders as $header => $value) { fwrite($client, "$header: $value\r\n"); } fwrite($client, "\r\n"); // 返回响应体 foreach ($responseBody as $chunk) { fwrite($client, "$chunk"); flush(); // 防止偶发多条合并返回 usleep(100000); } fclose($client); } else { // 0 表示永不超时 if ($server_timeout == 0) { continue; } // 超时则退出 if (time() - $start_time >= $server_timeout) { echo "Timeout reached. Stopping server. "; break; } // 否则继续等待 continue; } } // 释放资源 fclose($server); // 请求大模型服务, 并将大模型的返回数据逐行返回 function get_info_from_llm($prompt){ // 全局变量 global $ak, $sk, $token_url, $eb_url; // 获取 access_token $curl = curl_init(); $postData = array( 'grant_type' => 'client_credentials', 'client_id' => $ak, 'client_secret' => $sk ); curl_setopt_array($curl, array( CURLOPT_URL => $token_url, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => http_build_query($postData) )); $token_rtn = curl_exec($curl); curl_close($curl); // 请求大模型服务, 并将大模型的返回数据逐行返回给客户端 $body = ["messages" => [["role" => "user", "content" => $prompt]],"stream" => true]; $url = $eb_url . '?access_token=' . json_decode($token_rtn)->access_token . $source; $options = [ 'http' => [ 'method' => 'POST', 'header' => 'Content-Type: text/plain', 'content' => json_encode($body), 'ignore_errors' => true, 'protocol_version' => 1.1, 'timeout' => 60 ] ]; $context = stream_context_create($options); $stream = fopen($url, 'r', false, $context); stream_set_blocking($stream, 0); if ($stream) { $msg = ""; // 判断数据流是否已读取完 while (!feof($stream)) { // 逐行读取 $lines = explode("\n", fgets($stream)); foreach ($lines as $line) { // 去掉空行 if ($line != "") { echo "正在处理的行数据: $line\n"; $msgPart = explode(":", $line, 2); if ($msgPart[0] == "data") { if ($msg != "") { yield $msg; } $msg = $msgPart[1]; } else { $msg = $msg . $line; } } } } // 最后一次msg不需要yield,原因是最后一次请求大模型接口返回的result是空,只是使用is_end=true标记请求结束,忽略即可 } } ?>