虚幻引擎实现流式Http接口并接入百度文心一言大模型(仅适用于UE5.2之后的版 ...

打印 上一主题 下一主题

主题 954|帖子 954|积分 2862

在UE5.2(不包罗5.2)之后,UE的HTTP模块已经实现了对流式接口的支持,我也是偶尔间在写流式接接口时发现的,不过没有发现这方面的资料,现在来记录一下,实现流式接口接收百度的文心一言大模型。
注意:5.2之前的版本貌似是没有实现的,5.2之前的版本大概必要自己去扩展引擎的源码大概借助第三方库。
引入模块

起首在代码中引入对应模块,假如是在插件中实现,就在插件的Build.cs文件引入,假如在项目的Source中实现,就在项目的Build.cs文件引入,我这里是插件,以是在插件中引入
插件实现


我们在插件中创建名为AIFunction的文件,文件的类是继承UBlueprintAsyncActionBase

在头文件中,起首我们创建一个布局体,用于我们传入的请求参数,
  1. USTRUCT(BlueprintType)
  2. struct FAIRequest
  3. {
  4.         GENERATED_BODY()
  5.         UPROPERTY(BlueprintReadWrite)
  6.         FString URL;
  7.         UPROPERTY(BlueprintReadWrite)
  8.         TArray<FString> Message;
  9.         UPROPERTY(BlueprintReadWrite)
  10.         FString APIKey;
  11.         UPROPERTY(BlueprintReadWrite)
  12.         FString APISecret;
  13.         UPROPERTY(BlueprintReadWrite)
  14.         FString AppID;
  15.         UPROPERTY(BlueprintReadWrite)
  16.         FString access_token;
  17.         UPROPERTY(BlueprintReadWrite)
  18.         bool stream;
  19. };
复制代码
接着声明两个枚举范例,用来表示我们请求的AI范例与返回范例,用于蓝图中的多输出节点
  1. UENUM(BlueprintType)
  2. enum class EAIType  : uint8
  3. {
  4.   Baidu UMETA(DisplayName = "百度"),
  5.         Tencent UMETA(DisplayName = "腾讯"),
  6.         Ali UMETA(DisplayName = "阿里"),
  7.         Dianxin UMETA(DisplayName = "电信"),
  8.         XunFei UMETA(DisplayName = "讯飞")
  9. };
  10. UENUM()
  11. enum class EResponseType  : uint8
  12. {
  13.         Completed,
  14.         Failed,
  15.         Stream
  16. };
复制代码
声明一个多播代理,用于在http返回时将数据传出
  1. DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAIReceive,FString, Response);
复制代码
接着我们声明一个SendAI函数,GetBaiduToken函数,BaiduAI函数,来实现调用百度的接口
  1. UCLASS()
  2. class PAASAIMODULE_API UAIFunction: public UBlueprintAsyncActionBase
  3. {
  4.         GENERATED_BODY()
  5. public:
  6.         UPROPERTY(BlueprintAssignable)
  7.         FAIReceive OnCompleted;  //请求完成时
  8.         UPROPERTY(BlueprintAssignable)
  9.         FAIReceive OnFailed;    //请求失败时
  10.         UPROPERTY(BlueprintAssignable)
  11.         FAIReceive OnStream;     //流式请求时
  12.         FHttpRequestStreamDelegate StreamDelegate; //用于http流式请求的代理
  13.        
  14.         UFUNCTION(BlueprintCallable,meta=( BlueprintInternalUseOnly="true" ))
  15.         static  UAIFunction* SendAI( EAIType Type,const FAIRequest& Request);
  16.         UFUNCTION(BlueprintCallable,meta=( BlueprintInternalUseOnly="true" ))
  17.         static  UAIFunction* GetBaiduToken(const FString& apikey, const FString& secretkey);
  18.        
  19. protected:
  20.        
  21.         //自动生成token待完成
  22.         void BaiDuAI(const FAIRequest& Request);
  23.         void DianXinAI(const FAIRequest& Request);
  24. };
