【c++】提升用戶體驗:問答系統的交互優化實踐——關于我用AI編寫了一個聊天機器人……(12)

本期依舊使用豆包輔助完成代碼。

從功能到體驗的轉變

上個版本已經實現了問答系統的核心功能:基于 TF-IDF 算法的問題匹配和回答。它能夠讀取訓練數據,處理用戶輸入,并返回最相關的答案。但在用戶體驗方面還有很大提升空間。

讓我們看看改進版做了哪些關鍵優化:

1. 引導系統

上個版本僅在啟動時顯示簡單的 "Hello! 輸入 'exit' 結束對話。" 提示,對于初次使用的用戶來說不夠友好。

改進版增加了:

  • 詳細的歡迎信息和功能介紹
  • 專門的showHelp()函數,提供完整的使用指南
  • showTopics()函數,展示系統能回答的問題類型示例

這些引導信息讓用戶能快速了解系統功能和使用方法,減少了使用障礙。

2. 命令系統

上個版本僅支持 "exit" 一個命令,功能單一。

改進版擴展為多個命令:

  • "exit" 或 "quit":退出程序(支持多種退出方式)
  • "help":查看幫助信息
  • "topics":了解系統能回答的問題類型

多樣化的命令讓用戶能更好地掌控交互過程,提升了系統的可用性。

3.交互提示

上個版本使用簡單的 "You:" 作為輸入提示,顯得生硬。

改進版對此進行了全面優化:

  • 輸入提示改為更親切的 "請輸入您的問題:"
  • 機器人回復前綴從 "Robot:" 改為 "機器人:",更符合中文語境
  • 增加空輸入處理,當用戶輸入為空時給予明確提示

這些細節變化讓整個交互過程更加自然流暢。

4. 智能的錯誤處理與引導

上個版本在無法回答問題時,僅簡單返回 "I don't know how to answer this question.",沒有提供進一步指導。

改進版則提供了的建議:

cout << "機器人: 抱歉,我不太理解這個問題。" << endl;
cout << "您可以嘗試:" << endl;
cout << "- 用不同的方式表述問題" << endl;
cout << "- 輸入 'topics' 查看我能回答的問題類型" << endl;
cout << "- 輸入 'help' 查看幫助信息" << endl;

這種處理方式不僅告知用戶問題,還提供了解決方案,大大降低了用戶的挫敗感。

5. 錯誤提示

對于文件打開失敗等錯誤情況,改進版提供了更具體的指導:

cout << "無法打開訓練文件 training_data.txt" << endl;
cout << "請確保該文件存在于程序運行目錄下" << endl;
cout << "程序將退出..." << endl;

相比上個版本簡單的錯誤提示,用戶能更清楚地了解問題所在及如何解決。

為什么這些改進很重要?

這些看似細微的變化,實際上對用戶體驗有著顯著影響:

  1. 降低學習成本:良好的引導讓新用戶能快速上手
  2. 減少挫敗感:當系統無法回答時,提供建設性建議
  3. 增強掌控感:豐富的命令系統讓用戶能更好地控制交互過程
  4. 提升信任度:專業的錯誤處理和提示讓用戶更信任系統能力

在 AI 助手和問答系統日益普及的今天,技術實現固然重要,但能否提供自然、友好的交互體驗往往是決定產品成敗的關鍵因素。

代碼

