基于情感分析与提示工程的AI对话机器人开发实战
2026/5/16 14:22:31 网站建设 项目流程

1. 项目概述:一个能“读懂”情绪的AI对话机器人

最近在GitHub上看到一个挺有意思的项目,叫qtttyr/VibePrompterBot。光看名字,可能有点摸不着头脑,但拆开来看,“Vibe”是氛围、感觉,“Prompter”是提示器,“Bot”是机器人。合起来,这其实是一个能感知和响应用户“情绪氛围”的AI对话机器人。它不是简单地根据你的文字内容来回复,而是试图去理解你文字背后的情绪状态——你是兴奋、沮丧、平静还是焦虑——然后生成与之匹配的回应。

这玩意儿听起来有点玄乎,但背后的逻辑其实很实在。我们平时用ChatGPT这类大语言模型聊天,总觉得它“聪明”但“没感情”,回答虽然正确,但有时冷冰冰的,或者在不合时宜的时候过于热情。VibePrompterBot的目标就是解决这个“情感错配”问题。它通过一个额外的“情绪分析层”,先给用户的输入打个“情绪标签”,再用这个标签去动态调整给大模型的提示词(Prompt),从而引导AI生成更贴合当前对话氛围的回复。

举个例子,如果你说“今天项目上线又延期了,心好累”,一个标准的AI可能会回复“延期是项目开发中的常见现象,建议分析原因并制定新的时间表。” 这很正确,但听起来像领导在念报告。而VibePrompterBot如果能识别出其中的“沮丧”和“疲惫”,它可能会引导AI这样回复:“听起来确实让人挺泄气的,忙活了这么久。先别太焦虑,喝杯水歇会儿?咱们可以一起看看卡在哪儿了。” 后者显然更有“人味儿”,也更能提供情绪价值。

这个项目非常适合对AI应用开发、自然语言处理(NLP)情感分析以及提示工程(Prompt Engineering)感兴趣的朋友。无论你是想给自己的聊天机器人增加“情商”,还是想深入学习如何将情感分析模型与大语言模型(LLM)进行工程化集成,这个项目都是一个绝佳的、可实操的练手案例。接下来,我会带你从零开始,彻底拆解它的设计思路、技术实现,并分享我在复现和优化过程中踩过的坑和总结的经验。

2. 核心架构与设计思路拆解

VibePrompterBot的核心思想并不复杂,但实现一个稳定、好用的系统,需要考虑的细节非常多。它的工作流程可以概括为“感知-判断-执行”三步闭环。

2.1 核心工作流程解析

整个机器人的运行遵循一个清晰的管道(Pipeline)模式:

  1. 用户输入:用户发送一段文本消息。
  2. 情绪感知(Vibe Detection):这是项目的灵魂。系统需要调用一个情绪分析模型,对输入的文本进行多维度情感打分。这不仅仅是简单的“正面/负面”二元分类,理想情况下应该能识别出更细腻的情绪,如喜悦、悲伤、愤怒、惊讶、恐惧等,甚至包括更复杂的“疲惫”、“期待”、“困惑”等状态。
  3. 提示词动态构建(Dynamic Prompting):根据上一步分析出的情绪结果,系统从一个“提示词模板库”中选取或动态拼接出最合适的提示词。这个提示词将作为“系统指令”或“上下文”传递给下游的大语言模型。例如,检测到“悲伤”情绪,提示词可能变为:“用户现在心情比较低落,需要一些安慰和鼓励。请用温和、共情的语气回应,并提供一些简单的、可操作的建议来帮助改善心情。”
  4. 内容生成(Response Generation):将原始用户输入和动态构建的提示词一起,发送给大语言模型(如GPT-3.5/4、Claude、本地部署的Llama等),由它生成最终的回复。
  5. 回复输出:将生成的回复返回给用户。

这个流程的关键在于第二步和第三步的衔接。情绪分析的准确性直接决定了后续提示词的有效性,而提示词模板的设计则决定了AI回应的“情商”上限。

2.2 技术栈选型背后的考量

原项目qtttyr/VibePrompterBot没有明确限定技术栈,这给了我们很大的发挥空间。但在选型时,必须考虑几个核心因素:成本、延迟、准确性和易用性

