尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

LangChain4j+pgvector+Redis构建AI文档问答系统

LangChain4j+pgvector+Redis构建AI文档问答系统 CloudVaultLangChain4j RAG PostgreSQL/pgvector Redis 打造仿百度网盘的 AI 文档问答系统这次来看一个工程味道很足的项目CloudVault。它不是一个纯粹的 RAG demo也不是一个只有上传下载功能的网盘而是一个把“文件管理”和“AI 文档问答”串起来的完整后端系统。技术栈集中在 Java 生态LangChain4j 负责 RAG 问答编排PostgreSQL pgvector 存向量并做相似度检索Redis 做缓存、分布式锁和实时通知的辅助通道WebSocket 负责把任务状态推给前端界面。核心流程很直接用户上传文档后系统会自动解析并切片调用 Embedding 模型生成向量写入 pgvector用户提问时先把问题向量化在 pgvector 里检索相关片段再拼装成 Prompt交给 LLM 生成答案。整个过程围绕文件生命周期展开所以很适合做企业内部知识库、团队文档检索、个人网盘 AI 搜索这类场景。本文会从系统架构开始逐步拆解数据库设计、LangChain4j 的 RAG 接入、Redis 实时通知、Docker Compose 快速启动、接口 API 调用示例、批量任务思路和常见问题排查。目标读者是已经熟悉 Spring Boot 基础、想落一套“网盘 向量检索 文档问答”组合方案的 Java 后端开发者。阅读完你能得到一份可以直接照着搭的架构和代码骨架。1. 核心能力速览CloudVault 本身是一个后端服务核心能力集中在文件管理与 AI 问答的结合。项目类型Java / Spring Boot 后端服务仿百度网盘功能 RAG 文档问答核心功能文件上传下载、文档解析切片、向量化索引、RAG 文档问答、实时通知、缓存技术栈LangChain4j、Spring Boot、PostgreSQL、pgvector、Redis、WebSocket向量检索pgvector 存储文本向量支持 HNSW 或 IVFFlat 索引AI 问答基于文档内容的 RAG 问答问题上下文来自文件中检索到的相关片段实时通知Redis WebSocket 推送上传完成、索引完成、问答完成等状态是否支持 API支持可提供文件服务、索引任务、问答接口等 HTTP 接口是否支持批量任务适合做批量导入、批量建索引、批量问答结果导出推荐部署环境JDK 17 Maven 3.8 DockerPostgreSQL、RedisGPU 要求CloudVault 本体不需要 GPUGPU 取决于你接入的 LLM / Embedding 服务适合场景企业知识库、技术资料管理、团队文档问答、个人网盘 AI 搜索从材料看这个项目的关键卖点不是“网盘本身多复杂”而是把文件管理、向量检索、AI 问答和实时通知组合成了一条可工程化的链路。如果你只是想要一个网盘那没必要上 pgvector 和 LangChain4j如果你希望“上传一堆文档后能直接问里面的内容”这套组合就是比较典型的落地方案。2. 系统架构与模块划分CloudVault 的架构可以按职责拆成下面几个模块每个模块彼此独立通过 Spring 容器和消息通道串联。2.1 文件管理模块负责文件上传、下载、删除、分享链接生成和目录管理。文件物理存储可以放在本地磁盘或对象存储数据库只保存文件元数据。上传成功后会触发异步索引任务这是网盘场景里最基础的链路。2.2 文档解析与索引模块这是 RAG 的入口。文件上传后根据扩展名选择解析器文本文件直接读取Markdown 文件保留结构PDF / Word 类文件先用解析库抽取文本。解析完成后按固定大小或语义边界做切片再调用 Embedding 模型把每个切片转成向量写入 PostgreSQL 的 pgvector 表。2.3 RAG 问答模块用户提交问题时系统先向量化问题再在 pgvector 中检索最相关的 N 个文本片段把片段内容拼进 Prompt调用 ChatLanguageModel 生成答案。为了让答案可追溯返回结果中应该带上命中的文件 ID 和片段原文。2.4 缓存与协同模块Redis 在其中承担三类职责缓存热点文件元数据和问答结果用 SETNX 实现索引任务的分布式锁避免同一个文件被并发重复索引还可以借助 Redis 的 Pub/Sub 做轻量级事件通知。2.5 实时通知模块基于 WebSocket 协议服务端把文件上传完成、索引完成、问答任务完成、文件分享等事件推送到前端。配合 Redis可以做到多实例部署时的事件广播避免只有单个 WebSocket 节点能收到消息。这套架构把 RAG 和传统业务系统放在一起考虑值得借鉴的点在于文件上传不是终点而是索引任务的起点用户问答不是简单的 LLM 调用而是先做检索再生成通知不是只靠轮询而是由服务端主动推送。3. 适用场景与使用边界3.1 适合谁用团队内部文档管理把散落的 Markdown、Word、PDF 集中到一个系统里支持按内容提问。企业知识库问答用 RAG 的方式让员工直接问“报销流程是什么”“某个项目的服务器地址在哪”答案都来自内部文档。个人资料库上传自己的技术笔记后用自然语言检索过去写过的内容。Java 技术栈团队想研究 LangChain4j 如何与 Spring Boot 集成并落在真实业务里。3.2 不适合什么场景对文件管理要求极高、需要在线预览 Office、秒传、断点续传、版本回滚的完整网盘产品需要额外扩展能力。需要高并发在线编辑协作的场景这不是 CloudVault 的定位。对 AI 回答准确性要求极其严格且不允许出现幻觉的场景不能只靠 RAG 裸输出必须先加人工审核或答案引用校验。3.3 合规与安全边界涉及网盘和 RAG 问答必须把授权和隐私放在第一位。用户上传的文档可能包含版权材料、个人隐私、公司机密因此系统需要有清晰的权限隔离谁能看到哪个目录谁能检索哪个文件答案中拼接的片段是否越权都要在检索层做过滤。RAG 使用的文档只能来自用户有权限访问的文件。AI 生成的答案可能存在幻觉不应直接作为医疗、法律、财务等高风险领域的最终结论。如果后续接入人脸、声音、视频等模态能力还要额外确认素材授权和数据脱敏。4. 环境准备与前置条件4.1 基础软件组件建议版本用途JDK17 及以上运行 Spring Boot 服务Maven3.8 及以上构建项目、拉取依赖PostgreSQL14 及以上建议 16业务数据与向量数据存储pgvector与 PostgreSQL 版本匹配的最新版扩展向量类型与索引Redis6.x 或 7.x缓存、分布式锁、事件通道Docker / Docker Compose可选但推荐快速启动 PostgreSQL 和 Redis4.2 模型服务准备CloudVault 本身不训练模型它像一个调度器依赖外部的 LLM 和 Embedding 服务。你可以选择OpenAI 兼容接口很多云厂商和本地推理服务都提供v1/chat/completions形态接口。本地 Ollama在开发机上跑一个小模型做测试但此时需要关注本机内存和 GPU。企业内网模型服务通过自定义 Base URL 接入避免数据出内网。如果你在本地用 Ollama 或 llama.cpp 跑 7B 级模型显存占用一般在 6GB 到 12GB 不等具体要看量化方式和上下文长度。如果只是 CloudVault 本身的编译启动不需要 GPU。4.3 快速检查清单java -version mvn -version docker --version确保 Docker 能正常拉镜像本地 5432 和 6379 端口没有被占用。如果你已经装了本地 PostgreSQL要确认是否安装了 pgvector 插件如果没有直接使用 Docker 镜像会更快。5. 数据库设计与 pgvector 接入5.1 创建数据库并启用向量扩展进入 PostgreSQL 后先建库再启用插件。CREATE DATABASE cloudvault; \c cloudvault CREATE EXTENSION IF NOT EXISTS vector;如果提示 extension 不存在说明 pgvector 没有安装成功。Docker 场景下推荐直接使用带 pgvector 的镜像省去手动编译。5.2 基础表设计文件表保存网盘文件的基本信息CREATE TABLE files ( id BIGSERIAL PRIMARY KEY, file_id VARCHAR(64) UNIQUE NOT NULL, file_name VARCHAR(255) NOT NULL, file_path TEXT NOT NULL, file_size BIGINT DEFAULT 0, owner_id BIGINT NOT NULL, status VARCHAR(32) DEFAULT UPLOADED, created_at TIMESTAMP DEFAULT now(), updated_at TIMESTAMP DEFAULT now() );文档切片表用来保存每一个被索引的文本块CREATE TABLE file_documents ( id BIGSERIAL PRIMARY KEY, file_id VARCHAR(64) NOT NULL, chunk_index INT NOT NULL, chunk_text TEXT NOT NULL, embedding vector(1024), -- 维度按 Embedding 模型实际输出调整 created_at TIMESTAMP DEFAULT now() ); CREATE INDEX idx_file_docs_file_id ON file_documents(file_id);这里的vector(1024)只是一种示例。实际维度必须与 Embedding 模型输出对齐比如某些模型的维度是 768某些是 1024甚至更高。维度不一致会在 INSERT 时直接报错。5.3 向量索引当切片数据量大的时候必须建向量索引否则相似度检索会全表扫描速度不可接受。pgvector 提供 HNSW 和 IVFFlat 两种索引HNSW 查询精度和速度比较均衡适合大多数场景。CREATE INDEX ON file_documents USING hnsw (embedding vector_cosine_ops);在 LangChain4j 的 SQL 日志里你会看到它实际执行的是通过 cosine 距离取 Top N 的查询。索引建立了单次检索可以在毫秒级返回但数据量很小的时候不一定体现得出来。5.4 LangChain4j 的 pgvector 配置在 Spring Boot 的配置文件中数据源和向量存储指向同一个数据库。spring: datasource: url: jdbc:postgresql://localhost:5432/cloudvault username: cloudvault password: change-me redis: host: localhost port: 6379关键点在于不要让 LangChain4j 的 pgvector 集成自己去建第二个连接池。直接复用 Spring 的DataSource保证事务一致性和连接资源可控。6. LangChain4j RAG 文档问答实现6.1 引入依赖在pom.xml中加入 LangChain4j 核心、OpenAI 兼容模型模块和 pgvector 模块。具体版本号请按项目实际环境锁定。dependency groupIddev.langchain4j/groupId artifactIdlangchain4j/artifactId version${langchain4j.version}/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-open-ai/artifactId version${langchain4j.version}/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-pgvector/artifactId version${langchain4j.version}/version /dependency6.2 模型配置用 OpenAI 兼容协议接入内容生成模型和 Embedding 模型。base-url指向你们内部的 LLM 网关或 Ollama 服务地址。langchain4j: open-ai: chat-model: base-url: http://your-llm-server/v1 api-key: demo-key model-name: your-chat-model-name temperature: 0.2 embedding-model: base-url: http://your-llm-server/v1 api-key: demo-key model-name: your-embedding-model-name这里没有任何本地显存压力所有计算都发生在模型服务侧。CloudVault 这边只负责文本切分、调用接口和保存返回结果。6.3 文档切片与向量化文档上传后按文件类型解析文本。文本很长时直接用固定 chunk 大小切分通常 500 到 1000 个字符比较稳妥同时保留一个 overlap避免切断语义。public ListTextSegment splitText(String text) { Document document Document.from(text); DocumentSplitter splitter DocumentSplitters.recursive(600, 80); return splitter.split(document); }recursive(600, 80)的意思是每个片段约 600 字符重叠 80 字符保证上下文连贯。拿到TextSegment列表后调用 EmbeddingModel 生成向量再写入 pgvector。ListTextSegment segments splitText(rawText); ListEmbedding embeddings embeddingModel.embedAll(segments) .content() .stream() .map(Result::content) .toList(); embeddingStore.addAll(embeddings, segments);注意不同版本 LangChain4j 的 API 命名可能有差异比如embedAll返回值可能是ResponseListEmbedding写法要跟随你锁定的版本。6.4 RAG 问答核心流程问答服务是核心流程先向量化问题再检索最相关的片段拼装 Prompt最后交给 ChatLanguageModel 生成。Service public class RagService { private final ChatLanguageModel chatModel; private final EmbeddingModel embeddingModel; private final EmbeddingStoreTextSegment embeddingStore; public RagService(ChatLanguageModel chatModel, EmbeddingModel embeddingModel, EmbeddingStoreTextSegment embeddingStore) { this.chatModel chatModel; this.embeddingModel embeddingModel; this.embeddingStore embeddingStore; } public String answer(String userQuestion) { Embedding questionEmbedding embeddingModel.embed(userQuestion).content(); ListEmbeddingMatchTextSegment matches embeddingStore.findRelevant(questionEmbedding, 5); String context matches.stream() .map(match - match.embedded().text()) .collect(Collectors.joining(\n---\n)); String prompt 请根据下面的文档片段回答用户问题。 如果片段中没有足够信息请直接说明“当前文档中未找到相关内容”。 文档片段 %s 用户问题 %s .formatted(context, userQuestion); return chatModel.generate(prompt); } }检索到的片段数量不宜太多。Top 3 到 Top 5 通常足够太多会稀释 Prompt 中的有效信息同时增加 token 消耗。6.5 更完整的 RAG 链路如果你用的 LangChain4j 版本支持ContentRetriever和RetrievalAugmentor可以把检索器进一步封装让 LangChain4j 在每次 Chat 时自动完成“检索 - 组装 - 生成”。ContentRetriever retriever EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) .maxResults(5) .minScore(0.6) .build(); RetrievalAugmentor augmentor DefaultRetrievalAugmentor.builder() .contentRetriever(retriever) .build(); ChatLanguageModel ragModel OpenAiChatModel.builder() .baseUrl(http://your-llm-server/v1) .apiKey(demo-key) .modelName(your-chat-model-name) .build() .toBuilder() // 具体绑定方式以实际版本 API 为准 .build();这种写法更贴近 LangChain4j 官方推荐的“自动 RAG”形态但 API 变化较快实践时以官方文档或你锁定的版本源代码为准。7. Redis 缓存与分布式锁Redis 在整个系统中的角色非常多这里说三个最典型的用法。7.1 热点缓存文件列表、问答结果这类读取频率高但更新不频繁的数据适合放在 Redis 里。Service public class FileCacheService { private final StringRedisTemplate redisTemplate; public FileCacheService(StringRedisTemplate redisTemplate) { this.redisTemplate redisTemplate; } public void cacheFileMeta(String fileId, String metaJson) { redisTemplate.opsForValue().set(FILE_META: fileId, metaJson, Duration.ofMinutes(30)); } public String getFileMeta(String fileId) { return redisTemplate.opsForValue().get(FILE_META: fileId); } }缓存过期时间要根据业务定。文件元数据短时间内变化不大30 分钟比较合适。问答结果如果希望减少重复调用 LLM也可以按相同问题做短时间缓存但要注意用户问题千变万化缓存命中率可能不高。7.2 索引任务分布式锁同一个文件可能被用户重复点击“重新索引”如果服务多实例部署并发处理同一个文件会浪费资源。这里用 Redis 的SETNX做一个简单锁。public boolean tryAcquireIndexLock(String fileId) { return Boolean.TRUE.equals(redisTemplate.opsForValue() .setIfAbsent(INDEX_LOCK: fileId, LOCKED, Duration.ofMinutes(10))); }锁的过期时间要大于任务最大执行时间否则任务还没跑完锁就释放了。更稳妥的做法是任务结束后主动删锁并配合 Lua 脚本比较 value 再删除避免误删其他实例加的锁。7.3 Redis Pub/Sub 与多实例通知WebSocket 连接绑定在某一个实例上。当用户在实例 A 上传文件索引任务却在实例 B 执行完毕时需要把完成事件广播到所有实例让持有 WebSocket 会话的实例推送给前端。Redis Pub/Sub 天然适合这种轻量级广播。redisTemplate.convertAndSend(cloudvault:notification, payloadJson);消费端监听该频道再通过SimpMessagingTemplate推给对应的 WebSocket 会话。8. 实时通知机制WebSocket Redis实时通知用于解决“任务做完后怎么告诉用户”的问题。典型场景包括文件上传完成、索引建立完成、问答任务完成、文件分享成功。8.1 配置 WebSocketConfiguration EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker(/topic); registry.setApplicationDestinationPrefixes(/app); } Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint(/ws) .setAllowedOriginPatterns(*) .withSockJS(); } }简单模式下/topic前缀的消息会直接广播给订阅了对应地址的客户端。生产环境如果想做多节点广播可以把enableSimpleBroker换成一个完整的消息代理并用 Redis Pub/Sub 做事件桥接。8.2 推送任务状态索引任务完成后推送一条 JSON 消息给对应用户。Service public class NotificationService { private final SimpMessagingTemplate messagingTemplate; private final StringRedisTemplate redisTemplate; public NotificationService(SimpMessagingTemplate messagingTemplate, StringRedisTemplate redisTemplate) { this.messagingTemplate messagingTemplate; this.redisTemplate redisTemplate; } public void notifyIndexFinished(Long userId, String fileId, String status) { String payload { type: INDEX_FINISHED, fileId: %s, status: %s, time: %s } .formatted(fileId, status, LocalDateTime.now()); messagingTemplate.convertAndSend(/topic/user/ userId, payload); redisTemplate.convertAndSend(cloudvault:notification, payload); } }前端订阅地址为/topic/user/{userId}需要在 WebSocket 建立连接时带上鉴权信息避免越权订阅其他人频道。8.3 前端订阅示例const socket new SockJS(/ws); const stompClient Stomp.over(socket); stompClient.connect({}, function () { stompClient.subscribe(/topic/user/1001, function (message) { const data JSON.parse(message.body); if (data.type INDEX_FINISHED) { console.log(索引完成, data.fileId, data.status); } }); });9. 快速启动与验证流程9.1 使用 Docker Compose 启动依赖这里提供一个开发环境用的 Compose 配置。pgvector/pgvector:pg16镜像自带 PostgreSQL 16 和 pgvector 插件适合本地快速拉起数据库。services: postgres: image: pgvector/pgvector:pg16 container_name: cloudvault-pg environment: POSTGRES_USER: cloudvault POSTGRES_PASSWORD: change-me POSTGRES_DB: cloudvault ports: - 5432:5432 volumes: - pgdata:/var/lib/postgresql/data redis: image: redis:7 container_name: cloudvault-redis ports: - 6379:6379 volumes: pgdata:启动依赖docker compose up -d检查容器状态docker ps docker logs cloudvault-pg9.2 启动 Spring Boot 服务mvn clean package -DskipTests java -jar target/cloudvault.jarJava 服务默认监听 8080 端口。如果端口冲突可以加--server.port8081。启动日志里如果看到数据库连接成功、Redis 连接成功、模型服务连接测试通过说明前置准备做完。9.3 第一条验证链路上传一个 Markdown 文件。观察日志中是否出现分片、Embedding 生成、pgvector INSERT 记录。检查file_documents表中的向量数据条数。提交一个与文档内容相关的问题。看接口返回的答案是否引用了文档片段。打开 WebSocket 页面确认前端收到索引完成和问答完成通知。这一步能走到第 4 步说明 RAG 链路已经通走到第 6 步说明实时通知模块也正常。10. 接口 API 与批量任务10.1 主要接口接口方法作用/api/files/uploadPOST上传文件并触发异步索引/api/files/{fileId}GET获取文件元数据/api/files/{fileId}/contentGET获取文件解析后的文本/api/files/{fileId}/indexPOST手动触发索引任务/api/askPOST基于已索引文档进行 RAG 问答/api/notificationsWSWebSocket 实时通知通道10.2 上传文件curl -X POST http://localhost:8080/api/files/upload \ -H Authorization: Bearer token \ -F file./README.md预期返回数据中包含fileId。服务端收到文件后会先保存文件再异步执行文本解析与向量化。10.3 发起问答curl -X POST http://localhost:8080/api/ask \ -H Content-Type: application/json \ -H Authorization: Bearer token \ -d { question: 这个项目的核心功能是什么, fileIds: [FILE_001, FILE_002] }fileIds用于限定检索范围让用户只在自己有权限的文件里检索。这个设计比“全库检索”更安全也更符合网盘产品的权限模型。10.4 批量任务批量任务是 RAG 平台真正要关心的能力。常见批量场景有批量导入历史文档并建索引。定期对新增文件执行索引任务。对一批预置问题跑问答回归验证知识库效果。实现上可以用一张任务表和异步线程池。任务表记录文件 ID、状态、错误信息、开始和结束时间。CREATE TABLE index_tasks ( id BIGSERIAL PRIMARY KEY, task_type VARCHAR(32) NOT NULL, file_id VARCHAR(64) NOT NULL, status VARCHAR(32) DEFAULT PENDING, error_msg TEXT, created_at TIMESTAMP DEFAULT now(), updated_at TIMESTAMP DEFAULT now() );任务提交后扫描器不断拉起PENDING状态的任务。执行成功改成SUCCESS失败改成FAILED并写入尝试次数。每次重试要有最大次数限制同时把错误信息记录清楚方便运维定位。Scheduled(fixedDelay 5000) public void scanTasks() { ListIndexTask pendingTasks taskMapper.findByStatus(PENDING); for (IndexTask task : pendingTasks) { taskExecutor.execute(() - executeIndexTask(task)); } }这里只是给出通用任务扫描逻辑。实际项目中推荐在批量任务里加上每一条任务的日志输出、耗时统计和失败重试队列避免任务堆在一个线程池里互相影响。11. 资源占用与性能观察11.1 CloudVault 本体CloudVault 是一个 Java 服务资源占用取决于连接数、任务并发和文档大小。正常开发环境给它 2 到 4 GB 内存比较稳妥。CPU 消耗主要在文本解析、Embedding 转换和 JSON 序列化实测时你可以用top或jstat观察。11.2 模型服务侧真正的算力开销在接入的 LLM / Embedding 服务上。如果使用云端 API本机只消耗网络 IO如果本地部署 7B 级模型显存通常需要 6GB 以上量化模型会降低一些要求但要关注并发请求时的峰值显存。具体数字必须按你的模型和推理框架实测不建议凭感觉定资源规格。11.3 数据库与 Redis 观察启动后重点观察 PostgreSQL 连接池和 Redis 内存。docker stats看cloudvault-pg和cloudvault-redis的 CPU 和内存。向量索引建立后跑几条问答观察file_documents表的查询耗时。如果检索明显变慢优先检查是否建了 HNSW 索引以及查询是否命中了索引。11.4 性能优化方向向量检索限制每次问答只检索 Top 5 或 Top 10。缓存对高频访问的文件元数据、热点问答结果做 Redis 缓存。异步化文档解析、向量化、索引更新全部放到任务队列避免阻塞上传接口。连接池MySQL 和 Redis 连接池都要设置合理上限避免线程堆积。分批写入批量 embedding 后一次性写入 pgvector不要一条一条 INSERT。12. 常见问题与排查方法问题现象可能原因排查方式解决方案CREATE EXTENSION vector 报错PostgreSQL 没有安装 pgvector登录数据库查看已安装扩展换用 pgvector/pgvector 镜像或编译安装向量字段类型不存在建表时 extension 未生效执行\dx查看扩展列表先CREATE EXTENSION再建表LangChain4j 连接模型超时Base URL 或模型服务不可达用 curl 测试/v1/chat/completions修正 URL、检查鉴权或网络策略Embedding 维度与 vector 字段不一致模型输出维度与建表维度不同打印向量长度修改表字段维度或换模型Redis 连接拒绝Redis 未启动或配置错误redis-cli ping启动 Redis检查地址和密码WebSocket 页面连不上前端地址或鉴权错误看浏览器 Network 面板检查/ws路径和 Stomp 订阅地址文件上传后索引没生效异步任务失败或队列未消费查看index_tasks表和日志修复解析异常或任务执行器配置问答回答完全不相关检索的片段不相关或 prompt 太弱打印检索到的片段内容调整切片大小、增加 topK、加 minScore 过滤高并发时索引重复执行没有分布式锁或锁已过期查看 Redis 中锁 key 状态加 SETNX 锁并设置合理过期时间端口冲突本机已有服务占用 8080 或 5432lsof -i :8080换端口或停掉旧进程13. 最佳实践与合规提醒13.1 工程实践第一次跑通时先用小文件、小模型验证明白链路再上大文档和数据量。保存一份最小可运行配置模型地址、数据库地址、Redis 地址都放在环境变量或配置中心不硬编码。文件、向量、缓存、日志分目录管理。pgvector 表数据量大了以后要单独做备份策略。批量任务必须加日志和失败重试避免任务静默失败。接口服务要限制访问范围不要把带操作权限的接口暴露到公网。13.2 RAG 与网盘的合规边界用户上传的文档要有权限隔离。问答检索时必须根据当前用户过滤可见文件否则会造成数据越权访问。如果文档包含个人敏感信息需要先做脱敏或禁止入库检索。AI 生成的答案存在幻觉风险上线前建议给答案加“引用片段”方便用户人工核对。涉及版权材料、商业机密、人脸声音等素材时必须确认上传者拥有合法授权并设置明确的访问审计。如果系统部署在个人服务器上要避免把内部文档索引结果通过公网接口随意暴露。14. 总结与下一步CloudVault 这类项目最值得尝试的点是把“文件管理”和“RAG 问答”放进了同一个 Java 后端体系里。上传文档不是终点而是生成可检索知识的第一步用户提问也不是直接调大模型而是先从 PostgreSQL pgvector 中检索出相关片段再交给 LLM 生成。最先应该验证的功能是这条核心链路上传文档 - 解析切片 - Embedding - pgvector 存储 - 提问 - 检索相关片段 - 生成答案。最容易踩的坑是 pgvector 插件未启用、向量维度不匹配、模型服务地址不可达。只要这条链路通了剩下的缓存、实时通知、批量任务都是在给它做工程化加固。后续可以继续扩展的方向有很多接入本地 Ollama 模型实现内网离线问答在检索层增加用户权限过滤用消费队列替换定时扫描批量任务前端做一个简单的文档列表和 WebSocket 通知页面再加一个问答日志审计记录每次提问命中了哪些文档、最终答案是什么。整套架构从这开始可以一路生长成一个可商用的企业内部知识库系统。
返回列表