前言

当初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美元。本人用着玩的话足够应用了。