基于LangGraph与Mem0构建本地语音AI智能体:从架构到实践 1. 项目概述打造一个能听会说的本地AI伙伴最近在捣鼓一个挺有意思的东西一个完全运行在你本地电脑上的、能用语音对话的AI智能体。想象一下你对着麦克风说“帮我查一下明天的天气”或者“总结一下我刚打开的这篇PDF”一个声音就能回应你并且真的去执行任务。这听起来像是科幻电影里的场景但现在借助像LangGraph和Mem0这样的工具我们完全可以在自己的机器上把它搭建出来。这个项目的核心就是构建一个“语音控制本地AI大脑”的智能代理。它不依赖任何云端API所有数据处理、模型推理都在你的本地环境中完成这意味着你的对话数据、文件内容完全私密不会被上传到任何第三方服务器。同时通过语音交互它比传统的聊天框输入要自然和高效得多尤其适合在双手被占用比如做饭、开车或者需要快速获取信息时使用。我之所以选择LangGraph和Mem0作为技术栈是因为它们分别解决了智能体构建中的两个核心难题。LangGraph擅长用“图”的思维来编排复杂的工作流让AI的思考过程变得可视化、可调试而Mem0则为AI提供了长期记忆能力让它能记住之前的对话上下文甚至你的个人偏好从而实现真正连贯的、个性化的交流。把这两者结合起来再配上本地的语音识别和语音合成一个功能强大且私密的个人AI助手就初具雏形了。无论你是AI爱好者想深入理解智能体架构还是开发者希望为自己的应用增加一个自然的交互入口这个项目都能提供一个从零到一的完整实践路径。接下来我会详细拆解整个构建过程从设计思路到每一行代码并分享我在实操中踩过的坑和总结的技巧。2. 核心架构设计与技术选型解析2.1 为什么是“本地”“语音控制”在开始动手之前我们必须想清楚架构设计的出发点。选择“本地化”首要考虑的是隐私与数据安全。当AI处理你的日程、文档甚至是一些敏感的工作笔记时将数据留在本地是最让人安心的方案。其次是可控性与成本。你无需担心云服务商的API调用限制、费率变化或服务中断所有的算力都来自你自己的硬件一次部署长期使用。而“语音控制”则是为了极致的交互便利性。它降低了使用门槛让AI能力可以无缝嵌入到各种生活场景中。比如我在厨房时可以直接问“牛排要煎几分钟”而不需要擦干手再去打字。这种自然的交互方式是智能体能否真正成为“助手”而非“工具”的关键。2.2 技术栈深度剖析LangGraph与Mem0的角色LangGraph智能体的“决策大脑”与“工作流引擎”你可以把LangGraph理解为一个专门为AI智能体设计的工作流编排框架。传统的链式调用Chain在处理线性任务时很好用但智能体往往需要根据环境反馈做循环判断、分支选择这更像一个图Graph结构。LangGraph允许你定义多个“节点”Nodes每个节点代表一个特定的功能如调用LLM、执行工具、条件判断然后用“边”Edges来规定它们之间的流转逻辑。在这个语音AI项目中LangGraph的核心作用是协调整个交互流程。一个典型的流程可能是1. 语音识别节点接收音频并转成文本2. 路由节点分析用户意图判断是简单问答还是需要调用工具如查天气、读文件3. 根据路由结果调用相应的工具节点或直接让LLM节点生成回答4. 将LLM生成的文本回答送入语音合成节点。LangGraph让这个包含循环比如持续追问和分支不同意图对应不同工具的流程变得清晰、可维护。Mem0为智能体赋予“记忆”的能力没有记忆的AI每次对话都是全新的开始这显然不是一个合格助手该有的样子。Mem0就是一个为AI应用设计的长期记忆存储与检索库。它的核心功能是将对话历史、用户信息等以向量化的形式存储起来并在需要时快速检索出与当前对话最相关的历史片段作为上下文提供给LLM。在我们的项目里Mem0扮演着“个人档案管理员”的角色。它会记住你曾经说过“我喜欢喝黑咖啡”或者上周你让它总结过某份报告的关键点。当新的对话发生时Mem0会自动从记忆中检索出相关片段让LLM的回答更具连贯性和个性化。这极大地提升了对话体验的质量从一问一答的机器变成了一个能记住事情的“伙伴”。2.3 整体系统架构图概念层面整个系统可以划分为四个核心层交互层负责语音的输入与输出。包括麦克风音频采集、语音识别STT将音频转为文本以及语音合成TTS将文本回复转为语音播放。智能体核心层这是系统的大脑以LangGraph构建的工作流为核心。它接收交互层传来的文本结合Mem0提供的记忆上下文进行意图理解、决策并调用相应的工具或直接生成回复文本。工具与执行层智能体可以调用的“手和脚”。例如查询本地文件的工具、执行系统命令的工具、调用本地知识库的工具等。这部分决定了你的AI助手具体能“做”什么。记忆与知识层由Mem0主导负责存储和检索所有对话历史、用户偏好及从工具执行中获取的结构化信息如“用户昨天创建了名为‘项目计划’的文档”。这个分层架构确保了各模块职责清晰便于独立升级和调试。例如你可以轻松更换不同的语音识别引擎而无需改动核心的智能体逻辑。3. 环境搭建与核心组件配置实操3.1 基础Python环境与依赖管理我强烈推荐使用conda或venv创建独立的Python虚拟环境以避免包依赖冲突。这里以conda为例# 创建并激活一个名为voice_agent的Python 3.10环境 conda create -n voice_agent python3.10 -y conda activate voice_agent接下来是安装核心库。我们将使用pip进行安装。这里有一个关键点LangGraph和相关的LangChain库版本兼容性很重要建议锁定一组经过测试的版本。# 安装LangChain全家桶和LangGraph pip install langgraph langchain langchain-community # 安装Mem0用于记忆管理 pip install mem0ai # 安装本地LLM运行库这里以Ollama为例它支持在本地运行Llama、Mistral等模型 pip install ollama # 安装语音处理库 # SpeechRecognition用于语音识别pyaudio用于音频输入 pip install SpeechRecognition pyaudio # TTS库这里选择离线的pyttsx3也可以选择其他如TTSCoqui pip install pyttsx3注意在Windows上安装pyaudio可能会遇到问题。如果pip install pyaudio失败可以尝试从 Christoph Gohlke的非官方Windows二进制文件页面 下载对应Python版本和系统架构的.whl文件进行安装。3.2 本地大语言模型LLM的部署与连接为了让一切在本地运行我们需要一个本地LLM。Ollama是目前最易用的方案之一它支持一键拉取和运行多种开源模型。安装Ollama前往Ollama官网下载并安装对应操作系统的客户端。拉取模型在终端运行命令拉取一个适合你硬件配置的模型。对于8GB以上内存的电脑llama3.2:3b或mistral:7b是不错的起点它们在速度和能力上取得了很好的平衡。ollama pull llama3.2:3b在Python中连接Ollama安装langchain-community后我们可以很方便地集成。from langchain_community.llms import Ollama # 初始化本地LLM指定你拉取的模型名称 llm Ollama(modelllama3.2:3b) # 测试一下 print(llm.invoke(你好请用中文回答。))模型选型心得如果你的GPU显存足够例如大于8GB可以尝试更大的模型如llama3.1:8b以获得更强的推理能力。如果只有CPU那么3B或7B的模型是更实际的选择。关键在于在“响应速度”和“回答质量”之间找到符合你预期的平衡点。我实测在苹果M2芯片的MacBook Air上运行3B模型几乎无延迟体验非常流畅。3.3 Mem0记忆系统的初始化与配置Mem0的使用相对直接。你需要从Mem0官网获取一个API密钥但请注意Mem0也提供了本地部署的选项为了完全本地化我们这里使用其客户端库与自托管服务或配置本地存储策略。from mem0 import Memory # 初始化记忆系统 # 如果你使用Mem0的云服务非完全本地需要api_key # memory Memory(api_keyyour-mem0-api-key) # 为了追求完全本地化我们可以配置Mem0使用本地向量数据库如Chroma作为存储后端 # 这里假设你已安装chromadb # pip install chromadb from langchain.vectorstores import Chroma from langchain.embeddings import OllamaEmbeddings # 使用本地Ollama模型生成嵌入 # 创建本地嵌入函数和向量库 embeddings OllamaEmbeddings(modelnomic-embed-text) # 需要一个嵌入模型 vectorstore Chroma(embedding_functionembeddings, persist_directory./mem0_chroma_db) # 将vectorstore适配到Mem0注Mem0原生可能不支持直接传入此处为概念示意 # 实际中可能需要自定义一个继承自Memory的类重写其存储检索方法指向本地的Chroma。 # 这是一个高级定制点。对于初版我们可以先使用Mem0的基础内存功能它默认会在本地存储。 memory Memory()在项目初期我们可以直接使用Memory()它会将记忆以文件形式存储在本地。虽然检索效率可能不如向量数据库但足以实现“记住对话历史”的基本功能。后续优化时再考虑集成本地向量库实现更精准的语义记忆检索。3.4 语音模块的选型与测试语音识别Speech-to-Text, STT 我们使用SpeechRecognition库它支持多种后端引擎。对于离线场景recognize_sphinxCMU Sphinx是唯一选择但准确率一般。为了更好的体验我推荐使用开源的Vosk离线模型它准确率高且支持多语言。安装Voskpip install vosk下载Vosk中文小模型约40MB并从Vosk模型仓库下载解压。编写语音识别函数import json from vosk import Model, KaldiRecognizer import pyaudio def listen_with_vosk(model_pathvosk-model-small-cn-0.22): model Model(model_path) rec KaldiRecognizer(model, 16000) p pyaudio.PyAudio() stream p.open(formatpyaudio.paInt16, channels1, rate16000, inputTrue, frames_per_buffer8000) stream.start_stream() print(请开始说话...) while True: data stream.read(4000) if len(data) 0: break if rec.AcceptWaveform(data): result json.loads(rec.Result()) text result.get(text, ) if text: print(f识别结果: {text}) return text return 语音合成Text-to-Speech, TTS 对于离线TTSpyttsx3是一个简单直接的选择它调用系统自带的语音引擎。import pyttsx3 def speak_with_pyttsx3(text): engine pyttsx3.init() # 可以设置语速、音量等 # engine.setProperty(rate, 150) # engine.setProperty(volume, 0.9) engine.say(text) engine.runAndWait()踩坑记录pyttsx3在macOS上可能默认引擎效果不佳可以尝试安装libobjc并指定nsss引擎。如果追求更自然的声音可以研究离线部署Coqui TTS等开源项目但复杂度会大幅增加。对于初版系统自带引擎的稳定性和易用性是首要考虑。4. 基于LangGraph构建智能体工作流4.1 定义智能体的状态State在LangGraph中工作流围绕一个“状态”State对象流转。这个State是一个字典包含了整个流程中所有需要传递和更新的信息。我们需要仔细设计它。from typing import TypedDict, Annotated, List from langgraph.graph.message import add_messages import operator # 定义状态结构 class AgentState(TypedDict): # 消息历史LangGraph内置的add_messages操作会处理它 messages: Annotated[List, add_messages] # 从语音识别得到的用户原始输入文本 user_input: str # 经过LLM分析后的用户意图例如“query_weather”, “summarize_doc” intent: str # 从Mem0检索到的相关记忆上下文 memory_context: str # LLM生成的最终文本回复 llm_response: str # 控制流程的标志例如是否继续监听 should_continue: boolAnnotated和add_messages是LangGraph用于自动管理对话历史的便捷方式。operator模块用于后续的状态更新操作。4.2 创建关键功能节点Nodes节点是工作流中的具体执行单元。我们将创建以下几个核心节点1. 语音识别节点这个节点负责调用我们之前写好的listen_with_vosk函数并将识别结果存入状态。def speech_recognition_node(state: AgentState): print([节点] 正在聆听...) input_text listen_with_vosk() # 调用离线识别函数 if not input_text: input_text 我没听清请再说一遍。 # 更新状态 return {user_input: input_text}2. 记忆检索节点此节点查询Mem0获取与当前对话相关的历史信息。def memory_retrieval_node(state: AgentState): user_input state[user_input] # 向Mem0查询与当前输入相关的记忆 # 这里memories是一个包含相关记忆片段的列表 memories memory.search(user_input, limit3) context \n.join([m.text for m in memories]) if memories else 暂无相关记忆。 return {memory_context: context}3. 意图分析与路由节点核心这个节点将调用LLM分析用户输入决定下一步该做什么。这是智能体“思考”的关键。from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser # 定义提示词模板 intent_prompt ChatPromptTemplate.from_messages([ (system, 你是一个智能助手。请根据用户输入和历史对话上下文判断用户的意图。 历史上下文{memory_context} 可用的意图类型 - direct_chat: 普通聊天、问答不需要调用外部工具。 - query_weather: 查询天气。 - summarize_document: 总结文档内容。 - unknown: 无法识别的意图。 请只输出意图类型不要输出其他任何内容。), (human, 用户输入{user_input}) ]) def intent_router_node(state: AgentState): # 构建链 chain intent_prompt | llm | StrOutputParser() # 调用链获取意图 intent chain.invoke({ memory_context: state.get(memory_context, ), user_input: state[user_input] }) intent intent.strip().lower() print(f[节点] 识别到意图: {intent}) return {intent: intent}4. 工具执行节点根据路由节点的结果调用不同的工具函数。这里以“查询天气”为例。# 假设我们有一个本地的天气查询工具例如从本地文件或简单网络请求获取 def query_local_weather(city: str) - str: # 这里是一个模拟函数。真实场景可能需要连接本地数据库或调用可信的本地API。 weather_data { 北京: 晴15~25°C微风。, 上海: 多云18~27°C东南风3级。 } return weather_data.get(city, f未找到{city}的天气信息。) def tool_execution_node(state: AgentState): intent state[intent] user_input state[user_input] result if intent query_weather: # 简单地从输入中提取城市名实际应用可能需要更复杂的NLP提取 # 这里假设用户输入是“北京天气怎么样” city 北京 # 简化处理实际应用应使用LLM或规则提取实体 result query_local_weather(city) result f查询到{city}的天气{result} elif intent summarize_document: # 调用文档总结工具 result 文档总结功能待实现。 else: # 对于直接聊天或其他意图工具节点不执行操作结果留空 result return {tool_result: result}5. LLM生成回复节点这是另一个核心节点它综合用户输入、记忆上下文和工具执行结果生成自然、友好的文本回复。# 回复生成提示词 response_prompt ChatPromptTemplate.from_messages([ (system, 你是一个友好的本地AI助手。请根据以下信息生成回复 相关历史对话 {memory_context} 工具执行结果如果有 {tool_result} 请用自然、口语化的中文回复用户。), (human, {user_input}) ]) def llm_response_node(state: AgentState): chain response_prompt | llm | StrOutputParser() response chain.invoke({ memory_context: state.get(memory_context, ), tool_result: state.get(tool_result, ), user_input: state[user_input] }) print(f[节点] 生成回复: {response}) return {llm_response: response}6. 语音合成节点将LLM生成的文本回复转换为语音输出。def speech_synthesis_node(state: AgentState): response_text state[llm_response] if response_text: print(f[节点] 正在播报: {response_text}) speak_with_pyttsx3(response_text) return {}7. 记忆更新节点将本轮重要的对话信息存入Mem0以便未来检索。def memory_update_node(state: AgentState): user_input state[user_input] llm_response state[llm_response] # 将一轮完整的QA作为一条记忆存储 memory_entry f用户: {user_input}\n助手: {llm_response} memory.add(memory_entry) print([节点] 对话已存入记忆。) return {}4.3 编排工作流图Graph与条件边Edges有了节点我们需要用“边”把它们连接起来并定义流转逻辑。LangGraph的强大之处在于可以定义条件边实现动态路由。from langgraph.graph import StateGraph, END # 创建图构建器 workflow StateGraph(AgentState) # 添加所有节点 workflow.add_node(listen, speech_recognition_node) workflow.add_node(retrieve_memory, memory_retrieval_node) workflow.add_node(route_intent, intent_router_node) workflow.add_node(execute_tool, tool_execution_node) workflow.add_node(generate_response, llm_response_node) workflow.add_node(speak, speech_synthesis_node) workflow.add_node(update_memory, memory_update_node) # 设置入口点 workflow.set_entry_point(listen) # 添加普通边固定流转 workflow.add_edge(listen, retrieve_memory) workflow.add_edge(retrieve_memory, route_intent) workflow.add_edge(generate_response, speak) workflow.add_edge(speak, update_memory) workflow.add_edge(update_memory, END) # 一轮对话结束 # 添加条件边根据意图决定下一步是调用工具还是直接生成回复 def decide_next_step(state: AgentState): intent state[intent] # 如果意图是需要调用工具的则前往工具执行节点 if intent in [query_weather, summarize_document]: return execute_tool # 否则直接去生成回复节点 else: return generate_response # 将路由节点连接到这个决策函数 workflow.add_conditional_edges( route_intent, decide_next_step, { execute_tool: execute_tool, generate_response: generate_response } ) # 工具执行完后必然要去生成回复 workflow.add_edge(execute_tool, generate_response) # 编译图 app workflow.compile()至此一个完整的、具备记忆和分支决策能力的智能体工作流就定义好了。你可以通过app.invoke({})来启动一轮对话。但为了让其持续运行我们还需要一个主循环。5. 系统集成与持续对话循环实现5.1 构建主控循环与对话状态管理智能体需要能够持续监听、响应而不是运行一次就结束。我们需要一个while循环来驱动整个流程并妥善管理每一轮对话的初始状态。def run_voice_agent(): print(本地语音AI助手已启动说‘退出’或‘停止’来结束程序。) # 初始化一个空的对话状态 initial_state { messages: [], # LangGraph会管理这个列表 user_input: , intent: , memory_context: , llm_response: , should_continue: True, } while True: try: # 执行编译好的工作流图 final_state app.invoke(initial_state) # 检查用户是否要求退出可以在语音识别节点或意图分析中实现 user_input final_state.get(user_input, ) if 退出 in user_input or 停止 in user_input: print(收到退出指令助手即将关闭。) speak_with_pyttsx3(再见) break # 为下一轮对话重置部分状态但保留messages历史对话以实现多轮上下文 # LangGraph的add_messages会自动更新messages所以我们只需重置其他临时字段 initial_state[user_input] initial_state[intent] initial_state[llm_response] # memory_context和tool_result会在下一轮被覆盖无需手动清空 # messages字段保留为LLM提供对话历史 print(\n *50 \n等待下一次输入...\n *50) except KeyboardInterrupt: print(\n用户中断程序。) break except Exception as e: print(f运行过程中出现错误: {e}) # 可以选择让助手语音提示错误 speak_with_pyttsx3(抱歉我好像出了点问题。) # 重置状态避免错误累积 initial_state {messages: [], user_input: , intent: , memory_context: , llm_response: , should_continue: True}这个run_voice_agent函数就是整个应用的主引擎。它启动后会进入“监听-思考-响应-记忆”的循环直到用户明确说退出。5.2 优化为LLM提供完整的对话历史在上面的循环中我们每一轮都传入了initial_state但注意initial_state[messages]在第一次是空的。为了让LLM能基于整个对话历史来回复我们需要确保messages字段在每一轮之后都得到更新。LangGraph的add_messages注解已经帮我们做了这件事。在我们的AgentState定义中messages字段被Annotated修饰意味着每个节点对state的修改如果涉及到messages都会通过add_messages函数来合并。我们需要在合适的节点比如llm_response_node将用户输入和AI回复添加到messages中。更常见的做法是使用LangGraph内置的MessagesState和RunnableWithMessageHistory来更优雅地处理。但为了保持当前架构的清晰我们可以手动管理在llm_response_node中除了返回{llm_response: response}也返回更新后的消息列表。这需要对节点函数和状态更新逻辑做一些调整是下一步深度优化的方向。5.3 首次运行与效果测试将以上所有代码模块整合到一个Python脚本中例如main.py在激活的虚拟环境中运行。python main.py你会看到终端提示“本地语音AI助手已启动”。对着麦克风清晰地说“你好”观察流程终端显示“正在聆听...”和识别结果。经过一系列节点处理意图识别为direct_chat。LLM生成回复如“你好有什么可以帮你的吗”并在终端打印。系统语音播报该回复。尝试说“北京天气怎么样”意图节点应识别为query_weather工具节点会返回模拟的天气信息最终LLM会生成包含天气信息的完整回复并播报。6. 性能调优、问题排查与扩展方向6.1 常见问题与解决方案速查表在开发和测试过程中你几乎一定会遇到以下问题。这里是我的排查笔记问题现象可能原因解决方案语音识别无反应或报错1. 麦克风权限未开启。2.pyaudio安装失败或找不到设备。3. Vosk模型路径错误或未下载。1. 检查系统麦克风设置。2. 尝试用pip install pipwin然后pipwin install pyaudioWindows。或检查默认音频设备索引。3. 确认model_path指向正确的解压后的模型文件夹。识别准确率低1. 环境噪音大。2. 麦克风质量差。3. Vosk小模型能力有限。1. 在安静环境下使用或考虑增加噪音抑制库如noisereduce。2. 使用外接麦克风。3. 下载更大的Vosk模型如vosk-model-cn-0.22约1.2G但需权衡速度。LLM响应速度慢1. 本地模型太大如7B以上且硬件不足。2. Ollama服务未启动或模型未加载。1. 换用更小的模型如3B或确认电脑有足够内存/显存。使用ollama ps查看模型运行状态。2. 确保Ollama后台服务正在运行。意图识别不准1. 提示词Prompt设计不清晰。2. 小模型的理解能力有限。3. 未有效利用记忆上下文。1. 细化意图分类在Prompt中提供更明确的例子。2. 升级更大的本地LLM模型。3. 检查memory_retrieval_node是否返回了有效的上下文信息。程序运行一轮后卡住主循环逻辑或状态重置有问题导致app.invoke等待输入或状态异常。1. 在speech_recognition_node中增加超时机制避免无限期等待。2. 仔细检查run_voice_agent函数中的状态重置逻辑确保每一轮都是干净的。可以在循环开始打印initial_state进行调试。Mem0记忆未生效1. 记忆存储失败。2. 检索时未返回相关内容。1. 检查memory.add()是否成功查看是否有错误日志。2. 打印memory.search()的结果看检索到的记忆片段是否相关。可以尝试调整检索的相似度阈值或返回数量。6.2 关键性能优化点语音识别加速Vosk识别是同步的会阻塞主线程。可以考虑将语音识别放入独立线程或使用异步IO实现“边听边想”的预加载效果减少用户等待感。LLM缓存对于频繁出现的类似问题如“你好”可以使用langchain.cache如InMemoryCache缓存LLM的响应显著提升重复问题的响应速度。记忆检索优化当前每次对话都检索记忆如果记忆库很大可能会变慢。可以设计策略例如只在检测到对话主题切换或特定关键词时才进行深度检索。流式响应与语音打断目前必须等LLM生成完整文本后才进行TTS。可以结合支持流式输出的LLMOllama支持和流式TTS实现更实时的“边想边说”。同时可以增加语音活动检测VAD允许用户随时打断助手的发言。6.3 功能扩展思路这个基础框架就像一个乐高底座可以按需添加无数功能模块丰富工具集这是提升助手能力最直接的方式。文件操作集成langchain.document_loaders让助手能读取并总结PDF、Word、TXT文件。日程管理连接本地的日历应用如通过AppleScript操控Mac日历或读写ICS文件。智能家居控制通过MQTT或HTTP协议控制本地Home Assistant等平台。信息查询接入离线的知识库如基于ChromaDB搭建的本地维基百科摘要。个性化与记忆深化用户画像让Mem0不仅记住对话还主动构建用户画像如“用户经常在晚上询问天气”让回复更具预见性。记忆总结定期让LLM对长时间的记忆进行摘要压缩信息避免记忆库无限膨胀影响检索效率。交互模式升级唤醒词像智能音箱一样增加唤醒词如“小智小智”检测只有听到唤醒词后才开始录音保护隐私。多模态接入本地视觉模型如LLaVA让助手能“看”到你摄像头捕捉的画面并描述它。构建这个本地语音AI代理的过程就像在组装一个数字时代的“瑞士军刀”。从最初的语音识别和合成到用LangGraph编织复杂的决策逻辑再用Mem0赋予它成长的记忆每一步都充满了挑战和乐趣。最大的收获不是最终的程序而是在解决一个个具体问题比如Vosk的部署、LangGraph条件边的调试时对智能体技术栈理解的加深。这个项目目前只是一个起点它的每一个模块都有巨大的优化和替换空间。你可以尝试不同的本地LLM如Qwen、Gemma可以换上更精准的离线语音识别引擎如Whisper.cpp也可以为它开发专属的工具让它真正融入你的工作流。最重要的是你拥有了一个完全受控于自己、数据不出本地的AI伙伴这为探索更私密、更个性化的AI应用打开了大门。