1. 程式人生 > >用Python做一個聊天機器人

用Python做一個聊天機器人

最近在使用騰訊語音合成時發現了一個有趣的東西:智慧閒聊

好奇之下點了進去,發現是一個智慧聊天的功能。然後就順勢根據這個api寫了一個簡單的聊天機器人。

好了,廢話不多說,下面來一步一步實現聊天機器人

1:在騰訊ai開放平臺建立一個應用。

2:獲得該應用的app_id和app_key

3:分析開發文件

開發文件地址:https://ai.qq.com/doc/nlpchat.shtml

可以看到,要實現這個功能,需要訪問如下的地址來請求資料:

https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat

而要訪問這個地址獲得資料還需要攜帶一些引數,才能獲得想要的結果。如下:

app_id:應用的標識,剛才已經複製過。必須是 int 型別的

time_stamp:時間戳,這個引數需要實時獲取。後邊程式碼裡可以實現。(int型別)

nonce_str:隨機的字串,用來保證簽名(sign)不被預測。程式碼裡會實現。

sign:每次請求的簽名。需要根據官方提供的演算法進行計算獲得。

session:會話標識。(具體從哪獲得這個不太清楚,但是我隨便寫的10000可以用)。

question:你的問題。(最長不能超過100個漢字,(utf8編碼格式下一個漢字佔3個位元組))。

 

關於簽名(sign)的計算方法,可以看到官方文件提供的演算法如下:

官方文件寫的比較模糊,關於這個演算法具體的分析可以看我的另一篇部落格:

         https://blog.csdn.net/hungpangzi/article/details/84334325

這裡不再贅述。

關於這個獲得資料的http請求還有一些限制:

4:根據開發文件寫程式碼

<1>:首先定義一個基礎類(因為騰訊ai開放平臺的很多api的呼叫方式都差不多,只是引數略有不同。因此該類的作用就是將這些共同的程式碼寫出來,預留出的介面可以供不同的功能使用。)

class BaseClass:
    def __init__(self, url):
        """
        :param url:api的訪問地址
        """
        self.URL = url;
        self.APP_ID = 000000000000; # 你自己的app_id
        self.APP_KEY = "xxxxxxxxx"; # 你自己的app_key

        # params屬性需要使用者後來修改,新增對應api所需的引數
        # 這裡列舉出的引數都是共有的,特有的引數需要使用者自己傳入
        self.params = {
            'app_id' : self.APP_ID,
            'time_stamp' : None,
            'nonce_str' : None,
        };

        # 呼叫介面返回的結果
        self.result = None;


    def __get_sign(self):
        """
        計算獲得sign的方法
        :return:None
        """
        # 獲得時間戳(秒級),防止請求重放
        time_stamp = int(time.time());

        # 獲得隨機字串,保證簽名不被預測
        nonce_str = ''.join(random.sample(string.ascii_letters + string.digits, 10))

        # 組合引數(缺少sign,其值要根據以下獲得)
        self.params['time_stamp'] = time_stamp;
        self.params['nonce_str'] = nonce_str;

        # 計算獲得sign的值
        before_sign = '';
        # 對key排序拼接
        for key in sorted(self.params):
            before_sign += f'{key}={quote(str(self.params[key]).encode("utf-8"))}&';

        # 將應用祕鑰以app_key為鍵名,拼接到before_sign的末尾
        before_sign += f"app_key={self.APP_KEY}";

        # 對獲得的before_sign進行MD5加密(結果大寫),得到藉口請求籤名
        sign = hashlib.md5(before_sign.encode("utf-8")).hexdigest().upper();

        # 將請求籤名新增進引數字典
        self.params["sign"] = sign;


    def get_result(self):
        """
        該方法用於呼叫api,獲得返回的結果
        :return: None
        """
        # 完善params引數,將sign新增進引數字典
        self.__get_sign();

        params = urllib.parse.urlencode(self.params).encode("utf-8");
        req = request.Request(url=self.URL, data=params);

        # 設定超時10秒,重試3次
        count = 0;
        while True:
            try:
                count += 1;
                self.result = request.urlopen(req, timeout=10);
                break;
            except Exception as e:
                print(e)
                print(f"連線超時,正在進行第{str(count)}次重連")
                if count <= 3:
                    continue;
                else:
                    break;

    def do_result(self):
        """
        處理結果的方法
        :return: None
        """
        pass;

    
    def run(self):
        """
        主執行方法
        :return: None
        """
        pass;

<2>:實現智慧閒聊功能。(定義TencentChat類繼承上邊的基類)

class TencetChat(BaseClass):
    def __init__(self, question):
        """
        :param question: 聊天的問題
        """
        super(TencetChat, self).__init__("https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat");
        self.params["session"] = "10000" # 這個是我隨便寫的,可以使用。
        self.question = question;


    def deal_question(self):
        """
        對提出的問題進行處理,限制長度和型別
        :return: None
        """
        if not isinstance(self.question, str):
            raise TypeError(f"question引數必須是 ‘str’ 型別的,不能是 ‘{type(self.question)}’ 型別的!!!");
        else:
            if len(self.question.encode("utf-8")) > 300:
                raise ValueError("question引數的長度必須小於300個位元組(utf-8格式下)")
            else:
                self.params["question"] = self.question;
                self.do_result();


    def do_result(self):
        """
        處理結果
        :return:None
        """
        self.get_result();
        if self.result:
            res = json.loads(self.result.read().decode("utf-8"));
        # print(res)
            if not res["msg"] == "ok":
                self.answer = "我好像出錯了:"+res["msg"];
            else:
                self.answer = res["data"]["answer"];
        else:
            self.answer="我嘗試了4次,但還是失敗了,只能說我盡力了。";

    def run(self):
        """
        執行方法
        :return:None
        """
        self.deal_question();


if __name__ == '__main__':
    chat = TencentChat("你好");
    chat.run();
    print("智慧閒聊:"+chat.answer);

<3>:為了方便的使用,可以實現一個方法,用來持久化對話,程式碼如下:

# 整合之後的一個聊天程式
def complete_chat():
    """
    一個完整的聊天的方法
    :return: None
    """
    print("歡迎使用智慧閒聊,下面開始聊天吧(輸入quit退出聊天):")
    print("*"*50)
    while True:
        question = input("我:");
        if question == "quit":
            break;
        t_chat = TencetChat(question);
        t_chat.run();
        answer = t_chat.answer;
        print("智慧閒聊:",answer);


if __name__ == '__main__':
    complete_chat();

一個簡單的聊天程式就實現了。

以上程式碼可以直接複製貼上使用。

完整程式碼可到我的GitHub獲取:https://github.com/shyorange/InterestingProgram

=================轉載請註明出處=============================================