preface

Speaking of artificial retards, I believe you all remember the previous one with only a few intelligent replies from Replace.

Well, very intelligent.

As we all know, Chinese is so extensive and profound that a single sentence can have many interpretations.

Here’s an example:

Does he agree with me or not?

He approves, I disapprove.

Does he agree with me? Agree.

He agrees with me? Do not approve of.

Right? So if we want to achieve a very clever intelligence it’s very difficult.

But some intelligent assistants are also famously stupid.

So, I should have no problem implementing a stupid but not completely stupid intelligent response.

Let’s take a look at the demo:

There was a bug in the demo that said there was more than one weather in the reply, which has been fixed in the code below.

Github project address: github.com/lionet1224/…

Train of thought

At first I tried to parse a sentence by its meaning.

Distinguish between types of words, such as nouns, verbs, adjectives… And so on, and then correlate the words with the weights to come up with the best match.

But the implementation felt too complicated to give up.

And then I thought, well, I can simplify the process, not differentiate between word types, but just take the best match out of all the sentences that are defined.

Sentences are defined response templates

Such as:

  1. I send: I like to like
  2. So I like that the likes can be parsed into an array[' I ', 'like ',' like ']
  3. And then in aAn array of all sentencesTo get the best match in the sentence
  4. Finally, call the answer to this sentence: me too and I’ve clicked

So based on this idea we can develop.

The preparatory work

We need to use the nodejieba node library, so we need to start a node service.

NodeJieba is the Node.js version implementation of the Chinese word segmentation of “stuttering”, which is implemented by CppJieba with the underlying word segmentation algorithm. It is a Chinese word segmentation part of Node.js with both high performance and ease of use. – github.com/yanyiwu/nod…

Start by installing KOA and its associated libraries.

npm install koa2 koa-router koa-static nodejieba nodemon –save

Then create a file server.js in the root directory to write the corresponding service code.

The implementation is not complicated, but write an API that can decompose front-end input sentences into arrays and return them to the front-end, and create a static file server to display the page.

const nodejieba = require('nodejieba')
const Koa = require('koa2')
const Router = require('koa-router')
const static = require('koa-static')
const app = new Koa();
const router = new Router();

// A get request
router.get('/word'.ctx= > {
  // Cut means nodejieba to break up sentences
  // cxx.query. Word get link "? The word = "x x
  ctx.body = nodejieba.cut(ctx.query.word);
})

// Create a static file server
app.use(static('. '))
// Apply the route
app.use(router.routes())

// Start the service on the listening port
app.listen(3005.() = > {
  console.log('start server *:3005')})Copy the code

So we write the corresponding start command in package.json.

{
  // ...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"."dev": "nodemon server.js"
  },
  // ..
}
Copy the code

After the startup succeeds, a message indicating that the startup succeeds is displayed on the terminal.

Writing front-end code

First we need an interface to display and manipulate our dialogue.

Here whether you are imitation QQ or imitation wechat, nail what, all right, it is good to achieve.

I’m not going to post the code, but you can check it out on Github if you’re interested.

Implement the corresponding logic of sending information.

// Artificial retarded generation
function AImsg1(str){
  return str.replace(/ /?? /g.'! ')
            .replace(/ [b] / g.' ')
            .replace(/ [] you and me/g.(val) = > {
              if(val === '我') return 'you'
              else return '我'
            });
}

// Determine which generation of intelligence to use
// Use the second generation by default
function getAImsg(str, type = 2){
  return new Promise(resolve= > {
    if(type === 1){
      resolve(AImsg1(str))
    } else {
      AImsg2(str).then(res= > {
        resolve(res)
      })
    }
  })
}

/ / input box
let inputMsg = document.querySelector('#input-msg')
// Send button
let emitBtn = document.querySelector('#emit')
// Display the information in the container
let wrapper = document.querySelector('.content');

