2026/2/7 2:02:56
网站建设
项目流程
张掖做网站公司,wordpress进不到后台,网页培训机构,wordpress虚拟资源源码一、引言
随着大语言模型的快速发展#xff0c;RAG#xff08;Retrieval-Augmented Generation#xff09;技术已成为构建知识库问答系统的核心技术之一。本文将带领大家从零开始#xff0c;使用Spring AI框架构建一个支持文档上传的知识库问答机器人#xff0c;帮助大家…一、引言随着大语言模型的快速发展RAGRetrieval-Augmented Generation技术已成为构建知识库问答系统的核心技术之一。本文将带领大家从零开始使用Spring AI框架构建一个支持文档上传的知识库问答机器人帮助大家深入理解RAG技术的核心原理和实践应用。1.1 什么是RAGRAG检索增强生成是一种结合了信息检索和文本生成的技术。它的基本工作流程是用户提出问题系统从知识库中检索相关信息大语言模型基于检索到的信息生成答案从系统设计角度触发RAG 的核心作用可以被描述为在LLM调用生成响应之前由系统动态构造一个“最小且相关的知识上下文”。请注意两个关键词动态每次问题都不同检索的知识也不同比如用户问 A 产品时找 A 的文档问 B 产品时找 B 的文档最小只注入必要信息比如用户问 “A 产品的定价”就只塞定价相关的片段而非整份产品手册RAG可以有效的弥补上下文窗口的先天不足不再需要把所有知识塞进窗口而是只在需要时 “临时调取” 相关部分既避免了窗口溢出又减少了注意力竞争。1.2 RAG在交互链路中的位置接下来我们以RAG的经典应用场景——企业知识库为例来看一下RAG在这个流程中所处的位置在这个结构中RAG主要就是在用户提问与向LLM发起请求这个中间段用于检索关联的文档构建上下文1.3 RAG工作原理我们以一张图来介绍RAG的工作原理具体的RAG详细介绍请参照文末引用二、核心实现2.1 项目结构概览项目源码可以在 https://github.com/liuyueyi/spring-ai-demo[1] 获取文末有所有相关的参考信息D05-rag-qa-bot/├── src/main/java/com/git/hui/springai/app/│ ├── D05Application.java # 启动类│ ├── mvc/│ │ ├── QaApiController.java # API控制器│ │ └── QaController.java # 页面控制器│ ├── qa/QaBoltService.java # 问答服务│ └── vectorstore/│ ├── DocumentChunker.java # 文档分块工具│ ├── DocumentQuantizer.java # 文档量化器│ └── TextBasedVectorStore.java # 文本向量存储├── src/main/resources/│ ├── application.yml # 配置文件│ ├── prompts/qa-prompts.pt # 提示词模板│ └── templates/chat.html # 前端页面└── pom.xml # 依赖配置2.2 项目初始化2.2.1 Maven依赖配置首先我们需要在pom.xml中配置必要的依赖其中关于向量数据库、tika的文档解析属于核心依赖项hanlp适用于无法直接使用EmbeddingModel的场景在我们的示例中会实现一个基础的文档向量化方案其中会采用Hanlp来做中文分词使用智谱的免费大模型来体验我们的RAG知识库问答当然也可以基于OpenAI-Starter来切换其他的大模型使用层面并没有改变只需要替换依赖、api配置即可dependencies !-- 向量数据库 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-advisors-vector-store/artifactId /dependency !-- 文档提取使用apache-tika来实现 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-tika-document-reader/artifactId /dependency !-- pdf文档提取实际也可以用上面的tika来实现 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-pdf-document-reader/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-rag/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 使用智谱大模型 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-zhipuai/artifactId /dependency !-- 用于前端页面的支持 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency !-- 中文分词用在文档向量化 -- dependency groupIdcom.hankcs/groupId artifactIdhanlp/artifactId versionportable-1.8.4/version /dependency/dependencies这里我们引入了Spring AI的核心依赖以及用于文档处理的Tika和PDF读取器还特别加入了HanLP中文分词库来优化中文处理效果。2.2.2 应用配置在application.yml中配置API密钥和相关参数spring: ai: zhipuai: api-key:${zhipuai-api-key} chat: options: model:GLM-4-Flash temperature:0.1thymeleaf: cache:falseservlet: multipart: max-file-size:10MB max-request-size:50MBlogging:level: org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor:debug org.springframework.ai.chat.client:DEBUGserver:port:80802.3 自定义向量存储实现通常RAG会使用一些成熟的向量数据库如Pinecone、weaviate、qdrant、milvus或者es、redis等但是考虑到安装、环境配置等成本我们接下来会实现一个基础的自定义的文本向量库TextBasedVectorStore基于内存实现无需额外的外部依赖单纯的用来体验RAG并没有太大问题SpringAI原生提供了一个基于内存的向量数据库SimpleVectorStore在它的实现中向量数据写入依赖向量模型因此如果有额度使用大模型厂家提供的EmbeddingModel时直接用它进行测试即可当然如果你现在并没有渠道()使用向量模型的那也没关系接下来我们将参照SpringAI的SimpleVectorStore实现的一个自定义的向量库TextBasedVectorStore提供一套不依赖向量模型的解决方案特别适合快速原型开发核心实现如下当然你也完全可以忽略它它不是我们的重点2.3.1 TextBasedVectorStore - 文本匹配向量存储在下面的实现中重点体现了两个方法doAdd: 将文档保存到向量数据库中文档分片 - 向量化 - 存储doSimilaritySearch: 基于相似度的搜索需要注意一点文档的向量化与搜索时传入文本的向量化需要采用同一套向量化方案why?public classTextBasedVectorStoreextendsAbstractObservationVectorStore { Getter protected MapString, SimpleVectorStoreContent store newConcurrentHashMap(); /** * 已经存储到向量库的document用于幂等 */ private SetString persistMd5 newCopyOnWriteArraySet(); /** * 添加文档到向量数据库 * * param documents */ Override publicvoiddoAdd(ListDocument documents) { if (CollectionUtils.isEmpty(documents)) { return; } // 创建一个新的可变列表副本 ListDocument mutableDocuments newArrayList(); for (Document document : documents) { // 过滤掉重复的文档避免二次写入浪费空间 if (!persistMd5.contains((String) document.getMetadata().get(md5))) { mutableDocuments.add(document); } } if (CollectionUtils.isEmpty(mutableDocuments)) { return; } // 文档分片 ListDocument chunkers DocumentChunker.DEFAULT_CHUNKER.chunkDocuments(mutableDocuments); // 存储本地向量库 chunkers.forEach(document - { float[] embedding DocumentQuantizer.quantizeDocument(document); if (embedding.length 0) { return; } SimpleVectorStoreContentstoreContentnewSimpleVectorStoreContent( document.getId(), document.getText(), document.getMetadata(), embedding ); this.store.put(document.getId(), storeContent); }); mutableDocuments.forEach(document - persistMd5.add((String) document.getMetadata().get(md5))); } /** * 搜索向量数据库根据相似度返回相关文档 * * param request * return */ Override public ListDocument doSimilaritySearch(SearchRequest request) { PredicateSimpleVectorStoreContent documentFilterPredicate this.doFilterPredicate(request); finalfloat[] userQueryEmbedding this.getUserQueryEmbedding(request.getQuery()); returnthis.store.values().stream() .filter(documentFilterPredicate) .map((content) - content.toDocument( DocumentQuantizer.calculateCosineSimilarity(userQueryEmbedding, content.getEmbedding()) )) .filter((document) - document.getScore() request.getSimilarityThreshold()) .sorted(Comparator.comparing(Document::getScore).reversed()) .limit((long) request.getTopK()) .toList(); } privatefloat[] getUserQueryEmbedding(String query) { return DocumentQuantizer.quantizeQuery(query); }}2.3.2 DocumentChunker - 文档分块器合理地将长文档分块是RAG系统的关键环节合理的分块大小可以有效的增加检索效率、提高准确率、减少上下文长度在真实的RAG应用中这一块具体的方案挺多的比如固定尺寸下面的方案、地柜拆分、语义拆分、结构化拆分如结构化的markdown文档就很适合、延迟拆分、自适应拆分、层级拆分、LLM驱动拆分、智能体拆分等具体这一块我也没有深入学习有兴趣的小伙伴问下AI吧~public classDocumentChunker { privatefinalint maxChunkSize; privatefinalint overlapSize; publicDocumentChunker() { this(500, 50); // 默认值最大块大小500个字符重叠50个字符 } public ListDocument chunkDocument(Document document) { Stringcontent document.getText(); if (content null || content.trim().isEmpty()) { return List.of(document); } ListString chunks splitText(content); ListDocument chunkedDocuments newArrayList(); for (inti0; i chunks.size(); i) { Stringchunk chunks.get(i); StringchunkId document.getId() _chunk_ i; DocumentchunkDocnewDocument(chunkId, chunk, newHashMap(document.getMetadata())); chunkDoc.getMetadata().put(chunk_index, i); chunkDoc.getMetadata().put(total_chunks, chunks.size()); chunkDoc.getMetadata().put(original_document_id, document.getId()); chunkedDocuments.add(chunkDoc); } return chunkedDocuments; } private ListString splitText(String text) { ListString chunks newArrayList(); // 按多种分隔符分割优先在语义边界处分割 String[] sentences text.split((?。)|(?)|(?!)|(?)|(?\\?)|(?\\n\\n)); StringBuildercurrentChunknewStringBuilder(); for (String sentence : sentences) { if (sentence.trim().isEmpty()) { continue; // 跳过空句子 } if (currentChunk.length() sentence.length() maxChunkSize) { // 如果当前块加上新句子不超过最大大小就添加到当前块 if (currentChunk.length() 0) { currentChunk.append(sentence); } else { currentChunk.append(sentence); } } else { // 如果当前块为空但是单个句子太长需要强制分割 if (currentChunk.length() 0) { ListString subChunks forceSplit(sentence, maxChunkSize); for (inti0; i subChunks.size(); i) { StringsubChunk subChunks.get(i); if (i subChunks.size() - 1) { chunks.add(subChunk); } else { currentChunk.append(subChunk); } } } else { chunks.add(currentChunk.toString()); currentChunk newStringBuilder(); // 添加重叠部分如果句子长度大于重叠大小则只取末尾部分 if (sentence.length() overlapSize) { Stringoverlap sentence.substring(Math.max(0, sentence.length() - overlapSize)); currentChunk.append(overlap); currentChunk.append(sentence); } else { currentChunk.append(sentence); } } } } if (currentChunk.length() 0) { chunks.add(currentChunk.toString()); } return chunks; }}2.3.3 DocumentQuantizer - 文档量化器使用HanLP进行中文分词实现了一个简单的文档向量化工具类同样的你也完全可以忽略它的具体实现因为它的效果显然比使用EmbedingModel要差很多很多但用于学习体验RAG也基本够用public classDocumentQuantizer { privatestaticfinalSegmentSEGMENT HanLP.newSegment(); publicstaticfloat[] quantizeText(String text) { if (text null || text.trim().isEmpty()) { returnnewfloat[0]; } String[] words preprocessText(text); MapString, Integer wordFreq countWordFrequency(words); // 生成固定长度的向量表示这里使用前128个高频词 return generateFixedLengthVector(wordFreq, 128); } /** * 将文本转换为数值向量表示简化版 * 使用TF-IDF的基本思想但简化为词频统计 * * param text 输入文本 * return 数值向量 */ privatestatic String[] preprocessText(String text) { ListTerm termList SEGMENT.seg(text); return termList.stream() .filter(term - !isStopWord(term.word)) // 过滤停用词 .filter(term - !term.nature.toString().startsWith(w)) // 过滤标点符号 .map(term - term.word.toLowerCase()) // 转换为小写 .toArray(String[]::new); } /** * 生成固定长度的向量表示 * * param wordFreq 词频映射 * param length 向量长度 * return 固定长度的向量 */ privatestaticfloat[] generateFixedLengthVector(MapString, Integer wordFreq, int length) { float[] vector newfloat[length]; // 获取频率最高的词汇 ListMap.EntryString, Integer sortedEntries wordFreq.entrySet() .stream() .sorted(Map.Entry.String, IntegercomparingByValue().reversed()) .limit(length) .collect(Collectors.toList()); // 将词频填入向量 for (inti0; i Math.min(sortedEntries.size(), length); i) { vector[i] sortedEntries.get(i).getValue(); } return vector; } publicstaticdoublecalculateCosineSimilarity(float[] vectorA, float[] vectorB) { if (vectorA null || vectorB null || vectorA.length 0 || vectorB.length 0) { return0.0; } intminLength Math.min(vectorA.length, vectorB.length); float[] adjustedA Arrays.copyOf(vectorA, minLength); float[] adjustedB Arrays.copyOf(vectorB, minLength); doubledotProduct0.0; doublenormA0.0; doublenormB0.0; for (inti0; i minLength; i) { dotProduct adjustedA[i] * adjustedB[i]; normA Math.pow(adjustedA[i], 2); normB Math.pow(adjustedB[i], 2); } normA Math.sqrt(normA); normB Math.sqrt(normB); if (normA 0 || normB 0) { return0.0; } return dotProduct / (normA * normB); }}2.3.4 注册向量库接下来就是注册使用这个向量库在配置类or启动类中添加下面这个声明即可Beanpublic VectorStore vectorStore() { return TextBasedVectorStore.builder().build();}2.4 SpringAI向量存储上面2.3适用于无法直接使用大模型厂家的向量模型的场景如果可以直接使用那么上面的全部可以直接忽略掉直接使用下面的方式进行声明向量库即可Beanpublic VectorStore vectorStore(EmbeddingModel embeddingModel) { return SimpleVectorStore.builder(embeddingModel).build();}2.5 问答服务实现接下来我们进入核心的基于RAG的QA问答机器人的实现2.5.1 QaBoltService - 核心问答服务Pre. 问答服务流程我们先从时序的角度来看一下这个问答服务的核心交互流程在这个时序过程中为了简化大家的理解我们将文档的向量化存储与问答进行了拆分第一步文档向量化这一部分包含RAG应用数据准备阶段的完整过程数据提取文本分割向量化第二步问答应用层响应用户提问从向量数据库检索相似度高的文档信息注入提示词访问大模型获取答案Impl. 核心实现接下来我们看一下具体的实现上面的步骤分割得很清楚但是实际使用时用户可以在问答中上传附件这个附件也会作为我们知识库的一部分因此具体的实现中你会发现这两部耦合在一起了请不要惊讶step1: 初始化ChatClient在开始之前我们首先参照SpringAI的官方教程通过Advisor来初始化支持RAG的ChatClient官方文档https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html[2]ServicepublicclassQaBoltService { privatefinal ChatClient chatClient; privatefinal ChatMemory chatMemory; privatefinal VectorStore vectorStore; Value(classpath:/prompts/qa-prompts.pt) private Resource boltPrompts; publicQaBoltService(ChatClient.Builder builder, VectorStore vectorStore, ChatMemory chatMemory) { this.vectorStore vectorStore; this.chatMemory chatMemory; this.chatClient builder.defaultAdvisors( newSimpleLoggerAdvisor(ModelOptionsUtils::toJsonStringPrettyPrinter, ModelOptionsUtils::toJsonStringPrettyPrinter, 0), // 用于支持多轮对话 MessageChatMemoryAdvisor.builder(chatMemory).build(), // 用于支持RAG RetrievalAugmentationAdvisor.builder() .queryTransformers( // 使用大型语言模型重写用户查询以便在查询目标系统时提供更好的结果。 RewriteQueryTransformer.builder().chatClientBuilder(builder.build().mutate()).build() ) .queryAugmenter( // ContextualQueryAugmenter 使用来自所提供文档内容的上下文数据来增强用户查询。 // 默认不支持上下文为空的场景出现之后大模型会不返回用户查询这里调整为支持为空 ContextualQueryAugmenter.builder().allowEmptyContext(true).build() ) .documentRetriever( VectorStoreDocumentRetriever.builder() .similarityThreshold(0.50) .vectorStore(vectorStore) .build() ) .build() ).build(); }}接下来就是响应问答的实现这里分两步step2: 文档处理处理用户上传的附件即上面时序图中的第一步解析文档、切分、向量化、保存到向量库;下面的实现中主要体现的是基于SpringAI封装的tika与pdf文档解析starter来提取上传的文档生成供向量数据库使用的ListDocument; 而具体的文档切分、向量化等则是在上面的TextBasedVectorStore实现注为了一个文档重复进行数据处理我们在元数据中维护了文档的 md5这样当添加到向量库中时就可以基于这个md5来进行去重了一个工程化实现方面的小技巧~private ProceedInfo processFiles(String chatId, CollectionMultipartFile files) { StringBuildercontextnewStringBuilder(\n\n); ListMedia mediaList newArrayList(); files.forEach(file - { try { vardatanewByteArrayResource(file.getBytes()); varmd5 calculateHash(chatId, file.getBytes()); MimeTypemime MimeType.valueOf(file.getContentType()); if (mime.equalsTypeAndSubtype(MediaType.APPLICATION_PDF)) { PagePdfDocumentReaderpdfReadernewPagePdfDocumentReader(data, PdfDocumentReaderConfig.builder() .withPageTopMargin(0) .withPageExtractedTextFormatter(ExtractedTextFormatter.builder() .withNumberOfTopTextLinesToDelete(0) .build()) .withPagesPerDocument(1) .build()); ListDocument documents pdfReader.read(); documents.forEach(document - { document.getMetadata().put(md5, md5); if (document.getMetadata().containsKey(file_name) document.getMetadata().get(file_name) null) { document.getMetadata().put(file_name, file.getName()); } }); vectorStore.add(documents); varcontent String.join(\n, documents.stream().map(Document::getText).toList()); context.append(String.format(ATTACHMENT_TEMPLATE, file.getName(), content)); } elseif (text.equalsIgnoreCase(mime.getType())) { ListDocument documents newTikaDocumentReader(data).read(); documents.forEach(document - document.getMetadata().put(md5, md5)); vectorStore.add(documents); varcontent String.join(\n, documents.stream().map(Document::getText).toList()); context.append(String.format(ATTACHMENT_TEMPLATE, file.getName(), content)); } } catch (IOException e) { thrownewRuntimeException(e); } }); returnnewProceedInfo(context.toString(), mediaList);}step3: 问答实现然后就是具体的问答实现这里主要是借助QuestionAnswerAdvisor来封装RAG相关的信息说明在下面的实现中使用了自定义的提示词模板当然也可以直接使用SpringAI默认的方案public FluxString ask(String chatId, String question, CollectionMultipartFile files) { processFiles(chatId, files); // 自定义的提示词模板替换默认的检索参考资料的提示词模板 // 其中 query 对应的是用户的提问 question // question_answer_context 对应的是增强检索的document即检索到的参考资料 PromptTemplatecustomPromptTemplate PromptTemplate.builder() .renderer(StTemplateRenderer.builder().startDelimiterToken().endDelimiterToken().build()) .template( query Context information is below. --------------------- question_answer_context --------------------- Given the context information and no prior knowledge, answer the query. Follow these rules: 1. If the answer is not in the context, just say that you dont know. 2. Avoid statements like Based on the context... or The provided information.... ).build(); varqaAdvisor QuestionAnswerAdvisor.builder(vectorStore) .searchRequest(SearchRequest.builder().similarityThreshold(0.5d).topK(3).build()) .promptTemplate(customPromptTemplate) .build(); varrequestSpec chatClient.prompt() .system(boltPrompts) .user(question) .advisors(qaAdvisor) .advisors(a - a.param(ChatMemory.CONVERSATION_ID, chatId)); return requestSpec.stream().content().map(s - s.replaceAll(\n, br/));}到这里一个基于RAG的问答机器人的核心逻辑已经全部完成接下来我们进入体验阶段2.5.2 控制器实现QaApiController - API控制器RestControllerRequestMapping(/api)publicclassQaApiController { Autowired private QaBoltService qaBolt; GetMapping(path /chat/{chatId}, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxString qaGet(PathVariable(chatId) String chatId, RequestParam(question) String question) { return qaBolt.ask(chatId, question, Collections.emptyList()); } PostMapping(path /chat/{chatId}, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxString qaPost(PathVariable(chatId) String chatId, RequestParam(question) String question, RequestParam(value files, required false) CollectionMultipartFile files) { if (files null) { files Collections.emptyList(); } return qaBolt.ask(chatId, question, files); }}2.6 前端界面交互式聊天界面前端界面提供了文件上传和问答交互功能具体的代码实现请参考文末的项目源码地址这里就不贴了主要是太长了~三、体验与小结3.1 启动类SpringBootApplicationpublicclassD05Application { Bean public VectorStore vectorStore() { return TextBasedVectorStore.builder().build(); } publicstaticvoidmain(String[] args) { SpringApplication.run(D05Application.class, args); System.out.println(启动成功前端测试访问地址 http://localhost:8080/chat); }}3.2 问答提示词在resources/prompts/qa-prompts.pt中维护我们的qa机器人的系统提示词DeepSeek生成的## 角色设定你是一个智能问答助手专门负责根据用户提供的文档内容进行准确的回答和信息提取。## 核心任务- 仔细阅读并理解用户上传的文档内容- 基于文档中的信息回答用户的问题- 提供准确、相关且基于文档的答案- 当问题超出文档范围时明确告知用户该信息未在文档中提及## 工作流程1. 首先分析用户上传的文档提取关键信息2. 理解用户提出的问题3. 在文档中查找与问题相关的信息4. 整合相关信息并形成结构化答案5. 如无法从文档中找到相关信息则说明情况## 回答规范- 严格基于文档内容作答不得编造信息- 引用文档中的具体信息时请保持原文准确性- 如果问题涉及多个知识点在答案中清晰分点说明- 对于不确定的内容应诚实表达不确定性而非猜测- 保持回答简洁明了同时确保信息完整## 注意事项- 不得脱离文档内容进行回答- 遇到模糊或不明确的问题时可以请求用户提供更详细的信息- 如果文档中没有相关内容必须明确告知用户- 保持专业、礼貌的沟通态度3.3 运行与测试启动应用运行D05Application主类访问页面打开http://localhost:8080/chat上传文档选择PDF、Word或文本文件提问测试在输入框中输入关于文档的问题当然在启动时可以在启动参数中指定大模型的ApiKey也可以直接修改applicatino.yml直接维护上apiKey也可以哦在上面这个示意图中我上传的是 像Excel一样选择网页表格 支持选中表单生成sql的神器TableHelper[3] 的宣传文档显然是没有被大模型检索、训练过的从问答来看效果还是不错的但是请注意这个只是给大家用来体验RAG的用来学习验证还是不错的但是真实场景显然比我们提到的内容还多很多比如安全隐私不同用户的知识库文件需要隔离存储优化上下文窗口的管理、对话历史的管理检索优化多路召回、查询语义理解、查询改写等模型优化成本、效率的权衡监控体系链路追踪、日志分析、告警等DevOps: CI/CD容器编排体验、性能等3.4 核心技术要点小结1. RAG工作流程检索阶段当用户提问时系统首先将问题转换为向量然后在文档向量库中查找相似的文档片段生成阶段将检索到的相关文档内容与用户问题一起输入大语言模型生成最终答案2. 文档处理优化中文分词使用HanLP进行精确的中文分词提高语义理解准确性文档分块将长文档合理分块保持语义完整性的同时便于检索去重机制通过MD5哈希避免重复上传相同的文档3. 性能优化相似度计算使用余弦相似度算法计算文本相似度缓存机制对已处理的文档进行缓存避免重复处理流式响应使用SSE实现答案的流式返回提升用户体验本文通过一个最小成本技术和资金成本都很小的方案我们实现了一个完整的RAG知识库问答机器人。通过这个项目相信对RAG感兴趣想快速体验一下完成流程的小伙伴可以有一个动手实操的机会。学AI大模型的正确顺序千万不要搞错了2026年AI风口已来各行各业的AI渗透肉眼可见超多公司要么转型做AI相关产品要么高薪挖AI技术人才机遇直接摆在眼前有往AI方向发展或者本身有后端编程基础的朋友直接冲AI大模型应用开发转岗超合适就算暂时不打算转岗了解大模型、RAG、Prompt、Agent这些热门概念能上手做简单项目也绝对是求职加分王给大家整理了超全最新的AI大模型应用开发学习清单和资料手把手帮你快速入门学习路线:✅大模型基础认知—大模型核心原理、发展历程、主流模型GPT、文心一言等特点解析✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑✅开发基础能力—Python进阶、API接口调用、大模型开发框架LangChain等实操✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经以上6大模块看似清晰好上手实则每个部分都有扎实的核心内容需要吃透我把大模型的学习全流程已经整理好了抓住AI时代风口轻松解锁职业新可能希望大家都能把握机遇实现薪资/职业跃迁这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】