RAG实战+智谱GLM适配,零基础也能快速落地AI问答
其实RAG并不复杂,对Java后端来说,核心就是“用Spring Boot搭框架、用智谱SDK做生成、用PgVector做检索”,全程不用脱离自己的技术栈,不用学复杂的AI理论。增加文件上传接口,支持前端上传文档;引入Redis缓存,缓存高频问题的检索结果,提升响应速度;优化文本分块逻辑,引入LangChain4j实现语义分块;增加权限控制,确保私有知识库的安全性。
作为Java后端开发者,你是不是也有这样的困惑:想接入AI大模型做智能问答,但要么模型“一本正经地胡说八道”,要么私有业务数据无法接入,要么不知道怎么适配具体模型?
其实不用慌,今天就给大家讲透「RAG技术」—— 解决大模型“失忆”“不懂业务”的神器,再手把手教你适配智谱GLM模型(国内主流、易接入、免费额度足),全程实战代码可直接复制,避开所有常见坑,让你半天就能落地一个可用的AI问答接口。
一、先搞懂:RAG到底是什么?
很多后端同学一听到RAG(Retrieval-Augmented Generation,检索增强生成)就头大,其实一句话就能说透:
RAG = 本地知识库检索 + 大模型生成,相当于给大模型配了一个“专属笔记本”。
举个例子:你想做一个公司内部的智能问答系统,用户问“公司差旅报销标准是什么”,如果直接调用智谱GLM,模型大概率答不上来(因为它没学过你公司的私有规则);但用RAG,系统会先从你本地的《差旅报销制度》里检索相关内容,再把检索到的内容传给GLM,让它基于这些真实资料生成答案——既不会胡说,又能精准匹配业务。对Java后端来说,RAG的核心价值就3个,精准戳中我们的需求:
- 无需微调模型:不用花大量时间、算力训练模型,只需维护本地知识库,更新知识更简单。
- 解决知识私有:企业内部文档、业务数据不用上传到第三方,兼顾安全与合规(Java后端最关心的点)。
- 开发成本低:依托Spring生态和智谱GLM SDK,几行代码就能接入,不用切换技术栈。
二、核心流程:RAG+智谱GLM 完整链路
落地一个简单的「企业知识库问答系统」,核心流程分4步,每一步都对应Java代码实现:
- 准备工作:搭建环境、获取智谱GLM API密钥(免费);
- 知识库预处理:将本地文档(如PDF、TXT)转成向量,存入向量数据库;
- 检索环节:用户提问后,将问题转成向量,从向量库中检索最相关的知识片段;
- 生成环节:将检索到的知识+用户问题,传给智谱GLM,生成精准答案。
| 组件 | 选型 | 说明 |
|---|---|---|
| 后端框架 | Spring Boot 3.x | Java后端主流框架,快速搭建服务 |
| 向量数据库 | PgVector(PostgreSQL扩展) | 轻量、易集成,适合中小规模知识库(替代Milvus,降低部署成本) |
| Embedding模型 | 智谱GLM Embedding API | 将文本转成向量,不用本地部署,直接调用接口 |
| 大模型 | 智谱GLM-4 | 国内性能优秀,免费额度足,Java SDK完善 |
| 文档处理 | Apache Tika | 解析PDF、TXT等文档,提取文本内容 |
三、实战环节:手把手实现RAG+智谱GLM适配
第一步:准备工作
1. 获取智谱GLM API密钥
1. 访问 智谱AI开放平台,注册账号并登录;
2. 进入「控制台」,创建应用,获取API密钥(sk);
3. 注意:新账号有免费额度,足够开发测试,后续按需付费即可。
2. 搭建项目环境(Maven依赖)
创建Spring Boot项目,在pom.xml中添加以下依赖:
<!-- Spring Boot 核心依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/>
</parent>
<dependencies>
<!-- Spring Web(提供接口) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 智谱GLM Java SDK(核心,适配GLM-4和Embedding) -->
<dependency>
<groupId>cn.bigmodel.openapi</groupId>
<artifactId>openapi-java-sdk</artifactId>
<version>release-v4-2.3.4</version>
</dependency>
<!-- PostgreSQL + PgVector(向量数据库) -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.github.pgvector</groupId>
<artifactId>pgvector-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<!-- 文档解析(Apache Tika) -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers</artifactId>
<version>2.9.1</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Lombok(简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3. 配置文件(application.yml)
替换自己的智谱API密钥、PostgreSQL地址,其他配置可默认:
spring:
# 数据库配置(PostgreSQL,需提前创建数据库,并启用pgvector扩展)
datasource:
url: jdbc:postgresql://localhost:5432/rag-glm-db
username: postgres
password: 123456
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: true
# 智谱GLM配置
zhipu:
glm:
api-key: 你的智谱API密钥(sk)
# GLM-4模型地址(默认无需修改)
model: glm-4
# Embedding模型地址(默认无需修改)
embedding-model: embedding-3
# 服务器配置
server:
port: 8080
第二步:初始化向量数据库(PgVector)
1. 先在PostgreSQL中创建数据库(如rag-glm-db);
2. 启用pgvector扩展(执行SQL):
-- 启用pgvector扩展
CREATE EXTENSION IF NOT EXISTS vector;
-- 创建知识库表(存储文本片段和对应的向量)
CREATE TABLE IF NOT EXISTS knowledge_chunk (
id BIGSERIAL PRIMARY KEY,
content TEXT NOT NULL, -- 文本片段
embedding VECTOR(768) NOT NULL, -- 向量(智谱Embedding默认768维)
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
3. 创建实体类(对应数据库表):
package com.example.ragglm.entity;
import io.github.pgvector.hibernate.VectorType;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Type;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "knowledge_chunk")
public class KnowledgeChunk {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 文本片段(知识库中的具体内容)
@Column(columnDefinition = "TEXT NOT NULL")
private String content;
// 文本对应的向量(768维,智谱Embedding默认维度)
@Column(name = "embedding", columnDefinition = "vector(768) NOT NULL")
@Type(VectorType.class)
private float[] embedding;
// 创建时间
@Column(name = "create_time", nullable = false, updatable = false)
private LocalDateTime createTime = LocalDateTime.now();
}
4. 创建Repository(操作数据库):
package com.example.ragglm.repository;
import com.example.ragglm.entity.KnowledgeChunk;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface KnowledgeChunkRepository extends CrudRepository<KnowledgeChunk, Long> {
// 核心检索方法:根据问题向量,查询Top3最相似的文本片段(余弦相似度)
@Query(value = "SELECT * FROM knowledge_chunk ORDER BY embedding <-> :embedding LIMIT 3", nativeQuery = true)
List<KnowledgeChunk> findTop3BySimilarity(@Param("embedding") float[] embedding);
}
第三步:封装智谱GLM工具类(核心,适配Embedding和问答)
这个工具类封装了两个核心方法:文本转向量(Embedding)、调用GLM生成答案:
package com.example.ragglm.util;
import cn.bigmodel.openapi.api.ChatApi;
import cn.bigmodel.openapi.api.EmbeddingApi;
import cn.bigmodel.openapi.core.ClientV4;
import cn.bigmodel.openapi.core.config.ClientConfig;
import cn.bigmodel.openapi.pojo.ChatCompletionRequest;
import cn.bigmodel.openapi.pojo.ChatCompletionResponse;
import cn.bigmodel.openapi.pojo.EmbeddingRequest;
import cn.bigmodel.openapi.pojo.EmbeddingResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class ZhipuGLMUtil {
// 从配置文件中读取智谱API密钥
@Value("${zhipu.glm.api-key}")
private String apiKey;
@Value("${zhipu.glm.model}")
private String glmModel;
@Value("${zhipu.glm.embedding-model}")
private String embeddingModel;
// 初始化智谱客户端(单例,避免重复创建)
private ClientV4 getClient() {
return new ClientV4.Builder(apiKey)
.enableTokenCache() // 启用Token缓存,减少请求次数
// 配置网络超时(避免调用超时)
.networkConfig(30, 10, 10, 10, TimeUnit.SECONDS)
.build();
}
/**
* 文本转向量(Embedding):将文本片段/用户问题转成768维向量
* @param text 输入文本(如“公司差旅报销标准”)
* @return 向量数组(float[])
*/
public float[] getEmbedding(String text) {
try {
ClientV4 client = getClient();
EmbeddingApi embeddingApi = client.getEmbeddingApi();
EmbeddingRequest request = new EmbeddingRequest();
request.setModel(embeddingModel); // 智谱Embedding模型
request.setInput(List.of(text)); // 输入文本(支持批量,这里单条)
EmbeddingResponse response = embeddingApi.createEmbedding(request);
// 返回第一个文本的向量(768维)
return response.getData().get(0).getEmbedding();
} catch (Exception e) {
log.error("智谱Embedding接口调用失败:{}", e.getMessage(), e);
throw new RuntimeException("文本转向量失败,请检查API密钥和网络");
}
}
/**
* 调用智谱GLM生成答案(结合检索到的知识片段)
* @param userQuestion 用户问题
* @param context 检索到的知识片段(多个用换行分隔)
* @return 生成的答案
*/
public String generateAnswer(String userQuestion, String context) {
try {
ClientV4 client = getClient();
ChatApi chatApi = client.getChatApi();
ChatCompletionRequest request = new ChatCompletionRequest();
request.setModel(glmModel); // GLM-4模型
request.setTemperature(0.7f); // 温度(0~1,越低越精准)
// 构造Prompt:核心是让GLM基于context回答,不编造信息
List<ChatCompletionRequest.Message> messages = new ArrayList<>();
// 系统提示(固定,告诉模型回答规则)
messages.add(new ChatCompletionRequest.Message(
"system",
"请严格根据提供的上下文内容回答用户问题,不要编造任何信息。如果上下文没有相关内容,直接回复“未找到相关信息”。"
));
// 上下文(检索到的知识片段)
messages.add(new ChatCompletionRequest.Message(
"user",
"上下文:" + context + "\n\n用户问题:" + userQuestion
));
request.setMessages(messages);
ChatCompletionResponse response = chatApi.createChatCompletion(request);
// 返回生成的答案
return response.getChoices().get(0).getMessage().getContent();
} catch (Exception e) {
log.error("智谱GLM接口调用失败:{}", e.getMessage(), e);
throw new RuntimeException("生成答案失败,请检查API密钥和网络");
}
}
}
第四步:文档处理+知识库导入(本地文档转向量,存入数据库)
以PDF文档为例,实现“读取PDF内容→分割文本片段→转向量→存入数据库”,后续可扩展为接口上传文档:
package com.example.ragglm.service;
import com.example.ragglm.entity.KnowledgeChunk;
import com.example.ragglm.repository.KnowledgeChunkRepository;
import com.example.ragglm.util.ZhipuGLMUtil;
import lombok.RequiredArgsConstructor;
import org.apache.tika.Tika;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
@Service
@RequiredArgsConstructor
public class KnowledgeService {
private final ZhipuGLMUtil zhipuGLMUtil;
private final KnowledgeChunkRepository knowledgeChunkRepository;
// 文档解析工具(Apache Tika)
private final Tika tika = new Tika();
/**
* 导入PDF文档到知识库(核心方法)
* @param pdfPath PDF文件路径(如:D:/company/差旅报销制度.pdf)
*/
public void importPdfToKnowledgeBase(String pdfPath) {
try {
// 1. 读取PDF内容(提取文本)
File file = new File(pdfPath);
InputStream inputStream = new FileInputStream(file);
String content = tika.parseToString(inputStream); // 解析PDF为文本
// 2. 分割文本片段(避免文本过长,影响检索和生成)
// 简单分割:按换行分割,也可使用语义分块(更精准)
String[] chunks = content.split("\n");
for (String chunk : chunks) {
chunk = chunk.trim();
// 过滤空文本和过短文本(避免无效数据)
if (chunk.isEmpty() || chunk.length() < 10) {
continue;
}
// 3. 将文本片段转成向量
float[] embedding = zhipuGLMUtil.getEmbedding(chunk);
// 4. 存入数据库
KnowledgeChunk knowledgeChunk = new KnowledgeChunk();
knowledgeChunk.setContent(chunk);
knowledgeChunk.setEmbedding(embedding);
knowledgeChunkRepository.save(knowledgeChunk);
}
System.out.println("PDF文档导入知识库成功!");
} catch (Exception e) {
throw new RuntimeException("PDF导入失败:" + e.getMessage(), e);
}
}
}
第五步:实现RAG核心接口(用户提问→检索→生成答案)
编写一个接口,接收用户问题,完成“问题转向量→检索相似知识→调用GLM生成答案”的完整流程,可直接用于前端调用:
package com.example.ragglm.controller;
import com.example.ragglm.entity.KnowledgeChunk;
import com.example.ragglm.repository.KnowledgeChunkRepository;
import com.example.ragglm.service.KnowledgeService;
import com.example.ragglm.util.ZhipuGLMUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/rag")
@RequiredArgsConstructor
public class RagController {
private final ZhipuGLMUtil zhipuGLMUtil;
private final KnowledgeChunkRepository knowledgeChunkRepository;
private final KnowledgeService knowledgeService;
/**
* 导入PDF到知识库(测试用,可后续改为文件上传接口)
* @param pdfPath PDF文件路径
* @return 导入结果
*/
@GetMapping("/import-pdf")
public String importPdf(@RequestParam String pdfPath) {
try {
knowledgeService.importPdfToKnowledgeBase(pdfPath);
return "导入成功";
} catch (Exception e) {
return "导入失败:" + e.getMessage();
}
}
/**
* RAG核心接口:用户提问,返回精准答案
* @param question 用户问题(如“公司差旅报销标准是什么”)
* @return 生成的答案
*/
@PostMapping("/query")
public String ragQuery(@RequestBody String question) {
// 1. 将用户问题转成向量
float[] questionEmbedding = zhipuGLMUtil.getEmbedding(question);
// 2. 检索Top3最相似的知识片段
List<KnowledgeChunk> similarChunks = knowledgeChunkRepository.findTop3BySimilarity(questionEmbedding);
// 3. 拼接检索到的知识片段(作为上下文)
String context = similarChunks.stream()
.map(KnowledgeChunk::getContent)
.collect(Collectors.joining("\n"));
// 4. 调用智谱GLM生成答案
return zhipuGLMUtil.generateAnswer(question, context);
}
}
第六步:测试运行
- 启动Spring Boot项目;
- 导入PDF文档:访问
http://localhost:8080/api/rag/import\-pdf?pdfPath=你的PDF路径,提示“导入成功”即完成; - 测试问答接口:用Postman发送POST请求
http://localhost:8080/api/rag/query,请求体为用户问题(如“公司差旅报销标准是什么”); - 查看响应:返回的答案会基于你导入的PDF内容,不会编造信息,且带有精准的业务逻辑。
四、避坑指南:落地RAG+GLM最容易踩的6个坑
很多同学按照上面的代码实现后,会遇到各种问题,这里总结了最常见的6个坑,以及对应的解决方案。
坑1:智谱API调用失败,提示“API密钥无效”
原因:API密钥错误、未启用应用,或密钥已过期。
解决方案:
- 检查配置文件中的api-key,确保和智谱控制台的一致;
- 登录智谱控制台,确认应用已启用,且免费额度未用完;
- 如果是本地测试,不要用代理,避免网络拦截。
坑2:向量数据库检索不到相关内容,答案不准确
原因:文本分割不合理、Embedding模型不匹配,或检索相似度逻辑错误。
解决方案:
- 文本分割:不要按固定长度分割,优先用“语义分块”(可引入LangChain4j简化),保留章节标题和上下文;
- Embedding模型:必须用智谱的Embedding模型,不要混用其他模型(如OpenAI),否则向量维度不匹配;
- 检索逻辑:PgVector余弦相似度,如果检索结果不理想,可调整返回的Top数量(如Top5)。
坑3:调用GLM接口超时,报“connection timeout”
原因:网络延迟、智谱API接口压力大,或超时时间设置过短。
解决方案:
- 在智谱客户端配置中,延长超时时间;
- 加入重试机制(如用Spring的Retry注解),避免单次请求失败;
- 避免并发请求过多,可加入限流(如Redis限流)。
坑4:PDF解析乱码,提取的文本不完整
原因:PDF有加密、字体不兼容,或Tika依赖缺失。
解决方案:
- 确保PDF未加密(加密PDF需先解密);
- 补充Tika的字体依赖;
- 如果是扫描版PDF(图片),需先进行OCR识别。
坑5:项目启动失败,提示“vector类型不存在”
原因:PgVector扩展未启用,或数据库版本过低(PostgreSQL需12+)。
解决方案:
- 重新执行
CREATE EXTENSION IF NOT EXISTS vector;,确保扩展启用; - 升级PostgreSQL到12及以上版本(推荐14+)。
坑6:生成的答案仍有编造,不符合上下文
原因:Prompt提示不明确,或GLM的temperature设置过高。
解决方案:
- 优化Prompt:明确告诉模型“只能基于上下文回答,不能编造”;
- 降低temperature值(设置为0.7,越低越精准,建议0.5~0.8之间);
- 如果上下文无相关内容,让模型直接返回“未找到相关信息”,避免编造。
五、总结:Java后端落地RAG+GLM的核心要点
其实RAG并不复杂,对Java后端来说,核心就是“用Spring Boot搭框架、用智谱SDK做生成、用PgVector做检索”,全程不用脱离自己的技术栈,不用学复杂的AI理论。后续可扩展的方向:
- 增加文件上传接口,支持前端上传文档;
- 引入Redis缓存,缓存高频问题的检索结果,提升响应速度;
- 优化文本分块逻辑,引入LangChain4j实现语义分块;
- 增加权限控制,确保私有知识库的安全性。
更多推荐


所有评论(0)