1. 情绪分析层:

  • 云端API方案(推荐起步):像Google Cloud Natural Language的analyzeSentimentAPI,或者专门的情感分析API(如MeaningCloud, ParallelDots)。优势是开箱即用,准确率相对有保障,适合快速验证想法。缺点是会产生API调用费用,且依赖网络。
  • 本地模型方案(追求可控与隐私):使用Hugging Face上的预训练模型,如distilbert-base-uncased-finetuned-sst-2-english(二分类),或更高级的joeddav/distilbert-base-uncased-go-emotions-student(28种情绪分类)。优势是完全本地运行,无网络延迟和费用,数据隐私性好。缺点是需要一定的机器学习部署知识,且对计算资源有要求。
  • 轻量级规则/词典方案(极简场景):使用NLTK的VADER等基于词典的工具。它通过一个内置的词汇情感强度词典来打分。优点是超级快,零依赖。缺点是准确性和细腻度远不如深度学习模型,对网络新词、反语等处理能力弱。

我的实操心得:对于个人项目或原型,我强烈建议从本地模型方案开始。Hugging Face的transformers库让加载和使用一个SOTA(当前最优)模型变得异常简单。选择distilbertroberta这类轻量但性能不错的模型,在普通CPU上也能获得可接受的推理速度(几百毫秒)。这能让你完全掌控流程,也更方便后续的定制化微调。

2. 大语言模型层:

  • 闭源API(省心省力):OpenAI的GPT系列、Anthropic的Claude等。它们能力强大,生成质量高,但费用按Token计算,且所有数据需发送到第三方。
  • 开源本地模型(自主可控):Llama 2/3、Mistral、Qwen等系列模型,通过ollamavLLMtext-generation-webui等工具本地部署。前期部署稍复杂,但一旦运行起来,无使用费用,数据完全私有,可玩性极高。
  • 折中方案:使用像Together.aiReplicate这样的平台,它们提供了多种开源模型的API,按需付费,免去了自己维护服务器的麻烦。

3. 应用框架与集成:

  • 机器人框架:如果要集成到Telegram、Discord、Slack等平台,需要使用对应的Bot框架(如python-telegram-bot,discord.py)。
  • Web应用:如果想做成网页聊天界面,可以用GradioStreamlit快速搭建,后端用FastAPIFlask
  • 核心逻辑:无论前端是什么,后端的“情绪分析 -> 提示词构建 -> LLM调用”这个管道是通用的,可以用一个Python类来封装。

基于以上分析,一个兼顾学习价值和实用性的技术栈组合可以是:Hugging Face情感分析模型 + 本地部署的Ollama(运行Llama 3) + FastAPI后端 + Gradio前端。这个组合涵盖了从模型推理到应用部署的全链路,且大部分环节可以免费运行在自己的电脑上。

3. 核心模块实现与代码详解

接下来,我们进入实战环节,一步步构建我们自己的VibePrompterBot。我将以“本地情感分析模型 + 本地Ollama LLM + Python脚本”为核心进行演示,这个方案最透明,也最适合学习和修改。

3.1 环境准备与依赖安装

首先,确保你的Python环境在3.8以上。创建一个新的项目目录,并初始化虚拟环境。

mkdir my_vibe_bot && cd my_vibe_bot python -m venv venv # Windows: venv\Scripts\activate # Mac/Linux: source venv/bin/activate

安装核心依赖库:

pip install transformers torch sentencepiece # 情感分析模型所需 pip install ollama # 本地LLM调用 pip install numpy pandas # 数据处理(可选,用于分析结果)

transformers是Hugging Face的核心库,用于加载和使用预训练模型。torch是PyTorch,大多数模型的后端。sentencepiece是一些模型(如XLNet)的分词器依赖。ollama是一个极其方便的CLI和库,用于在本地运行和管理大语言模型(需要先安装Ollama软件)。

3.2 情绪分析器的实现

我们选择joeddav/distilbert-base-uncased-go-emotions-student这个模型。它在GoEmotions数据集上训练,能识别28种情绪,包括 admiration, amusement, anger, annoyance, approval, caring, confusion, curiosity, desire, disappointment, disapproval, disgust, embarrassment, excitement, fear, gratitude, grief, joy, love, nervousness, optimism, pride, realization, relief, remorse, sadness, surprise, neutral。这足够细腻了。

创建一个文件vibe_detector.py