// The user sends the message and the AI sends the message as well
function emitMsg(){
  let str = inputMsg.value;
  insertMsg(str, true);
  // Clear the input field after sending
  inputMsg.value = ' '

  // Delay the reply by one second
  setTimeout(() = > {
    getAImsg(str).then(res= > {
      insertMsg(res);
    });
  }, 1000);
}

// Insert into the page, flag to determine whether the user sent or the computer sent
function insertMsg(str, flag){
  let msg = document.createElement('div');
  msg.className = 'msg ' + (flag ? 'right' : ' ')
  msg.innerHTML = `<div>${str}</div>`;
  wrapper.appendChild(msg);

  wrapper.scrollTop = 100000;
}

emitBtn.onclick = () = > {
  emitMsg();
};
// The enter key can also be sent
inputMsg.addEventListener('keyup'.ev= > {
  if(ev.keyCode === 13) emitMsg();
})
Copy the code

Through the above code, we have realized the function of sending information.

Define the sentence

The sentence can be understood as a template, and if the user’s message matches a sentence, the AI responds with that sentence.

Next, let’s implement the definition of the sentence.

// Define the class of the sentence
/ / the sentence
class Sentence{
  constructor(keys, answer){
    / / keywords
    this.keys = keys || [];
    // The way to answer
    this.answer = answer;
    // Store variable data
    this.typeVariable = {}; }}Copy the code

“Then I define keys/answer/ Typeverunavailable variables.

  • keysIt’s the keyword that you’re going to match
  • answerThis is a method that is called when the sentence is matched and returns the corresponding sentence
  • typeVariableThis is to make the answer less rigid, you can extract some variable words and then in theanswerMake a judgment call and return the appropriate response.

Simple implementation

Let’s put aside the flexible answer and implement a simple question and answer.

I said: the weather is blue

Well, the sky is blue

