用 JavaScript 创建一个简单的聊天机器人!
请注意,关闭标签页后,您的对话记录将会丢失。
请注意,人工智能尚未完成,所以请不要妄下结论!
<br> var username = window.prompt("我们应该怎么称呼你?");</p> <p>document.write("嘿 " + username + "你今天过得怎么样?");</p> <div class="highlight"><pre class="highlight plaintext"><code></script></h1> </code></pre></div> <p><body style="background-color: aquamarine; background-image:url(Background.max-x-PIC-MCH043354.jpg); " ><br> <input id="textbox" type="text" placeholder="与AI聊天 :)" ><button id="button" onclick="ping.play()" >发送</button><br><br> <ul id="messages"></ul><br> <script src="script.js"> var messages = document.getElementById("messages"); var textbox = document.getElementById("textbox"); var button = document.getElementById("button"); button.addEventListener("click", function(){ var newMessage = document.createElement("li"); newMessage.innerHTML = textbox.value; messages.appendChild(newMessage); textbox.value = ""; }); var ping = new Audio(); ping.src = "ping.mp3";
在家工作让你感到孤独吗?想念人与人之间的社交互动吗?那么,现在终于可以接受你制作一个友好的聊天机器人来和自己聊天,而不是出门见人了。
我说的“从零开始”或“原生JS”是指我没有使用任何额外的库或API。这更多的是一次JS基础知识的练习,而不是任何人工智能或机器学习方面的练习。
但我的代码/灵感也很大程度上来源于现有的博客文章和YouTube教程!所以基本上,我尽量做到原创,但你不可能永远避免重复发明轮子。
步骤 1
首先是一个简单的index.html文件:
<!DOCTYPE html>
<html>
<head>
<title>Chatbot</title>
<script type="text/javascript" src="index.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id="main">
<div><input id="input" type="text" placeholder="Say something..." autocomplete="off"/></div>
</div>
</body>
</html>
类似这样的简单styles.css文件:
body {
color: #421;
font-weight: bold;
font-size: 18px;
font-family: "Courier New";
background: rgb(200, 232, 241);
}
body::after {
content: "";
background-image: url("bot.png");
background-repeat: repeat-y;
opacity: 0.5;
top: 0;
left: 0;
bottom: 0;
right: 0;
position: absolute;
z-index: -1;
}
span {
color: rgb(36, 17, 119);
}
::-webkit-input-placeholder {
color: #711
}
#main {
position: fixed;
top: 40%;
right: 200px;
width: 400px;
border: 0px solid #421;
padding: 40px;
}
#main div {
margin: 10px;
}
#input {
border: 0;
padding: 5px;
border: 1px solid #421;
}
我显然不是HTML这方面的CSS专家,但我来这里也不是为了这个!也许我故意把这些样式做得比较基础,这样你就可以自由地进行自定义,而不用费力去理解我复杂的样式。这里有个小技巧,我发现让背景稍微透明一些特别有用,因为我的背景图片在窗口缩小时有点太暗,文字会显示在图片上。背景图是bot.png我在谷歌图片上找到的,你可以把它替换成任何你喜欢的图片!
步骤 2
现在开始有趣的部分!创建一个.js文件,从一些基础知识开始。
//index.js
document.addEventListener("DOMContentLoaded", () => {
document.querySelector("#input").addEventListener("keydown", function(e) {
if (e.code === "Enter") {
console.log("You clicked the form and pressed the enter button!")
}
});
});
为文档添加事件监听器,并设置条件,DOMContentLoaded意味着你的 JavaScript 代码只有在 HTML 加载完毕后才会运行。这几乎总是最佳实践。然后是EventListener按下回车键的事件监听器。注意,我们还必须选择#input表单提交的事件监听器,否则每次按下回车键,我们的事件监听器都会响应!
这里有一些有趣但已弃用的替代方案。`.keycode` 、 ` .which`和`keypress`都已弃用。这些方法都只是告诉事件监听器我们只关心enter按键——这正是我们输入消息并按下回车键时,页面能够立即渲染的动态效果!这样,在给机器人朋友发消息时,就不用再费劲地点击“提交”按钮了。更多信息请参阅对象KeyboardEvent ,但总的来说,如果你的浏览器支持,它似乎是此事件监听器最新、最易用且最通用的方法。不过,你可能仍然会看到一些使用 `.keycode`来表示回车键的code方法。13
document.addEventListener("DOMContentLoaded", () => {
const inputField = document.getElementById("input")
inputField.addEventListener("keydown", function(e) {
if (e.code === "Enter") {
let input = inputField.value;
inputField.value = "";
output(input);
}
});
});
现在我们跳过这一步console.log(),进入一些重要的功能。但首先!请注意,我们选择.value并将其设置为一个变量input。这个变量就是我们在表单中输入的内容。我们可以用另一个 con log 来验证这一点!
if (e.code === "Enter") {
let input = inputField.value;
console.log(`I typed '${input}'`)
}
太好了!关于这部分还有最后一点——设置.value = ""可以确保表单在提交后被清空。你也可以.reset()在 `<input>` 标签上这样做HTMLFormElement,但在这里行不通,因为我们的输入字段实际上并不是一个 `<input>`form标签。
步骤 3:函数!
现在来说说真正让这个人变成机器人的功能。
function () {
//remove all characters except word characters, space, and digits
let text = input.toLowerCase().replace(/[^\w\s\d]/gi, "");
// 'tell me a story' -> 'tell me story'
// 'i feel happy' -> 'happy'
text = text
.replace(/ a /g, " ")
.replace(/i feel /g, "")
.replace(/whats/g, "what is")
.replace(/please /g, "")
.replace(/ please/g, "");
}
首先,我想把用户在输入框中输入的内容,通过一些基本的正则表达式操作,使其更符合规范。正如注释中提到的,这些方法会将输入的所有内容转换为小写,移除任何可能导致匹配困难的异常字符,并将某些特定字符(例如 `\n` 或 `\n`)替换whats up为`\n` what is up。如果用户输入 `\n` what is going on、whats going on`\n` 或 `\ what's going onn`,它们都会返回相同的有效机器人回复,而无需单独处理这些差异。
现在我们已经对文本输入框的格式有了大致的了解,接下来我将创建一些简单的数组,其中包含可能的触发条件(用户输入文本)和响应条件(机器人输入文本)。一开始,我会尽量简化这些数组,并将其定义在全局变量中:
const trigger = [
//0
["hi", "hey", "hello"],
//1
["how are you", "how are things"],
//2
["what is going on", "what is up"],
//3
["happy", "good", "well", "fantastic", "cool"],
//4
["bad", "bored", "tired", "sad"],
//5
["tell me story", "tell me joke"],
//6
["thanks", "thank you"],
//7
["bye", "good bye", "goodbye"]
];
const reply = [
//0
["Hello!", "Hi!", "Hey!", "Hi there!"],
//1
[
"Fine... how are you?",
"Pretty well, how are you?",
"Fantastic, how are you?"
],
//2
[
"Nothing much",
"Exciting things!"
],
//3
["Glad to hear it"],
//4
["Why?", "Cheer up buddy"],
//5
["What about?", "Once upon a time..."],
//6
["You're welcome", "No problem"],
//7
["Goodbye", "See you later"],
];
const alternative = [
"Same",
"Go on...",
"Try again",
"I'm listening...",
"Bro..."
];
注意每个数组的索引注释,以及它们的排列方式。如果我们收到与 trigger[0] 中的选项匹配的用户输入,例如“hi”,机器人将使用 reply[0] 中的选项进行回复,例如“Hello!”,依此类推。当然,备用数组用于存储所有与第一个数组不匹配的内容!这在某种程度上解释了为什么你曾经使用过的所有基础聊天机器人(例如在客户服务网站上使用的)都如此……有限。人工智能现在还不会毁灭我们!目前,这个机器人的智能程度几乎和这个人一样……
也就是说,如果你说的话不符合我们预设的回答选项,他很可能会说类似这样的话……
现在我添加实际比较这些数组的函数:
function compare(triggerArray, replyArray, text) {
let item;
for (let x = 0; x < triggerArray.length; x++) {
for (let y = 0; y < replyArray.length; y++) {
if (triggerArray[x][y] == text) {
items = replyArray[x];
item = items[Math.floor(Math.random() * items.length)];
}
}
}
return item;
}
然后将此功能重新添加到我们原来的函数中,并考虑“替代”响应:
function output(input) {
let product;
let text = input.toLowerCase().replace(/[^\w\s\d]/gi, "");
text = text
.replace(/ a /g, " ")
.replace(/i feel /g, "")
.replace(/whats/g, "what is")
.replace(/please /g, "")
.replace(/ please/g, "");
//compare arrays
//then search keyword
//then random alternative
if (compare(trigger, reply, text)) {
product = compare(trigger, reply, text);
} else if (text.match(/robot/gi)) {
product = robot[Math.floor(Math.random() * robot.length)];
} else {
product = alternative[Math.floor(Math.random() * alternative.length)];
}
//update DOM
addChat(input, product);
}
我在这里添加了另一个将用户输入与机器人响应匹配的选项。它让用户输入更加灵活,但降低了响应的精确度。请注意我添加的else iffor循环text.match(/robot/gi)——它保证如果用户输入的内容中包含“robot”这个词,就会从一个单独的“机器人相关”数组中获取响应。
const robot = ["How do you do, fellow human", "I am not a bot"];
你可以想象将其抽象成一种单独的动态搜索函数……或者只是有多个else ifs,或者caseand switch。
最后一步是更新 DOM,这样我们的消息才能真正显示出来!一个简单的方法是使用一个单独的元素来存储用户和机器人的文本,每次输入新消息时都更新该元素,这只需要将第一个事件监听器函数更改为:
document.addEventListener("DOMContentLoaded", () => {
...
if (e.code === "Enter") {
let input = document.getElementById("input").value;
document.getElementById("user").innerHTML = input;
output(input);
}
});
});
然后是function output():
function output(input) {
let product;
let text = (input.toLowerCase()).replace(/[^\w\s\d]/gi, "");
...
document.getElementById("chatbot").innerHTML = product;
speak(product);
//clear input value
document.getElementById("input").value = "";
}
或者,你可以让用户和机器人字段每次都更新,从而创建一个消息线程。但我希望它们都显示在页面上,所以我目前的函数是这样的……
function addChat(input, product) {
const mainDiv = document.getElementById("main");
let userDiv = document.createElement("div");
userDiv.id = "user";
userDiv.innerHTML = `You: <span id="user-response">${input}</span>`;
mainDiv.appendChild(userDiv);
let botDiv = document.createElement("div");
botDiv.id = "bot";
botDiv.innerHTML = `Chatbot: <span id="bot-response">${product}</span>`;
mainDiv.appendChild(botDiv);
speak(product);
}
实现这种 DOM 操作的方法有很多种。`vs.`.innerHTML就是.innerText一个不错的选择。`vs.`.append在.appendChild这里几乎实现了完全相同的目的,但以后可能会有不同的用途。如果我以后给这个项目添加 Rails 后端,我希望能够.dataset为每条消息添加属性。另外,似乎当帖子足够长时,我无法滚动页面。再次声明,我还是个新手,这篇文章更多的是关于 JS 逻辑而不是视图!
最后还有一点……
我说过我不会使用 API,但我在尝试实现这个功能时找到的一个示例视频使用了语音转文字功能,而要实现这个功能,你只需要添加以下代码:
function speak(string) {
const u = new SpeechSynthesisUtterance();
allVoices = speechSynthesis.getVoices();
u.voice = allVoices.filter(voice => voice.name === "Alex")[0];
u.text = string;
u.lang = "en-US";
u.volume = 1; //0-1 interval
u.rate = 1;
u.pitch = 1; //0-2 interval
speechSynthesis.speak(u);
}
我其实不太明白如何在这里指定不同的语音名称,但是查阅 Web Speech API 文档很有意思,我可以建议修改.pitch为2一个真正恐怖的声音,听起来确实有能力统治人类。