#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <vector>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <set>
using namespace std;// 將字符串轉換為小寫
string toLower(const string& str) {string result = str;for (string::size_type i = 0; i < result.length(); ++i) {result[i] = tolower(result[i]);}return result;
}// 從字符串中提取關鍵詞
vector<string> extractKeywords(const string& text) {vector<string> keywords;string word;for (string::const_iterator it = text.begin(); it != text.end(); ++it) {if (isalnum(*it)) {word += *it;} else if (!word.empty()) {keywords.push_back(toLower(word));word.clear();}}if (!word.empty()) {keywords.push_back(toLower(word));}return keywords;
}// 顯示幫助信息
void showHelp() {cout << "\n===== 使用幫助 =====" << endl;cout << "1. 直接輸入您的問題,我會盡力為您解答" << endl;cout << "2. 輸入 'exit' 或 'quit' 結束對話" << endl;cout << "3. 輸入 'help' 查看幫助信息" << endl;cout << "4. 輸入 'topics' 查看我能回答的問題類型" << endl;cout << "====================\n" << endl;
}// 顯示可回答的話題類型
void showTopics(const map<string, string>& exactAnswers) {if (exactAnswers.empty()) {cout << "暫無可用的話題信息" << endl;return;}cout << "\n===== 我可以回答這些類型的問題 =====" << endl;// 提取部分問題作為示例(最多顯示5個)int count = 0;for (map<string, string>::const_iterator it = exactAnswers.begin(); it != exactAnswers.end() && count < 5; ++it, ++count) {string sample = it->first;if (sample.length() > 30) {sample = sample.substr(0, 30) + "...";}cout << "- " << sample << endl;}if (exactAnswers.size() > 5) {cout << "... 還有 " << (exactAnswers.size() - 5) << " 個其他話題" << endl;}cout << "=================================\n" << endl;
}// 計算TF-IDF并返回最佳匹配答案
string getBestAnswerByTFIDF(const vector<string>& userKeywords,const map<string, vector<string> >& qas,const map<string, vector<string> >& questionKeywords,const map<string, double>& idfValues) {// 計算用戶問題的TF-IDF向量map<string, double> userTFIDF;for (vector<string>::const_iterator kit = userKeywords.begin(); kit != userKeywords.end(); ++kit) {const string& keyword = *kit;// 計算詞頻(TF)double tf = 0.0;for (vector<string>::const_iterator it = userKeywords.begin(); it != userKeywords.end(); ++it) {if (*it == keyword) tf++;}tf /= userKeywords.size();// 獲取IDF值double idf = 0.0;map<string, double>::const_iterator idfIt = idfValues.find(keyword);if (idfIt != idfValues.end()) {idf = idfIt->second;}// 計算TF-IDFuserTFIDF[keyword] = tf * idf;}// 計算每個問題的相似度map<string, double> similarityScores;for (map<string, vector<string> >::const_iterator pit = questionKeywords.begin(); pit != questionKeywords.end(); ++pit) {const string& question = pit->first;const vector<string>& keywords = pit->second;// 計算問題的TF-IDF向量map<string, double> questionTFIDF;for (vector<string>::const_iterator kit = keywords.begin(); kit != keywords.end(); ++kit) {const string& keyword = *kit;// 計算詞頻(TF)double tf = 0.0;for (vector<string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it) {if (*it == keyword) tf++;}tf /= keywords.size();// 獲取IDF值double idf = 0.0;map<string, double>::const_iterator idfIt = idfValues.find(keyword);if (idfIt != idfValues.end()) {idf = idfIt->second;}// 計算TF-IDFquestionTFIDF[keyword] = tf * idf;}// 計算余弦相似度double dotProduct = 0.0;double userNorm = 0.0;double questionNorm = 0.0;// 計算點積和范數for (map<string, double>::const_iterator uit = userTFIDF.begin(); uit != userTFIDF.end(); ++uit) {const string& keyword = uit->first;double userWeight = uit->second;userNorm += userWeight * userWeight;map<string, double>::const_iterator qit = questionTFIDF.find(keyword);if (qit != questionTFIDF.end()) {dotProduct += userWeight * qit->second;}}for (map<string, double>::const_iterator qit = questionTFIDF.begin(); qit != questionTFIDF.end(); ++qit) {double questionWeight = qit->second;questionNorm += questionWeight * questionWeight;}userNorm = sqrt(userNorm);questionNorm = sqrt(questionNorm);// 計算相似度double similarity = 0.0;if (userNorm > 0 && questionNorm > 0) {similarity = dotProduct / (userNorm * questionNorm);}similarityScores[question] = similarity;}// 找到相似度最高的問題string bestQuestion;double maxSimilarity = 0.0;for (map<string, double>::const_iterator it = similarityScores.begin(); it != similarityScores.end(); ++it) {if (it->second > maxSimilarity) {maxSimilarity = it->second;bestQuestion = it->first;}}// 如果相似度足夠高,返回對應的答案if (maxSimilarity >= 0.2) { // 相似度閾值map<string, vector<string> >::const_iterator ansIt = qas.find(bestQuestion);if (ansIt != qas.end() && !ansIt->second.empty()) {return ansIt->second[0]; // 假設第一個答案是最佳答案}}return ""; // 沒有找到匹配
}int main() {// 存儲訓練數據map<string, string> exactAnswers;       // 精確匹配回答map<string, vector<string> > qas;        // 問題-回答映射map<string, vector<string> > questionKeywords; // 問題-關鍵詞映射map<string, int> documentFrequency;     // 關鍵詞-文檔頻率映射// 加載訓練數據ifstream trainingFile("training_data.txt");if (trainingFile.is_open()) {string line;string question = "";bool readingAnswer = false;int totalDocuments = 0;while (getline(trainingFile, line)) {// 空行表示一個問答對結束if (line.empty()) {question = "";readingAnswer = false;continue;}// 問題行以Q:開頭if (line.substr(0, 2) == "Q:") {question = line.substr(2);readingAnswer = false;totalDocuments++;}// 回答行以A:開頭else if (line.substr(0, 2) == "A:") {if (!question.empty()) {string answer = line.substr(2);// 保存精確匹配回答exactAnswers[question] = answer;// 保存問題-回答映射qas[question].push_back(answer);// 提取關鍵詞并保存vector<string> keywords = extractKeywords(question);questionKeywords[question] = keywords;// 更新文檔頻率set<string> uniqueKeywords;for (vector<string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it) {uniqueKeywords.insert(*it);}for (set<string>::const_iterator it = uniqueKeywords.begin(); it != uniqueKeywords.end(); ++it) {documentFrequency[*it]++;}}readingAnswer = true;}// 多行回答的后續行else if (readingAnswer && !question.empty()) {exactAnswers[question] += "\n" + line;qas[question].back() += "\n" + line;}}trainingFile.close();cout << "已加載 " << exactAnswers.size() << " 條訓練數據" << endl;// 計算IDF值map<string, double> idfValues;for (map<string, int>::const_iterator it = documentFrequency.begin(); it != documentFrequency.end(); ++it) {const string& keyword = it->first;int df = it->second;// IDF公式: log(總文檔數 / (包含該詞的文檔數 + 1)) + 1double idf = log((double)totalDocuments / (df + 1)) + 1;idfValues[keyword] = idf;}// 歡迎信息和初始引導cout << "\n=================================" << endl;cout << "歡迎使用問答系統!我可以回答您的問題" << endl;cout << "輸入 'help' 查看可用命令,'exit' 退出程序" << endl;cout << "=================================\n" << endl;// 聊天界面string input;while (true) {cout << "請輸入您的問題: ";getline(cin, input);// 處理命令if (input == "exit" || input == "quit") {cout << "機器人: 再見!感謝使用!" << endl;break;}else if (input == "help") {showHelp();continue;}else if (input == "topics") {showTopics(exactAnswers);continue;}else if (input.empty()) {cout << "機器人: 您的輸入為空,請重新輸入或輸入 'help' 查看幫助" << endl;continue;}// 精確匹配map<string, string>::const_iterator exactIt = exactAnswers.find(input);if (exactIt != exactAnswers.end()) {cout << "機器人: " << exactIt->second << endl;continue;}// 關鍵詞匹配 (TF-IDF)vector<string> userKeywords = extractKeywords(input);string bestAnswer = getBestAnswerByTFIDF(userKeywords, qas, questionKeywords, idfValues);if (!bestAnswer.empty()) {cout << "機器人: " << bestAnswer << endl;continue;}// 沒有找到匹配,提供引導cout << "機器人: 抱歉,我不太理解這個問題。" << endl;cout << "您可以嘗試:" << endl;cout << "- 用不同的方式表述問題" << endl;cout << "- 輸入 'topics' 查看我能回答的問題類型" << endl;cout << "- 輸入 'help' 查看幫助信息" << endl;}} else {cout << "無法打開訓練文件 training_data.txt" << endl;cout << "請確保該文件存在于程序運行目錄下" << endl;cout << "程序將退出..." << endl;}return 0;
}

