原本是想实现调用ChatGPT接口,但是能力有限,没办法搞到ChatGPT的apiKey。于是就想着实现文言一心(来自于百度的千帆大模型)的接口。如果是需要实现对话的功能,调用该接口是需要付费的。具体付费规则如下
刚注册的账号存在一个20元的代金卷,可以使用代金券练手。
如果没有代金券而且不想开通就想测试调用接口的功能的话,存在一个免费试用的接口。Prompt模板 。
接下来我分别实现就实现一下调用Prompt模板的功能与调用对话接口的功能。
无论是实现调用免费的接口还是付费接口,都要先获取AccessToken,目的是鉴权。
查看对应文档获取access_token - 千帆大模型平台 | 百度智能云文档 (baidu.com)
根据文档我们知道我们向接口发起一个POST请求。一个请求头以及三个Query参数。
我们可以根据在线调试平台获取到部分Java代码。我们参考它的文档去写。
首先我们在pom文件中导入了如下jar包
com.squareup.okhttp3
okhttp
4.8.1
com.alibaba
fastjson
2.0.19
org.slf4j
slf4j-log4j12
1.7.36
org.projectlombok
lombok
1.18.8
编写测试获取AccessToken的方法
@Slf4j
public class Chat {
private final String ACCESS_TOKEN_URI = "https://aip.baidubce.com/oauth/2.0/token";
private String apiKey = "yourApikey";
private String secretKey = "yourSecetkey";
private String accessToken;
private OkHttpClient client = new OkHttpClient();
public boolean getAccessToken(){
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "");
//创建一个请求
Request request = new Request.Builder()
.url(ACCESS_TOKEN_URI+"?client_id=" + apiKey + "&client_secret=" + secretKey + "&grant_type=client_credentials")
.method("POST",body)
.addHeader("Content-Type", "application/json")
.build();
try {
//使用浏览器对象发起请求
Response response = client.newCall(request).execute();
//只能执行一次response.body().string()。下次再执行会抛出流关闭异常,因此需要一个对象存储返回结果
String responseMessage = response.body().string();
log.debug("获取accessToken成功");
JSONObject jsonObject = JSON.parseObject(responseMessage);
accessToken = (String) jsonObject.get("access_token");
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}
我们将调用的获取AccessToken接口获取到的信息转为JSON格式后,获取access_token属性值,复制给类属性。
public class Main {
public static void main(String[] args) throws InterruptedException {
Chat chat = new Chat();
boolean result = chat.getAccessToken();
System.out.println(result);
}
}
运行结果为:
19:46:13,387 DEBUG Chat:48 - 获取accessToken成功
true
查看对应文档Prompt模板 - 千帆大模型平台 | 百度智能云文档 (baidu.com)。
要想调用该接口,我们需要事先创建好Promet模板。创建模板过程如下
创建好模板后,我们后续需要调用模板ID。
具体实现代码如下
/**
* 调用Prompt接口
* @param param
*/
public void getPrompt(int id,String param){
Request request = new Request.Builder()
// https://aip.baidubce.com/rest/2.0/wenxinworkshop/api/v1/template/info?access_token=xxx&id=7964&name=value
.url(CHAT_URI + "?access_token="+accessToken+"&id="+id+"&name="+param)
.addHeader("Content-Type", "application/json")
.method("GET",null)
.build();
try {
Response responseMessage = client.newCall(request).execute();
JSONObject jsonObject = JSON.parseObject(responseMessage.body().string());
log.debug(jsonObject.toString());
Object result = jsonObject.get("result");
log.debug("{}",result.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Chat chat = new Chat();
boolean result = chat.getAccessToken();
if (result){
chat.getPrompt(7964,"zmbwcx");
}
}
}
运行结果为
19:48:04,906 DEBUG Chat:48 - 获取accessToken成功
19:48:05,144 DEBUG Chat:99 - {"result":{"templateName":"测试接口调用","templateContent":"文章内容:{name}测试接口调用成功","templateId":7964,"content":"文章内容:zmbwcx测试接口调用成功","templateVariables":"name"},"log_id":"0hkyy9izb4azvsgp","success":true,"status":200}
19:48:05,145 DEBUG Chat:101 - {"templateName":"测试接口调用","templateContent":"文章内容:{name}测试接口调用成功","templateId":7964,"content":"文章内容:zmbwcx测试接口调用成功","templateVariables":"name"}
同样是观察文档,不过实现对话接口调用比实现调用Prompt接口稍微复杂一些。需要创建一些类来设置我们的参数。
创建请求参数类
@Data
public class RequestMessage {
/**
* 聊天上下文
*/
List messages = new ArrayList<>();
/**
* 范围(0~1.0]
* 较高的数值会使输出更加随机
*/
float temperature = Float.parseFloat("0.95");
/**
* 影响文本的多样性,取值越大生成的文本多样性越强
* 建议该参数与temperature只设置一个。建议top_p和temperature不要同时更改
*/
float top_p = Float.parseFloat("0.8");
/**
* 通过对已生成的token增加惩罚,减少重复生成的现象
* 值越大,惩罚越大
* 取值范围[1,2]
*/
float penalty_score = Float.parseFloat("1.0");
/**
* 是否以流式接口形式返回数据
*/
boolean stream = false;
/**
* 模型人设
*/
String system = null;
/**
* 表示用户唯一标识符,用于监测和检测滥用行为。防止接口恶意调用。
*/
String user_id = "";
public void addMessage(Message message){
this.messages.add(message);
}
}
创建Message类
@Data
public class Message {
/**
* 用户角色
* 目前支持:
* user 用户
* assistant 对话助手
*/
String role;
/**
* 对话内容。
*/
String content;
public Message(String role, String content) {
this.role = role;
this.content = content;
}
}
设置接收响应信息类
@Data
public class ResponseMessage {
//本轮对话id
String id;
//回包类型。 chat.completion:多轮对话返回
String object;
//时间戳
int created;
//表示当前子句的序号。只有在流式接口模式下才会返回该字段
int sentence_id;
//表示当前子句是否是最后一句。只有在流式接口模式下会返回该字段。
boolean is_end;
//对话返回结果。
String result;
/**
* 表示用户输入是否存在安全,是否关闭当前会话,清理历史回话信息。
* true:是,表示用户输入存在安全风险,建议关闭当前会话,清理历史会话信息。
* false:否,表示用户输入无安全风险。
*/
boolean need_clear_history;
//token统计信息,token数 = 汉字数+单词数*1.3 (仅为估算逻辑)。
Usage usage;
}
设置Usage类
public class Usage {
//问题token数
int prompt_tokens;
//回答token数
int completion_tokens;
//token总数
int total_tokens;
}
如果看文档不太清楚的话,我们可以通过在线调试平台观察一下请求所需要的必须参数与响应过来的参数结构。
接下来我们编写Chat类
@Slf4j
public class Chat {
private final String ACCESS_TOKEN_URI = "https://aip.baidubce.com/oauth/2.0/token";
private final String CHAT_URI = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant";
private String apiKey = "yourApikey";
private String secretKey = "yourSecretkey";
private String accessToken = "";
private OkHttpClient client ;
//请求参数
private RequestMessage requestBody = new RequestMessage();
//响应超时时间
private int responseTimeOut = 5000;
public Chat(){
this.client = new OkHttpClient.Builder().readTimeout(responseTimeOut,TimeUnit.SECONDS).build();
}
public Chat(int responseTimeOut){
this.client = new OkHttpClient.Builder().readTimeout(responseTimeOut,TimeUnit.SECONDS).build();
}
public boolean getAccessToken(){
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "");
//创建一个请求
Request request = new Request.Builder()
.url(ACCESS_TOKEN_URI+"?client_id=" + apiKey + "&client_secret=" + secretKey + "&grant_type=client_credentials")
.method("POST",body)
.addHeader("Content-Type", "application/json")
.build();
try {
//使用浏览器对象发起请求
Response response = client.newCall(request).execute();
//只能执行一次response.body().string()。下次再执行会抛出流关闭异常,因此需要一个对象存储返回结果
String responseMessage = response.body().string();
log.debug("获取accessToken成功");
JSONObject jsonObject = JSON.parseObject(responseMessage);
accessToken = (String) jsonObject.get("access_token");
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
*
* 获取问题参数,返回答案
* @param question
*/
public void getAnswer(String question){
//通过参数获取一个Message
Message message = new Message("user",question);
//将新的问题添加到消息上下文
requestBody.addMessage(message);
String jsonStr = JSON.toJSONString(requestBody);
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, jsonStr);
Request request = new Request.Builder()
.url(CHAT_URI + "?access_token="+accessToken)
.addHeader("Content-Type", "application/json")
.method("POST",body)
.build();
try {
Response response = client.newCall(request).execute();
log.debug("发送一次请求,询问问题:{}",question);
String responseJsonStr = response.body().string();
ResponseMessage responseMessage = JSON.parseObject(responseJsonStr, ResponseMessage.class);
System.out.println("返回的响应结果为:"+responseJsonStr);
String result = responseMessage.getResult();
String answer = result.replaceAll("\n+", "\n");
log.debug("{}",answer);
Message assistant = new Message("assistant", answer);
requestBody.addMessage(assistant);
} catch (IOException e) {
e.printStackTrace();
}
}
public void getRequestBody(){
System.out.println(JSON.toJSONString(requestBody));
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Chat chat = new Chat();
boolean result = chat.getAccessToken();
if (result){
Scanner scanner = new Scanner(System.in);
String question = scanner.nextLine();
while(!"q".equals(question)){
chat.getAnswer(question);
chat.getRequestBody();
question = scanner.nextLine();
}
}
}
}
运行测试
将返回的结果与requestBody转为JSON格式观察
格式转化正确。测试是否可以上下文联系
将最后一行格式转化观察
发现可以正常响应。至此,我们实现了调用千帆大模型的对话接口调用。