一、 前言:为什么要做 WanXiangAI?

在“百模大战”的今天,各家大模型 API 层出不穷。作为一名开发者,与其每天在不同的网页端来回切换,不如亲手打造一个属于自己的全能 AI 终端。这正是 WanXiangAI(万象 AI 助手) 诞生的初衷。

在这个项目中,我没有使用过于沉重的 LangChain 或 LlamaIndex 框架,而是选择用纯 Python 结合现代化的 UI 框架,从底层构建核心逻辑。通过这个项目,我不仅串联了多家大模型的 API,还深入落地了文档解析、RAG(检索增强生成)以及多模态语音交互技术。这篇文章将对 WanXiangAI 的核心技术实现进行一次全面的复盘。


二、 架构概览:不仅仅是一个聊天框

WanXiangAI 的后端架构高度模块化,主要由以下几个核心部分驱动:

引擎层 (Models):统一封装了 Doubao(豆包)、DeepSeek、Kimi、Qwen(通义千问)和 Spark(星火)等主流大模型的 API。

核心逻辑层 (Modules):包含文件处理与检索 (file_utils.py)、对话状态调度 (chat.py)、系统提示词管理 (prompt.py) 以及工具链 (tools.py)。

表现层 (UI):基于 Gradio 框架,深度定制 CSS 以实现响应式、沉浸式的交互界面。

三、 核心技术点一:多模型统一调度与“随时中断”机制

在接入多个大模型时,我利用 Python 的多态特性和单例模式,优雅地解决了频繁实例化带来的性能开销,并实现了一个非常关键的 UX 功能:随时停止生成。

在 modules/chat.py 中,我维护了一个全局的 model_instances 字典来管理模型实例,确保每个模型只被初始化一次。为了实现停止功能,我在每个 LLM 引擎内部(例如 DeepSeekLlmEngine)都增加了一个 stop_flag 标志位。

核心调度与打断代码:

# models/deepseek.py (模型内部处理流式输出与打断)
for chunk in response:
    # 检测停止标志位,中断流式输出
    if self.stop_flag:
        print("\n生成已停止")
        break
    if chunk.choices and chunk.choices[0].delta.content:
        content = chunk.choices[0].delta.content
        history[-1]['content'] += content
        yield history

# modules/chat.py (全局触发打断)
def stop_generation(model_name):
    """停止生成的逻辑:设置标志位"""
    model = model_instances.get(model_name)
    if model:
        # 动态给对象设置属性,即使原类中没有定义 stop_flag 也能生效
        model.stop_flag = True
        print(f"🛑 {model_name} 接收到停止指令")

这种设计不仅解耦了前端按钮与后端网络请求,还有效避免了长文本生成时对服务器资源和 Token 的浪费。

四、 核心技术点二:手搓纯本地 RAG(检索增强生成)系统

为了让 WanXiangAI 能“读懂”本地文件,我用 Python 基础库手写了一套轻量级的 RAG 系统。

在 modules/file_utils.py 中,我集成了 PyPDF2 (处理 PDF)、python-docx (处理 Word)、openpyxl (处理 Excel) 甚至 pytesseract (通过 OCR 提取图片文字)。

当提取完长文本后,系统会进行分片(Chunking),并通过自研的关键词匹配算法,提取与用户当前问题最相关的片段。随后,在对话主循环中,将检索到的片段通过 Prompt 强制注入给大模型:

防幻觉的 Prompt 注入逻辑:

# modules/chat.py
if relevant_context and relevant_context.strip():
    doc_prompt = f"""
以下是和用户问题相关的文档内容(仅基于此内容回答,不要编造):
{relevant_context}

【回答规则】:
1. 优先使用上述文档内容回答用户问题;
2. 如果文档中没有相关信息,明确告知"文档中未提及相关内容";
3. 回答需基于文档,不要添加无关信息;
4. 保留文档中的关键信息(如数字、术语、逻辑)。
"""
    # 拼接并覆盖原有 prompt
    prompt = (prompt or "") + doc_prompt

通过这种极其严格的指令约束,WanXiangAI 在处理长文档问答时,极大地降低了“一本正经胡说八道”的幻觉概率。


五、 核心技术点三:赋予 AI 听觉——语音交互模块

为了丰富交互维度,WanXiangAI 接入了强大的开源音频处理库。
在语音转文本(STT)方面,系统在本地加载了 OpenAI 的 Whisper 模型。为了解决模型偶尔将环境杂音误识别为外语的痛点,我在调用时做了两个硬性约束:

# modules/tools.py
result = stt_model.transcribe(
    audio_path,                  # 临时录音文件
    language="zh",               # 1. 强制指定为中文
    initial_prompt="你好,这是一段清晰的普通话对话。" # 2. 给予语境暗示
)

而在文本转语音(TTS)方面,则采用了微软的 Edge-TTS 服务,实现了低延迟、拟真度极高的语音播报。

六、 UI 与交互:高仿 Gemini 的沉浸式体验

除了硬核的后端逻辑,我在前端体验上也下了很大功夫。原生的 Gradio 界面偏向学术风,为了达到工业级产品的质感,我在 app.py 中注入了大量的定制 CSS 以及 JavaScript。

例如,我重写了聊天气泡的样式,采用了非对称圆角;同时,彻底改造了输入框的布局,去掉了冗余边框,使用悬浮感设计,辅以正圆形的“➤”发送按钮,打造出了一体化、类似 Gemini 的现代搜索栏质感。

七、 总结与展望

从零堆砌 WanXiangAI 的过程,对我而言是一次技术思维的刷新。
在大模型 API 抹平了算法门槛的今天,如何通过优秀的架构设计(如单例模式调度引擎)、扎实的工程实现(如手搓 RAG 分片与评分)以及极致的用户体验打磨(定制 CSS 与多模态 I/O),才是开发者真正的护城河。

目前 WanXiangAI 的 RAG 检索仍基于关键词的频率与权重计算。下一步,我计划引入轻量级的向量数据库(如 ChromaDB 或 FAISS)以及 Embedding 模型,将其升级为真正的语义检索架构。

保持动手,保持热爱。期待 WanXiangAI 的下一次蜕变!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