調用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
),再次發送請求,有個請求頭引起來我的注意。
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()
@api_blueprint.route('/embeddings', methods=['POST'])
def emb():
拿到ProxyA
代碼之后,項目請求我本地的ProxyA
地址,再次發送請求,成功的進入到了emb()
里面。也就是說,調用ProxyA
是沒有問題的,是ProxyA
調用ModelB
出了問題。
后面debug
到了一段關鍵的代碼,這個代碼就是把項目的請求轉發到ModelB
服務,關鍵是這個請求頭,沒做什么處理,就直接轉發給了ModelB
服務
def emb():# 省略......# 調用具體的模型服務地址,并且把接收到的請求頭放進去resp = requests.post(ModelB_url, headers=headers, json=data, timeout=10)return .....
后面在轉發請求代碼之前處理了一下,代碼如下:
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_modulecontent_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
開發,這段話有說的不對的,還望大佬們指正。