用 Livewire 的 wire:stream 开发流式聊天机器人(Server-sent events, 百度文心一言)

上周见 Twitter 上有人提到用 wire:stream 处理 SSE (Server-sent event),看了 Livewire 的文档,wire:stream 还真是为创建聊天机器人而生的。而且它的 API 非常简单易用,只有两行代码:

后端发送 SSE 事件:

$this->stream(to: 'answer', content: 'hello');

前端处理 SSE 事件:

<div wire:stream="answer"></pre>

于是我试着用百度的千帆大模型 API,搭建了一个聊天功能,你可以在 learn-qianfan-livewire.xuchunyang.... 尝试,代码在 github.com/xuchunyang/learn-qianfa...

运行演示

用 Livewire 的 wire:stream 开发流式聊天机器人(Server-sent events, 百度文心一言)

一些收获

Dependency Injection

通过把百度 API 请求封装成一个 Service,然后在 Service Provider 中注册,最后在 App 即 Service Container 通过 Dependency injection 引入。虽然用常规的 Class 也能,但是借机了解了 Laravel 的核心概念。

Server-sent event

初步了解了如何用原生 PHP 编写 SSE 的服务器端,Livewire 把 ob_flush/X-Accel-Buffering 封装好了,方便我们调用。

用 symfony/http-client 处理 SSE 请求,symfony/http-client 是我注意到明确说支持 SSE 协议的客户端库,尽管 SSE 协议不复杂,百度的 API 也只用了最简单的部份,但感觉自行手动处理不大靠谱,容易出现截断或标准支持不全的情况。

laravel-markdown

渲染 Markdown,并支持语法高亮,尽管是通过第三方 Node 库实现的,但有个正常点儿的高亮还是不错的:

用 Livewire 的 wire:stream 开发流式聊天机器人(Server-sent events, 百度文心一言)

《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 3

我是直接用guzzle包请求,然后自己解析出结果直接返回给客户端的,目前用了半年多了,没出问题。
关键代码:

header('Content-Type: text/event-stream'); // 以事件流的形式告知浏览器进行显示
header('Cache-Control: no-cache');         // 告知浏览器不进行缓存
header('X-Accel-Buffering: no');           // 关闭加速缓冲
// 省略部分代码.......
$body = $response->getBody();
$buffer = '';
while (!$body->eof()) {
    $buffer .= $body->read(128);
    // 这里使用while是因为读取n个字节有可能同时读出n条 EventSource 消息
    while (($pos = strpos($buffer, "\n\n")) !== false) {
        $msg = substr($buffer, 0, $pos); // 一条event消息
        $buffer = substr($buffer, $pos + 2); // 去除已被解析的部分

        if (substr($msg, 0, 6) === 'data: ') { // 只解析了data,实际的EventSource还有 event、id、retry
            $obj = json_decode(substr($msg, 6));
            if (isset($obj->choices[0]->delta->content)) {
                echo $obj->choices[0]->delta->content;
                ob_flush();
                flush();
            }
        }
    }
}
1年前 评论

我装了 Pulse 和 Telescope,可以公开访问:

1年前 评论

之前写过一个通用的 sse 类 app/Support/Sse/ServerSentEvent.php,可方便的单独使用或者配合 symfony 的流响应类使用。

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!