调用Embedding模型失败
Spring AI
项目使用的Embedding
模型是公司平台部署的,请求模型服务的时候报错,返回了HTTP 400 - Invalid HTTP request received
错误。然后换成云厂商在线Embedding
模型地址,正常调通。我用Apifox直接调用公司的模型服务,能正常调通。当时真的百思不得其解。
Spring AI客户端排查
Spring AI
项目中我用的Http
客户端是apache
的httpclient5
(后面切成netty
的也报同样的错误),代码调试没发现有什么异常的地方,然后把httpclient5
的日志级别调成debug
(org.apache.hc: debug
),再次发送请求,有个请求头引起来我的注意。
shell
org.apache.hc.client5.http.wire : http-outgoing-0 >> "Transfer-Encoding: chunked[\r][\n]"
问了一下AI,这个请求头的意思
Transfer-Encoding: chunked 是一种HTTP分块传输编码。当发送方无法预先知道消息体总长度时(如动态生成内容),可将其分割为多个带大小标记的"块"流式发送。每个块先发十六进制长度,再发数据,以长度为0的块结束。它与Content-Length互斥,不能共存。
于是我在Apifox那边也加上这个请求头,调用直接返回同样的错误,去掉就能正常返回向量信息。直接通过Apifox调用在线的Embedding
模型地址,并且加上这个请求头,也能成功调通。所以问题大概率出现在公司部署的服务上。因为我找了一圈,也没找到Spring AI
有配置相关请求头的地方,所以这种请求方式无法改变(有可能有设置不分块传输的,只是我没发现,我感觉概率应该很低)。
部署的模型服务排查
由于模型是其他部门部署的,所以就去要了一个项目代码,这里称为ProxyA
,当时同事告诉我说,这个ProxyA
主要是做的代理服务,适配了一下OpenAI
的接口格式,项目调用的都是这个服务(说langchain4j
是能正常调通的,排除这个服务的问题),再由此服务转发至对应的模型服务(过了两天又拿到了这个服务的代码),模型服务这里称为ModelB
。
调用过程就是项目
--->ProxyA
--->ModelB
ProxyA排查
ProxyA
使用的是Flask
框架,处理/embeddings
地址的方法是emb()
python
@api_blueprint.route('/embeddings', methods=['POST'])
def emb():
拿到ProxyA
代码之后,项目请求我本地的ProxyA
地址,再次发送请求,成功的进入到了emb()
里面。也就是说,调用ProxyA
是没有问题的,是ProxyA
调用ModelB
出了问题。
后面debug
到了一段关键的代码,这个代码就是把项目的请求转发到ModelB
服务,关键是这个请求头,没做什么处理,就直接转发给了ModelB
服务
python
def emb():
# 省略......
# 调用具体的模型服务地址,并且把接收到的请求头放进去
resp = requests.post(ModelB_url, headers=headers, json=data, timeout=10)
return .....
后面在转发请求代码之前处理了一下,代码如下:
python
def emb():
# 省略......
# 如果存在Transfer-Encoding: chunked,就去掉,然后加上Content-Length头
if 'Transfer-Encoding' in headers and headers['Transfer-Encoding'] == 'chunked':
# 移除Transfer-Encoding头部
del headers['Transfer-Encoding']
# 添加Content-Length头部
import json as json_module
content_length = len(json_module.dumps(data).encode('utf-8'))
headers['Content-Length'] = str(content_length)
# 调用具体的模型服务地址,并且把接收到的请求头放进去
resp = requests.post(ModelB_url, headers=headers, json=data, timeout=10)
return .....
处理之后,再次调用,发现Spring AI
项目可以正常调用公司部署的Embedding
模型了
ModelB排查
后面要到ModelB
服务的git
权限之后,拉取代码本地试了一下,项目可以直接调通ModelB
服务,不再报HTTP 400 - Invalid HTTP request received
错误,只是因为格式不对,报了其他的错误。所以ModelB
服务也是没有问题的。就是ProxyA
服务请求转发的时候出了问题。
没拿到ModelB
代码之前,还一顿怀疑是ModelB
出了问题,纠结要不要去掉ProxyA
里面的那段处理代码(毕竟不是专业Python
开发😂)。
总结
ProxyA
接收到分块
传输请求之后,通过requests.post
转发请求的时候,由于没有对请求头进行过滤,导致转发的请求头中存在Transfer-Encoding: chunked
,所以调用ModelB
的时候出现无效Http请求 的异常。
进到emb()
方法中,实际项目对ProxyA
的请求已经被完整接收了,也就是数据都传输过来了,但是requests.post
转发的时候,json是一个确定的对象,可以明确大小的,也就是转发的时候压根不是分块请求,头部又设置成了分块传输,自然就有问题了。由于我不是python
开发,这段话有说的不对的,还望大佬们指正。