
在ThinkPHP中调用AI流式接口并实时返回给前端,可以通过以下步骤实现。这里以使用SSE(Server-Sent Events)技术为例:
如需完整代码请在留言区留下您的邮箱地址

namespace app\controller;
use think\Response;
use think\facade\Log;
class AiController
{
public function streamChat()
{
// 设置SSE响应头
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('X-Accel-Buffering: no'); // 禁用Nginx缓冲
// 获取用户输入
$input = input('question');
try {
// 调用AI流式接口
$this->callAiStream($input, function($chunk) {
// 处理AI返回的数据块
$data = $this->parseAiResponse($chunk);
if (!empty($data['content'])) {
// 发送SSE格式数据
echo "event: update\n"; // 自定义事件名
echo "data: " . json_encode(['content' => $data['content']], JSON_UNESCAPED_UNICODE) . "\n\n";
ob_flush();
flush();
}
});
} catch (\Exception $e) {
Log::error('AI请求失败: ' . $e->getMessage());
echo "event: error\n";
echo "data: " . json_encode(['msg' => '服务异常']) . "\n\n";
}
// 发送结束信号
echo "event: end\n";
echo "data: \n\n";
exit;
}
/**
* 调用AI流式接口
*/
private function callAiStream(string $prompt, callable $callback)
{
$apiKey = 'your-api-key'; // 替换为真实API密钥
$url = 'https://api.openai.com/v1/chat/completions'; // 替换为实际API地址
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => false,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $apiKey
],
CURLOPT_POSTFIELDS => json_encode([
'model' => 'gpt-3.5-turbo',
'messages' => [['role' => 'user', 'content' => $prompt]],
'stream' => true,
'temperature' => 0.7
]),
CURLOPT_WRITEFUNCTION => function($ch, $data) use ($callback) {
// 实时处理数据块
$callback($data);
return strlen($data);
}
]);
curl_exec($ch);
if (curl_errno($ch)) {
throw new \Exception(curl_error($ch));
}
curl_close($ch);
}
/**
* 解析AI响应数据
*/
private function parseAiResponse(string $chunk): array
{
$result = ['content' => ''];
// 处理流式数据格式(示例为OpenAI格式)
$lines = explode("\n", trim($chunk));
foreach ($lines as $line) {
if (strpos($line, 'data: ') === 0) {
$json = substr($line, 6);
if ($json === '[DONE]') break;
$data = json_decode($json, true);
if (isset($data['choices'][0]['delta']['content'])) {
$result['content'] .= $data['choices'][0]['delta']['content'];
}
}
}
return $result;
}
}<template>
<div>
<input v-model="question" placeholder="输入问题...">
<button @click="startStream">开始对话</button>
<div id="output" v-html="output"></div>
</div>
</template>
<script>
export default {
data() {
return {
question: '',
output: '',
eventSource: null
}
},
methods: {
startStream() {
// 清空前一次输出
this.output = '';
// 关闭现有连接
if (this.eventSource) this.eventSource.close();
// 创建SSE连接
this.eventSource = new EventSource(`/ai/streamChat?question=${encodeURIComponent(this.question)}`);
// 监听消息事件
this.eventSource.addEventListener('update', event => {
const data = JSON.parse(event.data);
this.output += data.content; // 增量更新内容
});
// 监听结束事件
this.eventSource.addEventListener('end', () => {
this.eventSource.close();
console.log('流式传输结束');
});
// 错误处理
this.eventSource.addEventListener('error', err => {
console.error('SSE错误:', err);
this.eventSource.close();
});
}
},
beforeUnmount() {
// 组件卸载时关闭连接
if (this.eventSource) this.eventSource.close();
}
}
</script>text/event-stream响应event: xxx\ndata: {...}\n\ncurl的CURLOPT_WRITEFUNCTION逐块处理ob_flush() + flush())X-Accel-Buffering: nowhile (@ob_end_flush());CURLOPT_TIMEOUT)完整实现时建议:
: heartbeat)