该智能体分为:「文档解析→知识提取→智能问答→动态更新」,智能体覆盖企业常见的知识库建设场景(如产品手册、内部制度、技术文档问答),核心亮点是 支持多格式文档、知识结构化存储、上下文记忆问答,同时体现 LangGraph4j 的「条件分支流转」和「状态回溯」能力。
一、项目核心目标
用户上传文档(PDF/Word/Markdown)或输入自然语言问题(如 “产品 X 的 API 调用限制是什么?”),智能体自动完成:
- 文档处理:解析上传的多格式文档,提取文本内容(处理表格、图片文字 OCR);
- 知识提取:将非结构化文本转化为结构化知识(如 FAQ、实体关系、关键要点);
- 知识存储:将结构化知识存入向量数据库(支持类似性检索);
- 智能问答:接收用户问题,结合上下文和知识库,生成精准回答(附引用来源);
- 知识更新:支持文档增量上传,自动更新知识库,避免重复存储;
- 上下文记忆:连续对话中记住历史问题,支持多轮追问(如 “这个限制是否适用于企业版?”)。
二、技术栈选型
|
组件 |
作用 |
版本提议 |
|
Spring Boot |
项目骨架、Web 接口、依赖管理 |
3.2.x |
|
Spring AI |
大模型调用、工具封装、向量嵌入生成 |
1.0.0+ |
|
LangGraph4j |
流程编排、状态管理、多轮对话上下文 |
0.1.0+ |
|
Apache Tika |
多格式文档解析(PDF/Word/Markdown) |
2.9.x |
|
Tesseract OCR |
图片文字提取(处理文档中的截图 / 图片) |
5.3.x |
|
Pinecone/Qdrant |
向量数据库(存储结构化知识 + 类似性检索) |
最新稳定版(云服务 / 本地部署) |
|
Spring Data JPA |
存储文档元数据(上传人、时间、标签) |
3.2.x |
|
Redis |
缓存对话上下文、临时文件路径 |
7.0+ |
|
Hutool |
工具类(文件操作、字符串处理) |
5.8.x |
|
Swagger/knife4j |
接口文档(便于测试上传 / 问答接口) |
最新稳定版 |

