本篇文章主要介绍一下,在Unity端,集成智谱AI开放平台提供的chatglm模型api,实现AI聊天互动相关的功能。从智谱AI官方站点上看到,提供有chatglm turbo的公共模型服务,能够实现32K超长上下文,应用到我们的AI二次元小姐姐项目中,完全足够了。
价格方面,官方页面上有看到,0.005元/千tokens这样的价格,而且目前新注册用户可以获取18元的API试用金额,核算下来,可以免费使用几百万的token,算是一笔不错的免费额度了。那么接下来,咱们来看一下如何在智谱AI开放平台申请API应用,并集成到Unity中吧。
首先,进入到智谱AI开放平台的官方站点,通过以下链接,进入到智谱AI开放平台网站
https://maas.aminer.cn/pricinghttps://maas.aminer.cn/pricing
进入官方站点后,自行注册账号,登录进入到智谱开放平台的控制台界面。
在控制台界面,可以看到平台赠送的18元试用金。点击查看api key按钮即可进入到应用创建页面,新增一个apikey,后续在Unity端调用api服务时,需要用到这个apikey。
根据官方文档说明,可以了解到,目前智谱AI开放平台针对Python以及Java提供有SDK,可以自行下载sdk示例。那么我们使用Unity就没有sdk可用了,只能根据官方文档的说明自行实现相关功能。
官方介绍了http调用的方式,总体流程上:
①接口鉴权,需要根据标准JWT的规则,生成token
在调用模型接口时需要传鉴权 token 进行认证;当前平台鉴权 token 由用户端生成,鉴权 token 生成采用标准 JWT 中提供的创建方法生成(详细参考:JSON Web Token Introduction - jwt.io)。
②api调用,采用post访问资源地址即可。
这里需要说明一下,POST访问的Url地址,chatglm turbo的模型访问地址:
https://open.bigmodel.cn/api/paas/v3/model-api/chatglm_turbo/sse-invokehttps://open.bigmodel.cn/api/paas/v3/model-api/chatglm_turbo/invoke
注:这里我使用的是官方不推进的同步方式,因为项目当前的情况,所以选择了这个方式,官方还提供有异步方式以及SSE两种方式,可以自行阅读文档。
官方文档地址:
https://maas.aminer.cn/dev/api#chatglm_turbohttps://maas.aminer.cn/dev/api#chatglm_turbo
本节内容将介绍在Unity中如何完成接口鉴权以及api的http访问的代码实现。
官方文档在接口鉴权流程中的token生成规则的描述不是很清晰,只描述了header和payload的格式,其实还有一部分Signature的生成规则,并没有在文档里说明,详细可以参见JWT的生成规则。
1、header的内容规则
根据官方文档,header部分的值为:
{"alg":"HS256","sign_type":"SIGN"}
alg
: 属性表示签名使用的算法,默认为 HMAC SHA256(写为HS256)
sign_type
: 属性表示令牌的类型,JWT 令牌统一写为 SIGN 。
2、payload 的内容规则
官方示例为
{"api_key":{ApiKey.id},"exp":1682503829130, "timestamp":1682503820130}
api_key
: 属性表示用户标识 id,即用户API Key的{id}
部分exp
: 属性表示生成的JWT的过期时间,客户端控制,单位为毫秒timestamp
: 属性表示当前时间戳,单位为毫秒这里需要注意一点,apikey的部分,前面从站点上创建的api key,观察api key的结构,是这样的规则{id}.{secret key},我们拿到apikey时,需要将密钥拆分成两部分,一部分就是上面这个payload需要使用的api_key,而另一部分的secret key,我们在后面生成Signature的时候要用到。
关于时间戳,生成13位时间戳编码即可。
鉴权token生成部分的代码如下:
#region 生成api鉴权token
///
/// 生成api鉴权 token
///
///
private string GetToken()
{
long expirationMilliseconds = DateTimeOffset.Now.AddHours(1).ToUnixTimeMilliseconds();
long timestampMilliseconds = DateTimeOffset.Now.ToUnixTimeMilliseconds();
string jwtToken = GenerateJwtToken(m_ApiKey, expirationMilliseconds, timestampMilliseconds);
return jwtToken;
}
//获取token
private string GenerateJwtToken(string apiKeyId, long expirationMilliseconds, long timestampMilliseconds)
{
// 构建Header
string _headerJson = "{\"alg\":\"HS256\",\"sign_type\":\"SIGN\"}";
string encodedHeader = Base64UrlEncode(_headerJson);
// 构建Payload
string _playLoadJson = string.Format("{{\"api_key\":\"{0}\",\"exp\":{1}, \"timestamp\":{2}}}", apiKeyId, expirationMilliseconds, timestampMilliseconds);
string encodedPayload = Base64UrlEncode(_playLoadJson);
// 构建签名
string signature = HMACsha256(m_SecretKey, $"{encodedHeader}.{encodedPayload}");
// 组合Header、Payload和Signature生成JWT令牌
string jwtToken = $"{encodedHeader}.{encodedPayload}.{signature}";
return jwtToken;
}
// Base64 URL编码
private string Base64UrlEncode(string input)
{
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
string base64 = Convert.ToBase64String(inputBytes);
return base64.Replace('+', '-').Replace('/', '_').TrimEnd('=');
}
// 使用HMAC SHA256生成签名
private string HMACsha256(string apiSecretIsKey, string buider)
{
byte[] bytes = Encoding.UTF8.GetBytes(apiSecretIsKey);
HMACSHA256 hMACSHA256 = new System.Security.Cryptography.HMACSHA256(bytes);
byte[] date = Encoding.UTF8.GetBytes(buider);
date = hMACSHA256.ComputeHash(date);
hMACSHA256.Clear();
return Convert.ToBase64String(date);
}
#endregion
获取到接口鉴权token后,通过POST访问api地址,接口地址如下:
https://open.bigmodel.cn/api/paas/v3/model-api/chatglm_turbo/sse-invokehttps://open.bigmodel.cn/api/paas/v3/model-api/chatglm_turbo/invoke
关于接口的报文结构,根据官方提供的接口文档说明,这里做一下详细说明。
请求报文结构:
prompt |
list |
是 |
调用对话模型时,将当前对话信息列表作为提示输入给模型; 按照 |
role |
string |
是 |
本条信息作者的角色,可选择 |
content |
string |
是 |
本条信息的具体内容 |
temperature |
float |
否 |
采样温度,控制输出的随机性,必须为正数 |
top_p |
float |
否 |
用温度取样的另一种方法,称为核取样 |
request_id |
string |
否 |
由用户端传参,需保证唯一性;用于区分每次请求的唯一标识,用户端不传时平台会默认生成。 |
incremental |
boolean |
否 |
SSE接口调用时,用于控制每次返回内容方式是增量还是全量,不提供此参数时默认为增量返回 |
return_type |
string |
否 |
用于控制每次返回内容的类型,空或者没有此字段时默认按照json_string返回 |
ref |
object |
否 |
用于控制请求时的外部信息引用,目前用于控制是否引用外部信息,空或者没有此字段时默认开启搜索,传参格式 |
回复报文结构:
code |
int |
错误码,详情请查看错误码说明 |
msg |
string |
错误信息 |
success |
boolean |
请求成功失败标识,true(成功),false(失败) |
data |
object |
|
request_id |
string |
用户在客户端请求时提交的任务编号或者平台生成的任务编号 |
task_id |
string |
智谱AI开放平台生成的任务订单号,调用请求结果接口时请使用此订单号 |
task_status |
string |
处理状态,PROCESSING(处理中),SUCCESS(成功),FAIL(失败) |
接口的调用,采用POST方式即可,示例代码如下:
///
/// 发送数据
///
///
///
///
public override IEnumerator Request(string _postWord, System.Action _callback)
{
stopwatch.Restart();
string jsonPayload = JsonConvert.SerializeObject(new RequestData
{
model=m_Type.ToString(),
prompt = m_DataList
});
using (UnityWebRequest request = new UnityWebRequest(url, "POST"))
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(jsonPayload);
request.uploadHandler = (UploadHandler)new UploadHandlerRaw(data);
request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
request.SetRequestHeader("Authorization", GetToken());
yield return request.SendWebRequest();
if (request.responseCode == 200)
{
string _msg = request.downloadHandler.text;
ResponseData response = JsonConvert.DeserializeObject(_msg);
if (response.data.choices.Count > 0)
{
string _msgBack = response.data.choices[0].content;
//回调
_callback(_msgBack);
}
else
{
Debug.Log(_msg);
}
}
}
stopwatch.Stop();
Debug.Log("chatGLM Turbo耗时:" + stopwatch.Elapsed.TotalSeconds);
}
数据定义:
[Serializable]
private class RequestData
{
[SerializeField] public string model;
[SerializeField] public List prompt;
[SerializeField] public float temperature = 0.7f;
}
[Serializable]
private class ResponseData
{
[SerializeField] public int code;
[SerializeField] public string msg = string.Empty;
[SerializeField] public string success = string.Empty;
[SerializeField] public ReData data=new ReData();
}
[Serializable]
private class ReData
{
[SerializeField] public string task_id = string.Empty;
[SerializeField] public string request_id = string.Empty;
[SerializeField] public string task_status = string.Empty;
[SerializeField] public List choices=new List();
}
项目的源码已经发布到Github了,我们可以直接下载,并导入到unity中使用,要求unity版本在2020.3.44及以上。导入工具包之后,可以在Scene文件夹下,找到示例场景,在场景中找到LLM->chatglm Turbo对象,这里就维护了智谱AI开放平台的模型调用脚本。
项目已经实现了智谱AI开放平台对接所需的接口鉴权以及API访问的代码,我们只需要在控制台中,找到创建好的api key,将密钥填写到脚本中的{Key}参数即可。
详细配置,可以到我的B站主页,查看本期视频的配置过程。
这次的文章简单介绍了智谱AI开放平台的API的对接流程,并针对接口对接的流程进行了介绍,包括接口的鉴权、基于JWT的token生成规则,以及chatglm turbo模型的接口调用代码示例等内容,通过上述的代码实现,我们就可以在unity引擎中,使用智谱AI开放平台的api来驱动AI二次元小姐姐的对话交互。完整的代码工程可以从我的开源项目下载使用,项目包含了针对多种GPT应用的集成工具,以及语音服务的集成,对我这个项目感兴趣的朋友,可以上我的B站号查看,我也做有详细的教程,相关源码可以在的哔哩哔哩主站找到相关视频,在视频介绍以及评论区获取。
[Unity+AI Chat+智谱AI]轻松实现32K超长上下文对话, 对接智谱AI开放平台ChatGLM Turbo,简单配置AI二次元小姐姐
项目地址传送门:
AI二次元老婆开源项目(unity-AI-Chat-Toolkit):
Github地址:https://github.com/zhangliwei7758/unity-AI-Chat-Toolkit
Gitee地址:https://gitee.com/DammonSpace/unity-ai-chat-toolkit