基于OpenAI的gpt-3.5模型API实现个人助手服务

引言

网上有不少OpenAI的API资料,实测下来,可能是环境因素踩了不少坑,这里分享一下我实践成功的技术路线。出于篇幅考虑,本文不会对开发前的部分工作,例如openai账号注册,外网访问权限获取,java,python环境搭建等问题过多赘述

相关资源地址

名称地址作用
ChatGPT访问地址https://chat.openai.com/chatchatgpt官方访问网址
GPT_KEYhttps://platform.openai.com/account/api-keys获取允许程序访问的key
discordhttps://dler.pro/auth/register?affid=142134获取外网访问权限
高德开放平台https://console.amap.com/dev/key/app获取高德开发平台服务,此案例中是需要高德的天气预报服务
163邮箱服务https://mail.163.com此案例中,通过邮件通知用户天气信息
腾讯云函数服务https://console.cloud.tencent.com/scf/list?rid=15&ns=default在本案例中没用到这个服务,曾经尝试过使用此服务作为python中转服务,因为节点可以选在国外,天然支持外网访问

设计思路

天气预报服务职能图

java端技术点及代码示例

  • 后台任务:目的是每天定时执行某段代码,此处采用的是公司产品自己封装的定时任务服务,用开源的同学可以自行去spring全家桶中找类似的框架
  • HTTP请求:采用的是post请求,代码可复用提供的HttpConnection类
  • 邮件发送:此处采用的是smtp协议,服务商为163,在使用163服务前需要去163邮箱官网开启smtp服务,并获取授权码。

定时任务及整体逻辑如下

package nc.plugin.cw_ext.reminder;

import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import nc.bs.logging.Logger;
import nc.bs.pub.pa.PreAlertObject;
import nc.bs.pub.taskcenter.BgWorkingContext;
import nc.bs.pub.taskcenter.IBackgroundWorkPlugin;
import nc.util.mrsy.CommonUtil;
import nc.util.mrsy.HttpConnection;
import nc.util.mrsy.UtilConstants.MaillSender;
import nc.vo.pub.BusinessException;
import nc.vo.pub.lang.UFDateTime;
import nccloud.message.util.MessageCenter;
import nccloud.message.vo.NCCMessage;
import nccloud.message.vo.NCCNoticeMessageVO;
import nccloud.util.cw_ext.JdbcUtil;
import nccloud.util.email.EmailUtil;


/**
 * WeatherReminderTaskPlugin:天气预报邮件通知插件
 * @author	CYQ
 * @date	2023年5月20日 下午5:05:14
 * @version	1.0.0
*/ 
public class WeatherReminderTaskPlugin implements IBackgroundWorkPlugin {

	private CommonUtil util = CommonUtil.getCommonUtil();
	@Override
	public PreAlertObject executeTask(BgWorkingContext context) throws BusinessException {
		try {
			String msg = sendEmail();
			
			//通过系统内置能力,调用消息通知,请忽略此功能
			sendMsg("CYQ",msg);
			
			context.setLogStr("天气预报邮件通知任务调用完成!");
			return null;
		} catch (Exception e) {
			throw new BusinessException("发生了未定义异常,"+e.getMessage());
		}
	}
	/**
	 * sendEmail:	发送邮件
	 * @param data	void	TODO(参数说明)
	 * 创  建  人 :CYQ
	 * 创建时间:2023年5月20日-下午5:53:50
	 * @throws Exception 
	*/
	private String sendEmail() throws Exception {
		Logger.error("begin...sendEmail");
		//获取明天天气
		String weather = getGDAPI();
		
		//通过gpt3.5获取信息
		String gptbody = getGPTbody(weather);
		
		//发送邮件
		EmailUtil.sendEmail(MaillSender.we, "明日天气预报,请查收~", gptbody);

		return "天气预报发送完毕";
	}
	/**
	 * 获取明天的天气
	 * @throws BusinessException
	 */
	private String getGDAPI() throws BusinessException {
		//url为高德天气预报服务的url,详情请参阅:https://console.amap.com/dev/key/app
		String url = util.getParameter("gd_url");
		String msg = HttpConnection.doGet(url, null, null);
		Logger.error("高德返回消息:"+msg);
		Map map = util.initMap(msg);
		JSONArray forecasts = (JSONArray)JSON.parse(util.initstr(map.get("forecasts")));
		Map info = util.initMap(forecasts.get(0));
		JSONArray casts = (JSONArray)JSON.parse(util.initstr(info.get("casts")));
		String tomorrow = util.initstr(casts.get(1));
		return tomorrow;
	}
	/**
	 * 调用GPT3.5中转服务,获取报文
	 * @param body
	 * @return
	 * @throws BusinessException 
	 */
	private String getGPTbody(String data) throws BusinessException {
		Logger.error("begin...getGPTbody");
		//请将XX,YY替换为身份和昵称
		String req = "请替我向我的XX,昵称YY,用中文讲述天气及注意事项,并问好。以下是明天的天气信息:"+data;
		JSONObject json = new JSONObject(); 
		json.put("msg", req);
		//由于python服务和java服务部署在一台机器上,所以访问127即可
		String msg = HttpConnection.doPost("http://127.0.0.1:8001/openai_gpt3", json.toString(), null);
		StringBuffer sb = new StringBuffer(msg);
		sb.delete(sb.length()-1, sb.length());
		sb.delete(0, 1);
		
		//解码unicode字符
		String text = unicodeDecode(sb.toString());
		Logger.error("getGPTbody...msg="+text);
		
		
		return text;
	}
    /**
     * @param string
     * @return 转换之后的内容
     * @Title: unicodeDecode
     * @Description: unicode解码 将Unicode的编码转换为中文
     */
    private String unicodeDecode(String string) {
        Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
        Matcher matcher = pattern.matcher(string);
        char ch;
        while (matcher.find()) {
            ch = (char) Integer.parseInt(matcher.group(2), 16);
            string = string.replace(matcher.group(1), ch + "");
        }
        return string;
    }