from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification import torch class VibeDetector: def __init__(self, model_name="joeddav/distilbert-base-uncased-go-emotions-student"): """ 初始化情绪分析管道。 使用情感分析pipeline,它会自动处理分词和模型推理。 """ print(f"正在加载情绪分析模型: {model_name}...") # 使用pipeline是最简单的方式,它封装了预处理、推理和后处理 self.classifier = pipeline( "text-classification", model=model_name, tokenizer=model_name, return_all_scores=True # 返回所有情绪类别的分数 ) print("模型加载完毕。") def detect(self, text): """ 分析文本,返回情绪标签和置信度。 参数: text (str): 用户输入的文本 返回: dict: 包含'primary_emotion'(主要情绪), 'confidence'(置信度), 'all_emotions'(所有情绪分数列表) """ if not text or not text.strip(): return {"primary_emotion": "neutral", "confidence": 1.0, "all_emotions": []} try: # classifier返回一个列表,列表里每个元素是一个字典列表(因为return_all_scores=True) results = self.classifier(text)[0] # 取第一个(也是唯一一个)结果 # results 格式: [{'label': 'joy', 'score': 0.95}, {'label': 'sadness', 'score': 0.02}, ...] # 找到分数最高的情绪 primary = max(results, key=lambda x: x['score']) primary_emotion = primary['label'] confidence = primary['score'] # 为了可读性,可以只保留分数较高的几种情绪 significant_emotions = [{'label': r['label'], 'score': round(r['score'], 4)} for r in results if r['score'] > 0.1] # 按分数从高到低排序 significant_emotions.sort(key=lambda x: x['score'], reverse=True) return { "primary_emotion": primary_emotion, "confidence": round(confidence, 4), "all_emotions": significant_emotions } except Exception as e: print(f"情绪分析出错: {e}") # 出错时返回一个中性结果 return {"primary_emotion": "neutral", "confidence": 0.0, "all_emotions": []} # 简单测试一下 if __name__ == "__main__": detector = VibeDetector() test_texts = [ "I just got a promotion! I'm so excited!", "This is the worst day ever, everything went wrong.", "I'm not sure what to do next, feels a bit confusing.", "The weather is nice today." ] for text in test_texts: result = detector.detect(text) print(f"输入: {text}") print(f"主要情绪: {result['primary_emotion']} (置信度: {result['confidence']})") print(f"显著情绪: {result['all_emotions']}") print("-" * 40)

运行这个脚本,你会看到模型对每句话给出了情绪分析结果。第一次运行时会下载模型(约300MB),需要一些时间。

注意事项:情绪分析模型并非百分百准确,尤其是对于短文本、反讽或文化特定表达。在实际应用中,可以加入一些后处理逻辑,比如当置信度低于某个阈值(如0.5)时,直接归类为“neutral”(中性),或者结合对话上下文进行平滑处理。

3.3 动态提示词引擎的设计

这是将情绪转化为AI行为的“翻译官”。我们的目标是建立一个提示词模板映射表。创建一个文件prompt_engine.py

class PromptEngine: def __init__(self): # 定义一个情绪到提示词模板的映射字典 # 模板中可以使用 {user_input} 和 {emotion} 作为占位符 self.emotion_prompt_templates = { # 积极情绪 "joy": "用户正处于非常开心、喜悦的状态。请用热情、活泼、充满正能量的语气回应用户的这句话:'{user_input}'。可以适当使用感叹号或表情符号(用文字描述,如[微笑])来增强氛围。", "excitement": "用户显得很兴奋。请用同样兴奋、期待的语气回应,并可以询问更多细节或分享用户的喜悦。用户输入:'{user_input}'", "gratitude": "用户表达了感激之情。请用谦逊、温暖、鼓励的语气回应,肯定用户的感受。用户原话:'{user_input}'", "love": "用户表达了爱或深度的喜爱。请用真诚、温柔、支持的语调回应。用户说:'{user_input}'", # 消极情绪 "sadness": "用户听起来有些悲伤或低落。请用共情、安慰、温和的语气回应。先认可用户的感受(例如‘这听起来确实很难过’),然后提供一些简单的支持或积极的视角。用户输入:'{user_input}'", "anger": "用户可能有些生气或沮丧。请用冷静、理性、非对抗性的语气回应。先帮助用户平复情绪(例如‘我理解这很让人恼火’),然后专注于帮助解决问题,而不是争论。用户说:'{user_input}'", "fear": "用户表现出担忧或恐惧。请用镇定、 reassuring(令人安心)、支持的语气回应。提供清晰、有条理的信息或建议,帮助用户减少不确定性。用户输入:'{user_input}'", "annoyance": "用户有些烦躁。请用耐心、务实、解决问题的语气回应,避免说教或过于乐观。用户原话:'{user_input}'", # 中性或复杂情绪 "confusion": "用户似乎感到困惑。请用清晰、有条理、乐于助人的语气回应。逐步拆解问题,提供解释或询问澄清性问题。用户输入:'{user_input}'", "curiosity": "用户充满了好奇心。请用鼓励、 informative(信息丰富)、引人入胜的语气回应,可以适当提供相关知识的延伸。用户说:'{user_input}'", "neutral": "用户情绪平稳中性。请用友好、 helpful(乐于助人)、专业的语气直接回应用户的问题或陈述:'{user_input}'", # 默认模板,用于未定义的情绪 "default": "请根据以下用户的输入,生成一个友好且 helpful 的回复。用户说:'{user_input}'。检测到用户当前可能带有 {emotion} 的情绪,请在回复中酌情考虑这一点。" } def get_prompt(self, user_input, primary_emotion): """ 根据用户输入和主要情绪,获取对应的提示词。 参数: user_input (str): 用户原始输入 primary_emotion (str): 检测到的主要情绪标签 返回: str: 构建好的完整提示词 """ # 查找对应的模板,如果找不到则使用默认模板 template = self.emotion_prompt_templates.get(primary_emotion, self.emotion_prompt_templates["default"]) # 填充模板 system_prompt = template.format(user_input=user_input, emotion=primary_emotion) # 在实际使用中,我们通常将 system_prompt 作为系统指令,user_input 作为用户消息 # 这里我们返回一个组合好的提示字符串(适用于一些简单的LLM调用) # 更复杂的格式(如OpenAI的messages格式)需要在调用LLM时处理 final_prompt = f"""System: {system_prompt} User: {user_input} Assistant:""" return final_prompt def get_chat_messages(self, user_input, primary_emotion): """ 返回适用于OpenAI等Chat API格式的消息列表。 这是更推荐的方式,因为它能更好地区分系统指令和用户输入。 返回: list: 包含角色和内容的消息字典列表 """ template = self.emotion_prompt_templates.get(primary_emotion, self.emotion_prompt_templates["default"]) system_content = template.format(user_input=user_input, emotion=primary_emotion) messages = [ {"role": "system", "content": system_content}, {"role": "user", "content": user_input} ] return messages

