Write the Code. Change the World.

7月 11

AI 对话项目,会用到流式返回。一般客户端不会直接调用 AI 对话,而是调用自己的服务,自己的服务再调用 AI. 服务端可以通过 websocket 的方式来达到这个效果,也可以通过 SSE 的方式来达到这个效果。而 AI 也可能会提供这两种方式来提供服务。这样一来,项目就可能有四种组合方式来进行 AI 对话。如下:

客户端 服务端 AI
APP、浏览器 websocket websocket
APP、浏览器 websocket SSE
APP、浏览器 SSE websocket
APP、浏览器 SSE SSE

https://developer.mozilla.org/zh-CN/docs/Web/API/Server-sent_events/Using_server-sent_events

找 AI

websocket 阵营

https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E

SSE 阵营

https://agents.baidu.com/docs/develop/out-deployment/conversation/

尝试实现四种组合

从最简单的开始。服务端通过 SSE 的方式, AI 也是 SSE 的方式。

先封装一个 curl 工具包,创建 internal/pkg/curl/curl.go,内容如下。

package curl

import (
    "bytes"
    "context"
    "fmt"
    "io"
    "net/http"

    "github.com/gogf/gf/v2/frame/g"
    "github.com/gogf/gf/v2/net/gclient"
)

type HttpClient struct {
    client *gclient.Client
}

func NewHttpClient() *HttpClient {
    return &HttpClient{
        client: g.Client(),
    }
}

func (h *HttpClient) Get(ctx context.Context, url string, headers map[string]string) (string, error) {
    res, err := h.client.Get(ctx, url, headers)
    if err != nil {
        return "", err
    }
    defer res.Close()

    return res.ReadAllString(), nil
}

func (h *HttpClient) Post(ctx context.Context, url string, data interface{}, headers map[string]string) (string, error) {
    for k, v := range headers {
        h.client.SetHeader(k, v)
    }

    res, err := h.client.Post(ctx, url, data, headers)
    if err != nil {
        return "", err
    }
    defer res.Close()
    return res.ReadAllString(), nil
}

func (h *HttpClient) PostStream(ctx context.Context, url string, data interface{}, headers map[string]string) (io.ReadCloser, error) {
    // 将 data 转换为 io.Reader
    var body io.Reader
    switch v := data.(type) {
    case string:
        body = bytes.NewReader([]byte(v))
    case []byte:
        body = bytes.NewReader(v)
    default:
        return nil, fmt.Errorf("unsupported data type: %T", v)
    }

    // 创建 HTTP 请求
    req, err := http.NewRequestWithContext(ctx, "POST", url, body)
    if err != nil {
        return nil, err
    }

    // 设置请求头
    for k, v := range headers {
        req.Header.Set(k, v)
    }

    // 发起请求
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }

    // 检查响应状态码
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("request failed with status code: %d", resp.StatusCode)
    }

    return resp.Body, nil
}

现在来完善 chat 接口。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注