三、核心架构设计
基于 LangGraph4j 的「状态机 + 条件分支」模式,拆解为 6 个核心模块,流程分为「文档入库流程」和「问答流程」两大分支:
1. 状态定义(State)
全局状态类需同时支持「文档处理」和「问答」两大流程,存储中间结果和上下文:
import lombok.Data;
import java.util.List;
import java.util.Map;
@Data
public class KnowledgeState {
// 通用字段
private String processType; // 流程类型:DOC_UPLOAD(文档上传)/ QA(问答)
private String status; // 状态:PROCESSING/COMPLETED/FAILED
private String errorMsg;
// 文档上传相关字段
private List<String> filePaths; // 上传文件的临时路径
private List<String> documentTexts; // 解析后的文档文本(按文件分)
private List<StructuredKnowledge> structuredKnowledges; // 结构化知识
private String documentId; // 入库后的文档ID(关联元数据)
// 问答相关字段
private String userId; // 用户ID(关联对话上下文)
private String userQuestion; // 当前用户问题
private List<String> historyQuestions; // 历史问题(上下文记忆)
private List<String> historyAnswers; // 历史回答(上下文记忆)
private String retrievedKnowledge; // 从向量库检索到的相关知识
private String finalAnswer; // 最终回答(含引用来源)
}
// 结构化知识实体(存储到向量库)
@Data
public class StructuredKnowledge {
private String id; // 唯一标识
private String content; // 知识内容(如“API调用限制:单用户日调用1000次”)
private String source; // 来源(如“产品手册v2.0.pdf第3页”)
private String type; // 类型(FAQ/ENTITY/KEY_POINT)
private Map<String, String> metadata; // 元数据(标签、创建时间等)
private float[] embedding; // 向量嵌入(由Spring AI生成)
}
2. 工具封装(Tools)
基于 Spring AI 的 Tool 接口,封装文档解析、知识提取、向量存储、类似性检索等核心能力:
(1)文档解析工具(多格式 + OCR)
import org.apache.tika.Tika;
import org.apache.tika.metadata.Metadata;
import org.springframework.stereotype.Component;
import java.io.File;
@Component
public class DocumentParserTool implements Tool {
private final Tika tika = new Tika();
private final TesseractOCRService ocrService; // 自定义OCR服务(封装Tesseract)
public DocumentParserTool(TesseractOCRService ocrService) {
this.ocrService = ocrService;
}
@Override
public String getName() {
return "document_parser_tool";
}
@Override
public String getDescription() {
return "解析PDF/Word/Markdown等格式文件,提取文本内容(含图片OCR识别)";
}
@Override
public Object call(Object input) {
List<String> filePaths = (List<String>) input;
return filePaths.stream().map(filePath -> {
try {
File file = new File(filePath);
Metadata metadata = new Metadata();
// 用Tika解析文本(自动识别文件格式)
String text = tika.parseToString(file, metadata);
// 若文本为空(可能是纯图片文档),调用OCR提取
if (text.trim().isEmpty()) {
text = ocrService.extractTextFromImageFile(file);
}
return text;
} catch (Exception e) {
throw new RuntimeException("解析文件失败:" + filePath, e);
}
}).toList();
}
}
(2)知识结构化提取工具(基于大模型)
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.stereotype.Component;
@Component
public class KnowledgeExtractionTool implements Tool {
private final ChatClient chatClient;
private final EmbeddingClient embeddingClient; // Spring AI向量嵌入客户端(如OpenAI Embedding)
public KnowledgeExtractionTool(ChatClient chatClient, EmbeddingClient embeddingClient) {
this.chatClient = chatClient;
this.embeddingClient = embeddingClient;
}
@Override
public String getName() {
return "knowledge_extraction_tool";
}
@Override
public String getDescription() {
return "将非结构化文本转化为结构化知识(FAQ/实体/关键要点),并生成向量嵌入";
}
@Override
public Object call(Object input) {
Map<String, Object> params = (Map<String, Object>) input;
String documentText = (String) params.get("text");
String source = (String) params.get("source"); // 文档来源(如文件名+页码)
// 构造提示词,让大模型生成结构化知识
String prompt = PromptTemplate.create("""
请从以下文本中提取结构化知识,格式要求:
1. 类型:FAQ(问题+答案)、KEY_POINT(关键要点)、ENTITY(实体关系)
2. 每个知识项需包含:content(内容)、type(类型)、source(来源)
3. 内容简洁准确,避免冗余
文本:{text}
来源:{source}
返回JSON数组,无需其他说明
""").render(Map.of("text", documentText, "source", source));
// 调用大模型获取结构化知识JSON
String jsonResult = chatClient.prompt(prompt).call().content();
List<StructuredKnowledge> knowledges = new ObjectMapper().readValue(jsonResult,
new TypeReference<List<StructuredKnowledge>>() {});
// 为每个知识项生成向量嵌入(用于后续检索)
knowledges.forEach(knowledge -> {
float[] embedding = embeddingClient.embed(knowledge.getContent()).getResult();
knowledge.setEmbedding(embedding);
});
return knowledges;
}
}
(3)其他核心工具
- 向量库存储工具:将结构化知识存入 Pinecone/Qdrant,支持增量更新(去重);
- 类似性检索工具:根据用户问题生成向量,从向量库检索 Top5 相关知识;
- 问答生成工具:结合历史上下文、检索到的知识,生成带来源引用的回答;
- 上下文管理工具:从 Redis 读取 / 存储用户对话历史,支持多轮追问。
3. 智能体角色定义(Agents)
每个角色对应一个流程节点,通过 LangGraph4j 的 Node 接口实现:
import dev.langchain4j.langgraph.Node;
// 1. 文档上传入口智能体(判断流程类型,分支流转)
public class EntryAgent implements Node<KnowledgeState> {
@Override
public KnowledgeState execute(KnowledgeState state) {
// 根据processType分支:文档上传→解析工具,问答→检索工具
if ("DOC_UPLOAD".equals(state.getProcessType())) {
state.setStatus("DOC_PARSING");
} else if ("QA".equals(state.getProcessType())) {
state.setStatus("KNOWLEDGE_RETRIEVING");
} else {
state.setStatus("FAILED");
state.setErrorMsg("未知流程类型");
}
return state;
}
}
// 2. 文档解析智能体(调用文档解析工具)
public class DocumentParseAgent implements Node<KnowledgeState> {
private final DocumentParserTool parserTool;
public DocumentParseAgent(DocumentParserTool parserTool) {
this.parserTool = parserTool;
}
@Override
public KnowledgeState execute(KnowledgeState state) {
if (!"DOC_PARSING".equals(state.getStatus())) return state;
try {
List<String> documentTexts = (List<String>) parserTool.call(state.getFilePaths());
state.setDocumentTexts(documentTexts);
state.setStatus("KNOWLEDGE_EXTRACTING");
} catch (Exception e) {
state.setStatus("FAILED");
state.setErrorMsg("文档解析失败:" + e.getMessage());
}
return state;
}
}
// 3. 知识提取智能体、4. 向量库存储智能体、5. 知识检索智能体、6. 问答生成智能体(类似实现)
4. 流程编排(LangGraph4j 分支流程图)
通过 LangGraph4j 定义「文档入库」和「问答」两大分支流程,支持条件流转:
import dev.langchain4j.langgraph.Graph;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class KnowledgeGraphConfig {
// 注入所有智能体和工具
private final EntryAgent entryAgent;
private final DocumentParseAgent parseAgent;
private final KnowledgeExtractionAgent extractionAgent;
private final VectorStoreAgent vectorStoreAgent;
private final KnowledgeRetrievalAgent retrievalAgent;
private final QAGenerationAgent qaAgent;
// 构造函数注入(Spring自动装配)
public KnowledgeGraphConfig(EntryAgent entryAgent, DocumentParseAgent parseAgent,
KnowledgeExtractionAgent extractionAgent, VectorStoreAgent vectorStoreAgent,
KnowledgeRetrievalAgent retrievalAgent, QAGenerationAgent qaAgent) {
this.entryAgent = entryAgent;
this.parseAgent = parseAgent;
this.extractionAgent = extractionAgent;
this.vectorStoreAgent = vectorStoreAgent;
this.retrievalAgent = retrievalAgent;
this.qaAgent = qaAgent;
}
@Bean
public Graph<KnowledgeState> knowledgeGraph() {
// 1. 创建图,起始节点为入口智能体(分支判断)
Graph<KnowledgeState> graph = Graph.builder(KnowledgeState.class)
.startWith(entryAgent)
.build();
// 2. 入口→文档解析(流程类型为DOC_UPLOAD)
graph.addEdge(entryAgent, state ->
"DOC_PARSING".equals(state.getStatus()) ? parseAgent : retrievalAgent);
// 3. 文档解析→知识提取→向量库存储→结束(文档入库流程)
graph.addEdge(parseAgent, state ->
"KNOWLEDGE_EXTRACTING".equals(state.getStatus()) ? extractionAgent : Graph.END);
graph.addEdge(extractionAgent, state ->
"VECTOR_STORING".equals(state.getStatus()) ? vectorStoreAgent : Graph.END);
graph.addEdge(vectorStoreAgent, Graph.END);
// 4. 入口→知识检索→问答生成→结束(问答流程)
graph.addEdge(retrievalAgent, state ->
"QA_GENERATING".equals(state.getStatus()) ? qaAgent : Graph.END);
graph.addEdge(qaAgent, Graph.END);
// 5. 所有节点失败时直接结束
graph.addEdge(parseAgent, state -> "FAILED".equals(state.getStatus()) ? Graph.END : null);
graph.addEdge(retrievalAgent, state -> "FAILED".equals(state.getStatus()) ? Graph.END : null);
return graph;
}
}
5. 接口暴露(Spring Web)
提供文档上传、智能问答、对话上下文清空等 HTTP 接口:
import dev.langchain4j.langgraph.Graph;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/api/knowledge")
public class KnowledgeController {
private final Graph<KnowledgeState> knowledgeGraph;
private final FileStorageService fileStorageService; // 自定义文件存储服务(临时存储上传文件)
private final RedisTemplate<String, Object> redisTemplate;
public KnowledgeController(Graph<KnowledgeState> knowledgeGraph, FileStorageService fileStorageService,
RedisTemplate<String, Object> redisTemplate) {
this.knowledgeGraph = knowledgeGraph;
this.fileStorageService = fileStorageService;
this.redisTemplate = redisTemplate;
}
// 1. 文档上传接口(支持多文件)
@PostMapping("/upload-document")
public ResponseEntity<ApiResponse> uploadDocument(
@RequestParam("files") MultipartFile[] files,
@RequestParam("userId") String userId) {
try {
// 保存上传文件到临时目录,获取文件路径
List<String> filePaths = Arrays.stream(files)
.map(fileStorageService::saveTempFile)
.toList();
// 初始化状态
KnowledgeState initialState = new KnowledgeState();
initialState.setProcessType("DOC_UPLOAD");
initialState.setFilePaths(filePaths);
initialState.setStatus("STARTED");
// 执行文档入库流程
KnowledgeState finalState = knowledgeGraph.execute(initialState);
if ("COMPLETED".equals(finalState.getStatus())) {
return ResponseEntity.ok(new ApiResponse("success", "文档入库成功", finalState.getDocumentId()));
} else {
return ResponseEntity.badRequest().body(new ApiResponse("failed", finalState.getErrorMsg(), null));
}
} catch (Exception e) {
return ResponseEntity.internalServerError().body(new ApiResponse("failed", "上传失败:" + e.getMessage(), null));
}
}
// 2. 智能问答接口(支持多轮对话)
@PostMapping("/qa")
public ResponseEntity<QAAnswerResponse> qa(
@RequestBody QAQuestionRequest request) {
String userId = request.getUserId();
String userQuestion = request.getUserQuestion();
// 从Redis获取用户历史对话上下文
List<String> historyQuestions = getHistory(userId, "questions");
List<String> historyAnswers = getHistory(userId, "answers");
// 初始化状态
KnowledgeState initialState = new KnowledgeState();
initialState.setProcessType("QA");
initialState.setUserId(userId);
initialState.setUserQuestion(userQuestion);
initialState.setHistoryQuestions(historyQuestions);
initialState.setHistoryAnswers(historyAnswers);
initialState.setStatus("STARTED");
// 执行问答流程
KnowledgeState finalState = knowledgeGraph.execute(initialState);
if ("COMPLETED".equals(finalState.getStatus())) {
// 更新Redis中的对话历史
updateHistory(userId, "questions", userQuestion);
updateHistory(userId, "answers", finalState.getFinalAnswer());
return ResponseEntity.ok(new QAAnswerResponse(
"success",
finalState.getFinalAnswer(),
finalState.getRetrievedKnowledge() // 返回引用来源
));
} else {
return ResponseEntity.badRequest().body(new QAAnswerResponse(
"failed",
finalState.getErrorMsg(),
null
));
}
}
// 辅助方法:获取/更新Redis对话历史
private List<String> getHistory(String userId, String key) {
String redisKey = "knowledge:history:" + userId + ":" + key;
return Optional.ofNullable(redisTemplate.opsForList().range(redisKey, 0, -1))
.map(list -> list.stream().map(String::valueOf).toList())
.orElse(List.of());
}
private void updateHistory(String userId, String key, String content) {
String redisKey = "knowledge:history:" + userId + ":" + key;
redisTemplate.opsForList().rightPush(redisKey, content);
redisTemplate.expire(redisKey, Duration.ofDays(7)); // 保留7天历史
}
// 请求/响应DTO
@Data
static class QAQuestionRequest {
private String userId;
private String userQuestion;
}
@Data
static class QAAnswerResponse {
private String code;
private String answer;
private String source; // 引用来源(如“产品手册v2.0.pdf第3页”)
}
}
四、关键特性与扩展点
1. 核心特性
- 多格式文档支持:通过 Apache Tika + OCR 覆盖 PDF/Word/Markdown/ 图片文档;
- 结构化知识存储:将非结构化文本转化为 FAQ / 实体 / 要点,向量库存储支持高效检索;
- 上下文记忆:Redis 缓存对话历史,支持多轮追问(如 “这个 API 限制是否可申请提升?”);
- 分支流程编排:LangGraph4j 实现 “文档入库” 和 “问答” 两大流程分支,逻辑清晰;
- 来源可追溯:回答中附带知识来源,提升可信度(企业场景关键需求)。
2. 扩展方向
- 知识去重与更新:新增文档时,先检索向量库,避免重复存储一样知识;
- 权限控制:按用户角色限制文档访问权限(如普通员工看不到核心技术文档);
- 批量导入导出:支持知识库批量导出(Excel/PDF)、批量导入(文件夹上传);
- 多语言支持:添加翻译工具,支持中英文文档混合问答;
- 知识可视化:集成 Neo4j 存储实体关系,前端展示知识图谱(如 “产品 X→API→调用限制”);
- 异步处理:大文件(如几百页 PDF)解析改为异步任务,通过 WebSocket 推送进度。
五、环境配置要点
- Spring AI 配置(application.yml):
spring:
ai:
openai:
api-key: sk-xxx
base-url: https://api.openai.com/v1
chat:
model: gpt-4o-mini
embedding:
model: text-embedding-3-small # 向量嵌入模型
servlet:
multipart:
max-file-size: 100MB # 支持大文件上传
max-request-size: 100MB
- 向量库配置(以 Pinecone 为例):
pinecone:
api-key: pcsk-xxx
environment: us-west1-gcp
index-name: knowledge-base-index
- OCR 配置:
tesseract:
path: /usr/bin/tesseract # Tesseract安装路径(Windows为exe路径)
data-path: /usr/share/tesseract-ocr/5/tessdata # 语言包路径

