Spring Al 项目为开发 AI 应用程序提供了一个 Spring 友好的 API 和抽象。
阿里通义也提供了抽象的实现和 Demo:
对话想实现一个个字蹦出来的,一般要用到 SSE, 对应的 OpenAI 接口:
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "No models available",
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "Hello!"
}
],
"stream": true
}'
这里比较重要的是 "stream": true,决定了响应是流式调用。
因为无法抗拒的网络问题,我选使用代理服务去访问 api.openai.com,测试的时候,先用 OpenAI Node 成功的进行了测试。
然后就是 Spring AI 上了,结果却遇到了问题。
Spring AI 中接收 OpenAI 接口响应的 Body 中, 多出了个 "data:", 转换JSON 报错:
我的第一反应是 Spring AI 是不是有些问题,即使使用了代理服务, OpenAI Node 中可以正常工作。
带着这个疑问,开始了漫长的 Debug 之路...
首先, 响应的 body 中 "data:" 是没有问题的, SSE 规范就是这样,然后 Spring AI 使用了 Spring WebFlux 去发送这个请求,WebFlux 针对 SSE 请求会自动处理掉 "data:", 但是这里为什么还是收到了 "data:" ?
疑问很快转移到了 Spring WebFlux, 经过 Debug 发现了比较关键的代码:
org.springframework.http.codec.ServerSentEventHttpMessageReader
这里处理了 SSE 请求的 "data: ", 遂先关注这里,测试发现正常 SSE 的请求的确会走这里,但是我使用的这个代理站的返回却没走到这里。
这里基本确认了没处理 "data: " 的原因。 但是为什么没有走到这里?
随后在 Spring WebFlux 中的:
org.springframework.web.reactive.function.BodyExtractors#readWithMessageReaders
这里的逻辑是根据 content-type,去找到合适的 HttpMessageReader 读 Body. Debug 信息中显示里面有足足 15 个 Reader。
经过跟踪发现出现问题的请求响应进了:
org.springframework.http.codec.DecoderHttpMessageReader
ServerSentEventHttpMessageReader 和 DecoderHttpMessageReader 都是 org.springframework.http.codec.HttpMessageReader 接口的实现。
没有进 ServerSentEventHttpMessageReader 是因为代理服务器返回的
"content-type: text/plain; charset=utf-8",而不是正确的 "content-typ: text/event-stream" 。
最后的问题,为什么 OpenAI Node 可以呢?
我是懒得再 Debug 了,但是我觉得 Spring 的这个实现也是没有问题的,毕竟发错了响应头。
本文由 考拉 创作,采用 知识共享署名4.0
国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: May 3,2024