	/**
	 * 失败信息发送指定业务员
	 * 
	 * @MethodName: sendMsg
	 * memo by CYQ 2023年4月26日 理论代码没问题,前台不显示通知,怀疑是标准bug
	 * @author CYQ
	 * @date 2023年2月26日
	 */
	private void sendMsg(String msg_users, String msg) throws BusinessException {
		try {
			if (msg_users == null || msg_users.isEmpty()) {
				throw new BusinessException("未加载到有效的[msg_users]参数,请检查!");
			}
			NCCMessage message = new NCCMessage();
			NCCNoticeMessageVO msgvo = new NCCNoticeMessageVO();
			// 消息标题内容
			msgvo.setSubject(msg);
			msgvo.setSender("NC_USER0000000000000");
			// 可以一次群发,发送人
			String pk = JdbcUtil.queryColumn("sm_user", "cuserid", "user_code", msg_users);
			msgvo.setReceiver(pk);
			// 消息内容
			msgvo.setContent(msg);
			msgvo.setMsgsourcetype("reconcilemeg");
			msgvo.setSendtime(new UFDateTime());
//			msgvo.setDetail(msg);
			msgvo.setContenttype("BIZ");// 内容格式
			msgvo.setMsgtype("nc");// 消息发送类型
			msgvo.setMsgsourcetype("notice");// 消息来源类型
			msgvo.setPriority(0);// 优先级
			msgvo.setSendtime(new UFDateTime());// 发送时间
			message.setMessage(msgvo);

			message.setMessageType("notice");// 消息类型——通知
			// 发送确认消息
			MessageCenter.sendMessage(new NCCMessage[] { message });

		} catch (Exception e) {
			throw new BusinessException("业务执行完毕,NC消息发送异常..." + e.getMessage());
		}
	}

	
}

email工具类如下

package nccloud.util.email;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import nc.bs.logging.Logger;
import nc.util.mrsy.UtilConstants.MaillSender;
import nc.vo.pub.BusinessException;

/**
 * 邮箱服务工具类
 */
public class EmailUtil {

    public static void sendEmail(String to, String subject, String body) throws BusinessException {
    	try {
	    	Logger.error("begin...sendEmail");
	        // 设置邮件服务器属性
	        Properties props = new Properties();
	        props.put("mail.smtp.host", "smtp.163.com");
	        props.put("mail.smtp.socketFactory.port", "465");
	        props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
	        props.put("mail.smtp.auth", "true");
	        props.put("mail.smtp.port", "465");
	
	        // 创建邮件会话
	        Session session = Session.getInstance(props,
	                new javax.mail.Authenticator() {
	                    protected PasswordAuthentication getPasswordAuthentication() {
	                        return new PasswordAuthentication(MaillSender.me, MaillSender.mailkey);
	                    }
	                });
	
	        // 创建邮件消息
	        Message message = new MimeMessage(session);
	        message.setFrom(new InternetAddress(MaillSender.me));
	        message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
	        message.setSubject(subject);
	        message.setText(body);
	
	        // 发送邮件
	        Transport.send(message);
		} catch (Exception e) {
			// TODO: handle exception
			throw new BusinessException("邮件发送失败,"+e.getMessage(),"-1001");
		}
    }
}

http工具类如下

package nc.util.mrsy;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;

import nc.vo.pub.BusinessException;

/**
 * HttpConnection:HTTP工具类
 * 
 * @author CYQ
 * @date 2021年12月6日 下午5:45:31
 * @version 1.0.0
 */
public class HttpConnection {

