共计 8562 个字符,预计需要花费 22 分钟才能阅读完成。
前言
当初 Chat GPT 性能越来越强了,简直你想问理论问题它都能给你答复。
正好,小组结课的 Android 我的项目 有一个解梦的性能。正好调用 chatGpt 的 Api 来实现。
上面就来简略实现在 Andorid 我的项目中打造一个繁难的聊天机器人。
先贴个成果, 还没进行丑化:
1. 创立一个 Andorid 我的项目
这里就不在具体地介绍如何创立一个 Andorid 我的项目了。谷歌上很多文章,比方官网示例:
https://developer.android.com/training/basics/firstapp/creati…
2. 在 build.gradle 中增加依赖
这是用到的依赖
implementation 'com.google.android.material:material:1.7.0'
implementation 'com.android.volley:volley:1.2.0'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.9.3'
compileOnly 'org.projectlombok:lombok:1.18.4'
annotationProcessor 'org.projectlombok:lombok:1.18.4'
implementation 'org.apache.commons:commons-lang3:3.6'
3. 在 AndroidManifest.xml 中增加联网权限
<uses-permission android:name="android.permission.INTERNET"/>
4. 注册 OpenAI api
来到 OpenAI api 注册账号,并生成 SECRET KEY
记得保留好,之后会用到。
5. 编写 xml 界面文件
在 res/layout 下新建 activity_chat.xml 文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/idTILQuery"
android:layout_alignParentTop="true"
android:padding="5dp"
android:layout_marginTop="5dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- text view for displaying question-->
<TextView
android:id="@+id/idTVQuestion"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="30dp"
android:padding="4dp"
android:text="Question"
android:textColor="@color/purple_700"
android:textSize="17sp"/>
<!-- text view for displaying response-->
<TextView
android:id="@+id/idTVResponse"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:padding="4dp"
android:text="Response"
android:textColor="@color/purple_700"
android:textSize="15sp"/>
</LinearLayout>
</ScrollView>
<!-- text field for asking question-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/idTILQuery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_margin="5dp"
app:endIconMode="custom"
app:endIconDrawable="@drawable/send"
android:hint="Enter your query"
android:padding="5dp"
android:textColorHint="@color/black"
app:hintTextColor="@color/black">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/idEdtQuery"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey"
android:ems="10"
android:imeOptions="actionSend"
android:importantForAutofill="no"
android:inputType="textEmailAddress"
android:textColor="@color/black"
android:textColorHint="@color/black"
android:textSize="14sp" />
</com.google.android.material.textfield.TextInputLayout>
</RelativeLayout>
一个简略的页面如下
5. 编写 Activity 文件
绑定控件
首先,咱们先创立了三个控件显示内容:问题、响应、用户输出。
用 setContentView 绑定 xml 文件,并应用 findViewById 给三个控件初始化
public class ChatActivity extends AppCompatActivity {
TextView responseTV;
TextView questionTV;
TextInputLayout queryEdt;
@Override
protected void onCreate(Bundle savedInstanceState) {DynamicColors.applyToActivityIfAvailable(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
// initializing
responseTV = findViewById(R.id.idTVResponse);
questionTV = findViewById(R.id.idTVQuestion);
queryEdt = findViewById(R.id.idTILQuery);
}
}
监听发送按钮
其次,咱们须要监听 发送按钮
,当用户点击后发送后,申请 chatGpt。并显示 Please wait..
,让用户期待申请后果
@Override
protected void onCreate(Bundle savedInstanceState) {DynamicColors.applyToActivityIfAvailable(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
// initializing variables on below line.
responseTV = findViewById(R.id.idTVResponse);
questionTV = findViewById(R.id.idTVQuestion);
queryEdt = findViewById(R.id.idTILQuery);
queryEdt.setEndIconOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {responseTV.setText("Please wait..");
if (queryEdt.getEditText().getText().toString().length() > 0) {getResponse(queryEdt.getEditText().getText().toString());
} else {Toast.makeText(ChatActivity.this, "Please enter your query..", Toast.LENGTH_SHORT).show();}
}
});
}
对 HttpURLConnection 设置代理
之后。咱们应该对 api 进行申请了。然而 OpenAI 的 APi 在外网上,如果手机没有设置代理,咱们就不能通过手机的网络间接对 api 进行申请。咱们须要对 HttpURLConnection 设置代理:
当然,如果手机的网络有代理的话,就不须要了。
新建 ProxiedHurlStack 类,填上代理服务器的地址和端口。
public class ProxiedHurlStack extends HurlStack {
@Override
protected HttpURLConnection createConnection(URL url) throws IOException {
// Start the connection by specifying a proxy server
Proxy proxy = new Proxy(Type.HTTP,
InetSocketAddress.createUnresolved("192.168.2.197", 7890));//the proxy server(Can be your laptop ip or company proxy)
HttpURLConnection returnThis = (HttpURLConnection) url
.openConnection(proxy);
return returnThis;
}
}
如果你电脑能应用代理, 那咱们还能够通过手机和电脑处在同一局域网下,使手机通过电脑的代理拜访。这里以 Clash 为例。点击 Clash, 凋谢端口给局域网连贯。
之后,咱们就能够把代理地址设置为电脑的 IPv4 地址了。
InetSocketAddress.createUnresolved("192.168.2.197", 7890));
新建响应实体
依据 OpenAI API Reference,响应如下:
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "\n\nHello there, how may I assist you today?",
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 12,
"total_tokens": 21
}
}
所以,咱们只须要获取到 content
字段,就能咱们和收到 ChatGPT 聊天的后果了。
对 ChatGpt 返回的响应,建设实体跟其对应
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class ChatGptResponse {
private String id;
private String object;
private Long created;
private List<Choice> choices;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Choice implements Serializable {
private String index;
private String finish_reason;
private Message message;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Message implements Serializable {
private String role;
private String content;
}
发动申请
之后,查看 OpenAI API Reference, 依据文档对 api 进行申请。这里申请的模型是 ChatGpt3.5。
记得在上面的代码中填写你的 SecretKey
private void getResponse(String query) {
try {questionTV.setText(query);
RequestQueue requestQueue = Volley.newRequestQueue(this, new ProxiedHurlStack());
String URL = "https://api.openai.com/v1/chat/completions";
JSONObject jsonBody = new JSONObject();
jsonBody.put("model", "gpt-3.5-turbo");
JSONArray array = new JSONArray();
JSONObject jsonObject = new JSONObject();
jsonObject.put("role", "user");
jsonObject.put("content", query);
array.put(jsonObject);
jsonBody.put("messages", array);
final String requestBody = jsonBody.toString();
StringRequest stringRequest = new StringRequest(Request.Method.POST, URL, new Response.Listener<String>() {
@Override
public void onResponse(String response) {Log.i("VOLLEY", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {Log.i("VOLLEY", String.valueOf(error));
}
}) {
@Override
public String getBodyContentType() {return "application/json; charset=utf-8";}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json");
params.put("Authorization", "Bearer yourSecretKey");
return params;
}
@Override
public byte[] getBody() throws AuthFailureError {
try {return requestBody == null ? null : requestBody.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8");
return null;
}
}
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {String responseString = new String(response.data, StandardCharsets.UTF_8);
// 创立 ObjectMapper 对象。ObjectMapper mapper = new ObjectMapper();
// Json 格局字符串转 Java 对象。try {ChatGptResponse javaEntity = mapper.readValue(responseString, ChatGptResponse.class);
String responseMsg = javaEntity.getChoices().get(0).getMessage().getContent();
runOnUiThread(new Runnable() {
@Override
public void run() {responseTV.setText(responseMsg);
}
});
} catch (JsonProcessingException e) {throw new RuntimeException(e);
} catch (IOException e) {throw new RuntimeException(e);
}
return Response.success(responseString, HttpHeaderParser.parseCacheHeaders(response));
}
};
stringRequest.setRetryPolicy(new RetryPolicy() {
@Override
public int getCurrentTimeout() {return 50000;}
@Override
public int getCurrentRetryCount() {return 50000;}
@Override
public void retry(VolleyError error) throws VolleyError {Log.i("VOLLEY", String.valueOf(error));
}
});
requestQueue.add(stringRequest);
} catch (JSONException e) {e.printStackTrace();
}
}
咱们在申请头上填上咱们的 SecretKey
作为认证信息。
期待 ChatGpt 响应后,把响应的 byte[]
转换为 Java 对象。
之后将信息显示在屏幕上。
目前我只取了响应的 content 信息,所以对话不是间断的,对接不了上文。
之后能够再参考 文档 将聊天做成间断的。
每个账户有 18 美元 的收费额度,测试的时候 发了 25 个申请,也才花了 0.01 美元。本人用着玩的话足够应用了。