總結

這個案例展示了如何通過關注用戶體驗細節,將一個功能性的程序轉變為一個易用、友好的工具。這些改進不需要復雜的技術實現,卻能顯著提升用戶滿意度。

在實際開發中,我們應該始終記住:代碼是寫給機器執行的,但最終是給人使用的。良好的用戶體驗設計,應該貫穿于軟件開發的每一個環節。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/90565.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/90565.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/90565.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android UI 控件詳解實踐

一、UI 開發基礎概念&#xff08;初學者必看&#xff09; 在學習具體控件前&#xff0c;先理解以下核心概念&#xff0c;能大幅降低后續學習難度&#xff1a; 1. View 與 ViewGroup 的關系 View&#xff1a;所有 UI 控件的基類&#xff08;如 Button、TextView&#xff09;&…

關于linux運維 出現高頻的模塊認知

一、Linux 基礎核心&#xff08;必掌握&#xff09;核心工具&#xff1a;Shell 腳本、Systemd、用戶權限管理、日志分析&#xff08;journalctl、rsyslog&#xff09;企業需求&#xff1a;中小型公司&#xff1a;需獨立完成系統部署、故障排查&#xff0c;對腳本開發&#xff0…

手語式映射:Kinova Gen3 力控機械臂自適應控制的研究與應用