六、运行流程演示
流程 1:文档入库
- 用户上传文件:POST /api/knowledge/upload-document,参数为 2 个文件(产品手册v2.0.pdf、API文档.md);
- 流程执行:入口智能体→文档解析(提取文本 + OCR)→知识提取(生成结构化 FAQ)→向量库存储;
- 响应结果:返回文档 ID,知识库新增 10 条结构化知识(如 “API 调用限制:单用户日调用 1000 次”)。
流程 2:智能问答
- 用户提问:POST /api/knowledge/qa,Body 为{“userId”:”user123″,”userQuestion”:”产品X的API日调用限制是多少?”};
- 流程执行:入口智能体→知识检索(向量库匹配相关知识)→问答生成(结合上下文生成回答);
- 响应结果:
{
"code": "success",
"answer": "产品X的API单用户日调用限制为1000次",
"source": "产品手册v2.0.pdf第3页、API文档.md第5节"
}
- 多轮追问:用户再提问{“userId”:”user123″,”userQuestion”:”企业版是否有更高限制?”},智能体结合历史问题,返回 “企业版用户 API 日调用限制为 10000 次,需联系客户经理开通”。
七、案例优势
- 业务价值明确:直接解决企业 “文档多、查询难” 的痛点,可落地为内部知识库、客户协助中心等产品;
- 技术覆盖全面:涵盖文档解析、OCR、向量嵌入、向量检索、多轮对话等 AI 应用核心技术;
- LangGraph4j 深度应用:体现「条件分支流转」(文档 / 问答双流程)、「状态存储」(上下文 + 中间结果)、「流程可扩展」(新增分支如 “知识导出”);
- 企业级适配:支持大文件、权限控制、来源追溯,符合企业生产环境需求。
该案例适合作为 Spring AI + LangGraph4j 的进阶实战,既覆盖技术难点,又贴近实际业务,可在此基础上快速迭代为商用知识库产品。

收藏了,感谢分享
创作不易,您的关注点赞,是我坚持的动力。
创作不易,您的关注+点赞,是我坚持创作的动力
Spring AI + LangGraph4j开发智能文档问答与知识管理智能体