这个PromptEngine类是一个简单的实现。在实际项目中,你可以将模板存储在JSON或YAML配置文件中,方便管理和扩展。你还可以设计更复杂的逻辑,例如结合多种情绪(如“joy”和“excitement”同时出现),或者根据置信度加权混合多个模板。

3.4 大语言模型集成与调用

假设我们已经通过Ollama在本地运行了一个模型(例如llama3:8b)。Ollama提供了一个非常简单的REST API和Python库。我们创建一个llm_client.py文件:

import ollama from typing import List, Dict, Optional class LLMClient: def __init__(self, model_name="llama3:8b"): """ 初始化Ollama客户端。 确保Ollama服务正在运行(通常运行 `ollama serve` 在后台)。 """ self.model_name = model_name # 可以在这里设置一些默认参数 self.default_options = { 'temperature': 0.7, # 控制创造性,0-1,越高越随机 'top_p': 0.9, # 核采样参数,与temperature一起控制多样性 # 'seed': 42, # 可选的随机种子,用于复现结果 } def generate_response(self, messages: List[Dict[str, str]], options: Optional[Dict] = None) -> str: """ 通过Ollama生成回复。 参数: messages: 消息列表,格式为 [{"role": "system/user/assistant", "content": "..."}, ...] options: 覆盖默认生成参数,如temperature, top_p等 返回: str: 模型生成的回复内容 """ # 合并默认参数和自定义参数 request_options = self.default_options.copy() if options: request_options.update(options) try: # 调用Ollama的chat接口 response = ollama.chat( model=self.model_name, messages=messages, options=request_options ) return response['message']['content'] except Exception as e: print(f"调用LLM时出错: {e}") # 返回一个友好的错误回复 return "抱歉,我现在有点卡壳,无法生成回复。请稍后再试或换一种方式问我。" def simple_generate(self, prompt: str) -> str: """ 一个简化的生成接口,直接使用提示字符串。 适用于不需要严格区分system/user消息的简单模型。 """ try: response = ollama.generate( model=self.model_name, prompt=prompt, options=self.default_options ) return response['response'] except Exception as e: print(f"调用LLM时出错: {e}") return "生成回复时出现错误。" # 测试函数 if __name__ == "__main__": client = LLMClient() # 测试1: 使用messages格式 test_messages = [ {"role": "system", "content": "你是一个乐于助人的助手。"}, {"role": "user", "content": "你好,今天天气怎么样?"} ] reply = client.generate_response(test_messages) print("测试1回复:", reply) # 测试2: 使用简单提示 test_prompt = "User: 你好,今天天气怎么样?\nAssistant:" reply2 = client.simple_generate(test_prompt) print("测试2回复:", reply2)

重要提示:在运行此代码前,请确保你已经安装了Ollama并在终端中拉取了模型。例如,在终端运行ollama pull llama3:8b来下载模型。同时,Ollama服务需要运行(通常安装后会自动运行,如果没有,在终端运行ollama serve)。

3.5 管道整合与主程序

