自己動手設計交談機器人 (Eliza 中文版) - 使用 Java

程式作品

C 語言

Java

C#

JavaScript

常用函數

文字處理

遊戲程式

衛星定位

系統程式

資料結構

網路程式

自然語言

人工智慧

機率統計

資訊安全

等待完成

訊息

相關網站

參考文獻

最新修改

簡體版

English

程式專案下載:ChatBot.zip

簡介

你的程式會與人聊天嗎,本文將說明如何用 Java 設計一個命令列的聊天虛擬機器人。以下是一個簡單的聊天對話過程,其中有 » 開頭的部份是程式自動產生的語句,

您好,我的朋友! 有心事嗎 ?
>> 我不開心
為何不開心?
>> 和同學吵架了
這樣喔!知道!
>> 心情很惡劣
可否多告訴ㄧ些呢?
>> 我最好的朋友罵我
你認識他多久了呢?
>> 很久了,超過十年了
...

規則庫

聊天程式的重點在於規則庫的使用,這些規則其實是一些樣式,一但比對成功後就會使用特定的回答語句,以回答問話者的問題,讀者可以看到這些規則都很簡單,但其實都只是抓住了一些常用的詞彙,然後以相當空泛的方式回答,這就是聊天程式得以矇混過關的原因。

// --- ChatBotRule.txt -----
Q:謝謝
不客氣!

Q:對不起 | 抱歉 | 不好意思
別說抱歉 !
別客氣,儘管說 !

Q:可否 | 可不可以
你確定想*

Q:我想
你為何想*

Q:我要
你為何要*

Q:你是
你認為我是*

Q:認為 | 以為
為何說*?

Q:感覺
常有這種感覺嗎?

Q:為何不
你希望我*

Q:是否
為何想知道是否*

Q:不能
為何不能*?
你試過了嗎?
或許你現在能*了呢?

Q:我是
你好,久仰久仰!

Q:甚麼 | 什麼 | 何時 | 誰 | 哪裡 | 如何 | 為何 | 因何
為何這樣問?
為何你對這問題有興趣?
你認為答案是甚麼呢?
你認為如何呢?
你常問這類問題嗎?
這真的是你想知道的嗎?
為何不問問別人?
你曾有過類似的問題嗎?
你問這問題的原因是甚麼呢?

Q:原因
這是真正的原因嗎?
還有其他原因嗎?

Q:理由
這說明了甚麼呢?
還有其他理由嗎?

Q:你好 | 嗨 | 您好
你好,有甚麼問題嗎?

Q:或許
你好像不太確定?

Q:不曉得 | 不知道
為何不知道?
在想想看,有沒有甚麼可能性?

Q:不想 | 不希望
有沒有甚麼辦法呢?
為何不想*?
那你希望怎樣呢?

Q:想 | 希望
為何想*?
真的想*?
那就去做阿?
為何不呢?

Q:不
為何不*?

Q:請
我該如何*
你想要我*

Q:你
你真的是在說我嗎?
別說我了,談談你吧!
為何這麼關心我*
不要再說我了,談談你吧!
你自己*

Q:總是 | 常常
能不能具體說明呢?
何時?

Q:像
有多像?
哪裡像?

Q:對
你確定嗎?
我了解!

Q:朋友
多告訴我一些有關他的事吧!
你認識他多久了呢?

Q:電腦
你說的電腦是指我嗎?

Q:難過
別想它了
別難過
別想那麼多了
事情總是會解決的 

Q:高興
不錯ㄚ
太棒了
這樣很好ㄚ

Q:是阿|是的
甚麼事呢?
我可以幫助你嗎?
我希望我能幫得上忙!

Q:比對失敗
我了解
我能理解
還有問題嗎 ?
請繼續說下去
可以說的更詳細一點嗎?
這樣喔! 我知道!
然後呢? 發生甚麼事?
再來呢? 可以多說一些嗎
接下來呢? 
可以多告訴我ㄧ些嗎?
多談談有關你的事,好嗎?
祝福你
想多聊ㄧ聊嗎
可否多告訴我ㄧ些呢?

程式實作

