问题:人工智能对话模型如何实现流式接口响应(一行一行打印出来)?
环境:php + nginx
答:
流式响应就是在请求接口时实时地一行一行传输给前端,第一反应是使用echo进行循环打印,所以即写了一段小代码测试,发现是一下子返回,无法实现一行一行的效果。
为什么呢?
查询资料了解到,在后台将数据返回给前端时,需要经过缓冲区,缓存区是为了优化请求过程而使用的方法,具体就是先将小数据缓存累计起来再一次性传输。
那么在请求的过程中经过了哪些缓冲区buffer呢?
1、php语言本身有一个output_buffering配置,就是默认的缓冲区,默认值是4096,也即是会累计4k的数据再进行传输;
(php.ini)
2、nginx和php之间有一个通用网关接口程序,叫cgi,是来链接nginx和php的桥梁,本身也有缓冲区的配置
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 4k; //用多大的缓冲区来读取响应的第一部分,也即是http的响应头
fastcgi_buffers 4 4k; //开启4个4k大小的缓冲区
fastcgi_busy_buffers_size 4k; //达到多大就先将数据传输
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;
3、nginx本身的配置,包括
gzip off;
话不多少,贴代码
前端:
async function getRes() {
const res = await fetch(url, {
method:'POST',
mode: 'cors', // no-cors, *cors, same-origin
headers: {
'Content-Type': 'application/json',
"Authorization":"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ4IiwiYXVkIjoieCIsImlhdCI6MTcxODY4ODE3NiwibmJmIjoxNzE4Njg4MTc2LCJleHAiOjE3MjEyODAxNzYsImV4dGVuZCI6eyJpZCI6MSwibmFtZSI6InBhZ2UiLCJlbWFpbCI6bnVsbCwicm9sZSI6WzFdLCJzdGF0dXMiOjF9fQ.t5yn5k2o-bgXQZoqwBp7I2BAr5EesK5S4XyMMg2Q3mg"
},
body:JSON.stringify({
"message": "介绍优乐公司",
}),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
while(1) {
// 读取数据流的第一块数据,done表示数据流是否完成,value表示当前的数
const {done, value} = await reader.read();
if (done) break;
const text = decoder.decode(value);
// 打印第一块的文本内容
console.log(text, done);
}
}
后端:
//如何将数据写到响应中
$fuc = function ($curl, $data) use (&$res, $user, $current) {
$filters = explode("\n", $data);
foreach($filters as $filter) {
//拼接字符串
$reply = json_decode(
substr($filter, 5),
true
);
if($reply != null) {
if($reply["choices"][0]["finish_reason"] == "stop") {
$rebuild = ["text" => "DONE","message_id" => ""];
echo json_encode($rebuild, JSON_UNESCAPED_UNICODE);
echo str_pad('', 8192);
} else {
$rebuild = ["text" => $reply["choices"][0]["delta"]["content"],"message_id" => ""];
}
if(!empty($reply["choices"][0]['delta'])
&& $reply["choices"][0]["delta"]["content"] != ""
) {
echo json_encode($rebuild, JSON_UNESCAPED_UNICODE);
echo str_pad('', 8192);
}
if($reply["choices"][0]["finish_reason"] == "stop") { //结束
break;
} else {
$res .= $reply["choices"][0]["delta"]["content"];
}
}
}
return strlen($data);
};
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, DeepSeekHelper::$base_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json; charset=utf-8',
'Accept: application/json',
"Authorization: Bearer ".DeepSeekHelper::$apiKey
]);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $fuc);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData));
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
// dd(json_encode($postData));
curl_exec($ch);
curl_close($ch);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。