// We can implement the AImsg2 method to make it easy to call
function  AImsg2(str){
  return new Promise(resolve= > {
    // Get the data from the API developed earlier, taking the user input text as a parameter.
    axios.get(`http://localhost:3005/word? word=${str}`).then(res= > {
      console.log(res.data)
      // To match the appropriate sentence
      let sentences = matchSentence(res.data);

      // If there is no matching reply
      if(sentences.length <= 0){
        resolve('Emm, what are you talking about? I don't understand.')}else {
      // If there is a match, get the answer
        resolve(sentences[0].sentence.get())
      }
    })
  })
}
Copy the code

Let’s implement matchSentence.

// Match the best sentence
// Matches below 30% are considered mismatches
function matchSentence(arr){
  let result = [];
  sentences.map(item= > {
    // Use the match method of the sentence subclass itself to determine whether there is a match, and return the number of successful matches
    let matchNum = item.match(arr);
    // If the number of matches is less than 1/3 of the total number of keywords, it is considered not seen
    if(matchNum >= item.keys.length / 3) result.push({
      sentence: item,
      // Here is the ratio of the number of matched keywords to the total number of keywords, in order to facilitate the sorting of the most suitable one
      searchNum: matchNum / item.keys.length
    })
  })
  
  result = result.sort((a, b) = > b.searchNum - a.searchNum);
  return result;
}
Copy the code

Implement the match and get methods in Sentence.

class Sentence{
  // ...
  
  // Gets the degree to which the statement sent by the user matches the sentence defined
  match(arr){
    // Reset the data for each match
    this.typeVariable = {};
    // Break it into a new array (shallow copy)
    // To match the next sentence without affecting the data
    let userArr = [...arr];
    let matchNum = this.keys.reduce((val, item) = > {
      // Whether the keyword matches
      let flag = userArr.find((v, i) = > {
        return v === val;
      })
      return val += flag ? 1 : 0;
    }, 0)

    return matchNum;
  }
  
  // Call the answer method and pass the variable in
  get(){
      return this.answer(this.typeVariable)
  }
}
Copy the code

Finally add an instance of the sentence to the array.

let sentences = [
  new Sentence(['day'.'the sky'.'is'.'blue'.The 'color'].type= > {
    let str = 'Uh-huh, the sky is blue.';

    returnstr; }),]Copy the code

Not surprisingly, we type in the sky is blue and get a response: uh-huh, the sky is blue.

Why is the keyword [‘ sky ‘, ‘sky ‘,’ is ‘, ‘blue ‘,’ color ‘] like this?

Since we might ask what color the sky is blue one day, we can add more keywords to make it easier to match.

Flexible answer

If only the above writing method, we can only very rigid answer, so we can define some keywords as variable data, and finally get these data to flexible answer.

What’s dad’s dad’s name?

Let’s define a class to hold the keywords of a class.

// Type is a series of words, such as the color red orange yellow green cyan blue purple, time today tomorrow the day after tomorrow
class Type{
  // Key is the key word
  // arr is a word in this category
  // exclude excludes keywords
  constructor(key, arr, exclude){
    this.key = key;
    this.arr = arr;
    this.exclude = exclude || [];
  }

  // Check for a match
  match(str){
    return this.arr.find(item= > {
      return str.indexOf(item) > -1; &&})this.exclude.indexOf(str) <= -1}}Copy the code

Then create an instance of the corresponding statement:

let sentences = [
  // Use %x% syntax to represent mutable data
  new Sentence(['%family%'.'the'.'%family%'.'call'.'is'.'what'].type= > {
    let data = {
      'daddy': {
        'daddy': 'grandfather'.'mother': 'grandma'
      },
      'mother': {
        'daddy': 'my grandfather'.'mother': 'grandma'}},// Determine if you own the name
    let result = data[type.family[0]] && data[type.family[0]][type.family[1]]

    // Finally return
    if(result){
      return `${type.family[0]}the${type.family[1]}call${result}Oh `
    } else {
      return 'Ahem, I don't know.'}}),]let types = {
  // Create the family class
  family: new Type('family'['daddy'.'mother'.'brother'.'sister'.'sister'.'brother'.'grandfather'.'grandma'.'mother'.'grandfather'])}Copy the code

Of course, once you’ve defined it, you need to write a method in Sentence to get mutable data.

class Sentence{
  // ...

  // Gets the degree to which the statement sent by the user matches the sentence defined
  match(arr){
    this.typeVariable = {};
    let userArr = [...arr];
    let matchNum = this.keys.reduce((val, item) = > {
      let flag = userArr.find((v, i) = > {
        // Use the re to match %x% and get x data
        let isType = / ^ % % $/ (. *).exec(item);
        
        if(isType){
          // Determine whether the keyword is in this category
          let matchType = types[isType[1]].match(v)

          if(matchType){
            // Save to typeVariable
            if(!this.typeVariable[isType[1]]) this.typeVariable[isType[1]] = [];
            this.typeVariable[isType[1]].push(v);
            // After the match, the stored data should be deleted, otherwise the first data will be reentered in subsequent matches
            userArr.splice(i, 1)}return matchType;
        } else {
          returnitem === v; }})return val += flag ? 1 : 0;
    }, 0)


    return matchNum;
  }

  // ...
}
Copy the code

This is where the function of changing the answer comes in, so let’s see what happens.

More features

There are many functions that can be added, such as:

  • I can say I like you a little bit, or I like you a lot.
  • Variable data fetching can add more options than just matchingType, you can also specify a keyword followed by the keyword, such as: I like to like, then I will get to like this keyword.
  • In the callanswerCall other APIS in order to achieve better functions, such as: get the weather, get the location, get local love words.
  • To distinguish between responses of different personalities, introverts, extroverts, warm people, and cold people have different tones of voice.

There are many more ideas on the list.

The last

Thank you for your reading, this article is only to throw a brick to introduce jade, code quality is not bad.

(Sincere eyes)