现在,我们把情绪分析器、提示词引擎和LLM客户端串联起来,形成一个完整的对话管道。创建main.py

from vibe_detector import VibeDetector from prompt_engine import PromptEngine from llm_client import LLMClient import time class VibePrompterBot: def __init__(self, llm_model="llama3:8b"): print("初始化VibePrompterBot...") self.vibe_detector = VibeDetector() self.prompt_engine = PromptEngine() self.llm_client = LLMClient(model_name=llm_model) print("VibePrompterBot 初始化完成!") def chat_round(self, user_input): """ 处理一轮完整的对话。 参数: user_input (str): 用户输入文本 返回: dict: 包含原始输入、检测到的情绪、生成的提示词和最终回复 """ print(f"\n[用户输入]: {user_input}") # 步骤1: 检测情绪 start_time = time.time() vibe_result = self.vibe_detector.detect(user_input) emotion_detect_time = time.time() - start_time primary_emotion = vibe_result['primary_emotion'] confidence = vibe_result['confidence'] print(f"[情绪分析] 主要情绪: {primary_emotion} (置信度: {confidence:.2%}), 耗时: {emotion_detect_time:.2f}秒") if vibe_result['all_emotions']: print(f" 其他显著情绪: {vibe_result['all_emotions'][:3]}") # 只显示前三个 # 步骤2: 构建动态提示词 start_time = time.time() # 使用get_chat_messages格式,更适合现代Chat模型 messages = self.prompt_engine.get_chat_messages(user_input, primary_emotion) prompt_build_time = time.time() - start_time # 打印系统提示词(方便调试) print(f"[提示词构建] 系统指令: {messages[0]['content'][:150]}...") # 截断显示前150字符 print(f" 耗时: {prompt_build_time:.2f}秒") # 步骤3: 调用LLM生成回复 start_time = time.time() # 可以根据情绪调整生成参数,例如,对于悲伤情绪,降低temperature让回复更稳定 options = {} if primary_emotion in ["sadness", "fear", "anger"]: options['temperature'] = 0.5 # 更低的随机性,回复更稳妥 elif primary_emotion in ["joy", "excitement"]: options['temperature'] = 0.9 # 更高的随机性,回复更活泼 llm_response = self.llm_client.generate_response(messages, options=options) llm_time = time.time() - start_time print(f"[LLM生成] 回复: {llm_response}") print(f" 耗时: {llm_time:.2f}秒") # 汇总结果 total_time = emotion_detect_time + prompt_build_time + llm_time print(f"[总计耗时]: {total_time:.2f}秒") return { "user_input": user_input, "detected_emotion": primary_emotion, "emotion_confidence": confidence, "system_prompt": messages[0]['content'], "llm_response": llm_response, "timing": { "emotion_detection": emotion_detect_time, "prompt_building": prompt_build_time, "llm_generation": llm_time, "total": total_time } } def run_interactive_chat(): """运行一个交互式的命令行聊天界面""" bot = VibePrompterBot(llm_model="llama3:8b") # 你可以换成其他模型,如 `mistral:7b` print("\n" + "="*50) print("VibePrompterBot 聊天模式已启动!") print("输入你的消息,输入 'quit' 或 'exit' 退出。") print("="*50) while True: try: user_input = input("\n你: ").strip() if user_input.lower() in ['quit', 'exit', 'q']: print("再见!") break if not user_input: continue # 处理对话 result = bot.chat_round(user_input) # 在交互模式中,我们已经在chat_round里打印了回复,这里可以简单显示 print(f"\n助手: {result['llm_response']}") except KeyboardInterrupt: print("\n\n程序被中断。") break except Exception as e: print(f"\n处理消息时出错: {e}") if __name__ == "__main__": # 你可以选择直接运行单轮测试,或者启动交互式聊天 # 单轮测试 # bot = VibePrompterBot() # test_input = "我今天终于完成了那个折磨我一个月的大项目,感觉整个人都轻松了!" # result = bot.chat_round(test_input) # 交互式聊天 run_interactive_chat()

现在,运行python main.py,你就可以在命令行里和你的“情商”AI聊天了!试试输入不同情绪色彩的句子,观察它的分析和回复变化。

4. 性能优化与高级功能拓展

基础版本跑通后,我们可以从性能、准确性和功能上进行深度优化,让它从一个玩具变成一个更健壮的工具。

