尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从RAG到上下文工程:大模型应用落地的核心技术解析与实践

从RAG到上下文工程:大模型应用落地的核心技术解析与实践 1. 从“喂数据”到“喂上下文”AI工程化的核心范式转变如果你还在把大模型当成一个简单的问答机每次对话都从零开始那你可能已经落后了。过去我们谈AI应用核心是“喂数据”——准备海量的训练数据让模型学习。但在大模型时代尤其是当我们基于API或开源模型进行应用开发时游戏规则变了。我们不再或者说很少去重新训练模型而是转向了“喂上下文”。这不仅仅是Prompt Engineering提示词工程那么简单它是一套系统工程关乎如何高效、精准、结构化地将信息“注入”到模型的每一次推理中从而让模型在特定任务上表现得像一个专家。“喂上下文”的本质是弥补大模型在实时性、私有性和精确性上的不足。模型的知识截止于某个时间点它不知道你公司内部的流程文档它的回答可能笼统无法精确引用你提供的技术手册。因此我们需要将最新的、私有的、精确的信息作为“上下文”Context和用户的“问题”Query一起构成完整的“提示”Prompt提交给模型。这听起来简单但实操起来从上下文的获取、处理、压缩、组装到最终提交每一步都充满了工程挑战。这就是“上下文工程”Context Engineering的核心也是现代AI工程化落地的关键路径。2. 上下文数据流图从原始资料到模型输入的完整链路理解“喂上下文”的第一步是看清数据是如何流动的。一个完整的上下文工程链路可以分解为几个核心阶段它们共同构成了一条高效的数据流水线。2.1 数据源的获取与加载上下文不会凭空产生。它的源头多种多样非结构化文本这是最常见的来源包括PDF技术文档、Word报告、公司Confluence/Wiki页面、网页内容、甚至聊天记录。处理这些数据首先需要将其从原始格式中提取出纯文本。例如使用PyPDF2、pdfplumber处理PDF用python-docx处理Word用BeautifulSoup或Readability算法清洗网页。结构化数据数据库记录、API返回的JSON、CSV/Excel表格。这些数据本身具有字段结构但需要转换成模型能理解的叙述性语言或特定格式如Markdown表格。例如将数据库查询结果序列化为一段描述性文字“当前用户表中有三条记录分别是用户A年龄25用户B年龄30...”代码仓库当需要模型理解或生成代码时整个代码库或特定文件就是上下文。这涉及到文件树的解析、关键代码片段的提取以及跨文件依赖关系的梳理。多模态数据虽然当前主流是文本模型但视觉内容上下文模型也在发展。处理图像、视频时需要先通过视觉模型如CLIP进行理解生成描述性文本再将此文本作为上下文喂给语言模型。注意数据加载阶段最容易被忽视的是编码和格式问题。确保你的文本加载器能正确处理UTF-8、GBK等各种编码并过滤掉无意义的乱码和特殊控制字符这是保证后续环节质量的基础。2.2 文本的分块与向量化你不可能把一本500页的书整个塞进模型的上下文窗口。因此必须对长文本进行“分块”Chunking。这不是简单的按字数切割而是一门学问。固定长度分块最简单的方法如每500个字符或token切一块。缺点是可能粗暴地切断一个完整的句子或段落破坏语义。基于分隔符的分块根据自然段落分隔符如\n\n、标题##、句号等进行切割。这能更好地保持语义完整性。递归分块一种更智能的方法。先尝试用大分隔符如\n\n分块如果块还是太大再用小分隔符如句号、逗号继续分直到块大小符合预设阈值。LangChain等框架提供了RecursiveCharacterTextSplitter工具来实现此逻辑。语义分块更高级的方法利用句子嵌入模型计算相邻句子的相似度在语义变化处进行切割。这能确保每个块在语义上尽可能内聚。分块之后为了能快速从海量块中检索出与用户问题最相关的部分我们需要将文本块“向量化”。即使用嵌入模型Embedding Model如OpenAI的text-embedding-ada-002或开源的BGE、Sentence-Transformers模型将每个文本块转换为一个高维向量一组数字。这个向量就像是文本的“数学指纹”语义相近的文本其向量在空间中的距离也更近。2.3 检索与相关性排序当用户提问时系统需要快速找到最相关的文本块作为上下文。这个过程就是“检索增强生成”RAG中的检索Retrieval步骤。向量相似度检索将用户问题也向量化然后计算问题向量与所有文本块向量的余弦相似度或点积取出相似度最高的前k个块例如top-5。这是最核心的检索方式。混合检索为了兼顾语义匹配和关键词匹配可以结合传统的全文检索如BM25算法。BM25擅长精确匹配关键词而向量检索擅长语义匹配。将两者的结果按分数融合能获得更鲁棒的检索效果。重排序初步检索出的top-k个块其排序可能并非最优。可以使用一个更精细但计算量也更大的“重排序模型”Reranker如BGE-Reranker对这几个候选块进行更精准的相关性打分和重新排序确保最相关的信息排在最前面。2.4 上下文的组装与格式化检索到的文本块不能直接堆砌起来就扔给模型。我们需要将它们组装成一个结构清晰、模型易于理解的提示。基础组装简单地将检索到的文本块用分隔符如\n---\n连接起来前面加上指令“请根据以下上下文回答问题”。结构化组装对于复杂任务需要更精细的结构。例如采用以下格式系统指令System Prompt: 你是一个技术支持助手请严格根据提供的文档片段回答问题。 文档片段 1: [检索到的文本块1] 文档片段 2: [检索到的文本块2] ... 用户问题: [用户的实际问题]引用与溯源在组装时为每个文本块添加一个来源标识如[来源: 用户手册第3章]。这样在模型生成答案时可以要求它引用这些来源既增加了可信度也方便用户追溯。处理超长上下文即使经过检索有时上下文总长度仍可能超过模型限制如超过128K token。这时就需要“上下文压缩”技术。3. 上下文压缩在有限窗口内塞入无限信息的艺术模型上下文窗口再大如Claude 3的200KDeepSeek的128K面对真正的海量资料库也是杯水车薪。更关键的是随着上下文增长模型处理中间信息的能力会下降出现“中间丢失”现象。因此上下文压缩成为高级RAG系统的必备技能。3.1 为什么需要压缩不只是长度限制压缩的目的有三个突破长度限制这是最直接的原因。降低计算成本输入模型的token数直接关联着API调用费用和计算延迟。提升信息密度与质量去除冗余、无关信息让模型专注于最精华的部分反而能提升回答质量。3.2 主流压缩策略与实践提取式摘要这是最直观的方法。使用另一个通常是更小、更快的模型对长文本进行摘要保留核心事实和实体。例如用gpt-3.5-turbo来总结检索到的文档块再将摘要作为上下文。但风险在于摘要过程可能丢失关键细节。抽象式重写让模型基于检索到的上下文和原始问题重新组织、改写出一段更精炼、更直接针对问题的背景信息。这比简单摘要更智能但成本也更高。选择性上下文不压缩内容本身而是压缩数量。通过更精细的检索和重排序只选取置信度最高的1-2个片段而不是5个。这要求检索系统非常精准。层次化压缩这正是网络热词中提到的“Claude Code上下文分层”思路。对于超长文档如代码库先构建一个高层级的索引如目录树、模块说明当用户提问具体问题时先检索高层级索引定位到相关模块再深入该模块检索详细代码。这模拟了人类阅读大型文档的方式。智能过滤与去重在组装上下文前对检索到的片段进行去重基于向量或文本并过滤掉与问题相关性极低的片段通过设置相似度阈值。3.3 Claude的“压缩上下文”命令一个启发网络热词中提到了“claude code压缩上下文命令”。虽然我们无法得知其具体实现但这给了我们一个工程启示压缩可以是一个交互式、可引导的过程。我们可以设计专门的“压缩提示词”让模型自己来执行压缩。例如给模型的指令可以是你是一个上下文压缩专家。我将给你一段长文本和一个核心问题。你的任务是从长文本中提取出所有与回答该问题直接相关的事实、数据和关键句子并组织成一段连贯、简洁的摘要。忽略所有无关的背景介绍、例子和冗余解释。 长文本[此处放入需要压缩的文本] 核心问题[用户的问题] 请开始压缩通过这种方式我们将压缩任务本身也“外包”给了大模型实现了动态的、基于查询的上下文优化。4. Prompt的精密组装系统指令、上下文与用户查询的三角舞上下文准备好了如何与Prompt结合是决定模型输出质量的临门一脚。一个健壮的Prompt模板通常包含三个部分系统指令System Prompt、上下文Context和用户查询User Query。它们各司其职共同引导模型。4.1 系统指令设定角色与行为边界系统指令在对话开始时一次性给定用于塑造模型的“人格”和回答范式。它与“function call”的区别在于系统指令是战略性的、风格性的而function call是战术性的、结构化的API调用。角色设定“你是一位资深Linux系统运维专家回答专业、准确、简洁。”输出格式约束“请用Markdown格式输出先给出结论再分点阐述原因。”安全与边界“你只能根据我提供的上下文回答问题。如果上下文不包含相关信息请明确说‘根据提供的信息我无法回答此问题’切勿杜撰。”处理流程说明“在回答前先简要复述你从上下文中找到的关键证据。”一个强大的系统指令能极大减少后续对话中的“调教”成本。你需要像产品经理设计交互规范一样去精心设计系统指令。4.2 上下文的嵌入技巧上下文如何放入Prompt直接影响模型对它的“关注度”。位置很重要将最重要的上下文放在靠近用户问题的地方。研究表明模型对Prompt开头和结尾的信息记忆更深刻首因效应和近因效应。可以考虑把核心证据放在最后。使用明确的标记用如context.../context、## 参考文档 ##这样的标记将上下文包裹起来与指令和问题清晰区分。添加引导性指令在上下文前后加上指令如“请仔细阅读以下背景信息”和“基于以上背景请回答”主动引导模型去“使用”上下文。处理多个来源当上下文来自多个不相关的文档时明确分隔并标注来源避免模型混淆。4.3 用户查询的优化用户的原生问题往往是模糊的。在将其送入Prompt前可以进行“查询重写”或“查询扩展”。查询重写用模型将“它怎么工作的”这种模糊问题结合对话历史重写成更具体的“根据之前讨论的X系统请解释其数据同步模块的工作流程。”查询扩展生成原问题的同义词或相关问题用于在向量检索时召回更多相关片段。例如对“如何备份数据库”可以扩展出“数据库备份步骤”、“backup database method”、“数据备份方案”等。4.4 一个完整的Prompt组装示例系统指令 你是一个IT知识库助手。你的回答必须严格基于提供的“参考文档”内容。如果文档中没有答案请说“文档中未提及”。回答请清晰并引用文档标题。 参考文档 【文档标题服务器安装指南-v2.1】 1. 安装前需确保系统为Ubuntu 20.04或更高版本。 2. 运行安装脚本的命令是sudo ./install.sh --modeprod。 3. 安装完成后默认服务端口为8080。 【文档标题常见问题排查】 若端口8080被占用安装脚本会自动尝试8081端口。 用户查询 我在Ubuntu 22.04上运行安装脚本应该用什么命令这个组装清晰地划分了责任系统指令定规则上下文提供弹药用户查询提出问题。5. 工程实践中的核心挑战与应对策略理论很美好但实践起来坑很多。下面分享几个在构建“喂上下文”系统时最常见的挑战和我的应对经验。5.1 上下文噪声与幻觉抑制最大的风险是检索系统可能返回不相关或轻微相关的文档片段这些“噪声”会干扰模型甚至导致它基于错误信息生成看似合理但完全错误的答案幻觉。策略1提高检索精度这需要投入精力优化嵌入模型、分块策略和检索算法。有时简单的调整分块大小从500调到300或尝试不同的嵌入模型就能显著提升效果。策略2设置置信度阈值为向量检索的相似度分数设置一个阈值例如只保留相似度0.7的片段。低于阈值的片段宁可不用也不要引入噪声。策略3让模型自我验证在Prompt中要求模型“请判断以下上下文是否足以回答该问题如果不足以请指出缺失什么信息。”这相当于让模型做一次质量检查。策略4输出引用与溯源强制模型在答案中引用上下文片段的编号。这样如果答案有问题我们可以快速定位到是哪个片段提供了错误信息从而反向优化我们的知识库。5.2 长上下文下的性能衰减即使上下文在窗口限制内模型对中间部分信息的理解和记忆能力也会下降。策略关键信息重复与强调对于最核心的指令或信息可以在Prompt的开头和结尾都提及一次。或者在上下文中用加粗、标题等形式突出关键句子。策略结构化与分节将长上下文用清晰的标题如“## 一、背景 ##”、“## 二、配置步骤 ##”组织起来这有助于模型建立内部索引更好地定位信息。5.3 动态上下文与多轮对话在聊天应用中上下文不仅包括检索到的文档还包括历史对话记录。如何管理不断增长的对话历史策略滑动窗口只保留最近N轮对话作为上下文。这是最简单有效的方法。策略历史摘要在对话轮数增多时用一个模型对之前的对话历史进行摘要然后用摘要最近几轮对话作为新的上下文。这需要在对话过程中异步调用模型进行摘要工程复杂度较高但能保留更长的记忆。策略关键记忆提取尝试从历史对话中提取出关键实体如讨论的产品名、决定的方案、提到的数字和用户偏好将这些结构化信息作为上下文而不是完整的对话记录。5.4 工具调用Function Calling与上下文的结合当模型需要执行具体操作查询天气、执行计算、调用数据库时需要将工具的描述作为上下文喂给模型。这就是“系统Prompt与Function Call区别”的体现。系统Prompt是总纲而Function Call的描述是具体的“工具说明书”。最佳实践将可用工具的详细描述名称、功能、输入参数JSON Schema、输出示例清晰地放在上下文中。当模型决定调用工具时它会产生一个符合Schema的JSON输出你的后端程序解析这个JSON再去真正调用API。这实现了模型与外部世界的安全、结构化交互。6. 构建你自己的上下文工程流水线从工具选型到部署纸上得来终觉浅我们来勾勒一个最小可行上下文工程系统的搭建思路。6.1 核心组件选型向量数据库这是存储和检索文本向量的核心。轻量级可选ChromaDB、FAISS本地库生产环境考虑Weaviate、Qdrant、Pinecone云服务。选择时考虑易用性、性能、过滤查询能力。嵌入模型开源首选BGE系列如BAAI/bge-large-zh或Sentence-Transformers模型。英文场景text-embedding-ada-002仍是标杆。关键是要做评测看它在你的领域数据上的表现。文本分块工具LangChain的RecursiveCharacterTextSplitter是个不错的起点可以基于它定制自己的分块逻辑。大语言模型API根据需求选择。高精度选GPT-4/GPT-4o性价比选Claude 3 Haiku或DeepSeek-V2私有化部署选开源模型如Qwen、GLM通过LM Studio、Ollama等工具本地运行。6.2 流水线搭建步骤知识库预处理离线收集所有文档PDF、Word、网页等。编写脚本使用相应的解析库提取纯文本。应用分块策略将长文本切成大小合适的片段。使用嵌入模型将每个文本块转化为向量。将(文本块, 向量, 元数据[如来源、页码])存储到向量数据库中。用户查询处理在线接收用户问题。可选对问题进行查询重写/扩展。使用相同的嵌入模型将问题向量化。在向量数据库中执行相似度搜索检索出Top-K个相关文本块。可选对检索结果进行重排序。Prompt组装与调用按照预设的模板将系统指令、检索到的上下文、用户问题组装成最终Prompt。调用大语言模型API发送Prompt。接收模型回复解析并返回给用户同时可解析其中的引用信息。6.3 一个简单的代码示例概念层面以下是一个使用Python和LangChain框架的简化示例展示核心流程# 注意此为概念示例需安装相应库并填写API密钥 from langchain_community.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma from langchain.chains import RetrievalQA from langchain_community.llms import OpenAI # 1. 加载与分块 loader TextLoader(knowledge_base.txt) documents loader.load() text_splitter RecursiveCharacterTextSplitter(chunk_size500, chunk_overlap50) texts text_splitter.split_documents(documents) # 2. 向量化与存储 embeddings HuggingFaceEmbeddings(model_nameBAAI/bge-small-zh) vectorstore Chroma.from_documents(texts, embeddings, persist_directory./chroma_db) # 3. 构建检索链 llm OpenAI(api_keyyour_key, temperature0) # 或使用其他LLM qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, # 简单地将所有上下文塞入Prompt retrievervectorstore.as_retriever(search_kwargs{k: 4}), chain_type_kwargs{prompt: YOUR_CUSTOM_PROMPT} # 这里可以注入精心设计的Prompt模板 ) # 4. 提问 result qa_chain.run(什么是AI工程化中的上下文) print(result)6.4 迭代与评估系统搭建完成后必须建立评估机制。构建测试集收集一批真实用户可能问的问题并准备好标准答案或关键要点。评估指标检索相关性人工或用小模型判断检索到的上下文是否真的相关。答案准确性对比模型答案与标准答案。答案忠实度判断答案是否严格源自上下文有无幻觉。用户体验回答是否流畅、清晰。持续优化根据评估结果回头调整分块大小、嵌入模型、检索数量、Prompt模板等各个环节的参数和策略。“给模型喂上下文”不是一个一蹴而就的魔法而是一个需要持续迭代和打磨的工程系统。它介于传统的软件工程和机器学习之间要求开发者既懂数据流程和系统架构又理解大模型的行为特性。当你看到模型能够精准地引用你公司内部文档来回答复杂问题时你就会明白这一切的工程努力都是值得的。这不再是简单的调用API而是真正在构建属于你自己的、具备深度领域知识的智能体。
返回列表