资讯动态

基于SAP CAP与RAG技术构建企业级智能问答系统实战指南

发布时间:2026/8/7 18:00:07 来源:尧图企业网站定制
1. 项目概述当企业级应用遇上生成式AI最近在做一个企业级应用的智能问答功能客户要求能基于他们内部的海量文档PDF、Word、Excel进行精准回答而不是让大模型“自由发挥”。这让我想起了SAP官方在GitHub上开源的那个项目——SAP-samples/btp-cap-genai-rag。这个项目可以说是一个“样板间”它清晰地展示了如何在一个标准的SAP Cloud Application Programming (CAP) 项目中集成生成式AIGenAI和检索增强生成RAG技术来构建一个真正“懂业务”的智能应用。简单来说这个项目解决了一个核心痛点大语言模型LLM虽然知识渊博但它不知道你的企业内部数据。RAG技术就像一个“超级外挂”当用户提问时它先从你的专属知识库比如SAP S/4HANA数据、内部流程文档里找到最相关的信息片段然后把这些片段作为“参考资料”喂给LLM让LLM基于这些“参考资料”来生成答案。这样生成的答案不仅准确、相关还能有效避免“幻觉”即模型编造不存在的信息。这个项目特别适合两类朋友一是正在使用SAP技术栈特别是CAP和BTP的开发者想为现有应用快速添加AI能力二是任何对如何将企业私有数据与GenAI安全、高效结合感兴趣的工程师。它提供了一个端到端的、生产可用的参考实现从数据准备、向量化存储到应用集成的完整链路都给你搭好了。2. 架构与核心组件深度解析2.1 整体架构CAP为骨AI为魂这个项目的骨架是SAP CAP。对于不熟悉的朋友CAP可以理解为一套用于快速构建企业级、云原生应用的服务端框架和约定它帮你处理了很多繁琐的事情比如数据建模CDS、服务暴露OData、数据库集成等。在这个项目中CAP负责构建最传统的CRUD服务和业务逻辑层。而AI能力则是通过一系列BTPBusiness Technology Platform服务和开源组件“注入”到这个骨架中的。整个数据流和工作流可以概括为以下几步文档摄取与预处理用户上传的文档如PDF首先被解析拆分成更小的、语义完整的文本块Chunking。这是RAG效果好坏的第一步块太大则信息不精准块太小则可能丢失上下文。文本向量化与存储每个文本块通过嵌入模型Embedding Model被转换成一个高维向量一组数字。这个向量就像是这段文本的“数学指纹”语义相近的文本其向量在空间中的距离也更近。这些向量被存储到专门的向量数据库Vector Database中以便进行高效的相似性搜索。用户查询与检索当用户提出问题时问题文本同样被向量化。系统在向量数据库中搜索与“问题向量”最相似的几个“文本块向量”从而找到与问题最相关的原始文档片段。提示工程与答案生成将检索到的相关文本片段作为上下文与用户的原始问题一起精心构造一个提示Prompt发送给大语言模型如GPT-4。模型基于这个“问题上下文”的提示生成最终答案。应用集成与返回生成的答案通过CAP构建的OData或REST API返回给前端应用完成一次智能问答。这个架构的精妙之处在于解耦CAP应用无需关心复杂的AI流水线它只需要调用一个服务而AI流水线可以独立演进和优化。项目默认使用了SAP BTP的AI服务但也保留了接入其他云服务如Azure OpenAI或开源模型如本地部署的Llama 2的灵活性。2.2 核心组件选型背后的考量项目中的每一个技术选型都经过了深思熟虑背后是生产环境的严苛要求。1. 向量数据库为什么是PostgreSQL pgvector项目选择了PostgreSQL配合pgvector扩展作为向量存储方案。这看似没有选择专门的向量数据库如Pinecone、Weaviate实则是一个极其务实和强大的选择。运维简化对于已经使用PostgreSQL作为主数据库的CAP应用无需引入新的数据库技术栈降低了运维复杂度和成本。数据一致性向量数据可以和业务数据放在同一个事务中处理保证了数据的一致性避免了分布式事务的麻烦。成熟稳定PostgreSQL是久经考验的关系型数据库pgvector扩展也日益成熟支持多种相似性搜索算法如余弦相似度、L2距离性能足以应对大多数企业场景。成本可控避免了使用专属向量数据库可能产生的额外服务费用。2. 嵌入模型平衡性能、成本与数据主权嵌入模型负责将文本转换为向量。项目示例中可能使用了SAP AI Core提供的嵌入模型或者OpenAI的text-embedding-ada-002。这里的关键考量是私有化部署需求许多企业客户的数据无法出境。因此架构上支持切换嵌入模型至关重要。你可以轻松替换为能在企业内部部署的开源模型如sentence-transformers系列模型。多语言支持如果业务涉及多语言文档需要选择在多语言语料上训练过的嵌入模型以确保跨语言检索的准确性。维度与性能向量的维度如1536维直接影响搜索精度和存储成本。更高的维度通常意味着更精细的语义区分但也会增加计算和存储开销。需要根据实际数据规模和精度要求做权衡。3. 大语言模型LLM任务适配与可控性对于答案生成环节项目通常对接GPT-4或ChatGPT。但在企业场景选择LLM需要考虑任务类型如果是简单的信息提取和总结性能强大的GPT-3.5-Turbo可能性价比更高如果需要复杂的推理和创作GPT-4是更好的选择。可控性与安全性通过精心设计的提示词Prompt Engineering可以将LLM的“发挥”限制在提供的上下文内并指令其避免生成不存在的信息。这是保障答案准确性和安全性的关键。备选方案架构应允许无缝切换到Claude、文心一言等其他API或本地部署的Llama 2、ChatGLM等开源模型以应对不同的合规和成本要求。实操心得组件选型的“黄金法则”在实际项目中我建议遵循“先云服务后自托管”的原则快速验证原型。先用SAP BTP AI服务或OpenAI API快速跑通整个流程验证业务价值。当需求明确、流量稳定后再评估哪些组件特别是嵌入模型和LLM有必要为了成本、数据主权或延迟而进行私有化部署。不要一开始就追求完全自研那样会极大拖慢项目进度。3. 从零到一关键实现步骤详解3.1 环境准备与项目初始化首先你需要一个SAP BTP账号免费层即可入门并配置好本地开发环境包括Node.js、CDS CLI、Cloud Foundry CLI等。克隆项目后你会发现它是一个标准的CAP项目结构但多了一些与AI相关的服务和配置。关键一步是理解项目的package.json和mta.yaml部署描述文件。mta.yaml文件定义了应用的所有模块如CAP服务、AI服务代理和需要绑定的BTP服务如PostgreSQL、AI Core。部署时BTP会根据这个文件自动创建和绑定这些服务资源。# 示例查看和安装项目依赖 git clone https://github.com/SAP-samples/btp-cap-genai-rag.git cd btp-cap-genai-rag npm install3.2 数据模型与服务的扩展CAP的核心是数据模型。在这个项目中除了定义业务实体如Documents,Questions你还需要扩展模型来支持AI流水线。例如你可能会有一个DocumentChunks实体用来在数据库里记录文档拆分后的文本块、它们的向量表示虽然向量实际存在pgvector扩展的特殊列中、以及源文档的关联。服务层srv/目录下的代码是核心。这里会包含文档处理服务一个后台任务或API接收上传的文档调用文件解析服务如docxtractrfor Word,pdf-parsefor PDF进行文本提取然后进行分块和向量化最后存储到PostgreSQL中。问答服务暴露给前端的OData或自定义操作。它接收用户问题触发检索流程调用LLM生成答案并记录这次问答的日志可用于后续分析和模型优化。3.3 检索增强生成RAG流水线实现这是项目的技术心脏。我们拆开看每一步的代码级实现细节。1. 文档分块Chunking分块不是简单按字数切割。好的分块策略能保留语义完整性。// 伪代码示例使用递归字符文本分割器一种常见策略 const { RecursiveCharacterTextSplitter } require(langchain/text_splitter); const splitter new RecursiveCharacterTextSplitter({ chunkSize: 1000, // 每个块的最大字符数 chunkOverlap: 200, // 块与块之间的重叠字符防止上下文断裂 separators: [\n\n, \n, 。, , , , ] // 按优先级尝试的分隔符 }); const chunks await splitter.splitText(extractedText);注意事项对于结构化文档如带标题的PDF更优的策略是按章节或标题进行分块。可以结合使用pdfjs-dist库来获取页面元素和布局信息实现更智能的分块。2. 向量化与存储使用选定的嵌入模型API将文本块转换为向量并存入PostgreSQL。-- 首先确保你的PostgreSQL安装了pgvector扩展并创建了带向量列的表 CREATE EXTENSION IF NOT EXISTS vector; CREATE TABLE document_chunks ( id UUID PRIMARY KEY, content TEXT, embedding vector(1536), -- 假设使用1536维的嵌入模型 document_id UUID, metadata JSONB );在Node.js服务中你需要计算嵌入向量并执行插入。// 伪代码使用OpenAI Embeddings API const { OpenAIEmbeddings } require(langchain/embeddings/openai); const embeddings new OpenAIEmbeddings({ openAIApiKey: process.env.OPENAI_API_KEY }); const vector await embeddings.embedQuery(chunkText); // 使用数据库客户端执行插入pgvector扩展使你可以直接插入vector类型 const query INSERT INTO document_chunks (id, content, embedding, document_id) VALUES ($1, $2, $3, $4); await dbClient.query(query, [uuidv4(), chunkText, [${vector.join(,)}], documentId]);3. 语义检索当用户提问时将问题向量化并在数据库中进行相似性搜索。-- 使用pgvector的运算符余弦距离或-欧氏距离进行相似度搜索 SELECT id, content, 1 - (embedding $1) as similarity FROM document_chunks ORDER BY embedding $1 LIMIT 5; -- 返回最相似的5个片段在应用层你需要设定一个相似度阈值如0.7只采纳高于此阈值的片段作为上下文过滤掉不相关的结果。4. 提示工程与答案生成这是决定答案质量的关键一步。一个糟糕的提示会让最强大的LLM也给出糟糕的答案。// 构造一个清晰的、带指令的提示模板 const promptTemplate 你是一个专业的业务助理请严格根据以下提供的上下文信息来回答问题。 如果上下文中的信息不足以回答问题请直接说“根据现有资料我无法回答这个问题”不要编造信息。 上下文信息 {context} 问题{question} 请根据上下文提供准确、简洁的回答 ; // 将检索到的多个文本片段合并填入{context}用户问题填入{question} const finalPrompt promptTemplate.replace({context}, retrievedContext).replace({question}, userQuestion); // 调用LLM API const response await openai.chat.completions.create({ model: gpt-4, messages: [{ role: user, content: finalPrompt }], temperature: 0.2, // 较低的温度值使输出更确定、更专注于上下文 max_tokens: 500 }); const answer response.choices[0].message.content;实操心得提示词的迭代是核心工作提示词不是一次写好的。你需要准备一个测试集QA对不断调整提示词的指令、格式和上下文组织方式例如是否在每个片段前加上来源标记通过评估答案的准确性和流畅度来迭代优化。这是一个实验性很强的过程。4. 部署、监控与性能优化4.1 部署到SAP BTP项目使用MTAMulti-Target Application进行打包和部署。你需要使用Cloud Foundry CLI登录到你的BTP子账户并指定正确的组织和空间。# 安装MTA构建工具 npm install -g mbt # 构建MTA归档文件 mbt build # 部署到Cloud Foundry环境 cf deploy mta_archives/your-app.mtar部署脚本会自动在BTP上创建所需的服务实例PostgreSQL with pgvector, AI Core等并将应用模块与之绑定。务必在部署前仔细检查mta.yaml中的资源名称和配置确保与你的BTP环境匹配。4.2 应用监控与日志上线后监控至关重要。你需要关注延迟从用户提问到收到答案的总时间。拆解看是嵌入模型调用慢、向量搜索慢还是LLM生成慢成本主要来自嵌入模型和LLM的API调用费用。监控Token的使用量特别是对于长文档拆分后产生的大量嵌入请求。准确性建立人工或自动化的评估机制定期抽查问答对的准确性。可以记录每次问答的检索到的上下文片段和生成的答案便于回溯分析。在CAP应用中你可以利用BTP的Application Logging服务在关键步骤如文档处理开始/结束、检索结果、LLM调用打上结构化的日志。// 使用CAP的日志功能 const LOG cds.log(ai-service); LOG.info(Starting document ingestion for, { documentId, fileName }); LOG.debug(Retrieved top 3 chunks with similarities:, similarities); if (answer.includes(无法回答)) { LOG.warn(LLM returned uncertain answer for question:, userQuestion); }4.3 性能与成本优化实战技巧当知识库文档量很大数万甚至百万级时以下几个优化点能带来显著提升1. 检索阶段优化分层索引pgvector支持IVFFlat或HNSW索引来加速搜索。在数据导入后创建索引。CREATE INDEX ON document_chunks USING ivfflat (embedding vector_cosine_ops) WITH (lists 100); -- 或者使用更快的HNSWPostgreSQL 16支持更好 CREATE INDEX ON document_chunks USING hnsw (embedding vector_cosine_ops);注意IVFFlat索引需要在有一定数据量后创建并且lists参数需要根据数据量调整。HNSW索引创建慢但查询快且无需训练。元数据过滤在相似性搜索前先使用文档类型、部门、日期等元数据进行过滤可以极大缩小搜索范围。确保你的document_chunks表有良好的元数据字段和索引。重排序Re-ranking先用向量检索出Top K个结果比如20个再用一个更精细但更慢的交叉编码器Cross-Encoder模型对这20个结果进行重排序选出最相关的3-5个。这能在不显著增加延迟的情况下提升精度。2. 提示与生成阶段优化上下文压缩检索到的多个文本片段可能包含冗余信息。可以使用LLM本身如GPT-3.5-Turbo先对检索到的上下文进行总结和去重再将压缩后的上下文发送给更强大的LLM如GPT-4生成最终答案。这能节省Token降低成本有时还能提升答案质量。缓存策略对于常见、重复的问题如“公司的休假政策是什么”可以将问题和对应的答案缓存起来例如用Redis下次直接返回避免重复调用昂贵的LLM。缓存键需要精心设计能捕捉问题的语义。3. 异步处理与队列文档解析和向量化是耗时操作不能阻塞HTTP请求。务必将其设计为异步任务。可以使用CAP内置的cds.spawn机制或者集成BTP的Enterprise Messaging服务将上传的文档放入消息队列由后台工作进程消费处理处理完成后更新文档状态。5. 常见问题排查与进阶思考5.1 实战问题速查表在实际开发和运维中你几乎一定会遇到下面这些问题。问题现象可能原因排查步骤与解决方案上传文档后状态一直为“处理中”。1. 异步处理worker未启动或崩溃。2. 文件解析库遇到特殊格式报错。3. 嵌入模型API调用超限或失败。1. 检查后台worker的日志确认进程是否存活。2. 在本地用示例文档测试解析逻辑确保代码健壮性try-catch。3. 检查AI服务的配额和账单确认API密钥有效且未超限。问答答案质量差经常“胡言乱语”。1. 检索到的上下文不相关。2. 提示词Prompt设计不佳。3. LLM的temperature参数设置过高。1. 检查检索环节打印出检索到的文本片段和相似度分数看是否与问题相关。可能需要调整分块大小或嵌入模型。2. 优化提示词加入更严格的指令如“仅根据上下文回答”。3. 将temperature调低如0.1使输出更确定性。问答响应时间很长10秒。1. 向量数据库未建索引全表扫描。2. 检索的Top K值设置过大。3. LLM生成速度慢如使用了GPT-4且回答较长。1. 在向量列上创建IVFFlat或HNSW索引。2. 尝试将检索数量从5减少到3观察精度和速度的平衡。3. 考虑对简单问题路由到更快的模型如GPT-3.5-Turbo或设置生成Token上限。提示“超出上下文长度”。检索到的文本片段总长度超过了LLM模型的最大上下文窗口如GPT-4是8K/32K Tokens。1. 减少检索的片段数量Top K。2. 对检索到的片段进行智能压缩或总结只保留核心信息。3. 使用具有更长上下文窗口的模型如GPT-4-32kClaude 100k。向量相似度分数都很低0.5。1. 问题与文档库领域完全不匹配。2. 嵌入模型不适合当前领域例如用通用模型处理高度专业的技术文档。1. 确认用户问题是否在知识库覆盖范围内。2. 考虑使用在专业领域语料上微调过的嵌入模型或者在你的数据上对通用模型进行微调如果数据量足够。5.2 从Demo到生产必须考虑的进阶问题当你把这个样板间跑通准备用于真实业务时下面这些更深层次的问题会浮现出来。1. 知识库的更新与维护业务文档不是静态的。当源文件更新后如何更新向量数据库简单的方案是删除该文档的所有旧块重新处理新文档。但更高效的方案是增量更新这需要建立文本块与源文档段落之间的精确映射并识别出更改的部分只更新受影响的向量。这是一个复杂的工程问题。2. 多轮对话与历史上下文当前的RAG通常是单轮的。但在客服场景中用户会连续提问。你需要让系统记住对话历史。简单的实现方式是将之前几轮的问答也作为上下文的一部分放入下一次的提示中。但这会迅速消耗Token。更高级的方案是使用LLM本身来总结对话历史或者使用专门的对话记忆管理模块。3. 答案的可追溯性与可信度企业应用要求可审计。系统不仅要知道答案还要知道“答案从哪来”。你必须在返回答案的同时附上引用的源文档片段甚至具体到页码和段落。在前端高亮显示这些引用能极大增强用户对答案的信任感。4. 评估体系构建如何量化你的RAG系统好坏需要建立一套评估体系。包括检索相关性人工标注一批问题评估系统检索到的片段是否相关。答案忠实度生成的答案是否严格基于提供的上下文没有增加或歪曲信息。答案有用性答案是否真正解决了用户的问题。 可以定期如每周用一批标准问题测试系统跟踪这些指标的变化。5. 安全与合规这是企业级应用的生死线。你需要确保数据输入安全对上传的文档进行病毒扫描防止恶意文件。内容过滤在用户输入问题和LLM输出答案两端都设置内容安全过滤器防止生成不当、有害或有偏见的内容。权限控制问答服务必须集成企业的身份认证如SAP IAS。确保用户只能检索和询问其权限范围内的文档内容。这需要在检索阶段就加入基于用户角色的元数据过滤。这个项目提供了一个坚实的起点但通往一个成熟、健壮的企业级智能问答系统还有很长的路要走。每一次优化、每一个异常处理都是对架构和代码的锤炼。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价