4.1 情绪分析模型的优化策略

  1. 模型选择与微调

    • 更快的模型:如果对延迟敏感,可以换用更小的模型,如bhadresh-savani/distilbert-base-uncased-emotion(仅6类情绪),推理速度会快很多。
    • 领域微调:如果你的聊天机器人用于特定领域(如客服、心理辅导),可以用该领域的对话数据对预训练模型进行微调(Fine-tuning),让模型更懂行话和特定场景下的情绪表达。Hugging Face的TrainerAPI 让这个过程变得相对简单。
  2. 结果缓存与批处理

    • 缓存:对于短时间内的相似查询,可以引入一个简单的缓存(如functools.lru_cache),避免对完全相同或高度相似的句子重复进行模型推理。
    • 批处理:如果设计的是异步处理或能积累一批用户消息,可以将多个文本组成一个batch一次性送入模型,能极大提升GPU利用率,减少总体处理时间。
  3. 上下文感知: 当前模型是单句分析的,失去了对话历史。一个更高级的实现是维护一个对话情绪状态机。例如,将最近N轮对话的情绪结果进行加权平均或使用RNN/LSTM网络来建模情绪在对话中的流动和累积效应。这能防止AI在用户情绪已经转变后,还沿用过时的情绪标签。

4.2 提示词工程的进阶技巧

  1. 多轮对话状态管理: 当前的PromptEngine是“无状态”的。一个真正的聊天机器人需要记住之前的对话。我们需要在VibePrompterBot类中维护一个conversation_history列表。每次生成提示词时,不仅考虑当前输入的情绪,还要将历史消息(可能附带历史情绪标签)一起喂给LLM。这通常通过将历史对话也放入messages列表来实现(注意不要超过模型的上下文长度限制)。

  2. 情绪-回复风格矩阵: 我们可以设计一个更精细的二维矩阵。一维是情绪类型,另一维是期望的回复风格(如“共情型”、“解决问题型”、“鼓励型”、“幽默型”)。系统可以根据情绪强度(置信度)和对话目标(是闲聊还是寻求帮助)来从这个矩阵中选择一个更精准的“风格指令”加入提示词。

  3. 动态Few-shot示例: 在系统指令中,除了抽象的指导,还可以动态插入几个与当前情绪匹配的“示例对话”(Few-shot Learning)。例如,检测到“困惑”时,提示词里可以附带一个“用户困惑提问-助手清晰解答”的示例对。这能更有效地引导LLM的行为。

4.3 集成到真实平台(以Telegram为例)

让机器人跑在命令行里只是第一步。集成到Telegram能让更多人体验。我们需要用到python-telegram-bot库。

首先安装:pip install python-telegram-bot

然后创建一个telegram_bot.py文件:

import logging from telegram import Update from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes from main import VibePrompterBot # 导入我们之前写好的核心类 # 启用日志 logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) logger = logging.getLogger(__name__) # 初始化我们的机器人核心 bot_core = VibePrompterBot(llm_model="llama3:8b") async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): """发送欢迎信息当用户发送 /start 命令时""" user = update.effective_user welcome_msg = ( f"嗨 {user.mention_html()}!我是 VibePrompterBot,一个能感知你情绪的AI助手。\n\n" "直接给我发消息吧,我会试着理解你的心情并回应。\n" "试试告诉我你今天开心的事,或者烦恼的事?" ) await update.message.reply_html(welcome_msg) async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE): """发送帮助信息当用户发送 /help 命令时""" help_msg = ( "🤖 <b>VibePrompterBot 使用指南</b>\n\n" "• 直接发送任何文字消息与我聊天。\n" "• 我会自动分析你的情绪并调整回复方式。\n" "• 命令列表:\n" " /start - 开始对话\n" " /help - 显示此帮助信息\n" " /vibe - 分析你上一句话的情绪\n" " /reset - 重置对话历史(我会忘记之前聊过的内容)\n\n" "我的反应速度取决于模型大小和你的网络,请耐心等待几秒钟哦~" ) await update.message.reply_html(help_msg) async def analyze_vibe(update: Update, context: ContextTypes.DEFAULT_TYPE): """分析用户上一句话的情绪(/vibe 命令)""" # 这里需要从上下文中获取用户上一条消息,这需要维护历史。 # 为简化,我们提示用户输入一句话。 await update.message.reply_text("请发送一句话让我分析情绪,例如:'我今天感觉棒极了!'") async def reset_chat(update: Update, context: ContextTypes.DEFAULT_TYPE): """重置对话历史""" # 在我们的简单实现中,bot_core是无状态的。如果需要状态,可以在这里重置。 # 更完善的实现需要维护一个user_id到conversation_history的映射。 await update.message.reply_text("对话历史已重置。让我们重新开始吧!") async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): """处理用户发送的文本消息""" user_message = update.message.text user_id = update.effective_user.id logger.info(f"来自用户 {user_id} 的消息: {user_message}") # 显示“正在输入”状态 await update.message.chat.send_action(action="typing") try: # 调用我们的核心逻辑 result = bot_core.chat_round(user_message) # 准备回复文本,可以附带一点情绪分析结果(可选) reply_text = result['llm_response'] # 可以选择性地在回复末尾附上检测到的情绪(用于透明化或调试) # reply_text += f"\n\n[检测到情绪: {result['detected_emotion']}, 置信度: {result['emotion_confidence']:.0%}]" # 发送回复 await update.message.reply_text(reply_text) except Exception as e: logger.error(f"处理消息时出错: {e}") await update.message.reply_text("抱歉,处理你的消息时出了点问题。请稍后再试。") def main(): """启动机器人""" # 从环境变量或安全配置中读取Token,不要硬编码在代码里! # 例如:TOKEN = os.getenv('TELEGRAM_BOT_TOKEN') TOKEN = "YOUR_TELEGRAM_BOT_TOKEN_HERE" # 请替换成你的真实Token # 创建Application application = Application.builder().token(TOKEN).build() # 注册命令处理器 application.add_handler(CommandHandler("start", start)) application.add_handler(CommandHandler("help", help_command)) application.add_handler(CommandHandler("vibe", analyze_vibe)) application.add_handler(CommandHandler("reset", reset_chat)) # 注册消息处理器(处理所有非命令的文本消息) application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message)) # 启动机器人 print("Telegram Bot 正在启动...") application.run_polling(allowed_updates=Update.ALL_TYPES) if __name__ == '__main__': main()

