php 8.4 可通过 curl 的 curlopt_writefunction 逐行解析 ollama 的 stream=true 响应,实时输出每行 json 中的 content 字段,实现模型生成进度的流式监听,无需等待完整响应。

PHP 8.4 本身不直接支持流式响应的实时进度监听(如 SSE 或 chunked transfer),但调用 Ollama 的 API(默认是 HTTP)时,若使用 stream 模式(stream=true),Ollama 会以逐行 JSON 流(NDJSON)方式返回生成过程中的每个 token 响应。PHP 要“获取模型输出进度”,本质是:**边接收、边解析、边处理每一行响应**,而不是等整个响应结束。
确保 Ollama 启动时支持流式响应
Ollama 默认支持流式(只要请求带 stream=true)。确认你本地 Ollama 正在运行:
-
终端执行
ollama serve(或已作为服务后台运行) - 模型已拉取,例如:
ollama pull llama3.2 - API 地址通常是
http://localhost:11434/api/chat(chat 接口支持流;/generate 也支持,但 chat 更推荐)
PHP 8.4 中用 cURL 启用流式读取
关键不是“监听进度条”,而是手动控制响应流读取节奏。需禁用 cURL 的自动响应缓冲,并逐行读取 body:
- 设置
CURLOPT_HTTPHEADER包含Content-Type: application/json - 设置
CURLOPT_POSTFIELDS为 JSON,且必须含"stream": true - 设置
CURLOPT_RETURNTRANSFER = false(不缓存整个响应) - 设置
CURLOPT_WRITEFUNCTION回调函数,每次收到一段数据就触发(推荐按行处理) - 注意:Ollama 流响应每行是一个独立 JSON 对象(如
{"message":{"content":"H"},"done":false}),需用trim()+json_decode()解析
示例代码(精简可运行)
以下为 PHP 8.4 兼容写法,打印每步生成的 content 片段(即“进度”):
$url = 'http://localhost:11434/api/chat';
$data = [
'model' => 'llama3.2',
'messages' => [['role' => 'user', 'content' => '用中文讲一个短故事']],
'stream' => true,
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
curl_setopt($ch, CURLOPT_HEADER, false);
// 关键:逐行接收并解析
$buffer = '';
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch, $data) use (&$buffer) {
$buffer .= $data;
$lines = explode("\n", trim($buffer));
$buffer = array_pop($lines); // 保留未结束的行
foreach ($lines as $line) {
if (empty($line)) continue;
$obj = json_decode($line, true);
if (json_last_error() === JSON_ERROR_NONE && isset($obj['message']['content'])) {
echo "→ " . $obj['message']['content'];
@ob_flush(); flush(); // 确保立即输出(CLI 下可选)
}
}
return strlen($data);
});
curl_exec($ch);
curl_close($ch);
注意事项与常见问题
- CLI 运行更可靠:Web SAPI(如 Apache/FPM)可能因输出缓冲、超时、gzip 压缩干扰流式响应,建议先在终端跑通
- 不要用 file_get_contents:它无法处理流式响应,会一直阻塞到完成
- 进度 ≠ token 数 / 百分比:Ollama 不返回总 token 数,所以只能展示“已生成内容”,无法预估剩余时间
-
错误处理要加:检查
$obj['done'] === true表示结束;isset($obj['error'])处理失败 - 内存友好:上面的缓冲逻辑避免整块加载大响应,适合长文本生成
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