有了上述的規則庫後,我們就可以利用字串比對的方式,看看哪一條規則比對成功,然後再從預先設定好的答案中隨機選出一個作為回答,其 Java 版的程式實作如下所示。

import java.util.*;
import java.io.*;

// ChatBot.java
// 執行方式 : 必需有 Java JVM,然後
//   方法 1. 直接點選 ChatBot.bat 即可。
//   方法 2. 執行命令列,將命令列切換到本資料夾打 java ChatBot 即可。
// 注意:該資料夾中必需有 ChatBotRule.txt 的檔案

public class ChatBot {
      Vector<QA> qaList = new Vector<QA>();

    public static void main(String[] args) throws Exception {
        ChatBot agent = new ChatBot();
        agent.load("ChatBotRule.txt");
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String question;
        System.out.println("您好,我的朋友! 有心事嗎 ?");
        do {
            System.out.print(">> ");
            question = reader.readLine();
            Thread.currentThread().sleep(1000);
            System.out.println(agent.answer(question));
        } while (!question.equals("再見"));
    }

    void load(String pFile) throws Exception {
        String kb = STR.file2text(pFile, "BIG5");
        String[] blocks = kb.split("Q\\:");
        for (int i=0; i<blocks.length; i++) {
            String block = blocks[i].trim();
            if (block.length()==0) continue;
            QA qa = new QA(block);
            qaList.add(qa);
        }
    }

    String answer(String input) {
        for (int i=0; i<qaList.size(); i++) {
            QA qa = qaList.get(i);
            String answer = qa.answer(input);
            if (answer != null)
                return answer;
        }
        return "然後呢?";
    }
}
class STR {
  public static String head(String pStr, String pSpliter) {
    int spliterPos = pStr.indexOf(pSpliter);
    if (spliterPos < 0) return pStr;
    return pStr.substring(0,spliterPos);
  }

  public static String tail(String pStr, String pSpliter) {
    int spliterPos = pStr.indexOf(pSpliter);
    if (spliterPos < 0) return "";
    return pStr.substring(spliterPos+pSpliter.length());
  }

  public static String replace(String pStr, String fromPat, String toPat) {
    if (fromPat.length()==0) return pStr;
    if (pStr.indexOf(fromPat)<0) return pStr;
    StringBuffer rzStr = new StringBuffer();
    int strIdx = 0, nextIdx;
    while ((nextIdx = pStr.indexOf(fromPat, strIdx))>=0) {
      rzStr.append(pStr.substring(strIdx, nextIdx));
      rzStr.append(toPat);
      strIdx = nextIdx + fromPat.length();
    }
    rzStr.append(pStr.substring(strIdx));
    return rzStr.toString();
  }

  public static String expand(String pText, String pMacros) {
    String[] macros = pMacros.split("\\|");
    for (int i=0; i<macros.length; i++) {
      String name   = head(macros[i], "=");
      String expand = tail(macros[i], "=");
      pText = replace(pText, name, expand);
    }
    return pText;
  }

  public static String file2text(String fileName, String encode) throws Exception {
    File f = new File(fileName);
    int length = (int)(f.length());
    FileInputStream fin = new FileInputStream(f);
    byte[] bytes = new byte[length];
    fin.read(bytes);
    return new String(bytes, encode);
  }
}

class QA {
    static Random random = new Random(3767);
    String q[], a[];

    QA(String block) {
        block = STR.replace(block, "\r", "");
        block = STR.replace(block, " ", "");
        String head = STR.head(block, "\n");
        String tail = STR.tail(block, "\n");
        q = head.split("\\|");
        a = tail.split("\n");
    }

    public String answer(String input) {
        for (int i=0; i<q.length; i++) {
            int matchAt = input.indexOf(q[i]);
            String tail;
            if (matchAt >= 0)
                tail = input.substring(matchAt+q[i].length());
            else if (q[i].equals("比對失敗"))
                tail = "";                
            else
                continue;
            int aIdx = Math.abs(random.nextInt())%a.length;
            String answer = STR.replace(a[aIdx], "*", tail);
            answer = STR.expand(answer, "我=");
            return answer;
        }
        return null;
    }

    public String toString() {
        return "Q:"+Arrays.asList(q)+" A:"+Arrays.asList(a);
    }
}

Facebook

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License