构建个人 AI 助理的终极指南
构建个人 AI 助理的终极指南
“再复杂的知识,也能用清晰的语言解释清楚。” - 技术博客写作大师
1. 开篇引言:为什么要构建自己的AI助理?
在这个AI技术飞速发展的时代,我们每天都在与各种智能助手打交道——Siri、Alexa、Google Assistant、小爱同学……它们帮助我们查询天气、设置提醒、播放音乐,甚至控制智能家居。但是,你有没有想过:如果有一个完全属于自己、懂你的习惯、保护你的隐私、可以根据你的需求定制的AI助理,那会是一种怎样的体验?
这就是我们今天要探讨的主题:构建个人AI助理的终极指南。在这篇超过10000字的深度文章中,我将带你从概念到实践,从零开始构建一个功能强大、高度定制化的个人AI助理。
1.1 什么是个人AI助理?
在深入之前,我们先来明确定义:
个人AI助理(Personal AI Assistant) 是一种基于人工智能技术,能够理解自然语言、执行特定任务、提供个性化服务的软件系统。与商业AI助理不同,个人AI助理完全由用户掌控,数据存储在本地或用户信任的服务器上,功能可以根据个人需求无限扩展。
1.2 本文的目标读者
这篇指南适合以下人群:
- 中级到高级开发者:希望深入了解AI助理技术栈
- AI爱好者:想动手实践构建自己的智能助手
- 隐私意识强的用户:不希望数据被大公司收集
- 技术极客:喜欢探索和定制技术产品
1.3 我们将学到什么?
在这篇指南中,你将学到:
- 个人AI助理的核心概念和技术架构
- 自然语言处理(NLP)的基础原理
- 语音识别与合成技术
- 如何从零开始构建一个简单的AI助理
- 如何扩展高级功能(如日程管理、智能家居控制等)
- 最佳实践和未来发展趋势
现在,让我们开始这段激动人心的旅程!
2. 核心概念
在开始动手之前,我们需要理解构建个人AI助理所涉及的核心概念。这些概念就像建筑的基石,理解它们将帮助我们构建更加稳固和高效的系统。
2.1 自然语言处理(NLP)
自然语言处理(Natural Language Processing, NLP) 是人工智能的一个分支,致力于使计算机能够理解、解释和生成人类语言。它是AI助理的核心技术之一。
NLP可以分为以下几个子领域:
- 自然语言理解(NLU):让计算机理解人类语言的含义
- 自然语言生成(NLG):让计算机生成自然流畅的人类语言
- 语音识别(ASR):将语音转换为文本
- 语音合成(TTS):将文本转换为语音
2.2 意图识别与实体提取
意图识别(Intent Recognition):确定用户想要做什么。例如,当用户说"明天天气怎么样?“时,意图是"查询天气”。
实体提取(Entity Extraction):从用户的输入中提取关键信息。在上面的例子中,"明天"是时间实体。
2.3 对话管理
对话管理(Dialogue Management):负责管理整个对话流程,保持上下文,决定系统下一步的响应。
2.4 知识图谱
知识图谱(Knowledge Graph):一种结构化的知识库,用于存储实体及其之间的关系。它可以帮助AI助理更好地理解世界和回答问题。
2.5 边缘计算与隐私保护
边缘计算(Edge Computing):在本地设备上进行计算,而不是将数据发送到云端。这对于保护用户隐私至关重要。
3. 问题背景与历史发展
3.1 问题背景:为什么商业AI助理不够用?
虽然商业AI助理(如Siri、Alexa)已经非常普及,但它们存在以下局限性:
- 隐私问题:你的对话数据可能被存储在云端,用于训练模型或其他用途。
- 功能固定:你无法轻易添加自己需要的特定功能。
- 定制性差:它们是为大众设计的,不一定完全符合你的个人习惯。
- 依赖性强:通常需要联网才能使用全部功能。
3.2 AI助理的发展历史
让我们通过一个表格来了解AI助理的演变历程:
| 时间 | 里程碑 | 代表产品 | 特点 |
|---|---|---|---|
| 1961年 | 第一个语音识别系统 | IBM Shoebox | 只能识别16个单词和数字0-9 |
| 1966年 | 第一个聊天机器人 | ELIZA | 通过模式匹配模拟心理医生对话 |
| 1990年代 | 语音识别技术进步 | Dragon NaturallySpeaking | 连续语音识别,词汇量大幅增加 |
| 2011年 | 现代AI助理诞生 | Siri | 集成在iPhone 4S上的智能助理 |
| 2014年 | 智能音箱时代 | Amazon Alexa | 以语音交互为核心的智能家居中心 |
| 2020年代 | 大语言模型时代 | ChatGPT、各种AI助手 | 基于LLM的新一代AI助理 |
3.3 个人AI助理的兴起
随着开源技术的发展和计算能力的提升,构建个人AI助理变得越来越可行。特别是在2022年以后,随着大型语言模型(LLMs)的开源化(如Llama、Alpaca、Mistral等),个人AI助理迎来了新的发展机遇。
4. 个人AI助理的技术架构
现在,让我们来设计个人AI助理的技术架构。一个完整的AI助理系统通常包含以下几个核心模块:
4.1 系统分层架构
┌─────────────────────────────────────────────────────────────┐
│ 用户界面层 (UI) │
│ (语音界面、文字界面、图形界面、移动端App、桌面端应用) │
└──────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ 输入处理层 │
│ ┌──────────────────┐ ┌───────────────────────────────┐ │
│ │ 语音识别(ASR) │ │ 文本输入预处理 │ │
│ └──────────────────┘ └───────────────────────────────┘ │
└──────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ 自然语言理解层 (NLU) │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────────┐ │
│ │ 意图识别 │ │ 实体提取 │ │ 上下文理解 │ │
│ └──────────────┘ └──────────────┘ └───────────────────┘ │
└──────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ 对话管理层 │
│ ┌──────────────────┐ ┌──────────────────────────────────┐ │
│ │ 对话状态追踪 │ │ 策略学习与决策 │ │
│ └──────────────────┘ └──────────────────────────────────┘ │
└──────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ 技能执行层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ 天气查询 │ │ 日程管理 │ │ 音乐控制 │ │ 自定义技能... │ │
│ └──────────┘ └──────────┘ └──────────┘ └───────────────┘ │
└──────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ 输出生成层 │
│ ┌──────────────────┐ ┌───────────────────────────────┐ │
│ │ 自然语言生成(NLG)│ │ 语音合成(TTS) │ │
│ └──────────────────┘ └───────────────────────────────┘ │
└──────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ 数据存储层 │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────────┐ │
│ │ 对话历史 │ │ 用户偏好 │ │ 知识库/知识图谱 │ │
│ └──────────────┘ └──────────────┘ └───────────────────┘ │
└─────────────────────────────────────────────────────────────┘
4.2 核心组件交互流程
让我们用Mermaid流程图来描述用户与AI助理的交互过程:
4.3 部署架构选择
在构建个人AI助理时,你有三种主要的部署架构选择:
-
纯云端架构:所有计算都在云端进行,设备只负责输入输出。
- 优点:设备要求低,功能强大
- 缺点:隐私问题,依赖网络
-
纯边缘架构:所有计算都在本地设备上进行。
- 优点:隐私保护好,不依赖网络
- 缺点:设备要求高,功能可能受限
-
混合架构:部分计算在本地,部分在云端。
- 优点:平衡隐私和功能
- 缺点:架构复杂
对于个人AI助理,我推荐混合架构,将敏感数据和基本功能放在本地,复杂的计算任务可以选择在云端完成。
5. 核心技术模块详解
在这一章中,我们将深入探讨构建个人AI助理所需的各个核心技术模块。
5.1 语音识别(ASR)
语音识别是将语音信号转换为文本的过程。近年来,基于深度学习的ASR技术取得了巨大进步。
5.1.1 主流开源ASR工具
| 工具名称 | 开发者 | 特点 | 适用场景 |
|---|---|---|---|
| Whisper | OpenAI | 多语言支持,准确性高 | 通用语音识别 |
| Kaldi | 社区维护 | 高度可定制,学术研究 | 研究和定制开发 |
| Mozilla DeepSpeech | Mozilla | 开源,基于TensorFlow | 嵌入式设备 |
| SpeechRecognition (库) | 社区 | 简单易用,支持多个引擎 | 快速原型开发 |
5.1.2 Whisper的工作原理
Whisper是OpenAI开源的一个通用语音识别模型,它采用了Transformer架构,经过了大量多语言和多任务训练。
Whisper的处理流程如下:
- 将音频分割成30秒的片段
- 转换为对数梅尔频谱图
- 输入到Transformer编码器
- 解码器生成对应的文本
5.2 自然语言理解(NLU)
自然语言理解是让计算机理解人类语言含义的技术。
5.2.1 意图识别
意图识别是分类任务,将用户输入分类到预定义的意图类别中。
传统方法:
- 基于规则的方法
- 机器学习方法(SVM、朴素贝叶斯)
现代方法:
- 深度学习(CNN、RNN、Transformer)
- 预训练语言模型(BERT、RoBERTa)
5.2.2 实体提取
实体提取是从文本中提取特定信息的任务,如时间、地点、人名等。
常用方法:
- 命名实体识别(NER)
- 正则表达式
- 条件随机场(CRF)
- 基于预训练模型的方法
5.2.3 开源NLU框架
| 框架名称 | 特点 | 适用场景 |
|---|---|---|
| Rasa NLU | 开源,可定制,对话友好 | 构建任务型对话系统 |
| spaCy | 工业级,高性能 | 通用NLP任务 |
| Hugging Face Transformers | 预训练模型丰富 | 快速原型开发 |
| NLTK | 教学友好,资源丰富 | 学习和研究 |
5.3 对话管理
对话管理负责控制对话流程,保持上下文,决定系统的下一步响应。
5.3.1 对话管理方法
- 基于规则的方法:使用状态机或流程图定义对话流程
- 基于统计的方法:使用机器学习模型预测下一步行动
- 强化学习方法:通过与用户交互学习最佳策略
- 基于大语言模型的方法:使用LLM直接生成回复
5.3.2 对话状态追踪(DST)
对话状态追踪是记录对话过程中关键信息的任务,如用户的需求、已提供的信息等。
5.4 自然语言生成(NLG)
自然语言生成是将结构化数据转换为自然语言文本的过程。
NLG方法的演变:
- 模板-based方法
- 统计方法
- 神经网络方法
- 大语言模型方法
5.5 语音合成(TTS)
语音合成是将文本转换为语音的过程。
5.5.1 主流开源TTS工具
| 工具名称 | 特点 |
|---|---|
| Coqui TTS | 开源,高质量,多语言 |
| eSpeak | 轻量级,多语言 |
| VITS | 端到端,高质量 |
| Whisper (反向) | 可以用于语音生成 |
6. 数学模型与算法原理
在这一章中,我们将深入探讨AI助理背后的数学模型和算法原理。
6.1 语音识别的数学基础
6.1.1 信号处理
语音信号首先需要转换为数字信号,然后进行预处理:
傅里叶变换:将时域信号转换为频域信号
X(k)=∑n=0N−1x(n)⋅e−i2πkn/NX(k) = \sum_{n=0}^{N-1} x(n) \cdot e^{-i2\pi kn/N}X(k)=n=0∑N−1x(n)⋅e−i2πkn/N
梅尔频谱:模拟人耳对频率的感知
m=2595⋅log10(1+f/700)m = 2595 \cdot \log_{10}(1 + f/700)m=2595⋅log10(1+f/700)
6.1.2 CTC损失函数
Connectionist Temporal Classification (CTC)是语音识别中常用的损失函数,它解决了输入和输出序列长度不一致的问题:
p(Y∣X)=∑A∈AX,Y∏t=1Tp(at∣X)p(Y|X) = \sum_{A \in \mathcal{A}_{X,Y}} \prod_{t=1}^{T} p(a_t | X)p(Y∣X)=A∈AX,Y∑t=1∏Tp(at∣X)
其中,AAA是对齐路径,AX,Y\mathcal{A}_{X,Y}AX,Y是所有可能的对齐路径集合。
6.2 Transformer模型
Transformer是目前NLP领域最核心的模型架构,它使用自注意力机制:
6.2.1 自注意力机制
Attention(Q,K,V)=softmax(QKTdk)VAttention(Q, K, V) = softmax(\frac{QK^T}{\sqrt{d_k}})VAttention(Q,K,V)=softmax(dkQKT)V
其中,Q、K、V分别是查询、键、值矩阵,dkd_kdk是键的维度。
6.2.2 多头注意力
MultiHead(Q,K,V)=Concat(head1,...,headh)WOMultiHead(Q, K, V) = Concat(head_1, ..., head_h)W^OMultiHead(Q,K,V)=Concat(head1,...,headh)WO
headi=Attention(QWiQ,KWiK,VWiV)head_i = Attention(QW_i^Q, KW_i^K, VW_i^V)headi=Attention(QWiQ,KWiK,VWiV)
6.3 意图识别的算法
6.3.1 文本分类
对于意图识别,我们可以使用各种文本分类算法。这里我们介绍朴素贝叶斯分类器:
P(C∣X)=P(X∣C)P(C)P(X)P(C|X) = \frac{P(X|C)P(C)}{P(X)}P(C∣X)=P(X)P(X∣C)P(C)
其中,CCC是类别(意图),XXX是输入文本。
6.3.2 词向量表示
Word2Vec是一种常用的词向量表示方法:
Skip-gram模型:预测中心词周围的上下文词
1T∑t=1T∑−c≤j≤c,j≠0logp(wt+j∣wt)\frac{1}{T}\sum_{t=1}^{T}\sum_{-c\leq j\leq c, j\neq 0} \log p(w_{t+j}|w_t)T1t=1∑T−c≤j≤c,j=0∑logp(wt+j∣wt)
CBOW模型:根据上下文词预测中心词
1T∑t=1Tlogp(wt∣wt−c,...,wt+c)\frac{1}{T}\sum_{t=1}^{T} \log p(w_t|w_{t-c},...,w_{t+c})T1t=1∑Tlogp(wt∣wt−c,...,wt+c)
6.4 强化学习在对话管理中的应用
在对话管理中,我们可以使用强化学习来优化对话策略:
马尔可夫决策过程(MDP):
(S,A,P,R,γ)(S, A, P, R, \gamma)(S,A,P,R,γ)
其中,SSS是状态集合,AAA是动作集合,PPP是状态转移概率,RRR是奖励函数,γ\gammaγ是折扣因子。
Q学习:
Q(st,at)←Q(st,at)+α[rt+1+γmaxaQ(st+1,a)−Q(st,at)]Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha[r_{t+1} + \gamma \max_a Q(s_{t+1}, a) - Q(s_t, a_t)]Q(st,at)←Q(st,at)+α[rt+1+γamaxQ(st+1,a)−Q(st,at)]
7. 项目实战:从零开始构建一个简单的AI助理
现在,让我们进入实战环节!我们将从零开始构建一个简单但功能完整的个人AI助理。
7.1 项目规划
我们的AI助理将具备以下基本功能:
- 语音交互(语音识别和语音合成)
- 基础对话能力
- 天气查询
- 日程管理
- 打开应用程序
7.2 技术选型
为了简化开发,我们将使用以下技术栈:
- 编程语言:Python
- 语音识别:OpenAI Whisper
- 自然语言理解:Rasa或简单的规则+开源LLM
- 语音合成:Coqui TTS或系统TTS
- 知识库:SQLite
- 日程管理:使用本地日历或简单的SQLite表
8. 开发环境搭建
在开始编码之前,我们需要搭建开发环境。
8.1 系统要求
- 操作系统:Windows 10/11、macOS 10.15+、Linux (Ubuntu 20.04+)
- Python版本:3.8 - 3.11
- 内存:至少8GB RAM(推荐16GB+)
- 存储:至少10GB可用空间
8.2 安装依赖
让我们创建一个项目目录并安装必要的依赖:
# 创建项目目录
mkdir personal-ai-assistant
cd personal-ai-assistant
# 创建虚拟环境
python -m venv venv
# 激活虚拟环境
# Windows:
venv\Scripts\activate
# macOS/Linux:
source venv/bin/activate
# 安装基础依赖
pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install openai-whisper
pip install SpeechRecognition
pip install pyaudio
pip install pyttsx3
pip install transformers
pip install sentencepiece
pip install accelerate
pip install sqlite3
pip install python-dotenv
pip install flask
pip install requests
注意:在某些系统上安装PyAudio可能会遇到问题,你可能需要先安装系统级依赖。
9. 系统核心实现源代码
现在,让我们开始编写代码。我们将分模块实现系统的各个部分。
9.1 项目结构
首先,让我们规划项目结构:
personal-ai-assistant/
├── config/ # 配置文件
│ └── settings.py
├── core/ # 核心模块
│ ├── __init__.py
│ ├── asr.py # 语音识别
│ ├── tts.py # 语音合成
│ ├── nlu.py # 自然语言理解
│ └── dialogue_manager.py # 对话管理
├── skills/ # 技能模块
│ ├── __init__.py
│ ├── base_skill.py
│ ├── weather.py
│ ├── calendar.py
│ └── app_launcher.py
├── data/ # 数据存储
│ ├── knowledge_base/
│ └── user_data/
├── ui/ # 用户界面
│ ├── cli.py
│ └── gui.py
├── tests/ # 测试文件
├── requirements.txt
├── .env.example
└── main.py
9.2 配置文件
创建 config/settings.py:
import os
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
# 基础路径
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 语音识别设置
ASR_MODEL = "base" # tiny, base, small, medium, large
# 语音合成设置
TTS_ENGINE = "pyttsx3" # pyttsx3, coqui
# 自然语言理解设置
NLU_ENGINE = "simple" # simple, rasa, llm
# 数据库设置
DATABASE_PATH = os.path.join(BASE_DIR, "data", "user_data", "assistant.db")
# API密钥(从环境变量获取)
OPENWEATHER_API_KEY = os.getenv("OPENWEATHER_API_KEY", "")
# 技能设置
ENABLED_SKILLS = [
"weather",
"calendar",
"app_launcher",
"conversation"
]
9.3 语音识别模块
创建 core/asr.py:
import whisper
import speech_recognition as sr
import numpy as np
from typing import Optional
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class ASR:
def __init__(self, model_name: str = "base"):
"""
初始化语音识别模块
Args:
model_name: Whisper模型名称 (tiny, base, small, medium, large)
"""
logger.info(f"加载Whisper模型: {model_name}")
self.model = whisper.load_model(model_name)
self.recognizer = sr.Recognizer()
def listen_from_microphone(self, timeout: int = 5, phrase_time_limit: int = 10) -> Optional[str]:
"""
从麦克风监听并识别语音
Args:
timeout: 等待开始说话的超时时间(秒)
phrase_time_limit: 最长说话时间(秒)
Returns:
识别的文本,如果失败则返回None
"""
try:
with sr.Microphone() as source:
logger.info("正在监听...")
self.recognizer.adjust_for_ambient_noise(source)
audio = self.recognizer.listen(
source,
timeout=timeout,
phrase_time_limit=phrase_time_limit
)
# 将AudioData转换为numpy数组
audio_data = np.frombuffer(audio.frame_data, dtype=np.int16)
audio_data = audio_data.astype(np.float32) / 32768.0
logger.info("正在识别...")
result = self.model.transcribe(audio_data, language="zh")
text = result["text"].strip()
if text:
logger.info(f"识别结果: {text}")
return text
else:
logger.warning("未识别到语音")
return None
except sr.WaitTimeoutError:
logger.warning("监听超时")
return None
except Exception as e:
logger.error(f"语音识别出错: {e}")
return None
def transcribe_audio_file(self, audio_path: str) -> Optional[str]:
"""
识别音频文件
Args:
audio_path: 音频文件路径
Returns:
识别的文本,如果失败则返回None
"""
try:
logger.info(f"正在识别音频文件: {audio_path}")
result = self.model.transcribe(audio_path, language="zh")
text = result["text"].strip()
logger.info(f"识别结果: {text}")
return text
except Exception as e:
logger.error(f"音频文件识别出错: {e}")
return None
9.4 语音合成模块
创建 core/tts.py:
import pyttsx3
import logging
from typing import Optional
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class TTS:
def __init__(self, engine: str = "pyttsx3"):
"""
初始化语音合成模块
Args:
engine: 语音合成引擎 (pyttsx3, coqui)
"""
self.engine_type = engine
if engine == "pyttsx3":
self.engine = pyttsx3.init()
# 设置语音属性
voices = self.engine.getProperty('voices')
# 尝试设置中文语音
for voice in voices:
if 'zh' in voice.id.lower() or 'chinese' in voice.name.lower():
self.engine.setProperty('voice', voice.id)
break
# 设置语速
self.engine.setProperty('rate', 150)
# 设置音量
self.engine.setProperty('volume', 0.9)
elif engine == "coqui":
# Coqui TTS的实现
logger.info("Coqui TTS初始化(需要额外安装)")
# 这里可以添加Coqui TTS的初始化代码
self.engine = None
def speak(self, text: str) -> None:
"""
朗读文本
Args:
text: 要朗读的文本
"""
if not text:
logger.warning("没有文本可以朗读")
return
logger.info(f"正在朗读: {text}")
if self.engine_type == "pyttsx3":
try:
self.engine.say(text)
self.engine.runAndWait()
except Exception as e:
logger.error(f"语音合成出错: {e}")
elif self.engine_type == "coqui":
# Coqui TTS的实现
pass
def save_to_file(self, text: str, file_path: str) -> None:
"""
将语音保存到文件
Args:
text: 要合成的文本
file_path: 保存路径
"""
logger.info(f"正在保存语音到: {file_path}")
if self.engine_type == "pyttsx3":
try:
self.engine.save_to_file(text, file_path)
self.engine.runAndWait()
except Exception as e:
logger.error(f"保存语音文件出错: {e}")
9.5 自然语言理解模块
创建 core/nlu.py:
import re
import logging
from typing import Dict, List, Optional, Tuple
from enum import Enum
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class Intent(Enum):
"""预定义的意图类型"""
UNKNOWN = "unknown"
GREETING = "greeting"
FAREWELL = "farewell"
ASK_WEATHER = "ask_weather"
ADD_CALENDAR = "add_calendar"
CHECK_CALENDAR = "check_calendar"
LAUNCH_APP = "launch_app"
CHAT = "chat"
THANK = "thank"
class EntityType(Enum):
"""实体类型"""
DATE_TIME = "date_time"
LOCATION = "location"
APP_NAME = "app_name"
EVENT = "event"
class SimpleNLU:
"""基于规则的简单NLU模块"""
def __init__(self):
# 意图关键词
self.intent_patterns = {
Intent.GREETING: [
r'你好', r'早上好', r'下午好', r'晚上好', r'嗨', r'哈喽', r'hi', r'hello'
],
Intent.FAREWELL: [
r'再见', r'拜拜', r'下次见', r'回头见', r'goodbye', r'bye'
],
Intent.ASK_WEATHER: [
r'天气', r'温度', r'下雨', r'晴天', r'阴天', r'刮风', r'下雪'
],
Intent.ADD_CALENDAR: [
r'提醒我', r'帮我记', r'添加日程', r'安排', r'创建提醒'
],
Intent.CHECK_CALENDAR: [
r'日程', r'安排', r'计划', r'今天有什么', r'明天有什么'
],
Intent.LAUNCH_APP: [
r'打开', r'启动', r'运行', r'开启'
],
Intent.THANK: [
r'谢谢', r'感谢', r'多谢', r'thanks', r'thank you'
]
}
# 实体提取模式
self.entity_patterns = {
EntityType.DATE_TIME: [
r'(\d{4}年\d{1,2}月\d{1,2}日)',
r'(\d{1,2}月\d{1,2}日)',
r'今天', r'明天', r'后天', r'昨天', r'前天',
r'下周', r'上周', r'本周',
r'(\d{1,2}点)', r'(\d{1,2}:\d{2})',
r'早上', r'上午', r'中午', r'下午', r'晚上'
],
EntityType.LOCATION: [
r'在(.*?)的天气',
r'(.*?)天气'
],
EntityType.APP_NAME: [
r'打开(.*?)$',
r'启动(.*?)$',
r'运行(.*?)$'
]
}
def extract_intent(self, text: str) -> Intent:
"""
从文本中提取意图
Args:
text: 用户输入文本
Returns:
识别到的意图
"""
text = text.lower()
for intent, patterns in self.intent_patterns.items():
for pattern in patterns:
if re.search(pattern, text):
logger.info(f"识别到意图: {intent}")
return intent
# 默认返回聊天意图
logger.info("未识别到特定意图,默认使用聊天意图")
return Intent.CHAT
def extract_entities(self, text: str, intent: Intent) -> Dict[str, str]:
"""
从文本中提取实体
Args:
text: 用户输入文本
intent: 识别到的意图
Returns:
提取到的实体字典
"""
entities = {}
# 根据意图提取不同的实体
if intent == Intent.ASK_WEATHER:
# 提取地点
for pattern in self.entity_patterns[EntityType.LOCATION]:
match = re.search(pattern, text)
if match:
location = match.group(1) if match.groups() else match.group(0)
# 清理提取的地点
location = re.sub(r'[的天气]', '', location).strip()
if location:
entities['location'] = location
break
# 提取时间
for pattern in self.entity_patterns[EntityType.DATE_TIME]:
match = re.search(pattern, text)
if match:
entities['datetime'] = match.group(0)
break
elif intent == Intent.LAUNCH_APP:
# 提取应用名
for pattern in self.entity_patterns[EntityType.APP_NAME]:
match = re.search(pattern, text)
if match:
app_name = match.group(1).strip()
if app_name:
entities['app_name'] = app_name
break
elif intent in [Intent.ADD_CALENDAR, Intent.CHECK_CALENDAR]:
# 提取时间
for pattern in self.entity_patterns[EntityType.DATE_TIME]:
match = re.search(pattern, text)
if match:
entities['datetime'] = match.group(0)
break
# 对于添加日程,提取事件
if intent == Intent.ADD_CALENDAR:
# 简单提取:从提醒我后面的内容
event_patterns = [
r'提醒我(.*?)(?:在|明天|今天|后天|$)',
r'帮我记(.*?)(?:在|明天|今天|后天|$)'
]
for pattern in event_patterns:
match = re.search(pattern, text)
if match:
event = match.group(1).strip()
if event:
entities['event'] = event
break
return entities
def parse(self, text: str) -> Dict:
"""
完整的NLU解析
Args:
text: 用户输入文本
Returns:
解析结果,包含意图和实体
"""
intent = self.extract_intent(text)
entities = self.extract_entities(text, intent)
result = {
'text': text,
'intent': intent,
'entities': entities
}
logger.info(f"NLU解析结果: {result}")
return result
9.6 技能模块基类
创建 skills/base_skill.py:
from abc import ABC, abstractmethod
from typing import Dict, Any, Optional
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class BaseSkill(ABC):
"""技能基类"""
def __init__(self, name: str):
self.name = name
self.enabled = True
@abstractmethod
def can_handle(self, nlu_result: Dict[str, Any]) -> bool:
"""
判断该技能是否能处理给定的NLU结果
Args:
nlu_result: NLU解析结果
Returns:
是否能处理
"""
pass
@abstractmethod
def handle(self, nlu_result: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> str:
"""
处理用户请求并生成回复
Args:
nlu_result: NLU解析结果
context: 对话上下文
Returns:
生成的回复文本
"""
pass
def enable(self):
"""启用技能"""
self.enabled = True
logger.info(f"技能 {self.name} 已启用")
def disable(self):
"""禁用技能"""
self.enabled = False
logger.info(f"技能 {self.name} 已禁用")
9.7 对话管理模块
创建 core/dialogue_manager.py:
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime
from core.nlu import SimpleNLU, Intent
from skills.base_skill import BaseSkill
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class DialogueManager:
"""对话管理器"""
def __init__(self, nlu: SimpleNLU, skills: List[BaseSkill]):
"""
初始化对话管理器
Args:
nlu: 自然语言理解模块
skills: 可用的技能列表
"""
self.nlu = nlu
self.skills = {skill.name: skill for skill in skills}
self.context = {
'history': [],
'last_intent': None,
'last_entities': {},
'user_preferences': {}
}
def process_input(self, user_input: str) -> str:
"""
处理用户输入并生成回复
Args:
user_input: 用户输入文本
Returns:
生成的回复文本
"""
# 记录用户输入
self._update_context('user', user_input)
# NLU解析
nlu_result = self.nlu.parse(user_input)
# 更新上下文
self.context['last_intent'] = nlu_result['intent']
self.context['last_entities'] = nlu_result['entities']
# 找到合适的技能处理
response = self._find_and_handle_skill(nlu_result)
# 记录系统回复
self._update_context('assistant', response)
return response
def _find_and_handle_skill(self, nlu_result: Dict[str, Any]) -> str:
"""
找到并使用合适的技能处理
Args:
nlu_result: NLU解析结果
Returns:
生成的回复文本
"""
# 首先尝试找到专门处理该意图的技能
for skill_name, skill in self.skills.items():
if skill.enabled and skill.can_handle(nlu_result):
try:
logger.info(f"使用技能: {skill_name}")
return skill.handle(nlu_result, self.context)
except Exception as e:
logger.error(f"技能 {skill_name} 处理出错: {e}")
return f"抱歉,处理你的请求时出错了: {e}"
# 如果没有找到专门的技能,使用默认回复
return self._default_response(nlu_result)
def _default_response(self, nlu_result: Dict[str, Any]) -> str:
"""
生成默认回复
Args:
nlu_result: NLU解析结果
Returns:
默认回复文本
"""
intent = nlu_result['intent']
if intent == Intent.GREETING:
return "你好!我是你的个人AI助理,有什么可以帮助你的吗?"
elif intent == Intent.FAREWELL:
return "再见!有需要随时找我。"
elif intent == Intent.THANK:
return "不客气,能帮到你我很开心!"
elif intent == Intent.CHAT:
return "我在听呢,你想聊点什么?"
else:
return "抱歉,我不太理解你的意思,能换种说法吗?"
def _update_context(self, role: str, content: str):
"""
更新对话上下文
Args:
role: 发言角色 ('user' 或 'assistant')
content: 发言内容
"""
# 添加到历史记录
self.context['history'].append({
'role': role,
'content': content,
'timestamp': datetime.now().isoformat()
})
# 保留最近的20条记录
if len(self.context['history']) > 20:
self.context['history'] = self.context['history'][-20:]
def get_context(self) -> Dict[str, Any]:
"""
获取当前对话上下文
Returns:
对话上下文
"""
return self.context
def reset_context(self):
"""重置对话上下文"""
self.context = {
'history': [],
'last_intent': None,
'last_entities': {},
'user_preferences': self.context.get('user_preferences', {})
}
logger.info("对话上下文已重置")
9.8 天气查询技能
创建 skills/weather.py:
import logging
from typing import Dict, Any, Optional
import requests
from datetime import datetime, timedelta
from skills.base_skill import BaseSkill
from core.nlu import Intent
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class WeatherSkill(BaseSkill):
"""天气查询技能"""
def __init__(self, api_key: str = ""):
super().__init__("weather")
self.api_key = api_key
self.base_url = "https://api.openweathermap.org/data/2.5"
# 默认城市
self.default_location = "北京"
def can_handle(self, nlu_result: Dict[str, Any]) -> bool:
"""判断是否能处理"""
intent = nlu_result.get('intent')
return intent == Intent.ASK_WEATHER
def handle(self, nlu_result: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> str:
"""处理天气查询"""
entities = nlu_result.get('entities', {})
# 获取位置
location = entities.get('location', self.default_location)
# 获取时间(简单处理)
datetime_str = entities.get('datetime', '今天')
try:
# 查询天气
weather_data = self._get_weather(location)
if weather_data:
return self._format_weather_response(weather_data, location, datetime_str)
else:
return f"抱歉,未能获取{location}的天气信息。"
except Exception as e:
logger.error(f"查询天气出错: {e}")
return f"查询天气时出错了: {e}"
def _get_weather(self, location: str) -> Optional[Dict[str, Any]]:
"""
调用天气API获取天气信息
Args:
location: 位置名称
Returns:
天气数据
"""
if not self.api_key:
# 如果没有APIKey,返回模拟数据
logger.warning("没有设置天气API密钥,返回模拟数据")
return self._get_mock_weather(location)
try:
# 首先使用地理编码API获取城市坐标
geo_url = f"{self.base_url}/weather"
params = {
'q': location,
'appid': self.api_key,
'units': 'metric', # 使用摄氏度
'lang': 'zh_cn'
}
response = requests.get(geo_url, params=params, timeout=10)
response.raise_for_status()
return response.json()
except Exception as e:
logger.error(f"调用天气API出错: {e}")
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)