复制代码
我这里使用的异步蓝图节点,非壅闭式的请求http。对于蓝图的异步节点,大家可以自行了解。
完备的.h文件内容为下
  1. // 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)
  2. struct FAIRequest
  3. {
  4.         GENERATED_BODY()
  5.         UPROPERTY(BlueprintReadWrite)
  6.         FString URL;
  7.         UPROPERTY(BlueprintReadWrite)
  8.         TArray<FString> Message;
  9.         UPROPERTY(BlueprintReadWrite)
  10.         FString APIKey;
  11.         UPROPERTY(BlueprintReadWrite)
  12.         FString APISecret;
  13.         UPROPERTY(BlueprintReadWrite)
  14.         FString AppID;
  15.         UPROPERTY(BlueprintReadWrite)
  16.         FString access_token;
  17.         UPROPERTY(BlueprintReadWrite)
  18.         bool stream;
  19. };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文件中实现相应的函数
  1. //函数根据AI类型去调用相应的AI请求
  2. UAIFunction* UAIFunction::SendAI( EAIType Type,const FAIRequest& Request)
  3. {
  4.         UAIFunction* AIFunction = NewObject<UAIFunction>();
  5.         switch (Type)
  6.         {
  7.         case EAIType::Baidu:
  8.                 AIFunction->BaiDuAI(Request);
  9.                 break;
  10.         case EAIType::Ali:
  11.                 break;
  12.         case EAIType::Tencent:
  13.                 break;
  14.         case EAIType::Dianxin:
  15.                 break;
  16.         default:
  17.                 break;
  18.         }
  19.        
  20.         return AIFunction;
  21. }
  22. //获取Baidu的Token
  23. UAIFunction* UAIFunction::GetBaiduToken(const FString& apikey, const FString& secretkey)
  24. {
  25.         UAIFunction* AIFunction = NewObject<UAIFunction>();
  26.         TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = FHttpModule::Get().CreateRequest();
  27.         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 );
  28.         HttpRequest->SetURL(URL);
  29.         HttpRequest->SetVerb(TEXT("POST"));
  30.         HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
  31.         HttpRequest->OnProcessRequestComplete().BindLambda(([AIFunction](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
  32.         {
  33.                 if (bWasSuccessful && Response.IsValid())
  34.                 {
  35.                         FString ResponseContent =Response->GetContentAsString();
  36.                         TSharedPtr<FJsonObject> ResultObj;
  37.                         TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseContent);
  38.                         FJsonSerializer::Deserialize(Reader,ResultObj);
  39.                         FString DataString;
  40.                         if (ResultObj->TryGetStringField(TEXT("access_token"),DataString))
  41.                         {
  42.                                 AIFunction->OnCompleted.Broadcast(DataString);
  43.                         }
  44.                 }
  45.                 else
  46.                 {
  47.                         AIFunction->OnFailed.Broadcast(TEXT("失败"));
  48.                 }
  49.                
  50.         }));
  51.         HttpRequest->ProcessRequest();
  52.         return AIFunction;
  53. }
  54. //发送AI请求
  55. void UAIFunction::BaiDuAI(const FAIRequest& Request)
  56. {
  57.         AddToRoot();
  58.         TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = FHttpModule::Get().CreateRequest();
  59.         FString URL = FString::Printf(TEXT("%s?access_token=%s"), *Request.URL, *Request.access_token);
  60.         HttpRequest->SetURL(URL);
  61.         HttpRequest->SetVerb(TEXT("POST"));
  62.         HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
  63.         FString OutJsonData;
  64.         TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutJsonData);
  65.         Writer->WriteObjectStart();                                                // JSON对象开始
  66.         //Writer->WriteValue(L"category", InfoCategory);  // 填充普通字段
  67.         Writer->WriteArrayStart(L"messages");                        // Json 数组字段开始       
  68.         for (FString str : Request.Message)
  69.         {
  70.                 Writer->WriteObjectStart();
  71.                 Writer->WriteValue(L"role", L"user");                // 填充普通字段
  72.                 Writer->WriteValue(L"content", str);        // 填充普通字段
  73.                 Writer->WriteObjectEnd();
  74.         }
  75.         Writer->WriteArrayEnd();// Json 数组字段结束
  76.         Writer->WriteValue(L"stream", Request.stream); //是否流式
  77.         Writer->WriteObjectEnd();              //JSON对象结束
  78.         Writer->Close();
  79.         HttpRequest->SetContentAsString(OutJsonData); //设置流式请求代理
  80.         if (Request.stream)
  81.         {
  82.                 // 流式请求
  83.                 StreamDelegate.BindLambda([=,this](void* st,int64 length)
  84.                 {
  85.                         if (length >0)
  86.                         {
  87.                                 //处理数据
  88.                                 FString str = FString(UTF8_TO_TCHAR(static_cast<const char*>(st)));
  89.                                 str.RemoveAt(0,5); //去掉返回数据的前置字符,因为百度返回的数据前有一个data:的字符,无法解析
  90.                                 if (!str.IsEmpty())
  91.                                 {
  92.                                         str.ReplaceInline(TEXT("\\n"),TEXT(""));
  93.                                         str.RemoveSpacesInline();
  94.                                         TSharedPtr<FJsonObject> ResultObj;
  95.                                         TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(str);
  96.                                         FString DataString;
  97.                                         bool success =  FJsonSerializer::Deserialize(Reader,ResultObj);
  98.                                         FString Meaasge;
  99.                                         if (!success)
  100.                                         {
  101.                                                 return true;
  102.                                         }
  103.                                         if (ResultObj->TryGetStringField(TEXT("result"),Meaasge))
  104.                                         {
  105.                                                 if (!Meaasge.IsEmpty())
  106.                                                 {
  107.                                                         //Meaasge.RemoveFromStart(TEXT("\n")); 从开始删除第一个匹配的字符
  108.                                                         this->OnStream.Broadcast(Meaasge);
  109.                                                 }
  110.                                         }
  111.                                         else
  112.                                         {
  113.                                                 return true;
  114.                                         }
  115.                                 }
  116.                                 return true;
  117.                         }
  118.                         return false;
  119.                 });
  120.                 HttpRequest->SetResponseBodyReceiveStreamDelegate(StreamDelegate);
  121.         }
  122.         HttpRequest->OnProcessRequestComplete().BindLambda(([=,this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
  123.         {
  124.                 if (bWasSuccessful && Response.IsValid())
  125.                 {
  126.                         this->StreamDelegate.Unbind();
  127.                         FString ResponseContent =Response->GetContentAsString();
  128.                         this->OnCompleted.Broadcast(ResponseContent);
  129.                         RemoveFromRoot();
  130.                 }
  131.                
  132.         }));
  133.         HttpRequest->ProcessRequest();
  134. }
复制代码
完成后,编译乐成后会有下面两个节点。

注意:在选择了流式接口返回后,正常在请求Completed的时候是不会在返回数据的,也就是说StreamCompleted两个只会有一个有数据。

注:对于百度的模型,大家还是去看一下官方文档会对代码有更多的理解,而且必要去申请相应的账号。
文心一言API接入指南-百度开发者中心
https://developer.baidu.com/article/detail.html?id=1089328

开局逆风的才是主角。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

千千梦丶琪

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表