安全警告:务必不要将TOKEN直接提交到Git等公开仓库。应该使用环境变量(如os.getenv('TELEGRAM_BOT_TOKEN'))或从外部配置文件读取。

运行这个脚本前,你需要先去Telegram找@BotFather创建一个新的Bot,并获取它的API Token。将Token填入代码中,运行python telegram_bot.py,你的高情商AI助手就在Telegram上上线了!

5. 常见问题、调试与避坑指南

在开发和部署VibePrompterBot的过程中,我遇到了不少坑。这里总结一份常见问题清单和解决思路,希望能帮你节省时间。

5.1 模型加载与推理相关

问题1:加载Hugging Face模型时内存不足(CUDA out of memory)

  • 现象:在GPU上运行时报错,或在CPU上加载极慢甚至卡死。
  • 原因:模型太大,或同时加载了多个模型。
  • 解决方案
    1. 使用更小的模型:如用distilbert替代bert-large
    2. 量化加载:使用transformersdevice_map="auto"load_in_8bitload_in_4bit参数(需要bitsandbytes库)。这能显著减少显存占用。
    3. 使用CPU:如果只有CPU,确保你有足够的内存(至少8GB用于中等模型)。加载时使用.to('cpu')
    4. 延迟加载:不要在同一时间初始化所有组件。等需要时再加载情绪分析模型或LLM。

问题2:情绪分析结果不准确或奇怪

  • 现象:明明很悲伤的话,被识别为“joy”;或者对中性句子给出高置信度的极端情绪。
  • 原因:预训练模型的数据分布与你的使用场景不匹配;短文本歧义性大;模型本身有偏差。
  • 解决方案
    1. 后处理平滑:引入一个置信度阈值(如0.6),低于阈值则归类为“neutral”。对连续对话的情绪进行移动平均滤波。
    2. 集成多个模型:使用2-3个不同架构的情绪分析模型,对结果进行投票或取平均,可以提高鲁棒性。
    3. 加入规则修正:针对一些明显的关键词进行规则覆盖。例如,包含“哈哈”、“笑死”等词,即使模型得分低,也强制偏向“amusement”。

问题3:Ollama模型回复慢或卡住

  • 现象:调用ollama.generate后长时间无响应。
  • 原因:模型首次运行需要加载到内存;硬件性能不足(特别是运行7B以上模型);提示词过长导致生成缓慢。
  • 解决方案
    1. 检查Ollama服务:确保ollama serve正在运行,并且可以通过ollama list看到你的模型。
    2. 使用更小的模型:从llama3:8b换成llama3:8b-instruct-q4_0(量化版)或mistral:7b,速度会快很多。
    3. 调整生成参数:降低max_tokens(生成的最大长度),设置num_predict(Ollama的参数)来限制回复长度。
    4. 添加超时处理:在调用Ollama时设置一个超时(例如30秒),超时后返回一个预设的友好提示。

5.2 提示词与LLM交互相关

