虚幻引擎实现流式Http接口并接入百度文心一言大模型(仅适用于UE5.2之后的版
在UE5.2(不包罗5.2)之后,UE的HTTP模块已经实现了对流式接口的支持,我也是偶尔间在写流式接接口时发现的,不过没有发现这方面的资料,现在来记录一下,实现流式接口接收百度的文心一言大模型。注意:5.2之前的版本貌似是没有实现的,5.2之前的版本大概必要自己去扩展引擎的源码大概借助第三方库。
引入模块
起首在代码中引入对应模块,假如是在插件中实现,就在插件的Build.cs文件引入,假如在项目的Source中实现,就在项目的Build.cs文件引入,我这里是插件,以是在插件中引入
https://i-blog.csdnimg.cn/direct/42e8274af57b4865a40959cea25a75bc.png插件实现
我们在插件中创建名为AIFunction的文件,文件的类是继承UBlueprintAsyncActionBase
https://i-blog.csdnimg.cn/direct/c30a185f76434557ad35b46f2c13483d.png
在头文件中,起首我们创建一个布局体,用于我们传入的请求参数,
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" ))
staticUAIFunction* SendAI( EAIType Type,const FAIRequest& Request);
UFUNCTION(BlueprintCallable,meta=( BlueprintInternalUseOnly="true" ))
staticUAIFunction* 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" )) staticUAIFunction* SendAI( EAIType Type,const FAIRequest& Request); UFUNCTION(BlueprintCallable,meta=( BlueprintInternalUseOnly="true" )) staticUAIFunction* 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(((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();
} 完成后,编译乐成后会有下面两个节点。
https://i-blog.csdnimg.cn/direct/1cfff96d59db4a67a567eed78190658b.png
注意:在选择了流式接口返回后,正常在请求Completed的时候是不会在返回数据的,也就是说Stream和Completed两个只会有一个有数据。
https://i-blog.csdnimg.cn/direct/80382e951c3f4c3db09717746b77ea8f.png
注:对于百度的模型,大家还是去看一下官方文档会对代码有更多的理解,而且必要去申请相应的账号。
文心一言API接入指南-百度开发者中心https://csdnimg.cn/release/blog_editor_html/release2.3.7/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=O83Ahttps://developer.baidu.com/article/detail.html?id=1089328
开局逆风的才是主角。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]