webman中发带stream=true的openai请求必须用webman-php/openai异步客户端,因同步调用会阻塞进程;需显式设stream=true,用回调处理sse流,手动构造text/event-stream响应,注意空data保活帧、缓冲控制及连接复用限制。

Webman里怎么发带stream=true的OpenAI请求
必须用webman-php/openai这个异步客户端,不能用guzzlehttp/guzzle或curl同步调用。原因很简单:OpenAI流式响应是分块返回的,同步方式会卡住整个PHP进程,Webman的常驻内存优势就白费了。
关键点在于调用时传入stream回调函数,而不是等返回值:
-
stream参数必须显式设为true,否则OpenAI后端不会启用SSE - 回调函数接收的是原始SSE数据流(每行以
data:开头),不是JSON对象 - 需要自己按
\n\n切分、过滤空行、剥离data:前缀,再json_decode() - 注意处理
[DONE]结尾标识,避免解析失败
如何把OpenAI的SSE流转发给前端浏览器
Webman本身不内置SSE支持,得手动构造符合text/event-stream规范的响应。别直接echo,要用Response对象设置头并保持连接不关闭。
将小说章节转换为电影分镜剧本。用户上传txt/md/docx文本,AI分析场景、角色、情绪、镜头语言,输出专业分镜脚本。适用于用户提及“分镜”“storyboard”“小说转分镜”“影视改编”“镜头脚本”或需要将小说改编为分镜的场景。
- 响应头必须包含
Content-Type: text/event-stream和Cache-Control: no-cache - 每个数据块要以
event: message\n+data: {...}\n\n格式输出,结尾双换行不能少 - 每次
echo后必须调用ob_flush()和flush(),否则浏览器收不到实时数据 - 别在回调里做耗时操作(比如写数据库),会拖慢流速;真要记录日志,用
Worker::async()丢到异步队列
为什么stream回调里$data有时是空字符串
这是OpenAI服务端的正常行为——它会在连接建立后先发一个空的data:行,或者在token间隔较长时发送心跳保活帧。你看到的空字符串大概率是这类保活数据,不是bug。
- 检查
$data是否为''或只含空白符,直接continue跳过即可 - 别假设每次回调都对应一个有效token;实际可能多个回调才拼出一个完整JSON片段
- 如果连续收到10次以上空
$data,要检查timeout配置是否过短(建议设为60秒) - 某些代理(如Nginx)会缓冲SSE响应,需在配置里加
proxy_buffering off;和chunked_transfer_encoding off;
并发高了之后流式响应开始卡顿甚至断连
根本原因不是Webman或OpenAI,而是PHP的输出缓冲机制和底层socket缓冲区被填满。尤其当用户网络差、前端没及时读取时,数据会堆积在PHP的stdout缓冲区里。
- 启动时加
ini_set('output_buffering', 'off');,关掉默认4096字节缓冲 - 在每次
echo前加if (connection_aborted()) { return; },主动检测客户端断开 - 别用
file_get_contents()或json_encode()处理大文本,改用JsonSerializable接口或流式JSON编码器 - 如果单机并发超500,考虑加一层Redis队列做请求限流,避免OpenAI限频触发
429错误
max_connections和keepalive_timeout。