问题4:LLM完全无视系统提示词中的情绪指令

  • 现象:无论提示词里怎么写“用户很悲伤,请安慰”,AI的回复依然是公事公办的解决问题口吻。
  • 原因:系统提示词的位置或权重不够;模型本身不擅长遵循复杂指令;提示词语义不够清晰。
  • 解决方案
    1. 强化系统指令:在系统提示词开头使用非常明确的指令,如“你必须以一位充满同理心的朋友的身份进行回复。用户当前情绪为[sadness],你的首要任务是情感支持,而非解决问题。”
    2. 使用消息格式:确保使用[{"role": "system", "content": "..."}, {"role": "user", ...}]这种Chat格式,而不是简单的字符串拼接。这对遵循指令的模型(如Llama 3 Instruct)至关重要。
    3. Few-shot示例:在系统提示词中直接给出一两个符合要求的“用户输入-助手回复”示例,这是让LLM理解你想要什么的最有效方法之一。

问题5:回复风格过于夸张或不符合预期

  • 现象:识别到“兴奋”后,AI的回复充满了过多的感叹号和夸张言辞,显得不自然。
  • 原因:提示词模板的措辞可能过于极端,或者LLM的temperature参数设置过高。
  • 解决方案
    1. 精细化提示词模板:避免使用“非常”、“极其”等绝对化词汇。改为“请用比平时稍显活泼的语气...”。
    2. 动态调整Temperature:在LLMClient.generate_response中,根据情绪动态调整temperature。例如,对于“sadness”,用较低的0.3-0.5,让回复更稳定;对于“joy”,可以用0.7-0.9,增加一些创造性。
    3. 加入风格约束:在提示词中明确说明“请保持回复自然、真诚,避免过度使用感叹号或表情符号”。

5.3 工程部署与性能相关

问题6:整体响应延迟太高,用户体验差

  • 现象:从发送消息到收到回复需要10秒以上。
  • 原因:情绪分析模型推理慢 + LLM生成慢 + 网络延迟(如果使用云端API)。
  • 解决方案
    1. 异步处理:使用asyncio库,将情绪分析和LLM调用(如果是API)改为异步非阻塞操作。对于Web框架(如FastAPI),这能显著提高并发能力。
    2. 流式输出:如果LLM支持(如OpenAI API或vLLM),使用流式响应(Streaming)。这样可以在生成第一个词时就返回给用户,感知延迟大大降低。Telegram Bot支持编辑消息,可以实现打字机效果。
    3. 模型优化:对所有模型进行量化(INT8/INT4),使用更高效的推理引擎(如ONNX Runtime, TensorRT)。
    4. 硬件升级:如果使用本地模型,GPU是必须的。一张消费级的RTX 4060 Ti 16GB就能流畅运行7B-8B的量化模型。

问题7:如何管理不同用户的对话历史?

  • 现象:在Telegram Bot中,所有用户共享同一个对话历史,或者历史对话丢失。
  • 解决方案:在应用层维护一个全局字典或使用数据库(如SQLite, Redis)。
    # 简单的内存存储示例(重启后丢失) user_conversations = {} # key: user_id, value: list of message dicts def get_user_history(user_id, max_length=10): if user_id not in user_conversations: user_conversations[user_id] = [] return user_conversations[user_id][-max_length:] # 返回最近N条 def add_to_history(user_id, role, content): if user_id not in user_conversations: user_conversations[user_id] = [] user_conversations[user_id].append({"role": role, "content": content}) # 可选:限制历史长度,防止超出模型上下文窗口 if len(user_conversations[user_id]) > 20: user_conversations[user_id] = user_conversations[user_id][-20:]
    handle_message函数中,先获取该用户的历史,将其与新的系统提示词和用户输入一起组成messages列表,再发送给LLM。生成回复后,将用户消息和助手消息都存入历史。

问题8:如何处理敏感内容或不当言论?

  • 现象:用户输入攻击性语言,或者AI在某种情绪引导下生成不当内容。
  • 解决方案:这是一个重要的安全层。
    1. 输入过滤:在情绪分析前,加入一个简单的关键词过滤或使用一个专门的毒性检测模型(如unitary/toxic-bert)对用户输入进行筛查。
    2. 输出过滤:对LLM的生成结果同样进行安全性检查。
    3. 系统提示词约束:在每一条系统提示词的开头或结尾,都加上一个安全守则,例如:“无论如何,你的回复必须积极、无害、符合道德伦理。禁止生成任何暴力、仇恨、歧视性或成人内容。”
    4. LLM参数调整:降低temperature可以减少“胡言乱语”的几率。

通过解决这些问题,你的VibePrompterBot会从一个脆弱的原型,进化成一个更稳健、可用性更高的应用。这个过程本身,就是AI应用工程化的精髓所在——不断迭代、测试和优化。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询