恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
LangChain4j+PGVector构建RAG智能客服与工单系统实战
首页
资讯中心
/
LangChain4j+PGVector构建RAG智能客服与工单系统实战
LangChain4j+PGVector构建RAG智能客服与工单系统实战
发布时间:2026/9/1 22:36:55
企业客服系统一旦接上大模型最容易出现的问题不是模型不会说话而是模型什么话都敢说。为了让人工智能客服先查资料再回答RAGRetrieval-Augmented Generation检索增强生成成为企业知识库客服落地的核心方案。本文基于 LangChain4j Spring Boot Vue RAG PGVector Embedding 这套技术栈完整实现一个企业智能客服与工单处理系统。文章会从系统链路设计开始先说明 RAG 在企业客服场景中的价值和边界然后完成数据库、后端、前端三部分的搭建。后端覆盖文档导入、文本切分、向量存储、相似度检索、大模型生成回答、转人工工单创建六个环节前端覆盖聊天窗口、知识库管理、工单列表三个界面。最后给出运行验证、常见问题排查和生产环境落地清单。这套方案适合正在做企业知识库问答、内部客服机器人、工单系统智能化的后端或全栈开发者。学完后你可以把方案迁移到 FAQ 助手、售前咨询、运维工单等场景而不只是看一个演示项目。1. 先把智能客服和工单系统的整体链路想清楚1.1 RAG 为什么适合企业客服场景大模型直接回答业务问题时有两个明显短板一是模型训练数据里没有企业内部信息例如某产品的退款政策、售后流程、排障手册二是模型可能一本正经地编造答案也就是幻觉。RAG 的思路是在模型回答问题之前先从企业知识库中检索出相关片段把这些片段作为上下文拼进提示词里再让模型基于这些材料回答。这种方式的优点很直接回答有据可依模型被限定在给定文档范围内。企业文档更新后不需要重新训练模型只需要更新向量库。可以追溯答案来源给用户展示“这段回答来自哪份文档”。实现成本远低于微调适合中小团队快速落地。RAG 不是万能方案。如果企业知识库文档质量差、切分不合理或检索命中率低模型回答质量依然上不去。所以本文会在验证阶段给出评价指标而不是只看“能对话”这个表面效果。1.2 系统职责划分智能问答、知识库、工单一个完整的企业客服系统至少要拆成三个模块。智能问答模块负责接收用户问题调用 RAG 链路生成回答并判断是否需要进行人工介入。知识库模块负责文档上传、解析、切分、向量化、存储和删除。工单模块负责在回答无法解决用户问题时创建工单、分配处理人、记录处理状态。这三个模块的关系是知识库为问答提供数据问答为工单提供触发条件工单承载无法自动解决的问题。把这三个模块拆开设计后面无论是替换 Embedding 模型还是接入企业工单审批流都不会牵一发动全身。1.3 最小闭环链路整套系统按下面这条链路工作用户提问后后端对问题做 Embedding 向量化在 PGVector 中检索相似的历史知识片段把 topK 片段拼入 Prompt调用大模型生成回答。模型回答时如果发现知识库内容不足以回答问题会在回答中标记“需要转人工”。此时前端可以展示转人工按钮用户确认后创建工单工单进入后台处理队列。这条链路中PGVector 承担向量存储和相似度检索LangChain4j 提供模型调用、向量检索、文档切分的标准化接口Spring Boot 负责把各环节串成 REST 接口Vue 负责交互界面。注意RAG 链路里最容易出错的是“检索到的内容到底有没有被大模型真正使用”。调试时建议把命中片段和最终回答一起返回方便判断是检索问题还是生成问题。2. 环境准备与项目初始化2.1 环境版本清单先明确版本边界。LangChain4j 的版本迭代速度较快不同版本之间 API 存在差异尤其是EmbeddingStore的构建方式和 Spring Boot Starter 的自动配置类。下面表格是最小可用组合实际项目以官方文档为准。组件版本建议说明JDK17LangChain4j 对 Java 8 支持有限推荐 17Spring Boot3.2.x3.3 以上需要确认 LangChain4j 版本兼容性LangChain4j0.35.0 为例版本变化快落地前查看官方 release notePostgreSQL16对向量索引支持更稳定pgvector0.7.0需要安装 PostgreSQL 扩展Vue3.4使用 Vite 构建Node.js18 或 20对应 Vite 5 的构建要求模型Qwen 或 BGE 系列通过 OpenAI 兼容接口或 Ollama 本地调用本机需要同时安装 PostgreSQL、JDK、Maven、Node.js。建议先用学习环境跑通再考虑生产部署。2.2 初始化数据库和 PGVector安装 PostgreSQL 后进入数据库创建业务库和扩展。pgvector 不是 PostgreSQL 自带的需要单独安装。Linux 下通过apt install postgresql-16-pgvector安装Windows 下需要下载对应 PostgreSQL 16 版本的预编译 DLL 文件放到 PostgreSQL 的lib目录。psql -U postgres -c CREATE DATABASE customer_service; psql -U postgres -d customer_service -c CREATE EXTENSION IF NOT EXISTS vector;vector扩展创建成功后创建知识库向量表和工单表。CREATE TABLE knowledge_chunk ( id BIGSERIAL PRIMARY KEY, doc_id VARCHAR(64) NOT NULL, chunk_index INT NOT NULL, content TEXT NOT NULL, source VARCHAR(255), embedding VECTOR(1024), created_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX idx_knowledge_chunk_vector ON knowledge_chunk USING hnsw (embedding vector_cosine_ops); CREATE TABLE ticket ( id BIGSERIAL PRIMARY KEY, question TEXT NOT NULL, answer TEXT, status VARCHAR(16) DEFAULT PENDING, assignee VARCHAR(64), created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() );embedding字段的长度必须和 Embedding 模型输出的向量维度一致。例如text-embedding-v3输出 1024 维就用VECTOR(1024)换成 BGE-M3 后输出维度可能不同需要重新建表或迁移字段。hnsw索引适合数据量大的场景vector_cosine_ops表示使用余弦相似度。数据量小时可以不建索引直接暴力检索。2.3 Spring Boot 基础工程与依赖创建一个 Spring Boot 3.2 工程引入以下依赖。以 Maven 为例dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency dependency groupIdorg.postgresql/groupId artifactIdpostgresql/artifactId scoperuntime/scope /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-spring-boot-starter/artifactId version0.35.0/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-pgvector/artifactId version0.35.0/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-open-ai/artifactId version0.35.0/version /dependencylangchain4j-open-ai用于通过 OpenAI 兼容协议调用大模型和 Embedding 服务。这样可以兼容多种模型服务而不绑定某一家。2.4 后端配置文件application.yml中配置数据源、模型服务和向量库参数。spring: datasource: url: jdbc:postgresql://localhost:5432/customer_service username: postgres password: postgres langchain4j: open-ai: chat-model: base-url: ${AI_BASE_URL} api-key: ${AI_API_KEY} model-name: qwen-plus temperature: 0.1 embedding-model: base-url: ${AI_BASE_URL} api-key: ${AI_API_KEY} model-name: text-embedding-v3API Key 不要写死在配置里使用环境变量注入。temperature调低到 0.1 是为了让客服回答更保守减少创造性发挥。2.5 前端项目初始化使用 Vite 创建 Vue 3 项目并安装 Element Plus 和 Axios。npm create vitelatest ai-cs-frontend -- --template vue cd ai-cs-frontend npm install element-plus axios前端只需要三个视图聊天窗口、知识库管理、工单列表。路由可以放在/chat、/knowledge、/ticket下。3. 后端核心代码知识库导入、向量存储、检索问答3.1 知识库文档处理流程企业知识库中常见的文件格式包括 Markdown、TXT、PDF、Word。前端上传文件后后端需要完成提取文本、切分片段、向量化、入库四步。文本切分是影响检索质量的关键步骤。切分太大会让片段包含过多无关信息检索精度下降切分太小会让语义不完整大模型无法理解上下文。LangChain4j 提供了DocumentSplitter接口常见实现包括切分器策略适用场景ParagraphDocumentSplitter按段落切分结构化文档RecursiveDocumentSplitter递归切分长文本、混合结构DocumentByWordSplitter按词数切分英文文档对中文场景推荐先按段落切分再控制最大字符数。示例中每段约 200 字重叠 20 字。3.2 Embedding 与向量存储 Bean创建VectorStoreConfig把EmbeddingStore注册为 Spring Bean。Configuration public class VectorStoreConfig { Bean public EmbeddingStoreTextSegment embeddingStore(DataSource dataSource) { return PgVectorEmbeddingStore.builder() .dataSource(dataSource) .tableName(knowledge_chunk) .dimension(1024) .build(); } }PgVectorEmbeddingStore在写入向量时会自动往knowledge_chunk表插入数据读取时会执行相似度查询。dimension(1024)必须和表中VECTOR(1024)以及模型输出维度保持一致否则运行时会直接报错。3.3 知识库导入服务KnowledgeBaseService负责文件解析、切分、向量化和入库。Service public class KnowledgeBaseService { private final EmbeddingModel embeddingModel; private final EmbeddingStoreTextSegment embeddingStore; public KnowledgeBaseService(EmbeddingModel embeddingModel, EmbeddingStoreTextSegment embeddingStore) { this.embeddingModel embeddingModel; this.embeddingStore embeddingStore; } public int importDocument(String docId, String content, String source) { Document document Document.from(content, Metadata.from(source, source)); DocumentSplitter splitter new ParagraphDocumentSplitter(200, 20); ListTextSegment segments splitter.split(document); ListEmbedding embeddings embeddingModel.embedAll(segments).content(); embeddingStore.addAll(embeddings, segments); return segments.size(); } }这里需要注意embedAll是批量接口比循环调用embed效率高。addAll会把 Embedding 和 TextSegment 一起写入 PGVector。3.4 问答检索服务与提示词ChatService是 RAG 链路的核心。先向量化用户问题再检索 topK 片段最后构造提示词调用大模型。Service public class ChatService { private final EmbeddingModel embeddingModel; private final EmbeddingStoreTextSegment embeddingStore; private final ChatLanguageModel chatModel; public String answer(String question, double minScore) { Embedding queryEmbedding embeddingModel.embed(question).content(); ListEmbeddingMatchTextSegment matches embeddingStore.findRelevant(queryEmbedding, 4, minScore); if (matches.isEmpty()) { return 系统未在知识库中找到相关内容建议转人工处理。; } String context matches.stream() .map(match - match.embedded().text()) .collect(Collectors.joining(\n\n)); String prompt 你是一名企业客服。请只根据下面的知识库内容回答问题。 如果知识库内容无法回答用户问题请直接回复需要转人工。 回答要简洁、准确不要编造。 知识库内容 %s 用户问题 %s .formatted(context, question); return chatModel.generate(prompt); } }minScore是相似度阈值。低于该阈值的片段即使被检索到也不使用。这个参数需要根据实际数据调阈值太高会导致大量问题无法回答太低会导致无关内容被拼进提示词。注意不要把检索结果全部拼入提示词。topK 取 4 到 6 为宜片段太多会超过上下文窗口也会让模型被无关信息干扰。3.5 工单处理服务工单服务负责创建工单、更新状态和查询列表。Service public class TicketService { private final JdbcTemplate jdbcTemplate; public TicketService(JdbcTemplate jdbcTemplate) { this.jdbcTemplate jdbcTemplate; } public Long create(String question) { KeyHolder keyHolder new GeneratedKeyHolder(); jdbcTemplate.update(con - { PreparedStatement ps con.prepareStatement( INSERT INTO ticket(question, status) VALUES (?, PENDING), Statement.RETURN_GENERATED_KEYS); ps.setString(1, question); return ps; }, keyHolder); return keyHolder.getKey().longValue(); } }在生产环境中工单处理还需要考虑分配策略、超时通知、回访记录和权限控制。这里先保留最小可运行接口。3.6 Controller 接口提供三个 REST 接口问答、导入知识库、创建工单。RestController RequestMapping(/api) public class CustomerServiceController { private final ChatService chatService; private final KnowledgeBaseService knowledgeBaseService; private final TicketService ticketService; PostMapping(/chat) public MapString, Object chat(RequestBody ChatRequest request) { String answer chatService.answer(request.question(), 0.5); return Map.of(answer, answer); } PostMapping(/knowledge/import) public MapString, Object importKnowledge(RequestParam String docId, RequestParam String source, RequestBody String content) { int chunks knowledgeBaseService.importDocument(docId, content, source); return Map.of(chunks, chunks); } PostMapping(/ticket) public MapString, Object createTicket(RequestBody ChatRequest request) { Long ticketId ticketService.create(request.question()); return Map.of(ticketId, ticketId); } }实际项目里文件上传接口应该接收MultipartFile而不是直接接收原始字符串。上面代码用于展示核心流程文件解析部分需要根据实际格式扩展。4. 前端页面聊天窗口、知识库管理、工单列表4.1 聊天窗口聊天窗口使用 Vue 3 组合式 API调用后端/api/chat接口。template div classchat-panel div v-for(msg, index) in messages :keyindex classmessage div :classmsg.role{{ msg.content }}/div /div div classinput-row el-input v-modelquestion placeholder请输入问题 keyup.entersend / el-button typeprimary clicksend发送/el-button /div /div /template script setup import { ref } from vue import axios from axios const question ref() const messages ref([]) async function send() { if (!question.value.trim()) return messages.value.push({ role: user, content: question.value }) const resp await axios.post(/api/chat, { question: question.value }) messages.value.push({ role: assistant, content: resp.data.answer }) question.value } /script如果后端返回的内容包含文档来源前端可以在回答下方展示“参考来源某文档.pdf”。这样用户能确认回答是否可信。4.2 知识库上传页面知识库管理页面提供一个上传入口调用/api/knowledge/import。实际项目中建议把文件先上传到服务端由服务端完成文本提取而不是在前端直接发送原文。这样可以统一处理 PDF、Word 等格式。4.3 工单列表工单列表页面调用/api/ticket/list接口展示状态和处理人。为了简化这里不展开完整实现核心点是当聊天窗口出现“需要转人工”时点击按钮调用创建工单接口然后跳转到工单列表页查看进度。4.4 跨域与接口联调Vite 开发服务器默认端口是 5173Spring Boot 默认端口是 8080存在跨域问题。推荐在 Vite 配置代理而不是在后端开启全局 CORS。// vite.config.js export default { server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这样前端请求/api/chat时会自动转发到后端避免前端开发时反复处理跨域。5. 运行验证与效果评估5.1 启动顺序和初始化检查按以下顺序启动系统启动 PostgreSQL确认customer_service库和knowledge_chunk、ticket表存在。启动 Spring Boot 应用观察日志中是否有EmbeddingStore初始化成功的记录。启动 Vue 开发服务器访问聊天页面。如果后端日志出现 PGVector 相关报错优先检查CREATE EXTENSION vector是否执行成功以及VECTOR维度是否匹配。5.2 验证知识库导入导入一个 FAQ 文档例如退货政策用户签收后 7 天内可以申请无理由退货。 发货时间工作日 16:00 前付款的订单当天发货。调用导入接口后查询knowledge_chunk表确认切分结果。SELECT doc_id, chunk_index, left(content, 30) FROM knowledge_chunk;正常结果应该出现 1 到 2 条记录。如果记录数为 0检查切分器配置和文本内容是否被正确读取。5.3 验证问答链路在聊天窗口输入“退货有几天时间”预期回答来自退货政策片段。如果回答不对按以下顺序排查先确认knowledge_chunk表是否有数据。再确认minScore阈值是否过高导致检索结果为空。再确认模型调用是否成功查看后端日志是否出现模型服务报错。建议在ChatService中临时打印命中的片段文本确认检索结果是否相关。5.4 验证工单闭环输入一个知识库中没有的问题例如“我的发票开错了怎么改”。如果提示词中设置了“无法回答时回复需要转人工”模型会返回“需要转人工”。此时前端显示转人工按钮点击后创建工单在工单列表中能看到状态为PENDING的记录。5.5 用命中率、拒答率评估 RAG 效果RAG 系统不能只看“能不能回答”还需要关注质量指标。指标计算方式期望命中率检索到相关片段的提问数 / 总提问数越高越好拒答率模型明确拒绝回答的提问数 / 总提问数过低说明模型可能在胡编引用准确率回答中引用的片段是否真实相关应接近 100%响应耗时从提问到返回回答的耗时200ms 检索 模型生成耗时如果命中率低优先优化切分策略和 topK如果拒答率过低且回答内容出现幻觉提高minScore阈值或在提示词中加强“无法回答时必须拒绝”的约束。6. 常见问题排查6.1 pgvector 扩展不存在现象启动时执行 SQL 报错type vector does not exist或者CREATE EXTENSION vector失败。原因PostgreSQL 服务端没有安装 pgvector 扩展。只添加了 Java 依赖还不够数据库需要单独安装。排查psql -U postgres -d customer_service -c SELECT * FROM pg_available_extensions WHERE name vector;如果没有结果说明扩展未安装。Linux 使用apt install postgresql-16-pgvectorWindows 需要把对应版本的 DLL 文件复制到 PostgreSQL 的lib目录并重启服务。预防写初始化脚本把CREATE EXTENSION放入数据库迁移脚本中。6.2 向量维度不一致现象写入向量时抛出类似expected 1536 dimensions, not 1024的异常。原因表结构中的VECTOR(1024)和当前 Embedding 模型输出维度不一致。更换模型后如果保留旧表结构就会报错。排查确认application.yml中 embedding-model 的model-name。查询模型 API 文档确认输出向量维度。查看建表 SQL。解决删除旧表重建或新建一张维度匹配的表。不要试图在同一张表中混用不同维度的向量。6.3 Spring Boot 版本过高导致的自动配置问题现象引入 LangChain4j Starter 后ChatLanguageModelBean 注入失败或者配置项完全不生效。原因LangChain4j 的 Spring Boot Starter 与 Spring Boot 版本存在兼容窗口。如果使用 Spring Boot 3.4 或更高版本某些自动配置类可能因为版本变化未被加载。排查查看启动日志中DevLangChain4jAutoConfiguration是否生效。检查spring.factories或AutoConfiguration.imports中的自动配置类。使用debugtrue启动观察条件装配结果。解决根据官方文档选择匹配的 LangChain4j 版本或者回退 Spring Boot 到 3.2.x。不要盲目升级 Spring BootRAG 系统稳定运行比新版本特性更重要。6.4 中文知识库检索效果差现象知识库里明明有相关内容但模型回答总是“未找到”。原因中文按句切分时如果片段太短单个片段可能不包含完整的业务信息如果 Embedding 模型对中文支持一般检索分数整体偏低导致minScore把它们全部过滤掉。排查打印检索到的EmbeddingMatch的分数看是否低于阈值。打印 topK 片段看内容是否和问题相关。尝试调整切分器最大字符数和重叠字符数。解决先降低minScore确认内容能被检索出来。再优化切分优先按段落切分保留标题和结构。中文场景优先选择对中文支持更好的模型例如text-embedding-v3或本地 BGE 系列。6.5 模型服务调用失败或超时现象问答时返回 5xx 错误或者等待很长时间后才返回错误。排查检查 API Key 和base-url是否配置正确。用 curl 直接调用模型服务确认接口可用。检查超时配置模型服务响应慢时调整 HTTP 客户端超时时间。解决在application.yml中为模型服务增加超时和重试配置。生产环境建议使用异步任务处理问答请求避免用户请求阻塞在模型调用上。7. 生产环境落地清单7.1 学习环境与生产环境的差异本文示例可以直接在本地运行但生产环境需要补充多项能力。项目学习环境生产环境配置管理application.yml 写本地参数使用配置中心或环境变量知识库更新手动导入定时同步企业文档库日志默认日志结构化日志 检索链路追踪监控无命中率、拒答率、响应耗时指标权限无区分客服、管理员、用户角色工单流程只有创建和查询分配、流转、通知、回访安全无数据脱敏、文档访问权限7.2 发布前检查清单上线前逐项确认数据库迁移脚本是否包含CREATE EXTENSION vector和索引创建语句。Embedding 模型维度是否和表结构一致。API Key 是否通过环境变量注入是否已加入服务发布配置。是否配置了模型调用的超时和重试。是否准备了一份测试问题集至少覆盖知识库命中、未命中、边界相似度三种情况。知识库导入是否具备幂等性重复导入同一文档不会产生重复数据。工单创建是否具备防重复提交机制。是否记录 RAG 链路日志包括问题原文、检索片段、相似度分数、模型回答。7.3 可复用排错清单接口 404检查 Controller 路由和前端代理配置。接口 500先看后端堆栈日志确认是数据库、模型还是代码问题。回答为空检查模型调用参数确认ChatLanguageModel是否正确配置。检索结果为空检查knowledge_chunk表数据再检查minScore阈值。回答与知识库无关检查 topK 片段内容确认切分质量。工单创建失败检查数据库表是否存在字段是否匹配。8. 从演示系统走向可维护产品三条建议第一把知识库的更新变成自动化流程。演示系统里是手动上传文档生产环境应该监听企业文档平台文档变更后自动重新切分、重新向量化并标记旧版本内容失效。第二把 RAG 质量评估日常化。至少维护一份问题集每次模型或切分策略调整后批量跑一遍对比命中率、拒答率和回答质量。没有指标约束的 RAG 系统上线后很难判断到底变好了还是变坏了。第三把转人工的判定机制做得更精细。当前示例使用的是“模型主动返回需要转人工”过于依赖提示词。更稳的方式是结合相似度分数检索分数低于阈值时直接触发转人工让模型决定和分数判断互为兜底。这套基于 LangChain4j Spring Boot Vue RAG PGVector Embedding 的企业智能客服系统作为入门项目已经覆盖了完整链路。下一步可以继续扩展多轮会话记忆、客服知识权限隔离、工单自动分类和更细粒度的 RAG 指标采集。