近日&#xff0c;美國明尼蘇達大學研究團隊在《從人手到機械臂&#xff1a;遙操作中運動技能具身化研究》中&#xff0c;成功開發出基于??Kinova的7軸力控機械臂Gen3的智能控制系統。這項創新性技術通過人工智能算法&#xff0c;實現了人類手臂動作到機械臂運動的精準映射&am…

P5535 【XR-3】小道消息

題目描述 小 X 想探究小道消息傳播的速度有多快&#xff0c;于是他做了一個社會實驗。 有 n 個人&#xff0c;其中第 i 個人的衣服上有一個數 i1。小 X 發現了一個規律&#xff1a;當一個衣服上的數為 i 的人在某一天知道了一條信息&#xff0c;他會在第二天把這條信息告訴衣…

ChatGPT Agent架構深度解析:OpenAI如何構建統一智能體系統

引言&#xff1a;AI智能體的范式躍遷 2025年7月17日&#xff0c;OpenAI發布的ChatGPT Agent標志著對話式AI從“被動應答”向主動執行的歷史性轉變。這款融合Operator網頁操作與Deep Research信息分析能力的新型智能體&#xff0c;通過統一架構設計實現了復雜任務的端到端自主執…

計算機網絡(第八版)— 第2章課后習題參考答案

2-01 物理層要解決哪些問題&#xff1f;物理層的主要特點是什么&#xff1f;答&#xff1a;物理層要解決的主要問題&#xff1a;&#xff08;1&#xff09;物理層要盡可能地屏蔽掉物理設備和傳輸媒體&#xff0c;通信手段的不同&#xff0c;使數據鏈路層感覺不到這些差異&#…

Hive【Hive架構及工作原理】

?博客主頁&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客內容》&#xff1a;.NET、Java.測試開發、Python、Android、Go、Node、Android前端小程序等相關領域知識 &#x1f4e2;博客專欄&#xff1a; https://blog.csdn.net/m0_63815035/cat…

數據管理能力成熟度評估模型(DCMM)詳解

數據管理能力成熟度評估模型(DCMM)詳解 1. DCMM概述 數據管理能力成熟度評估模型(Data Management Capability Maturity Assessment Model, DCMM)是我國首個數據管理領域的國家標準(GB/T 36073-2018)&#xff0c;由國家工業信息安全發展研究中心牽頭制定。該模型為我國企業數據…

學習C++、QT---34(使用QT庫實現串口調試助手01:解決串口調試助手的UI)

&#x1f31f; 嗨&#xff0c;我是熱愛嵌入式的濤濤同學&#xff01;每日一言別害怕改變&#xff0c;走出舒適圈才能遇見更好的自己。串口調試助手項目好的現在我們來學習串口調試助手的項目&#xff0c;我們依舊是項目引領學習好的我們最后就是要做成一個類似我們市面上的串口…

Dockerfile 文件及指令詳解

