配置要求:
高配台式机即可

1、32G内存 1T硬盘,主流CPU,要求8G显卡,正常市场价格1万元左右的台式机
2、Windows系统,64位最好

在当今企业信息化建设中,知识管理已成为核心竞争力的一部分。然而,许多企业面临数据安全、网络限制等挑战,使得在线大模型方案难以落地。本文将介绍一套完全离线的企业知识库问答系统,结合向量化检索(RAG)语音交互技术,实现安全、高效、自然的问答体验。

一、系统架构概览

本系统的核心架构包含四大模块:

┌─────────────────────────────────────────────────────────┐
│                      用户交互层                          │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐              │
│  │ 语音唤醒 │→│ 语音听写 │→│ 语音合成 │              │
│  └──────────┘  └──────────┘  └──────────┘              │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│                      智能处理层                          │
│  ┌──────────────────────────────────────────────────┐  │
│  │          RAG向量化检索 + 本地大模型              │  │
│  └──────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│                      数据存储层                          │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐              │
│  │ 知识库   │  │ 对话历史 │  │ 配置文件 │              │
│  └──────────┘  └──────────┘  └──────────┘              │
└─────────────────────────────────────────────────────────┘

技术选型

模块 技术方案 说明
语音唤醒 讯飞IVW离线唤醒 支持自定义唤醒词
语音听写 Vosk离线识别 中文模型,16kHz采样
语音合成 讯飞TTS离线合成 实时流式播放
大语言模型 Ollama + DeepSeek 本地部署,完全离线
向量检索 自定义RAG实现 基于TF-IDF + 余弦相似度

二、核心实现详解

2.1 离线语音唤醒模块

语音唤醒是交互的第一步,系统通过讯飞IVW模块监听麦克风,检测预设唤醒词“小甲小甲”。

public static void startIvw() {
    // 1. 登录唤醒服务
    Integer ret = IvwService.INSTANCE.MSPLogin(null, null, Constants.IVW_LOGIN_PARAMS);
    
    // 2. 开启唤醒会话
    String sessionId = IvwService.INSTANCE.QIVWSessionBegin(null, Constants.IVW_SSB_PARAMS, Constants.IVW_ERROR_CODE);
    
    // 3. 注册回调函数
    ret = IvwService.INSTANCE.QIVWRegisterNotify(sessionId, new IvwCallback(), null);
    
    // 4. 循环读取麦克风音频并写入
    while (true) {
        byte[] audioDataByteArray = new byte[Constants.IVW_FRAME_SIZE];
        int len = new AudioInputStream(Constants.IVW_ASR_TARGET_DATA_LINE).read(audioDataByteArray);
        ret = IvwService.INSTANCE.QIVWAudioWrite(sessionId, audioDataByteArray, len, Constants.IVW_AUDIO_STATUS);
        Thread.sleep(200); // 每200ms写入一帧
    }
}

关键点

  • 音频帧大小需与模型匹配(通常16k采样,20ms帧长)

  • 使用回调机制异步处理唤醒事件

2.2 离线语音听写(ASR)

唤醒后,系统启动Vosk识别器将语音转换为文字:

public static void startIat() {
    // 初始化Vosk模型(中文模型约1GB)
    model = new Model("src/main/resources/vosk-model-cn-0.22");
    recognizer = new Recognizer(model, 16000);
    
    while (true) {
        bytesRead = Constants.IVW_ASR_TARGET_DATA_LINE.read(buffer, 0, buffer.length);
        
        if (recognizer.acceptWaveForm(buffer, bytesRead)) {
            // 获取最终识别结果
            JsonParseFinal result = gson.fromJson(recognizer.getFinalResult(), JsonParseFinal.class);
            startModel(result.text); // 调用大模型处理
            break;
        }
    }
}

2.3 向量化检索(RAG)

这是系统的核心亮点。在调用大模型前,系统先从知识库中检索相关内容:

// 1. 读取知识库文件(支持txt、pdf等)
List<String> inputContentList = ReadFiles.readFilesWork("src/main/resources/word_files/");

// 2. 向量化检索相关片段
String knowledge = Rag.ragWork(
    inputContentList,      // 知识库内容
    mySettings.getMax(),   // 最大返回片段数
    userQuestion,          // 用户问题
    mySettings.getTop(),   // 检索TopK
    mySettings.getSimilarity() // 相似度阈值
);

// 3. 构造增强提示词
String prompt = "根据已知知识,答复我的问题。如果问题和已知知识无关,忽略已知知识直接答复问题。"
    + "我的问题是:【" + userQuestion + "】\n" 
    + "已知知识:" + knowledge;
RAG检索算法实现(Rag.java)
public class Rag {
    // TF-IDF向量化
    public static Map<String, Double> computeTFIDF(List<String> documents) {
        // 计算词频和逆文档频率
        // 返回特征向量
    }
    
    // 余弦相似度计算
    public static double cosineSimilarity(Map<String, Double> vec1, Map<String, Double> vec2) {
        double dotProduct = 0.0;
        double norm1 = 0.0;
        double norm2 = 0.0;
        // 计算向量夹角余弦值
        return dotProduct / (norm1 * norm2);
    }
    
    // 主检索逻辑
    public static String ragWork(List<String> documents, int maxReturn, String query, int topK, double threshold) {
        // 1. 对文档分块
        // 2. 计算每个块的向量
        // 3. 计算问题向量与各块的相似度
        // 4. 返回TopK相关片段
    }
}

2.4 离线大模型调用

系统通过Ollama调用本地部署的DeepSeek模型:

public static void startModel(String userQuestion) throws Exception {
    // 1. 读取配置和对话历史
    Settings mySettings = gson.fromJson(settings, Settings.class);
    List<ModelHistory> modelHistoryList = loadHistory();
    
    // 2. 构建请求(支持上下文)
    JSONArray messages = new JSONArray();
    if (mySettings.getContext() && modelHistoryList != null) {
        for (ModelHistory temp : modelHistoryList) {
            messages.put(new JSONObject()
                .put("role", temp.getRole())
                .put("content", temp.getContent()));
        }
    }
    
    // 3. 添加RAG检索的知识
    messages.put(new JSONObject()
        .put("role", "user")
        .put("content", enhancedPrompt));
    
    // 4. 流式调用Ollama API
    Request request = new Request.Builder()
        .url("http://localhost:11434/v1/chat/completions")
        .post(body)
        .build();
        
    // 5. 处理流式响应
    while ((line = responseBody.source().readUtf8Line()) != null) {
        if (line.contains("data: ") && !line.contains("[DONE]")) {
            // 解析并实时合成语音
            AIMain.startTts(tempRes);
        }
    }
}

2.5 实时语音合成(TTS)

采用流式合成策略,边生成边播放:

public static void startTts(String ttsText) {
    // 1. 开启会话
    String session_id = TtsService.INSTANCE.QTTSSessionBegin(params, errorCode);
    
    // 2. 放入待合成文本
    TtsService.INSTANCE.QTTSTextPut(session_id, ttsText, ttsText.length(), null);
    
    // 3. 循环获取音频并实时播放
    while (true) {
        Pointer audioPointer = TtsService.INSTANCE.QTTSAudioGet(session_id, audioLen, status, errorCode);
        byte[] audioData = audioPointer.getByteArray(0, audioLen.getValue());
        Constants.TTS_SOURCE_DATA_LINE.write(audioData, 0, audioLen.getValue());
        
        if (status.getValue() == 2) { // 音频取完
            break;
        }
    }
}

2.6 打断功能实现

为了提升用户体验,系统支持语音播报打断:

new Thread(() -> {
    Scanner scanner = new Scanner(System.in);
    while (true) {
        String input = scanner.nextLine();
        if (input.isEmpty()) {  // 回车打断
            STOP_FLAG = true;
            break;
        }
    }
}).start();

// 在TTS播放时检查打断标志
if (!STOP_FLAG) {
    Constants.TTS_SOURCE_DATA_LINE.write(audioDataByteArray, 0, length);
}

三、配置文件设计

Settings.json

{
    "model": "deepseek-v2:16b",
    "context": true,
    "clear": false,
    "max": 5,
    "top": 3,
    "similarity": 0.6
}
参数 说明
model Ollama模型名称
context 是否启用对话上下文
clear 是否清空历史记录
max 最大检索文档数
top TopK检索数量
similarity 相似度阈值

四、系统部署指南

4.1 环境要求

  • JDK 11+

  • 内存 ≥ 8GB(推荐16GB)

  • 操作系统:Windows/Linux/macOS

4.2 安装步骤

# 1. 安装Ollama
curl -fsSL https://ollama.com/install.sh | sh

# 2. 下载DeepSeek模型
ollama pull deepseek-v2:16b

# 3. 下载Vosk中文模型
wget https://alphacephei.com/vosk/models/vosk-model-cn-0.22.zip
unzip vosk-model-cn-0.22.zip -d src/main/resources/

# 4. 配置讯飞SDK
# 将libmsc32.so/msc.dll放入项目资源目录

# 5. 准备知识库
# 将文档放入 src/main/resources/word_files/

4.3 运行

mvn clean compile
mvn exec:java -Dexec.mainClass="com.day.AIMain"

五、性能优化建议

5.1 检索优化

  • 分块策略:建议按段落分块,每块200-500字

  • 缓存机制:对常见问题的检索结果进行缓存

  • 索引预建:启动时预计算知识库向量

5.2 语音交互优化

  • 音频缓冲:使用双缓冲减少播放延迟

  • 唤醒阈值:根据环境调整唤醒灵敏度

  • 打断响应:降低音频帧读取延迟

5.3 大模型优化

  • 量化部署:使用INT8量化减少显存占用

  • 并发控制:限制同时处理的请求数

  • 流式输出:减少首字延迟

六、应用场景

场景 应用方式
企业内部培训 员工语音查询产品知识、流程规范
智能客服 离线部署,保障数据安全
教育辅导 中学生学科问题解答
医疗辅助 病历知识库查询

七、总结

本文实现的企业知识库离线问答系统具有以下优势:

  1. 数据安全:完全离线部署,杜绝数据泄露风险

  2. 成本可控:无需支付API调用费用

  3. 交互自然:全语音交互,支持打断

  4. 知识精准:RAG检索确保答案有据可依

    Logo

    有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

    更多推荐