langchain4j [BUG] Azure Open AI - 完成原因管理

zsbz8rwp  于 4个月前  发布在  其他
关注(0)|答案(7)|浏览(50)

问题:AzureOpenAi模型在达到令牌长度限制时应使用FinishReason.LENGTH。如果我们发送了太多的令牌,那么Azure Open AI会用一个http状态400和一个包含错误代码的json响应体来回答。这个400状态会引发OpenAiClient的getChatCompletions方法抛出一个异常。

这个问题在这里处理:
langchain4j/langchain4j-azure-open-ai/src/main/java/dev/langchain4j/model/azure/AzureOpenAiChatModel.java
第313行到第336行

} catch (HttpResponseExceptionhttpResponseException) {
    logger.info("Error generating response, {}", httpResponseException.getValue());
    FinishReasonexceptionFinishReason = contentFilterManagement(httpResponseException, "content_filter");
    Response<AiMessage> response = Response.from(
        aiMessage(httpResponseException.getMessage()),
        null,
        exceptionFinishReason
    );
    ChatModelErrorContexterrorContext = newChatModelErrorContext(
        httpResponseException,
        modelListenerRequest,
        null,
        attributes
    );

    listeners.forEach(listener -> {
        try {
            listener.onError(errorContext);
        } catch (Exceptione2) {
            logger.warn("Exception while calling model listener", e2);
        }
    });
    returnresponse;
}

辅助类只处理ContentFilter的情况。
langchain4j/langchain4j-azure-open-ai/src/main/java/dev/langchain4j/model/azure/InternalAzureOpenAiHelper.java
第337行到第359行

publicstaticFinishReasoncontentFilterManagement(HttpResponseExceptionhttpResponseException, StringcontentFilterCode) {
    FinishReasonexceptionFinishReason = FinishReason.OTHER;
    if (httpResponseException.getValue() instanceof Map) {
        try {
            Map<String, Object> error = (Map<String, Object>) httpResponseException.getValue();
            ObjecterrorMap = error.get("error");
            if (errorMapinstanceof Map) {
                Map<String, Object> errorDetails = (Map<String, Object>) errorMap;
                ObjecterrorCode = errorDetails.get("code");
                if (errorCodeinstanceof String) {
                    Stringcode = (String) errorCode;
                    if (contentFilterCode.equals(code)) {
                        // The content was filtered by Azure OpenAI's content filter (for violence, self harm, or hate).
                        exceptionFinishReason = FinishReason.CONTENT_FILTER;
                    }
                }
            }
        } catch (ClassCastExceptionclassCastException) {
            logger.error("Error parsing error response from Azure OpenAI", classCastException);
        }
    }
    returnexceptionFinishReason;
}

我认为它还应该管理令牌限制达到的情况,然后这个方法应该被重命名为例如TokenLimitReached。在当前实现中,我们得到了一个TokenLimitReached,但我们失去了达到了限制的事实(即使限制应该由业务应用程序正确管理;-)

pod7payv

pod7payv1#

/cc @agoncal (azure), @jdubois (azure)

xyhw6mcr

xyhw6mcr2#

我怀疑在这个情况下,400错误不应该Map到FinishReason.LENGTH
FinishReason.LENGTH表示LLM产生了一些响应,但由于***输出***令牌限制已达到(无论是模型的最大令牌限制还是用户设置的maxTokens),它未能完成(生成完整的响应)。
400 HTTP错误意味着请求有问题,在这种情况下,***输入***令牌限制已达到。

xn1cxnb4

xn1cxnb43#

你好@langchain4j
这是从com.azure.ai.openai.implementation.OpenAIClientImpl.getChatCompletionsSync(...)获取的返回消息:

{
  "az.sdk.message": "HTTP response",
  "statusCode": 400,
  "url": "https://xxxxxx.openai.azure.com//openai/deployments/completions/chat/completions?api-version=2024-03-01-preview",
  "durationMs": 106,
  "content-length": 281,
  "Date": "Thu, 04 Jul 2024 09:44:54 GMT",
  "Content-Type": "application/json",
  "x-ms-client-request-id": "0cf9dfba-065d-4024-98db-2ab5164855ed",
  "redactedHeaders": "x-request-id,x-ms-region,apim-request-id,x-ratelimit-remaining-tokens,x-ratelimit-remaining-requests,Strict-Transport-Security,azureml-model-session,access-control-allow-origin,x-content-type-options,x-ms-rai-invoked,ms-azureml-model-error-statuscode,ms-azureml-model-error-reason",
  "Date": "Thu, 04 Jul 2024 09:44:54 GMT",
  "Content-Type": "application/json",
  "x-ms-client-request-id": "0cf9dfba-065d-4024-98db-2ab5164855ed",
  "redactedHeaders": "x-request-id,x-ms-region,apim-request-id,x-ratelimit-remaining-tokens,x-ratelimit-remaining-requests,Strict-Transport-Security,azureml-model-session,access-control-allow-origin,x-content-type-options,x-ms-rai-invoked,ms-azureml-model-error-statuscode,ms-azureml-model-error-reason",
  "content-length": 281,
  "body": {
  "error": {
    "message": "This model's maximum context length is 8192 tokens. However, your messages resulted in 9948 tokens. Please reduce the length of the messages.",
    "type": "invalid_request_error",
    "param": "messages",
    "code": "context_length_exceeded"
  }
}
}

所以,也许FinishReason.LENGTH不是正确的使用原因。但是,由于FinishReason是在类似于这里的http 400上计算的:
https://github.com/langchain4j/langchain4j/blob/0a706ecb444b114ef079613da3e7b4e1f3e1070a/langchain4j-azure-open-ai/src/main/java/dev/langchain4j/model/azure/AzureOpenAiLanguageModel.java#L233-240
我想,我们也可以管理context_length_exceeded。也许,我们可以引入一个FinishReason.REQUEST_LENGTH?或者我们应该只用FinishReason处理LLM响应和错误,然后使用另一个枚举来表示技术错误?

d5vmydt9

d5vmydt94#

在我的了解中,当请求LLM提供者成功(除了过滤内容之外)时,FinishReason是有意义的。如果请求有问题(400),那么这是一个开发者应该修复的bug。所以在这种情况下,我会抛出一个RuntimeException。WDYT?

7gs2gvoe

7gs2gvoe5#

是的,我同意。
问题是,LanguageModel.generate(...)应该以某种功能性方式实现(即即使出现技术错误也始终返回一个Response对象,这也是它开始处理400和过滤内容的方式),还是应该用异常来实现?

2ul0zpep

2ul0zpep6#

我们通常在出现技术错误时抛出异常,我会遵循相同的风格,而不是引入一个新的。

gwbalxhn

gwbalxhn7#

@fb33 what's the status of this issue/PR? Should we close it?

相关问题