	/**
	 * doPost:	doPost方法
	 * @param json
	 * @param url
	 * @return
	 * @throws Exception	String	TODO(参数说明)
	 * 创  建  人 :CYQ
	 * 创建时间:2022年5月19日
	*/
	public static String doPost(String url, String json, Map<String,String> head) throws BusinessException {

		BufferedReader in = null;
		InputStream inputStream = null;
		OutputStream outputStream = null;
		HttpURLConnection httpURLConnection = null;
		try {		
			if(url == null || url.isEmpty()) {
				throw new BusinessException("URL不能为空!");
			}
			URL adress=new URL(url);
			// 创建连接 测试
			httpURLConnection = (HttpURLConnection) adress.openConnection();
			// 设置请求的内容类型
			httpURLConnection.setRequestProperty("x-zop-ns", "budget");
			httpURLConnection.setRequestProperty("accept", "*/*");
			httpURLConnection.setRequestProperty("connection", "Keep-Alive");
			httpURLConnection.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
			httpURLConnection.setRequestProperty("Content-Type","application/json");

			httpURLConnection.setRequestProperty("User-Agent","PostmanRuntime/7.32.2");
			httpURLConnection.setRequestProperty("Accept","*/*");
			httpURLConnection.setRequestProperty("Accept-Encoding","gzip, deflate, br");
			httpURLConnection.setRequestProperty("Connection","keep-alive");
			httpURLConnection.setConnectTimeout(30000000);
			httpURLConnection.setReadTimeout(30000000);
			//追加请求头
			if(head!=null && head.size()>0) {
				for(String key:head.keySet()) {
					httpURLConnection.setRequestProperty(key,head.get(key));
				}
			}
			// 设置发送数据
			httpURLConnection.setDoOutput(true);
			// 设置接受数据
			httpURLConnection.setDoInput(true);
			httpURLConnection.setUseCaches(false);
			// 发送数据,使用输出流
			outputStream = httpURLConnection.getOutputStream();
			// 发送的soap协议的数据
			String content = json.toString();
			// 发送数据
			outputStream.write(content.getBytes("UTF-8"));
			// 接收数据 
			inputStream = httpURLConnection.getInputStream();
			in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
			StringBuffer buffer = new StringBuffer();
			String line = "";
			while ((line = in.readLine()) != null)
				buffer.append(line);
			String result = buffer.toString();
			outputStream.flush();
			outputStream.close();
			try {
				in.close();
				httpURLConnection.disconnect();
			} catch (IOException e) {
				e.printStackTrace();
				throw new Exception( e.getMessage()+e.getCause());
			}
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			throw new BusinessException("doPost异常,"+e.getMessage()+e.getCause());
		}
	}
	
}

python端技术点及代码示例

  • python版本采用的是3.11
  • 使用falcon发布http_post接口
  • 调用openai依赖获取gpt服务

具体服务发布步骤如下:

  1. 请确保python服务及pip服务安装正确
  2. 执行以下指令安装falcon依赖
pip install falcon
  1. 执行以下指令安装openai依赖
pip install openai
  1. 编写openai调用工具,文件命名为【api.py】,具体代码如下

案例中使用的gpt-3.5-turbo模型是综合了使用体验和费用的综合选择,若想使用其他模型,可以修改模型编码

# -*- coding: utf-8 -*-

import openai

openai.api_key = "填入openai中的key"
model_engine = "gpt-3.5-turbo"


def getgpt(reqmsg):
    # 发送API请求,获取响应
    response = openai.ChatCompletion.create(
        model=model_engine,
        messages=[
            {"role": "user", "content": reqmsg}
        ]
    )
    # 解析响应,获取回复
    output_text = response["choices"][0]["message"]["content"]
    return output_text

  1. 发布post接口,文件命名为【app.py】,具体代码如下
# -*- coding: utf-8 -*-

import falcon
import json
import api


class AppResource(object):

    # get请求
    def on_get(req, resp):
        msg = {
            "message": "Welcome to the Falcon"
        }
        resp.body = json.dumps(msg)

    # post请求
    def on_post(self, req, resp):
        try:
            result = req.media
            reqmsg = result['msg']
            msg = api.getgpt(reqmsg)
            resp.body = json.dumps(msg)
        except Exception as e:
            print(str(e))
            resp.body = '调用发生异常'+str(e)
        except SyntaxError as e1:
            print(str(e1))
            resp.body = '调用发生异常'+str(e1)


app = falcon.API()
app.add_route("/openai_gpt3", AppResource())

if __name__ == "__main__":
    from wsgiref import simple_server

    httpd = simple_server.make_server("127.0.0.1", 8001, app)
    httpd.serve_forever()

  1. 在cmd中执行以下指令,启动python中转服务
python app.py

效果如图所示
启动python服务

效果展示

效果展示

注:此技术路线仅为最终测试通过的路线,中间还验证过使用java,okhttp访问openai服务,使用腾讯云函数实现免费访问外网等技术路线,最终因种种原因未能验证通过。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值