ChatGPT 的/v1/chat/completions 接口流式响应设计有点不科学 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
brader
V2EX    程序员

ChatGPT 的/v1/chat/completions 接口流式响应设计有点不科学

  •  1
     
  •   brader 2023-03-07 10:26:18 +08:00 8164 次点击
    这是一个创建于 955 天前的主题,其中的信息可能已经有所发展或是发生改变。

    当启用 stream=true 的时候,以流响应,返回的数据大体如下:

    data: {"id":"chatcmpl-6r3B875xFqmzK9lMm8sousVO3iBN4","object":"chat.completion.chunk","created":1678101622,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"role":"assistant"},"index":0,"finish_reason":nul l}]} data: {"id":"chatcmpl-6r3B875xFqmzK9lMm8sousVO3iBN4","object":"chat.completion.chunk","created":1678101622,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"\n\n"},"index":0,"finish_reason":null} ]} data: {"id":"chatcmpl-6r3B875xFqmzK9lMm8sousVO3iBN4","object":"chat.completion.chunk","created":1678101622,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"作"},"index":0,"finish_reason":null}]} data: {"id":"chatcmpl-6r3B875xFqmzK9lMm8sousVO3iBN4","object":"chat.completion.chunk","created":1678101622,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"为"},"index":0,"finish_reason":null}]} data: {"id":"chatcmpl-6r3B875xFqmzK9lMm8sousVO3iBN4","object":"chat.completion.chunk","created":1678101622,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"一个"},"index":0,"finish_reason":null}} ... data: [DONE] 

    每一个 event data 的 json 串,得到的 content 内容仅仅是一个字,看完真觉得浪费流量啊,是否我的调用方式不对呢?有其他的参数或调用方式来避免这种浪费吗?

    54 条回复    2024-08-27 11:49:08 +08:00
    string2020
        1
    string2020  
       2023-03-07 10:38:41 +08:00
    流量要花钱?
    brader
        2
    brader  
    OP
       2023-03-07 10:49:21 +08:00
    @string2020 更多思考的是技术方面的东西,这样 API 的传输效率不好
    orangie
        3
    orangie  
       2023-03-07 10:53:20 +08:00
    传输效率不高,但是用户体验确实很好的,追求的是响应时间更短,而不是吞吐量更高,不过感觉其他的 id 和标记可以再简化一下,一个会话用一个简单的 id 。
    string2020
        4
    string2020  
       2023-03-07 10:55:04 +08:00   1
    api 提供商都不在乎的东西你个外人跑来在乎。有这功夫,优化下前端,帮用户省点磁盘内存
    brader
        5
    brader  
    OP
       2023-03-07 11:00:41 +08:00   1
    @string2020 这和我是相关的,我们并不想暴露 token 等敏感信息,不是由前端直接调用的 chatgpt ,中间有一层我们的服务端在代理,所以这情况同样会影响到我们服务端的带宽和响应时间
    Kinnice
        6
    Kinnice  
       2023-03-07 11:05:46 +08:00
    那关闭 stream 呗
    ibegyourpardon
        7
    ibegyourpardon  
       2023-03-07 11:06:11 +08:00   11
    我认为恰恰是楼主想提供更好的方式和体验,才试图找到更好的方法来优化。
    而不是像有的人说的很搞笑,花不花钱,提供商在不在乎。
    ideacco
        8
    ideacco  
       2023-03-07 11:07:00 +08:00
    这可能是未来一段时间 AI 接口请求的基础“通病”了,因为之前的 ai 接口,不太依靠上下文,所以直接请求即可。而现在是非常依赖上下文的,那你怎么办?就是你想请求的问题,必须是在上线问基础上它才能回答的更好,SO ,现在有些大佬的方案是,每过一段时时间,对上面的问题进行总结(同样使用 gpt 接口)。然后删掉旧的上下文,使用总结后的上下文继续请求。
    另外需要 API 接口这边做一些中间件,比如把会话内容存在数据库中,然后前端用的时候可以中间插入或修改某些回话,就像 GPT 网页端做的那样。
    tool2d
        9
    tool2d  
       2023-03-07 11:11:50 +08:00
    我昨天还想发帖来着,就是很浪费流量。

    关键点还不是 data:这种格式,因为这种格式不是 openai 发明的,而是 firefox 发明的。

    而是 openai 服务器,无法进行任何的压缩传输!!我试了所有的压缩方法,都没有能开启文本压缩功能( content-encoding 始终不生效)。
    locoz
        10
    locoz  
       2023-03-07 11:18:04 +08:00
    当时抓包看到这返回方式的时候就感觉很蠢...明明可以分开两部分传输,却非要放在一个 json 里返回,导致流量浪费极大。只能说做术业有专攻,做 AI 的并不懂后端和网络。
    brader
        11
    brader  
    OP
       2023-03-07 11:21:20 +08:00
    @Kinnice 经测试,关闭 stream ,在提问某些问题的时候,AI 思考需要的时间太久了,响应的 20s 以上,我想用户是无法等待的
    brader
        12
    brader  
    OP
       2023-03-07 11:23:54 +08:00
    @ideacco 这里我讨论的不是上下文的问题,请求入参传递的 messages 数组上下文对话,才是你说的东西。
    这里质疑的是它的响应报文,该响应报文是单次回答的,只是流式响应的时候,拆分后数据格式太臃肿
    zhang77555
        13
    zhang77555  
       2023-03-07 11:38:37 +08:00 via Android
    就是原 API 返回的字符串结果改成了用 stream 返回,方便实现前端单个字吐出来的效果,我做 demo 的时候也是这么干的
    string2020
        14
    string2020  
       2023-03-07 12:01:37 +08:00
    离谱。大兄弟,传送 10b 和传送 100b 的响应时间差别有多大 有测试过?
    string2020
        15
    string2020  
       2023-03-07 12:03:13 +08:00
    服务端的带宽,我想知道你们服务端最大带宽是多少,平常用到了 1%没有?
    brader
        16
    brader  
    OP
       2023-03-07 12:09:12 +08:00   12
    @string2020 作为一个 5 年以上的服务端开发人员,虽然我的大部分菱角和追求都被磨灭了,但我依然很不认可你的这个心态。
    一个有一定发展历史的系统,它的 API 接口非常多,不仅仅只这一个,如果每一个接手项目的人,做东西的心态都是:这个接口 100ms 和 150ms 的响应时间有什么区别,没关系、不理他。系统多年堆积下来,整体的响应时间、并发率,是非常堪忧的
    8520ccc
        17
    8520ccc  
       2023-03-07 12:10:46 +08:00 via iPhone
    宽带影响不大吧,自己二次开发一下,然后减少返回的信息就行
    lambdaq
        18
    lambdaq  
       2023-03-07 12:13:38 +08:00
    @brader 你以为这样效率不好。。实际上,gpt 模型就是这样一字一字输出的。。
    brader
        19
    brader  
    OP
       2023-03-07 12:15:21 +08:00
    @8520ccc 目前我也是有这方面的想法,就是服务端似乎很少有像 js 解析 Event stream format 数据的扩展包,正打算自己写一个解析工具
    brader
        20
    brader  
    OP
       2023-03-07 12:16:31 +08:00
    @lambdaq 我认可这种一字一字输出的行为的,我意思是它的 API 设计,在流传输的时候,传一个字过来,附带大量其它臃肿的信息
    youthfire
        21
    youthfire  
       2023-03-07 12:25:05 +08:00
    我昨天也在看这个 stream ,发现比非流式慢很多。
    以下理解不知道对不对:
    按照官方说法,free-20RPM,credit card (48hrs)-60RPM,credit card (aft 48hrs)-3500RPM
    我现在理解是 60RPM 相当于 1s 吐一个字呗,因为它一次请求就返回一个字的内容。非流式只有在 3500RPM 才有实用价值吧。
    问 ai ,它解释非流式默认 600RPM ,那的确就是必须绑卡 48 小时后才有实用价值,否则还不如老老实实非流式了。
    另外我想说,stream=True 返回的是生成器,不加应该直接可以用 response['choices'][0]['message']['content'].strip(),但是我发现设定 stream=False 返回的也是生成器,不能取值,不知道是不是我方法不对,第一次接触这类。
    tool2d
        22
    tool2d  
       2023-03-07 12:26:49 +08:00
    @brader 其实不臃肿,你试一下用 gzip 流式压缩这些重复字符串,

    我这里 60k 左右的文本数据回答,可以压缩到 2k 之内。

    就是 openai 把这项特性关了,只打开了 chunk 传输,贼讨厌。
    tool2d
        23
    tool2d  
       2023-03-07 13:05:59 +08:00
    我又想了一下。

    既然 openai 在 http 协议上不支持压缩( accept-encoding: gzip, deflate, br ),那么干脆换个思路,让 openai 用 HTTPS 代理文本内容的时候,用 SSL 开启压缩功能(HANDSHAKE_SERVER_HELLO 里的 compression flag)就可以了。

    这样 openai 的服务器代码,也可以不需要改动。
    string2020
        24
    string2020  
       2023-03-07 13:06:34 +08:00
    装个锤子,正常开发人员都会知道带宽根本没影响。
    你自己一点测试都不做,凭空优化(不,你根本优化不了。api 的代码你都控制不了。凭空要求 openai 优化)。
    最好的情况是,openai 帮你做了优化。
    最后响应时间从 100ms 优化到了 100ms 。带宽从 1%占用优化到了 1%占用。
    你浪费了大家时间。
    otakustay
        25
    otakustay  
       2023-03-07 13:08:43 +08:00
    楼主的意思是用 stream 是好的,但没必要 stream 里每一份 data 都包一个 json ,可以每个 data 就一个内容字符串就行了
    我是认同楼主的想法的,json 重复内容太多了
    tool2d
        26
    tool2d  
       2023-03-07 13:10:29 +08:00   1
    @youthfire "但是我发现设定 stream=False 返回的也是生成器,不能取值,不知道是不是我方法不对"

    我这里可以正常取值,就是普通的 json 。
    devliu1
        27
    devliu1  
       2023-03-07 13:11:15 +08:00
    @brader 同意楼主观点,冗余信息过多
    string2020
        28
    string2020  
       2023-03-07 13:19:31 +08:00
    离大谱。想到了,父母让女儿找富二代的笑话。
    nxcdJaNnmyF9O90X
        29
    nxcdJaNnmyF9O90X  
       2023-03-07 13:35:15 +08:00
    离大谱。想到了,父母让女儿找富二代的笑话。
    vishun
        30
    vishun  
       2023-03-07 15:05:24 +08:00
    @string2020 #24 ?? 我都怀疑你是否是开发人员了?又不是只有一个用户在访问,怎么可能没有影响?
    dobelee
        31
    dobelee  
       2023-03-07 15:17:38 +08:00
    可以理解为元数据。做成长连接也得有基础协议的元数据。
    string2020
        32
    string2020  
       2023-03-07 15:21:03 +08:00
    @vishun 什么东西。这里说的带宽是指什么你懂吗?
    vishun
        33
    vishun  
       2023-03-07 16:53:37 +08:00
    @string2020 #32 说下指什么?
    string2020
        34
    string2020  
       2023-03-07 17:05:17 +08:00
    @vishun 这里说的是楼主的服务器和 openai 之间的下行带宽,和用户关系在哪?
    wqhui
        35
    wqhui  
       2023-03-07 17:42:12 +08:00
    像极了人,人说话也是一个字一个字的说(doge
    r4aAi04Uk2gYWU89
        36
    r4aAi04Uk2gYWU89  
       2023-03-07 17:52:49 +08:00
    @string2020 如果有 1000k 的用户同时用,中间做日志存储,如果再有实时分析,怎么可能 IO 没影响。带宽不是问题,你这意思是不够用就加硬件呗,反正不是自己花钱。程序员要都是你这想法,淘宝 JD 都不一定能开起来。
    string2020
        37
    string2020  
       2023-03-07 17:57:44 +08:00
    大兄弟。在这个提问中,用户就只有楼主自己,占用的带宽是服务器的下行带宽,下行带宽一般情况下免费的 1%都不会用到。你该不会是在为 openai 考虑吧?
    yuezk
        38
    yuezk  
       2023-03-07 18:01:54 +08:00
    你如果抓一下 ChatGPT 的接口,你会发现每个 data 都会重复前面的内容,还不如 OpenAI 的接口省带宽。

    ```
    event: add
    id: 1
    data: {"message":{"id":"chatcmpl-6rOOU6qBvhphYF8aL0Rw1VMitV5U8","role":"assistant","user":null,"create_time":null,"update_time":null,"end_turn":null,"weight":0,"recipient":"all","metadata":null,"content":{"content_type":"text","parts":["I"]}},"error":null}

    event: add
    id: 2
    data: {"message":{"id":"chatcmpl-6rOOU6qBvhphYF8aL0Rw1VMitV5U8","role":"assistant","user":null,"create_time":null,"update_time":null,"end_turn":null,"weight":0,"recipient":"all","metadata":null,"content":{"content_type":"text","parts":["I'm"]}},"error":null}

    event: add
    id: 3
    data: {"message":{"id":"chatcmpl-6rOOU6qBvhphYF8aL0Rw1VMitV5U8","role":"assistant","user":null,"create_time":null,"update_time":null,"end_turn":null,"weight":0,"recipient":"all","metadata":null,"content":{"content_type":"text","parts":["I'm sorry"]}},"error":null}

    event: add
    id: 4
    data: {"message":{"id":"chatcmpl-6rOOU6qBvhphYF8aL0Rw1VMitV5U8","role":"assistant","user":null,"create_time":null,"update_time":null,"end_turn":null,"weight":0,"recipient":"all","metadata":null,"content":{"content_type":"text","parts":["I'm sorry,"]}},"error":null}

    event: add
    id: 5
    data: {"message":{"id":"chatcmpl-6rOOU6qBvhphYF8aL0Rw1VMitV5U8","role":"assistant","user":null,"create_time":null,"update_time":null,"end_turn":null,"weight":0,"recipient":"all","metadata":null,"content":{"content_type":"text","parts":["I'm sorry, I"]}},"error":null}

    event: add
    id: 6
    data: {"message":{"id":"chatcmpl-6rOOU6qBvhphYF8aL0Rw1VMitV5U8","role":"assistant","user":null,"create_time":null,"update_time":null,"end_turn":null,"weight":0,"recipient":"all","metadata":null,"content":{"content_type":"text","parts":["I'm sorry, I'm"]}},"error":null}
    ```
    string2020
        39
    string2020  
       2023-03-07 18:04:51 +08:00
    (带宽费用)[!https://imgur.com/HT9oEzk]
    string2020
        40
    string2020  
       2023-03-07 18:15:03 +08:00
    阿里云都是免费的,不知道要省啥
    UIXX
        41
    UIXX  
       2023-03-07 18:32:54 +08:00
    OP 的问题可以总结为:能否省略或者压缩流式响应中的元数据。

    我的看法倾向于 8L 的观点,这个接口输出被设计成通用性更强、结构更明显的 chunk 形式,方便调用者直接使用。将其他形式的输出实现交给二次开发者。
    zhwithsweet
        42
    zhwithsweet  
       2023-03-07 18:53:01 +08:00
    支持 event source / SSE 的接口是这样的,写得时候图省事,直接把数据体写到 response 里边,出来必要的 { event, id data, retry } 别的感觉是能够压缩
    neotheone2333
        43
    neotheone2333  
       2023-03-07 21:57:42 +08:00
    楼主想要的应该是类似 pb 的那种形式
    上面有的人可能没用过除了 JSON 以外的数据交换格式
    byzod
        44
    byzod  
       2023-03-07 23:50:32 +08:00
    虽然但是,ai 答复的后端资源需求是非常高的,传输带宽只是流程上一个优先级非常低的环节,大概率不是性能热点

    正经程序员也不会在业务逻辑耗时 2000ms 的时候扭头去想办法把 parser 的时间从 2.5ms 优化到 1ms 吧,虽然这是 60%的性能提升对于这个环节来说
    iseki
        45
    iseki  
       2023-03-08 00:07:52 +08:00 via Android
    但是这样的 API 对接起来容易
    iseki
        46
    iseki  
       2023-03-08 00:13:33 +08:00 via Android
    楼主的关注点可能是每个条目都包含了重复的信息,id object 什么的…只能说优化这个挺麻烦,得在现在这种简单的模式基础上再包装一层了
    superares
        47
    superares  
       2023-03-08 08:22:00 +08:00 via iPhone
    有没有可能一个流就是一个 token ?
    vincirts
        48
    vinciarts  
       2023-03-08 09:22:40 +08:00 via Android
    弱弱的问一下,为什么要自己服务器转发,而不是直出啊?
    conn4575
        49
    conn4575  
       2023-03-08 13:34:39 +08:00 via Android
    我觉得 websocket 才是解决方案
    yuhangch
        50
    yuhangch  
       2023-03-08 14:05:05 +08:00
    @vinciarts #48 不想暴露自己的 token
    vinciarts
        51
    vinciarts  
       2023-03-08 14:57:41 +08:00
    @yuhangch 这样的话那直接用 versel 或者 netlify 部署就可以啊
    brader
        52
    brader  
    OP
       2023-03-08 15:46:36 +08:00
    @vinciarts 在目前 ChatGPT 的 API 采用直传 token 的授权形式的情况下,不管你采用什么方式,要么你有服务端代理来授权,要么你直接客户端访问,你隐藏的再怎么好,还是有暴露 token 的风险。
    上面只是其中之一原因,最近开始,最主要的原因还是需要代理的问题,被 GFW 墙了
    vinciarts
        53
    vinciarts  
       2023-03-09 09:36:49 +08:00
    @brader 原来如此
    nancccc
        54
    nancccc  
       2024-08-27 11:49:08 +08:00
    @yuezk 这个设计真是太差了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2369 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 00:57 PVG 08:57 LAX 17:57 JFK 20:57
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86