什么是Dockerfile 文件Dockerfile 文件是用于構建 docker 鏡像的腳本文件&#xff0c;由一系列的指令構成。通過 docker build 命令構建鏡像時&#xff0c;Dockerfile 文件中的指令會由上到下執行&#xff0c;每條 指令都將會構建出一個鏡像層&#xff0c;這就是鏡像的分層。因…

主流Java Redis客戶端對決:Jedis、Lettuce與Redisson性能特性深度評測

&#x1f49d;&#x1f49d;&#x1f49d;歡迎蒞臨我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 持續學習&#xff0c;不斷…

刷題日記0725

今日計劃5道。2/5晚上被一些事影響了心神不寧&#xff0c;再加上感覺睡前做完時間有點緊&#xff0c;逃避的念頭出現了。代碼意思不進腦子了。做一道是一道。21. 合并兩個有序鏈表默認構造??&#xff1a;用于創建??值為0的孤立節點??&#xff08;不連接其他節點&#xff…

從數據脫敏到SHAP解釋:用Streamlit+XGBoost構建可復現的川崎病診斷系統

基于機器學習的川崎病輔助診斷工具&#xff0c;結合了數據預處理、模型訓練、特征解釋和交互式可視化。以下是深度解讀&#xff1a;1. 技術架構框架&#xff1a;使用 Streamlit 構建 Web 應用&#xff0c;適合快速開發交互式數據科學應用。核心算法&#xff1a;XGBoost&#xf…

【C++詳解】模板進階 非類型模板參數,函數模板特化,類模板全特化、偏特化,模板分離編譯

文章目錄一、非類型模板參數應用場景二、模板的特化函數模板特化類模板特化全特化偏特化三、模板分離編譯解決方法四、模板總結一、非類型模板參數 先前介紹的函數模板和類模板都是針對類型的類模板參數&#xff0c;非類型模板參數有哪些使用場景呢&#xff1f;我們先來看下面這…

10BASE-T1S核心機制——PLCA參數詳解

導語&#xff1a; PLCA是10BASE-T1S的核心機制&#xff0c;了解PLCA才能更好地使用10BASE-T1。 本文將通過介紹具體配置&#xff0c;以及實戰例子&#xff0c;帶你掌握PLCA。 以下測試內容使用KUNHONG-U10BT1S-EVB設備測試&#xff0c; 設備符合IEEE 802.3cg標準&#xff0…

uniapp vue apk那邊輸入法遮擋頁面內容

解決辦法&#xff1a;pages.json配置如下{"globalStyle": {"app-plus": {"softinputMode": "adjustResize"}} }效果&#xff1a; 鍵盤彈出時自動調整窗口大小&#xff0c;所有內容上推&#xff08;兼容性最佳&#xff09;文件內容如下…

2507C++,系統服務0與1

原文 窗口上的系統調用通過,每個由系統調用(x64)或sysenter(x86)CPU指令調用的NTDLL.dll,如NTDLL的NtCreateFile的以下輸出所示: 這里 0:000> u ntdll!NtCreateFile: 00007ffcc07fcb50 4c8bd1 mov r10,rcx 00007ffcc07fcb53 b855000000 mov eax,55h…

人工智能冗余:大語言模型為何有時表現不佳(以及我們能做些什么)

像 GPT - 4 這樣的大語言模型&#xff08;LLMs&#xff09;徹底改變了我們與技術交互的方式。它們可以撰寫文章、生成代碼、回答問題&#xff0c;甚至幫助我們構思創意。但任何花時間使用過這些模型的人都知道&#xff0c;它們的輸出有時會讓人感覺……不太對勁。表述冗長、格式…

Cursor替代品亞馬遜出品Kiro下載

Cursor替代品亞馬遜出品Kiro下載 支持Claude Sonnet4.0與3.7 點擊下載 備用鏈接&#xff1a;https://pan.xunlei.com/s/VOW-nBmVgR3ewIIAm7jDsf99A1?pwd6bqu#

MySQL 事務管理

一、前言 CURD 不加控制&#xff0c;會有什么問題&#xff1f; CURD 滿足什么屬性&#xff0c;能解決上述問題&#xff1f; 買票的過程得是原子的。買票應該不能受互相的影響。買完票應該要永久有效。買前和買后都要是確定的狀態。 什么是事務&#xff1f; 事務就是一組 DML 語…