在UE5.2(不包罗5.2)之后,UE的HTTP模块已经实现了对流式接口的支持,我也是偶尔间在写流式接接口时发现的,不过没有发现这方面的资料,现在来记录一下,实现流式接口接收百度的文心一言大模型。
注意:5.2之前的版本貌似是没有实现的,5.2之前的版本大概必要自己去扩展引擎的源码大概借助第三方库。
引入模块
起首在代码中引入对应模块,假如是在插件中实现,就在插件的Build.cs文件引入,假如在项目的Source中实现,就在项目的Build.cs文件引入,我这里是插件,以是在插件中引入
插件实现
我们在插件中创建名为AIFunction的文件,文件的类是继承UBlueprintAsyncActionBase
在头文件中,起首我们创建一个布局体,用于我们传入的请求参数,
- USTRUCT(BlueprintType)
- struct FAIRequest
- {
- GENERATED_BODY()
- UPROPERTY(BlueprintReadWrite)
- FString URL;
- UPROPERTY(BlueprintReadWrite)
- TArray<FString> Message;
- UPROPERTY(BlueprintReadWrite)
- FString APIKey;
- UPROPERTY(BlueprintReadWrite)
- FString APISecret;
- UPROPERTY(BlueprintReadWrite)
- FString AppID;
- UPROPERTY(BlueprintReadWrite)
- FString access_token;
- UPROPERTY(BlueprintReadWrite)
- bool stream;
- };
复制代码 接着声明两个枚举范例,用来表示我们请求的AI范例与返回范例,用于蓝图中的多输出节点
- UENUM(BlueprintType)
- enum class EAIType : uint8
- {
- Baidu UMETA(DisplayName = "百度"),
- Tencent UMETA(DisplayName = "腾讯"),
- Ali UMETA(DisplayName = "阿里"),
- Dianxin UMETA(DisplayName = "电信"),
- XunFei UMETA(DisplayName = "讯飞")
- };
- UENUM()
- enum class EResponseType : uint8
- {
- Completed,
- Failed,
- Stream
- };
复制代码 声明一个多播代理,用于在http返回时将数据传出
- DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAIReceive,FString, Response);
复制代码 接着我们声明一个SendAI函数,GetBaiduToken函数,BaiduAI函数,来实现调用百度的接口
- UCLASS()
- class PAASAIMODULE_API UAIFunction: public UBlueprintAsyncActionBase
- {
- GENERATED_BODY()
- public:
- UPROPERTY(BlueprintAssignable)
- FAIReceive OnCompleted; //请求完成时
- UPROPERTY(BlueprintAssignable)
- FAIReceive OnFailed; //请求失败时
- UPROPERTY(BlueprintAssignable)
- FAIReceive OnStream; //流式请求时
- FHttpRequestStreamDelegate StreamDelegate; //用于http流式请求的代理
-
- UFUNCTION(BlueprintCallable,meta=( BlueprintInternalUseOnly="true" ))
- static UAIFunction* SendAI( EAIType Type,const FAIRequest& Request);
- UFUNCTION(BlueprintCallable,meta=( BlueprintInternalUseOnly="true" ))
- static UAIFunction* GetBaiduToken(const FString& apikey, const FString& secretkey);
-
- protected:
-
- //自动生成token待完成
- void BaiDuAI(const FAIRequest& Request);
- void DianXinAI(const FAIRequest& Request);
- };
复制代码 我这里使用的异步蓝图节点,非壅闭式的请求http。对于蓝图的异步节点,大家可以自行了解。
完备的.h文件内容为下
- // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"#include "Interfaces/IHttpRequest.h"#include "Kismet/BlueprintFunctionLibrary.h"#include "Kismet/BlueprintAsyncActionBase.h"#include "AIFunction.generated.h"USTRUCT(BlueprintType)
- struct FAIRequest
- {
- GENERATED_BODY()
- UPROPERTY(BlueprintReadWrite)
- FString URL;
- UPROPERTY(BlueprintReadWrite)
- TArray<FString> Message;
- UPROPERTY(BlueprintReadWrite)
- FString APIKey;
- UPROPERTY(BlueprintReadWrite)
- FString APISecret;
- UPROPERTY(BlueprintReadWrite)
- FString AppID;
- UPROPERTY(BlueprintReadWrite)
- FString access_token;
- UPROPERTY(BlueprintReadWrite)
- bool stream;
- };UENUM()enum class EResponseType : uint8{ Completed, Failed, Stream};UENUM(BlueprintType)enum class EAIType : uint8{ Baidu UMETA(DisplayName = "百度"), Tencent UMETA(DisplayName = "腾讯"), Ali UMETA(DisplayName = "阿里"), Dianxin UMETA(DisplayName = "电信"), XunFei UMETA(DisplayName = "讯飞") };/** * */DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAIReceive,FString, Response);UCLASS()class PAASAIMODULE_API UAIFunction: public UBlueprintAsyncActionBase{ GENERATED_BODY()public: UPROPERTY(BlueprintAssignable) FAIReceive OnCompleted; //请求完成时 UPROPERTY(BlueprintAssignable) FAIReceive OnFailed; //请求失败时 UPROPERTY(BlueprintAssignable) FAIReceive OnStream; //流式请求时 FHttpRequestStreamDelegate StreamDelegate;//用于http流式请求的代理 UFUNCTION(BlueprintCallable,meta=( BlueprintInternalUseOnly="true" )) static UAIFunction* SendAI( EAIType Type,const FAIRequest& Request); UFUNCTION(BlueprintCallable,meta=( BlueprintInternalUseOnly="true" )) static UAIFunction* GetBaiduToken(const FString& apikey, const FString& secretkey); protected: //自动生成token待完成 void BaiDuAI(const FAIRequest& Request);};
复制代码 接着我们在cpp文件中实现相应的函数
- //函数根据AI类型去调用相应的AI请求
- UAIFunction* UAIFunction::SendAI( EAIType Type,const FAIRequest& Request)
- {
- UAIFunction* AIFunction = NewObject<UAIFunction>();
- switch (Type)
- {
- case EAIType::Baidu:
- AIFunction->BaiDuAI(Request);
- break;
- case EAIType::Ali:
- break;
- case EAIType::Tencent:
- break;
- case EAIType::Dianxin:
- break;
- default:
- break;
- }
-
- return AIFunction;
- }
- //获取Baidu的Token
- UAIFunction* UAIFunction::GetBaiduToken(const FString& apikey, const FString& secretkey)
- {
- UAIFunction* AIFunction = NewObject<UAIFunction>();
- TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = FHttpModule::Get().CreateRequest();
- FString URL = FString::Printf(TEXT("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s"),*apikey,*secretkey );
- HttpRequest->SetURL(URL);
- HttpRequest->SetVerb(TEXT("POST"));
- HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
- HttpRequest->OnProcessRequestComplete().BindLambda(([AIFunction](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
- {
- if (bWasSuccessful && Response.IsValid())
- {
- FString ResponseContent =Response->GetContentAsString();
- TSharedPtr<FJsonObject> ResultObj;
- TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseContent);
- FJsonSerializer::Deserialize(Reader,ResultObj);
- FString DataString;
- if (ResultObj->TryGetStringField(TEXT("access_token"),DataString))
- {
- AIFunction->OnCompleted.Broadcast(DataString);
- }
- }
- else
- {
- AIFunction->OnFailed.Broadcast(TEXT("失败"));
- }
-
- }));
- HttpRequest->ProcessRequest();
- return AIFunction;
- }
- //发送AI请求
- void UAIFunction::BaiDuAI(const FAIRequest& Request)
- {
- AddToRoot();
- TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = FHttpModule::Get().CreateRequest();
- FString URL = FString::Printf(TEXT("%s?access_token=%s"), *Request.URL, *Request.access_token);
- HttpRequest->SetURL(URL);
- HttpRequest->SetVerb(TEXT("POST"));
- HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
- FString OutJsonData;
- TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutJsonData);
- Writer->WriteObjectStart(); // JSON对象开始
- //Writer->WriteValue(L"category", InfoCategory); // 填充普通字段
- Writer->WriteArrayStart(L"messages"); // Json 数组字段开始
- for (FString str : Request.Message)
- {
- Writer->WriteObjectStart();
- Writer->WriteValue(L"role", L"user"); // 填充普通字段
- Writer->WriteValue(L"content", str); // 填充普通字段
- Writer->WriteObjectEnd();
- }
- Writer->WriteArrayEnd();// Json 数组字段结束
- Writer->WriteValue(L"stream", Request.stream); //是否流式
- Writer->WriteObjectEnd(); //JSON对象结束
- Writer->Close();
- HttpRequest->SetContentAsString(OutJsonData); //设置流式请求代理
- if (Request.stream)
- {
- // 流式请求
- StreamDelegate.BindLambda([=,this](void* st,int64 length)
- {
- if (length >0)
- {
- //处理数据
- FString str = FString(UTF8_TO_TCHAR(static_cast<const char*>(st)));
- str.RemoveAt(0,5); //去掉返回数据的前置字符,因为百度返回的数据前有一个data:的字符,无法解析
- if (!str.IsEmpty())
- {
- str.ReplaceInline(TEXT("\\n"),TEXT(""));
- str.RemoveSpacesInline();
- TSharedPtr<FJsonObject> ResultObj;
- TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(str);
- FString DataString;
- bool success = FJsonSerializer::Deserialize(Reader,ResultObj);
- FString Meaasge;
- if (!success)
- {
- return true;
- }
- if (ResultObj->TryGetStringField(TEXT("result"),Meaasge))
- {
- if (!Meaasge.IsEmpty())
- {
- //Meaasge.RemoveFromStart(TEXT("\n")); 从开始删除第一个匹配的字符
- this->OnStream.Broadcast(Meaasge);
- }
- }
- else
- {
- return true;
- }
- }
- return true;
- }
- return false;
- });
- HttpRequest->SetResponseBodyReceiveStreamDelegate(StreamDelegate);
- }
- HttpRequest->OnProcessRequestComplete().BindLambda(([=,this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
- {
- if (bWasSuccessful && Response.IsValid())
- {
- this->StreamDelegate.Unbind();
- FString ResponseContent =Response->GetContentAsString();
- this->OnCompleted.Broadcast(ResponseContent);
- RemoveFromRoot();
- }
-
- }));
- HttpRequest->ProcessRequest();
- }
复制代码 完成后,编译乐成后会有下面两个节点。

注意:在选择了流式接口返回后,正常在请求Completed的时候是不会在返回数据的,也就是说Stream和Completed两个只会有一个有数据。
注:对于百度的模型,大家还是去看一下官方文档会对代码有更多的理解,而且必要去申请相应的账号。
文心一言API接入指南-百度开发者中心https://developer.baidu.com/article/detail.html?id=1089328
开局逆风的才是主角。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |