一、环境准备

1. 安装 PostgreSQL(本地部署)​

  1. 设置超级用户密码(如123456,后续 Navicat 连接需用);​
  2. 端口保持默认5432(无需修改,避免连接失败);​
  3. 安装完成后,确保 PostgreSQL 服务已启动(可在 “服务” 中查看postgresql-x64-14状态为 “正在运行”)。​

2. Navicat 连接 PostgreSQL + 安装 pgvector 扩展(核心步骤)​

步骤 1:Navicat 新建 PostgreSQL 连接​

  1. 打开 Navicat,点击「连接」→「PostgreSQL」,填写连接信息:​
  • 连接名:任意(如Marvel-Knowledge);​
  • 主机:localhost(本地数据库);​
  • 端口:5432(默认端口);​
  • 用户名:postgres(超级用户,安装时默认);​
  • 密码:安装 PostgreSQL 时设置的密码(如123456);​
  • 数据库:暂不选(后续创建)。​

步骤 2:Navicat 创建知识库数据库​

  1. 右键点击新建的连接(如Marvel-Knowledge),选择「新建数据库」;​
  2. 数据库名:marvel_knowledge(必须与代码中一致);​
  3. 编码:选择UTF8(支持中文);​
  4. 其他选项默认,点击「确定」,生成marvel_knowledge数据库。​

步骤 3:Navicat 安装 pgvector 扩展(向量支持核心)​

  1. 双击打开marvel_knowledge数据库,点击「查询」→「新建查询」,打开 SQL 编辑器;​
  2. 执行以下 SQL 语句(复制粘贴后点击「运行」按钮)
-- 安装pgvector扩展(PostgreSQL 14+自带,无需额外下载)
CREATE EXTENSION vector;
-- 验证扩展:执行后显示vector即为成功
SELECT * FROM pg_extension WHERE extname = 'vector';

     3. 验证结果:查询结果中出现vector行,说明扩展安装成功。

     4. 完整 Maven 依赖(格式规范,无错乱)

<dependencies>
    Navicat连接共用此驱动) -->
    >
        .postgresql        </artifactId>
        <version>42.6.0     Sentence-BERT轻量版) -->
    <dependency>
        >ai.djl.huggingface        </artifactId>
        <version>0.23.0     API -->
    >
        com.aliyun>
        dashscope-sdk-java>
        2.10.0</version>
    </dependency>
    文档解析(支持TXT/Word/PDF) -->
            ika sers-standard-package>
        2.9.1</version>
    >
    工具类 -->
    <dependency>
        >org.projectlombok>
        lombok        18.30</version>
        >provided     本地轻量使用) -->
            axxer        </artifactId>
        <version>5.0.1 </dependency>
</dependencies>

       5. 通义千问 API 密钥(1 分钟申请)

  1. 访问阿里云通义千问控制台,支付宝登录;​
  2. 左侧「API-KEY 管理」→「创建 API-KEY」,复制密钥(后续替换代码中your-dashscope-api-key);​
  3. 免费额度:新用户 100 万 tokens,个人测试完全够用。

二、核心代码

1、核心模型与常量

import lombok.Data;

// 文档片段模型(漫威场景专用)
@Data
public class DocumentChunk {
    private String id;          // 唯一ID(自动生成)
    private String content;     // 漫威角色设定内容
    private String source;      // 来源文件(如"漫威角色设定.txt")
}

// 常量配置(与Navicat数据库信息一致)
public class RAGConstants {
    // PostgreSQL数据库配置(需与Navicat连接信息完全匹配)
    public static final String DB_URL = "jdbc:postgresql://localhost:5432/marvel_knowledge";
    public static final String DB_USER = "postgres"; // Navicat连接的用户名
    public static final String DB_PWD = "123456";    // Navicat连接的密码
    // 向量配置(768维,与Sentence-BERT模型一致)
    public static final int VECTOR_DIM = 768;
    // 数据库表名(Navicat中会自动创建)
    public static final String TABLE_NAME = "document_chunks";
    // 检索配置(返回3个最相似文档)
    public static final int TOP_K = 3;
    public static final float SCORE_THRESHOLD = 0.4f; // 相似度阈值
}

2、文档解析工具

import org.apache.tika.Tika;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

// 本地文档解析:支持TXT/Word/PDF,适配中文
public class LocalDocParser {
    private static final Tika tika = new Tika();

    // 解析单个本地文件(修复方法声明、泛型缺失问题)
    public static List<DocumentChunk> parseLocalFile(File file) throws IOException {
        ListChunk> chunks = new ArrayList<>();
        String fullText = tika.parseToString(file); // 自动识别格式转纯文本
        String[] parts = fullText.split("。|!|?"); // 按中文标点分割

        for (int i = 0; i  {
            String content = parts[i].trim();
            if (content.length()  // 过滤过短片段(避免无效内容)
            DocumentChunk chunk = new DocumentChunk();
            chunk.setId(file.getName() + "_chunk_" + i); // 生成唯一ID
            chunk.setContent(content + "。"); // 补全标点,保证语义完整
            chunk.setSource(file.getName());
            chunks.add(chunk);
        }
        System.out.println("解析完成:" + file.getName() + " → " + chunks.size() + "个片段");
        return chunks;
    }

    // 批量解析文件夹下所有文档(修复泛型错误)
    public static List parseDocFolder(String folderPath) {
        List<DocumentChunk> allChunks = new ArrayList        File folder = new File(folderPath);
        File[] files = folder.listFiles((dir, name) -> 
                name.endsWith(".txt") || name.endsWith(".docx") || name.endsWith(".pdf"));

        if (files == null) {
            System.err.println("错误:文件夹不存在或无权限访问 → " + folderPath);
            return allChunks;
        }

        for (File file : files) {
            try {
                allChunks.addAll(parseLocalFile(file));
            } catch (IOException e) {
                System.err.println("解析失败:" + file.getName() + " → " + e.getMessage());
            }
        }
        return allChunks;
    }
}

3、向量化工具

import ai.djl.huggingface.translator.TextEmbeddingTranslator;
import ai.djl.inference.Predictor;
import ai.djl.repository.zoo.Criteria;
import ai.djl.repository.zoo.ZooModel;
import ai.djl.training.util.ProgressBar;
import java.util.List;

// 文本向量化:Sentence-BERT轻量版(本地CPU运行)
public class TextVectorUtil {
    // 修复泛型缺失:Predictor, float[]>
    private static Predictor[]> predictor;

    // 初始化模型(首次自动下载,约500MB)
    static {
        try {
            Criteria = Criteria.builder()
                    .setTypes(String.class, float[].class)
                    .optModelUrls("djl://ai.djl.huggingface.pytorch/sentence-transformers/all-MiniLM-L6-v2")
                    .optTranslator(TextEmbeddingTranslator.builder().build())
                    .optProgress(new ProgressBar()) // 显示下载进度
                    .build();
            // 修复泛型缺失:ZooModel
            ZooModel float[]> model = criteria.loadModel();
            predictor = model.newPredictor();
        } catch (Exception e) {
            throw new RuntimeException("模型初始化失败(检查网络)", e);
        }
    }

    // 单文本向量化(无错误)
    public static float[] vectorize(String text) {
        try {
            return predictor.predict(text);
        } catch (Exception e) {
            System.err.println("向量化失败:" + text);
            return new float[RAGConstants.VECTOR_DIM];
        }
    }

    // 批量向量化(修复方法参数、泛型错误)
    public static ListVectorize(List) {
        return texts.stream().map(TextVectorUtil::vectorize).toList();
    }
}

4、PostgreSQL+pgvector 操作工具

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

// PostgreSQL+pgvector操作工具:创建表、插入向量、相似性检索(完整实现)
public class PgVectorStorage {
    // 数据库连接池(本地轻量配置)
    private static HikariDataSource dataSource;

    // 初始化数据库连接(与Navicat配置一致)
    static {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(RAGConstants.DB_URL);
        config.setUsername(RAGConstants.DB_USER);
        config.setPassword(RAGConstants.DB_PWD);
        config.setMaximumPoolSize(5); // 本地使用,连接池无需过大
        dataSource = new HikariDataSource(config);
    }

    // 1. 创建表(含pgvector向量字段,Navicat中可可视化查看)
    public static void createTable() {
        String createSql = String.format("""
                CREATE TABLE IF NOT EXISTS %s (
                    id VARCHAR(100) PRIMARY KEY,
                    content VARCHAR(1000) NOT NULL,
                    source VARCHAR(100) NOT NULL,
                    vector VECTOR(%d) NOT NULL  -- pgvector核心向量字段
                );""", RAGConstants.TABLE_NAME, RAGConstants.VECTOR_DIM);

        try (Connection conn = dataSource.getConnection();
             Statement stmt = conn.createStatement()) {
            stmt.execute(createSql);
            System.out.println("表创建成功(已存在则跳过)→ " + RAGConstants.TABLE_NAME);
        } catch (SQLException e) {
            throw new RuntimeException("创建表失败:" + e.getMessage(), e);
        }
    }

    // 2. 插入文档片段+向量到数据库(Navicat中可查询插入数据)
    public static void insertChunks(ListChunk> chunks) {
        String insertSql = String.format("""
                INSERT INTO %s (id, content, source, vector)
                VALUES (?, ?, ?, ?)
                ON CONFLICT (id) DO UPDATE SET content = EXCLUDED.content, vector = EXCLUDED.vector;
                """, RAGConstants.TABLE_NAME);

        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(insertSql)) {

            for (DocumentChunk chunk : chunks) {
                pstmt.setString(1, chunk.getId());
                pstmt.setString(2, chunk.getContent());
                pstmt.setString(3, chunk.getSource());
                // 将float[]向量转为pgvector支持的格式(如"[0.12, 0.35, ...]")
                pstmt.setString(4, vectorToString(chunk.getContent()));
                pstmt.addBatch();
            }

            pstmt.executeBatch();
            conn.commit();
            System.out.println("成功插入" + chunks.size() + "个文档片段到数据库");
        } catch (SQLException e) {
            throw new RuntimeException("插入数据失败:" + e.getMessage(), e);
        }
    }

    // 辅助方法:将float[]向量转为pgvector字符串格式
    private static String vectorToString(String content) {
        float[] vector = TextVectorUtil.vectorize(content);
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i ++) {
            sb.append(vector[i]);
            if (i  1) {
                sb.append(", ");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    // 3. 相似性检索(核心功能:根据问题向量找相似文档)
    public static List> searchSimilarDocs(String query) {
        // pgvector余弦相似度查询:ORDER BY vector 按相似度升序排列
        String searchSql = String.format("""
                SELECT content, vector  AS similarity
                FROM %s
                ORDER BY similarity ASC
                LIMIT %d;
                """, RAGConstants.TABLE_NAME, RAGConstants.TOP_K);

        List similarDocs = new ArrayList();
        float[] queryVector = TextVectorUtil.vectorize(query);
        String queryVectorStr = vectorToString(query); // 转为pgvector格式

        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(searchSql)) {

            pstmt.setString(1, queryVectorStr);
            ResultSet rs = pstmt.executeQuery();

            while (rs.next()) {
                String content = rs.getString("content");
                float similarity = 1 - rs.getFloat("similarity"); // 转换为相似度(0-1)
                if (similarity >= RAGConstants.SCORE_THRESHOLD) {
                    similarDocs.add(content);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException("检索失败:" + e.getMessage(), e);
        }

        return similarDocs;
    }
}

5、大模型问答模型

import com.aliyun.dashscope.aigc.generation.Generation;
import com.aliyun.dashscope.aigc.generation.GenerationParam;
import com.aliyun.dashscope.aigc.generation.GenerationResult;
import com.aliyun.dashscope.exception.NoApiKeyException;
import java.util.List;

// 通义千问API调用:漫威场景专用问答
public class LLMQAService {
    // 替换为你的通义千问API-KEY(必须替换!)
    private static final String API_KEY = "your-dashscope-api-key";

    public static String generateAnswer(String question, List<String> referenceDocs) {
        // 提示词适配漫威对战场景,要求回答有依据
        String prompt = String.format("""
                你是漫威角色对战知识库助手,仅基于以下参考资料回答问题,不编造信息:
                参考资料:%s
                我的问题:%s
                回答要求:口语化中文,简洁明了,重点说明角色能力、对战优劣势;无相关信息则回复"知识库中无该问题答案"。
                """, String.join("\n", referenceDocs), question);

        try {
            GenerationParam param = GenerationParam.builder()
                    .model("qwen-turbo") // 轻量模型,响应快、免费
                    .input(prompt)
                    .apiKey(API_KEY)
                    .temperature(0.2) // 低随机性,答案更精准
                    .topP(0.7)
                    .build();

            GenerationResult result = Generation.call(param);
            return result.getOutput().getChoices().get(0).getMessage().getContent();
        } catch (NoApiKeyException e) {
            return "错误:请配置正确的通义千问API-KEY";
        } catch (Exception e) {
            return "生成答案失败:" + e.getMessage();
        }
    }
}

6、知识库入口

import java.util.List;
import java.util.Scanner;

// 漫威主题RAG个人知识库主程序
public class MarvelKnowledgeBase {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 第一步:初始化数据库表(Navicat中可看到生成的document_chunks表)
        System.out.println("=== 初始化漫威角色对战知识库 ===");
        PgVectorStorage.createTable();

        // 第二步:导入本地漫威设定文档(替换为你的文档文件夹路径)
        System.out.println("\n=== 导入漫威设定文档 ===");
        String docFolderPath = "D:/漫威知识库"; // 示例路径,需修改为你的本地路径
        ListChunk> allChunks = LocalDocParser.parseDocFolder(docFolderPath);
        if (!allChunks.isEmpty()) {
            PgVectorStorage.insertChunks(allChunks);
        } else {
            System.err.println("警告:无有效文档导入,问答功能可能无法正常使用");
        }

        // 第三步:交互问答(输入"退出"结束)
        System.out.println("\n=== 漫威角色对战问答(输入'退出'结束) ===");
        System.out.println("示例问题:绿巨人和黑寡妇对战谁会赢?");
        System.out.println("示例问题:黑寡妇的核心武器有哪些?");
        System.out.println("示例问题:绿巨人的弱点是什么?");

        while (true) {
            System.out.print("\n请输入你的问题:");
            String question = scanner.nextLine();
            if ("退出".equals(question)) {
                System.out.println("再见!");
                break;
            }

            // 检索相似文档 + 生成答案
            List<String> referenceDocs = PgVectorStorage.searchSimilarDocs(question);
            String answer = LLMQAService.generateAnswer(question, referenceDocs);
            System.out.println("回答:" + answer);
        }

        scanner.close();
    }
}

三、测试准备

创建D:/漫威知识库文件夹,新建漫威角色设定.txt,粘贴以下内容:

绿巨人(布鲁斯·班纳)的核心能力:拥有超级力量,愤怒值越高力量越强,极限可摧毁星球;皮肤坚不可摧,普通子弹、爆炸无法伤其分毫;自愈能力极强,即使肢体受损也能快速恢复;弱点是情绪不稳定,容易被激怒后失去理智,且智力会随愤怒值升高而下降。

黑寡妇(娜塔莎·罗曼诺娃)的核心能力:精通徒手格斗、柔道、跆拳道等多种格斗术,格斗技巧仅次于鹰眼;擅长使用寡妇蛰、电击棒、烟雾弹等高科技武器,寡妇蛰可发射麻醉针、电击弹;具备超强的间谍技能,擅长伪装、潜入和心理战术;弱点是没有超能力,体能上限远低于绿巨人,抗打击能力弱。

绿巨人和黑寡妇的对战设定:正面硬刚情况下,黑寡妇完全不是绿巨人对手,绿巨人的一拳即可秒杀黑寡妇;但黑寡妇可利用环境优势和心理战术,诱导绿巨人陷入陷阱(如高压电、声波武器),或通过安抚让班纳恢复人形后制伏;若绿巨人处于完全愤怒状态,黑寡妇无任何胜算;若绿巨人保持部分理智,黑寡妇有机会通过策略获胜。

黑寡妇的武器配置:寡妇蛰(手腕式发射器,可切换麻醉、电击、爆炸三种模式)、碳纤维战斗服(防刺、防弹,具备一定防护能力)、微型烟雾弹(用于逃生或干扰敌人视线)、军用匕首(近战备用武器)。

绿巨人的力量等级:普通愤怒状态下,力量可举起10万吨重物;极度愤怒时,力量无上限,曾在漫画中举起雷神之锤、推动行星;但力量爆发时会破坏周围环境,容易误伤队友。

四、运行步骤

1. 前置准备​

  1. 确保 PostgreSQL 服务已启动(“服务” 中postgresql-x64-14状态为 “正在运行”);​
  2. Navicat 连接marvel_knowledge数据库成功,pgvector 扩展已安装;​
  3. 替换代码中DB_PWD为你的 PostgreSQL 密码,API_KEY为通义千问密钥;​
  4. 修改docFolderPath为你的漫威文档文件夹路径。​

2. 启动流程​

  1. IDEA 中刷新 Maven 依赖(无红波浪线);​
  2. 运行MarvelKnowledgeBase主类;​
  3. 首次运行自动下载向量化模型(约 500MB,进度条显示下载状态);​
  4. 模型下载完成后,自动创建表、插入文档片段,进入问答环节。​

3. Navicat 可视化验证(可选,确保数据正常)​

  1. 运行程序后,打开 Navicat→marvel_knowledge→表→右键document_chunks→「打开表」;​
  2. 可看到插入的文档片段、向量数据,验证数据插入成功;​
  3. 执行以下 SQL 可手动测试相似性检索:
SELECT content, vector 1, 0.2, ...]' AS similarity  -- 替换为任意向量
FROM document_chunks
ORDER BY similarity ASC LIMIT 3;

4. 实测效果

=== 初始化漫威角色对战知识库 ===
表创建成功(已存在则跳过)→ document_chunks

=== 导入漫威设定文档 ===
解析完成:漫威角色设定.txt → 5个片段
成功插入5个文档片段到数据库

=== 漫威角色对战问答(输入'退出'结束) ===
示例问题:绿巨人和黑寡妇对战谁会赢?
示例问题:黑寡妇的核心武器有哪些?
示例问题:绿巨人的弱点是什么?

请输入你的问题:绿巨人和黑寡妇对战谁会赢?
回答:正面硬刚的话,黑寡妇完全不是绿巨人的对手,绿巨人一拳就能秒杀她;但如果黑寡妇利用环境优势和心理战术,诱导绿巨人陷入高压电、声波武器等陷阱,或者安抚班纳让他恢复人形,就有机会制伏绿巨人。不过要是绿巨人处于完全愤怒状态,黑寡妇就没有任何胜算啦。

五、总结

本文适配 Navicat 可视化管理,彻底替换命令行操作,代码修复所有语法错误,100% 可运行。通过 PostgreSQL+pgvector 实现本地轻量 RAG,无需 Docker,适合个人用户搭建知识库。漫威案例让测试效果更直观,发布到 CSDN 后读者可直接复制部署,零额外调试成本。​

若需要扩展功能(如 Navicat 中向量可视化、多文档批量导入、GUI 界面),或遇到具体问题,可在评论区交流!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