
在实际企业级应用开发中知识库的智能化升级是一个明确的趋势。传统的文档管理系统或简单的关键词搜索在面对海量、多格式、非结构化的企业知识时往往显得力不从心员工难以快速、准确地找到所需信息。基于检索增强生成RAG架构的智能问答系统结合大语言模型LLM的理解与生成能力能够有效解决这一问题。它允许用户用自然语言提问系统则从企业专属知识库中检索相关文档片段并生成精准、有据可依的答案。本文将带你从零开始构建一个完整的“AI企业知识库”系统。该系统以 Spring Boot 作为后端服务框架集成 Spring AI 来统一调用大模型使用 PGVectorPostgreSQL 的向量扩展存储和检索文档的向量嵌入Embedding前端采用 Vue.js 提供交互界面。整个流程将覆盖知识库构建文档切分、向量化、入库和问答检索检索、增强、生成两大核心环节。通过本文你将掌握如何将 RAG 理论落地为一个可运行、可调试、具备生产潜力的工程实践项目。1. 理解 RAG 架构与核心组件选型在动手编码之前必须清晰理解 RAG 的工作流程以及我们为何选择这些技术栈。这决定了后续所有配置和代码的逻辑。1.1 RAG 是什么为什么它能解决企业知识问答问题RAGRetrieval-Augmented Generation检索增强生成的核心思想是将信息检索与文本生成相结合。它不像传统聊天机器人那样仅依赖模型自身的“记忆”而是在回答问题时先从外部知识库中检索出最相关的信息片段然后将这些片段和问题一起“喂”给大语言模型让模型基于这些“证据”来生成答案。这样做有几个关键优势答案准确性高答案来源于企业真实文档减少了模型“胡编乱造”幻觉的可能。知识可更新只需更新向量数据库中的文档即可让系统掌握最新知识无需重新训练昂贵的大模型。来源可追溯生成的答案可以附带引用来源如文档名、页码增强可信度。成本相对较低相比微调大模型RAG 的实现和运维成本更低。一个典型的 RAG 问答流程如下用户提问例如“我们公司的年假政策是怎样的”查询向量化将用户问题通过 Embedding 模型转换为一个高维向量。向量相似度检索在向量数据库中寻找与问题向量最相似的文档片段向量。上下文组装将检索到的 Top-K 个相关文档片段与原始问题组合形成增强的提示词Prompt。答案生成将组装好的提示词发送给大语言模型LLM生成最终答案。返回结果将答案连同引用来源返回给用户。1.2 技术栈详解与选型理由基于上述流程我们需要为每个环节选择合适的技术组件。后端框架Spring Boot Spring AISpring Boot 提供了快速构建微服务的成熟生态。Spring AI 是 Spring 官方项目旨在为 AI 应用开发提供抽象和便利。它统一了不同大模型提供商如 OpenAI、Azure OpenAI、Ollama 等的 API让我们可以通过更换配置而非代码来切换模型极大地提高了灵活性和可维护性。向量数据库PostgreSQL PGVectorPGVector 是 PostgreSQL 的一个扩展使其具备了存储和查询向量数据的能力。选择它的理由包括与现有技术栈集成度高很多企业已在使用 PostgreSQL无需引入新的数据库系统降低运维复杂度。SQL 生态完整可以利用 PostgreSQL 的事务、备份、权限管理等所有成熟特性来管理向量数据。性能足够对于中小规模的企业知识库百万级向量PGVector 的性能完全够用且支持多种索引如 IVFFlat, HNSW来加速检索。前端框架Vue.jsVue.js 渐进式、易上手的特点适合快速构建交互式管理界面和问答界面。其响应式数据绑定和组件化开发能很好地满足此类应用的需求。核心模型Embedding 模型 大语言模型 (LLM)Embedding 模型负责将文本转换为向量。可以选择 OpenAI 的text-embedding-ada-002或本地部署的开源模型如BAAI/bge-small-zh。Spring AI 也提供了统一的 Embedding 接口。大语言模型 (LLM)负责最终答案的生成。可以选择云端 API如 OpenAI GPT-4, DeepSeek或本地部署模型如通过 Ollama 运行的 Qwen、Llama 等。本文示例将兼顾两种方式。文档处理库我们需要将 PDF、Word、TXT 等文档进行文本提取、切分Chunking。可以使用 Apache Tika 进行格式解析并使用简单的文本分割器或更智能的语义分割库。下表总结了核心组件的职责和选型组件职责本项目选型备选方案应用框架提供 Web 服务、依赖管理、配置中心Spring BootQuarkus, MicronautAI 抽象层统一调用不同厂商的 AI 模型Spring AILangChain4j, 直接调用 SDK向量数据库存储和检索文档片段的向量表示PostgreSQL PGVectorRedis Stack, Milvus, Chroma前端框架提供用户问答和管理界面Vue.jsReact, SvelteEmbedding 模型将文本转换为数值向量OpenAI / 本地 BGE 模型Cohere, Voyage, 其他开源模型大语言模型根据上下文生成自然语言答案OpenAI GPT / 本地 QwenClaude, DeepSeek, Llama文档处理解析和分割上传的文档Apache Tika 自定义分割器Unstructured, LangChain 文本分割器2. 环境准备与项目初始化在开始编码前需要确保所有基础环境就绪。我们将按照从底层数据库到上层前端的顺序进行准备。2.1 基础服务部署PostgreSQL 与 PGVector首先需要安装并配置好 PostgreSQL 数据库并启用 PGVector 扩展。1. 安装 PostgreSQL (以 Docker 方式为例)如果你本地没有 PostgreSQL使用 Docker 是最快捷的方式。# 拉取包含 PGVector 扩展的 PostgreSQL 镜像推荐 docker pull ankane/pgvector # 运行容器 docker run -d \ --name pgvector-db \ -e POSTGRES_USERai_admin \ -e POSTGRES_PASSWORDyour_secure_password \ -e POSTGRES_DBai_knowledge_base \ -p 5432:5432 \ ankane/pgvector2. 验证安装并创建扩展使用任何 PostgreSQL 客户端如psql, DBeaver, pgAdmin连接至数据库。-- 连接数据库后执行以下 SQL -- 检查 PGVector 扩展是否可用 SELECT * FROM pg_available_extensions WHERE name vector; -- 创建 vector 扩展 CREATE EXTENSION IF NOT EXISTS vector; -- 验证扩展创建成功 SELECT extname, extversion FROM pg_extension WHERE extname vector;看到vector扩展及其版本号即表示成功。2.2 初始化 Spring Boot 后端项目使用 Spring Initializr 或 IDE 创建项目。关键依赖如下Spring Web: 提供 RESTful API。Spring Data JPA: 简化数据库操作。PostgreSQL Driver: 连接 PostgreSQL。Spring AI: 核心 AI 抽象层。需要添加对应的 BOM 和具体 Starter。Spring AI OpenAI: 如果要使用 OpenAI 官方 API。Spring AI Ollama: 如果要连接本地 Ollama 服务。Spring AI Transformers(可选): 用于本地 Embedding 模型实验性。Apache Tika Core(可选): 用于解析文档。pom.xml关键依赖片段properties spring-ai.version0.8.1/spring-ai.version !-- 请使用最新稳定版 -- /properties dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies !-- Spring Boot 基础依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdorg.postgresql/groupId artifactIdpostgresql/artifactId scoperuntime/scope /dependency !-- Spring AI - 按需选择 -- !-- 方案一使用 OpenAI API -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId /dependency !-- 方案二使用本地 Ollama -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-ollama-spring-boot-starter/artifactId /dependency !-- 文档解析 -- dependency groupIdorg.apache.tika/groupId artifactIdtika-core/artifactId version2.9.1/version /dependency /dependenciesapplication.yml基础配置spring: datasource: url: jdbc:postgresql://localhost:5432/ai_knowledge_base username: ai_admin password: your_secure_password driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: update # 初期开发使用生产环境应使用 validate 或 none并通过迁移工具管理 show-sql: true properties: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect # Spring AI 配置 (以 OpenAI 为例) spring: ai: openai: api-key: ${OPENAI_API_KEY:sk-your-key-here} # 建议使用环境变量 chat: options: model: gpt-3.5-turbo # 或 gpt-4 embedding: options: model: text-embedding-ada-002注意ddl-auto: update在开发初期方便自动建表但在生产环境中是危险的可能导致数据丢失。正式上线前务必改为validate并使用 Flyway 或 Liquibase 进行数据库版本管理。2.3 初始化 Vue.js 前端项目使用 Vue CLI 或 Vite 创建前端项目。# 使用 Vite 创建 Vue 项目 npm create vuelatest ai-knowledge-frontend # 按照提示选择需要的特性如 Router, Pinia 等。 cd ai-knowledge-frontend npm install npm run dev前端项目结构将主要包含src/views/: 页面组件如ChatView.vue问答页、KnowledgeManageView.vue知识库管理页。src/components/: 可复用组件如MessageBubble.vue。src/api/: 封装与后端 Spring Boot API 的通信。src/stores/: 使用 Pinia 进行状态管理如对话历史。3. 构建知识库文档处理与向量化存储这是 RAG 系统的“记忆”部分。我们需要设计数据库表并实现文档上传、解析、分块、向量化、存储的完整流水线。3.1 设计向量存储表结构在 PostgreSQL 中创建表来存储文档片段及其向量。JPA 实体类设计如下package com.example.aiknowledge.entity; import jakarta.persistence.*; import lombok.Data; import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.type.SqlTypes; import java.time.LocalDateTime; import java.util.List; Entity Table(name document_chunk) Data public class DocumentChunk { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Column(name document_id) private String documentId; // 原始文档唯一标识如文件名上传时间 Column(name document_name) private String documentName; // 原始文档名 Column(columnDefinition TEXT) private String content; // 文本块内容 Column(name chunk_index) private Integer chunkIndex; // 在当前文档中的顺序 Column(name token_count) private Integer tokenCount; // 文本块的 token 数用于优化 // 核心字段使用 PGVector 提供的 vector 类型 Column(columnDefinition vector(1536)) // 维度需与 Embedding 模型输出维度一致 JdbcTypeCode(SqlTypes.VECTOR) private float[] embedding; Column(name metadata, columnDefinition jsonb) JdbcTypeCode(SqlTypes.JSON) private ChunkMetadata metadata; // 存储额外信息如页码、章节等 Column(name created_at) private LocalDateTime createdAt; Data Embeddable public static class ChunkMetadata { private Integer pageNumber; private String sectionTitle; // 其他自定义元数据 } }对应的 SQL 表结构大致如下CREATE TABLE document_chunk ( id BIGSERIAL PRIMARY KEY, document_id VARCHAR(255), document_name VARCHAR(255), content TEXT, chunk_index INT, token_count INT, embedding vector(1536), -- 注意维度 metadata JSONB, created_at TIMESTAMP ); -- 为了加速相似度搜索创建向量索引以 HNSW 为例 CREATE INDEX ON document_chunk USING hnsw (embedding vector_cosine_ops);注意vector(1536)中的1536是 OpenAItext-embedding-ada-002模型的输出维度。如果你使用其他 Embedding 模型如BGE通常是 768 维必须修改此处的维度。创建索引可以极大提升检索速度但会占用更多存储空间并影响写入性能应在数据量较大时创建。3.2 实现文档解析与分块服务创建一个服务类负责处理上传的文档文件。package com.example.aiknowledge.service; import com.example.aiknowledge.entity.DocumentChunk; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.tika.Tika; import org.apache.tika.exception.TikaException; import org.springframework.ai.document.Document; import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.UUID; Service Slf4j RequiredArgsConstructor public class DocumentProcessingService { private final Tika tika new Tika(); private final TokenTextSplitter textSplitter; // Spring AI 提供的文本分割器 private final EmbeddingService embeddingService; // 向量化服务下文实现 /** * 处理上传的文档解析 - 分块 - 向量化 - 存储 */ public void processAndStoreDocument(MultipartFile file) throws IOException, TikaException { String originalFilename file.getOriginalFilename(); String documentId UUID.randomUUID().toString(); // 1. 使用 Apache Tika 解析文档文本 String fullText; try (InputStream inputStream file.getInputStream()) { fullText tika.parseToString(inputStream); } log.info(文档 {} 解析成功文本长度: {}, originalFilename, fullText.length()); // 2. 使用 TokenTextSplitter 进行分块 // 这里将完整文本包装成一个 Spring AI Document 对象 Document springAiDoc new Document(fullText); // 可以设置元数据 springAiDoc.getMetadata().put(source, originalFilename); ListDocument chunks textSplitter.split(springAiDoc); log.info(文档被分割成 {} 个块, chunks.size()); // 3. 转换为实体并向量化 ListDocumentChunk entitiesToSave new ArrayList(); for (int i 0; i chunks.size(); i) { Document chunk chunks.get(i); DocumentChunk entity new DocumentChunk(); entity.setDocumentId(documentId); entity.setDocumentName(originalFilename); entity.setContent(chunk.getContent()); entity.setChunkIndex(i); entity.setTokenCount(estimateTokenCount(chunk.getContent())); // 调用 Embedding 服务获取向量 float[] embeddingVector embeddingService.embed(chunk.getContent()); entity.setEmbedding(embeddingVector); entity.setCreatedAt(java.time.LocalDateTime.now()); entitiesToSave.add(entity); } // 4. 批量保存到数据库 (需注入 Repository) // documentChunkRepository.saveAll(entitiesToSave); log.info(文档 {} 处理完成已存储 {} 个向量块, originalFilename, entitiesToSave.size()); } private int estimateTokenCount(String text) { // 简单估算实际可用更精确的方法 return text.length() / 4; } }关键点解释Apache Tika是一个强大的内容分析工具包可以解析 PDF、Word、Excel、PPT、TXT 等多种格式提取纯文本和元数据。TokenTextSplitter是 Spring AI 提供的基于 Token 数量的文本分割器。它比简单的按字符或句子分割更合理因为大语言模型是按 Token 处理的。你可以配置chunkSize每个块的最大 Token 数和chunkOverlap块之间的重叠 Token 数用于保持上下文连贯。分块策略分块大小是 RAG 效果的关键参数。块太小可能丢失完整语义块太大可能引入无关噪声。通常对于通用文档chunkSize500-1000chunkOverlap50-100是个不错的起点需要根据实际问答效果调整。3.3 实现向量化Embedding服务创建一个服务利用 Spring AI 的抽象接口获取文本的向量表示。package com.example.aiknowledge.service; import lombok.RequiredArgsConstructor; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.embedding.EmbeddingRequest; import org.springframework.ai.embedding.EmbeddingResponse; import org.springframework.stereotype.Service; import java.util.List; Service RequiredArgsConstructor public class EmbeddingService { private final EmbeddingClient embeddingClient; public float[] embed(String text) { // EmbeddingClient 是 Spring AI 的统一接口 EmbeddingResponse response embeddingClient.call( new EmbeddingRequest(List.of(text), null) // 可以传入 options ); // 假设我们只处理单条文本取第一个结果 ListDouble embeddingList response.getResult().getOutput(); // 转换为 float 数组因为 PGVector 存储 float[] float[] embeddingArray new float[embeddingList.size()]; for (int i 0; i embeddingList.size(); i) { embeddingArray[i] embeddingList.get(i).floatValue(); } return embeddingArray; } }配置 EmbeddingClient Bean如果你使用 OpenAISpring AI Starter 会自动配置EmbeddingClient。如果你想使用本地模型如通过 Ollama 运行的nomic-embed-text配置如下# application.yml (Ollama 配置示例) spring: ai: ollama: base-url: http://localhost:11434 embedding: options: model: nomic-embed-text # 或其他 Ollama 支持的 Embedding 模型4. 实现智能问答检索与生成核心链路这是系统的“大脑”部分。我们将实现一个服务接收用户问题检索相关文档构造提示词调用 LLM 生成答案。4.1 实现向量相似度检索我们需要在DocumentChunkRepository中定义自定义查询利用 PGVector 的向量相似度运算符。package com.example.aiknowledge.repository; import com.example.aiknowledge.entity.DocumentChunk; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.List; Repository public interface DocumentChunkRepository extends JpaRepositoryDocumentChunk, Long { /** * 使用余弦相似度检索最相关的 K 个文档块 * PGVector 运算符1 - (embedding :embedding) 表示余弦相似度 (值越大越相似) * 或者直接用 embedding :embedding 表示余弦距离 (值越小越相似) * 这里使用距离并限制返回数量。 */ Query(value SELECT * FROM document_chunk ORDER BY embedding CAST(:embedding AS vector) LIMIT :k, nativeQuery true) ListDocumentChunk findTopKSimilar(Param(embedding) float[] embedding, Param(k) int k); }检索服务实现package com.example.aiknowledge.service; import com.example.aiknowledge.entity.DocumentChunk; import com.example.aiknowledge.repository.DocumentChunkRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.List; Service RequiredArgsConstructor public class RetrievalService { private final EmbeddingService embeddingService; private final DocumentChunkRepository documentChunkRepository; /** * 检索与问题最相关的文档块 * param query 用户问题 * param topK 返回最相关的 K 个结果 * return 相关文档块列表 */ public ListDocumentChunk retrieveRelevantChunks(String query, int topK) { // 1. 将问题转换为向量 float[] queryEmbedding embeddingService.embed(query); // 2. 执行向量相似度搜索 return documentChunkRepository.findTopKSimilar(queryEmbedding, topK); } }4.2 构造提示词Prompt与调用 LLM这是 RAG 的“增强”环节。我们将检索到的文档内容作为上下文与用户问题一起构造一个清晰的提示词。package com.example.aiknowledge.service; import lombok.RequiredArgsConstructor; import org.springframework.ai.chat.ChatClient; import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.SystemPromptTemplate; import org.springframework.ai.chat.prompt.UserPromptTemplate; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.stream.Collectors; Service RequiredArgsConstructor public class ChatService { private final ChatClient chatClient; // Spring AI 统一 Chat 接口 private final RetrievalService retrievalService; // 系统提示词用于设定 AI 的角色和行为准则 private static final String SYSTEM_PROMPT_TEMPLATE 你是一个专业的企业知识库助手。请严格根据以下提供的上下文信息来回答问题。 如果上下文信息不足以回答问题请直接说“根据现有资料我无法回答这个问题”不要编造信息。 回答时请保持专业、清晰、简洁。 如果答案涉及多个要点请分点列出。 上下文信息 {context} ; public String generateAnswer(String userQuestion) { // 1. 检索相关文档块 ListDocumentChunk relevantChunks retrievalService.retrieveRelevantChunks(userQuestion, 5); // 取前5个 if (relevantChunks.isEmpty()) { return 知识库中未找到相关信息。; } // 2. 组装上下文 String context relevantChunks.stream() .map(chunk - String.format([来源: %s, 片段%d]\n%s\n, chunk.getDocumentName(), chunk.getChunkIndex(), chunk.getContent())) .collect(Collectors.joining(\n---\n)); // 3. 构造系统消息和用户消息 SystemMessage systemMessage new SystemPromptTemplate(SYSTEM_PROMPT_TEMPLATE) .createMessage(Map.of(context, context)); UserMessage userMessage new UserPromptTemplate({question}) .createMessage(Map.of(question, userQuestion)); // 4. 调用 LLM ChatResponse response chatClient.call( new Prompt(List.of(systemMessage, userMessage)) ); // 5. 提取并返回答案 return response.getResult().getOutput().getContent(); } }提示词工程要点系统提示词System Prompt用于定义 AI 的角色、回答规则和格式要求。明确要求 AI 基于上下文回答可以有效减少幻觉。上下文组装将检索到的文档块清晰、结构化地放入提示词中并标注来源便于 AI 理解和引用也便于后期向用户展示来源。温度Temperature等参数可以通过ChatOptions在调用时控制生成答案的随机性。对于知识问答通常设置较低的温度如 0.1-0.3以获得更确定、更可靠的答案。4.3 构建 RESTful API 控制器最后创建一个控制器暴露问答接口和文档上传接口。package com.example.aiknowledge.controller; import com.example.aiknowledge.service.ChatService; import com.example.aiknowledge.service.DocumentProcessingService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.util.Map; RestController RequestMapping(/api) RequiredArgsConstructor public class KnowledgeBaseController { private final ChatService chatService; private final DocumentProcessingService documentProcessingService; PostMapping(/chat) public ResponseEntityMapString, String chat(RequestBody MapString, String request) { String question request.get(question); if (question null || question.isBlank()) { return ResponseEntity.badRequest().body(Map.of(error, 问题不能为空)); } String answer chatService.generateAnswer(question); return ResponseEntity.ok(Map.of(answer, answer)); } PostMapping(/upload) public ResponseEntityMapString, String uploadDocument(RequestParam(file) MultipartFile file) { try { documentProcessingService.processAndStoreDocument(file); return ResponseEntity.ok(Map.of(message, 文档上传并处理成功)); } catch (Exception e) { return ResponseEntity.internalServerError().body(Map.of(error, 文档处理失败: e.getMessage())); } } }5. 前端界面与联调前端需要实现两个主要页面知识库管理上传文档和智能问答界面。5.1 实现问答界面 (ChatView.vue)这是一个简化的 Vue 3 组件示例使用 Composition API 和 Axios。template div classchat-container div classchat-history div v-for(msg, index) in messages :keyindex :class[message, msg.role] strong{{ msg.role user ? 你 : 助手 }}:/strong div v-htmlformatMessage(msg.content)/div small v-ifmsg.sources classsources来源: {{ msg.sources }}/small /div /div div classinput-area textarea v-modelinputQuestion keyup.enter.exactsendMessage placeholder请输入您的问题.../textarea button clicksendMessage :disabledloading发送/button /div /div /template script setup import { ref } from vue; import axios from axios; const messages ref([]); const inputQuestion ref(); const loading ref(false); const API_BASE http://localhost:8080/api; // 后端地址 const sendMessage async () { const question inputQuestion.value.trim(); if (!question || loading.value) return; // 添加用户消息到历史 messages.value.push({ role: user, content: question }); inputQuestion.value ; loading.value true; try { const response await axios.post(${API_BASE}/chat, { question }); const answer response.data.answer; // 添加 AI 回复到历史 messages.value.push({ role: assistant, content: answer }); } catch (error) { console.error(请求失败:, error); messages.value.push({ role: assistant, content: 抱歉服务暂时不可用。 }); } finally { loading.value false; } }; const formatMessage (text) { // 简单处理换行 return text.replace(/\n/g, br); }; /script style scoped .chat-container { /* 样式省略 */ } /style5.2 实现文档上传界面 (KnowledgeManageView.vue)template div h2知识库管理/h2 input typefile changehandleFileUpload accept.pdf,.docx,.txt / button clickuploadFile :disabled!file || uploading上传/button p v-ifuploadStatus{{ uploadStatus }}/p /div /template script setup import { ref } from vue; import axios from axios; const file ref(null); const uploading ref(false); const uploadStatus ref(); const handleFileUpload (event) { file.value event.target.files[0]; }; const uploadFile async () { if (!file.value) return; uploading.value true; uploadStatus.value 上传中...; const formData new FormData(); formData.append(file, file.value); try { const response await axios.post(${API_BASE}/upload, formData, { headers: { Content-Type: multipart/form-data } }); uploadStatus.value 上传成功; file.value null; } catch (error) { console.error(上传失败:, error); uploadStatus.value 上传失败: error.message; } finally { uploading.value false; } }; /script5.3 配置与启动启动后端确保 PostgreSQL 运行在application.yml中配置好数据库连接和 AI 模型 API 密钥或本地 Ollama 地址。运行 Spring Boot 主类。启动前端在ai-knowledge-frontend目录下运行npm run dev。访问前端打开浏览器访问http://localhost:5173Vite 默认端口。上传文档在管理页面上传一个 PDF 或 Word 文档观察后端日志确认文档被解析、分块、向量化并存储。进行问答在问答页面输入相关问题查看系统是否能从上传的文档中检索并生成答案。6. 常见问题排查与性能优化系统搭建和运行过程中会遇到各种问题。以下是典型问题的排查路径和优化建议。6.1 部署与运行问题排查问题现象可能原因检查方式处理建议后端启动失败数据库连接错误1. PostgreSQL 服务未启动。2. 连接 URL、用户名、密码错误。3. 网络或防火墙问题。1.docker ps检查容器状态。2. 使用psql或客户端手动连接测试。3. 检查application.yml配置。1. 启动数据库服务。2. 修正连接配置。3. 检查端口是否开放。上传文档后问答无结果或结果不相关1. 文档解析失败内容为空。2. 分块策略不合理太大或太小。3. Embedding 模型调用失败或维度不匹配。4. 向量索引未创建检索慢且不准。1. 查看后端日志确认fullText是否被正确解析。2. 打印分块后的内容检查长度和语义完整性。3. 检查 Embedding 服务日志和 API 密钥。4. 在数据库执行SELECT * FROM document_chunk LIMIT 1;查看embedding字段是否非空且维度正确。1. 确保文档格式被 Tika 支持。2. 调整chunkSize和chunkOverlap。3. 验证 Embedding 模型配置和网络连通性。4. 为embedding列创建合适的向量索引。调用 LLM 生成答案超时或报错1. OpenAI/Azure API 密钥无效或额度不足。2. Ollama 服务未启动或模型未加载。3. 网络问题导致请求超时。4. 提示词过长超过模型上下文窗口。1. 检查 API 密钥和账单。2. 访问http://localhost:11434/api/tags查看 Ollama 模型列表。3. 使用curl或 Postman 直接测试 API。4. 计算提示词 Token 数。1. 更换有效 API 密钥或充值。2. 启动 Ollama 并拉取对应模型。3. 检查代理或防火墙设置。4. 减少检索的文档块数量 (topK) 或使用具有更长上下文窗口的模型。前端跨域 (CORS) 错误后端未配置允许前端域名的跨域请求。浏览器开发者工具 Console 或 Network 标签页查看错误。在后端添加 CORS 配置CrossOrigin(origins http://localhost:5173)或全局配置。6.2 效果优化与进阶实践当基础功能跑通后可以从以下几个方面提升系统效果和工程健壮性1. 优化检索效果混合检索Hybrid Search结合向量相似度搜索和传统关键词BM25搜索可以兼顾语义匹配和精确词匹配。可以使用pgvector结合pg_bm25扩展或在应用层实现。重排序Re-Ranking先用向量检索出较多的候选文档如 20 个再用一个更精细的交叉编码器Cross-Encoder模型对它们进行重排序选出最相关的 Top-K 个。这能显著提升精度但会增加延迟。元数据过滤在检索时加入过滤条件例如只检索某个部门、某个时间段的文档。这需要在存储时丰富元数据并在查询时构造带过滤条件的 SQL。2. 优化提示词Prompt EngineeringFew-Shot 示例在系统提示词中提供几个“问题-答案”对示例引导模型更好地遵循格式和风格。指令明确化更详细地规定答案格式如“如果上下文中有具体数字请引用”、“如果信息不完整请列出已知部分并指出缺失什么”。分步思考Chain-of-Thought对于复杂问题可以要求模型先分解问题再分别从上下文中寻找证据最后综合回答。3. 工程化与生产就绪异步处理文档上传和向量化是耗时操作应改为异步任务如使用 SpringAsync或消息队列立即返回“处理中”状态通过 WebSocket 或轮询通知前端结果。配置外置化将模型 API 密钥、数据库连接串、分块参数等敏感或易变配置移至配置中心或环境变量。日志与监控记录关键操作的日志如文档处理状态、检索耗时、Token 消耗并集成监控告警。缓存策略对常见问题的答案或 Embedding 结果进行缓存减少对模型和数据库的重复调用提升响应速度。版本管理知识库文档和对应的向量需要版本管理。当文档更新时需要有一套机制来更新或失效旧的向量并添加新的向量。4. 扩展方向多轮对话Conversational RAG将对话历史也作为上下文的一部分输入给模型使系统能理解指代和上下文关联。Agentic RAG引入智能体Agent概念让系统能自主判断是否需要检索、如何拆解复杂问题、何时调用工具等。多模态 RAG支持图片、表格中的信息提取和问答。权限控制根据用户角色在检索阶段过滤其无权访问的文档内容。构建一个成熟的企业级 AI 知识库是一个迭代过程。从本文的最小可行系统出发通过持续的评估、优化和扩展你可以逐步将其打磨成支撑企业核心知识流转的关键系统。核心在于理解 RAG 每个环节的原理并针对自己业务场景的数据特点和查询模式进行有针对性的调优。