1. 从提示词到上下文为什么你的LangChain应用还不够“聪明”如果你已经用LangChain构建过一些应用大概率是从写提示词Prompt开始的。你精心设计了一个模板把用户的问题和数据库里的知识拼在一起丢给大模型然后满怀期待地等着一个精准的答案。但很快你就会发现事情没那么简单。模型可能会“一本正经地胡说八道”引用不存在的文档或者当用户的问题稍微绕个弯需要结合多段上下文时模型的表现就开始断崖式下跌。你可能会归咎于模型不够强或者自己的提示词写得不好于是开始疯狂搜索“最佳提示词模板”。但问题可能出在更上游的地方上下文Context。提示词工程告诉模型“怎么想”而上下文工程决定了模型“能看到什么”。你给模型看的材料上下文本身的质量、组织方式和安全性直接决定了模型输出的天花板。想象一下你是一位专家我给你一份杂乱无章、甚至包含错误信息的报告然后让你基于这份报告做决策无论我如何清晰地提问你的答案都很难可靠。大模型也是如此。这就是为什么在构建严肃的、生产级的LangChain应用时我们不能只停留在提示词层面。上下文工程Context Engineering的核心任务是确保输入给模型的“原材料”——也就是检索到的文档片段、对话历史、工具调用结果等——是高质量的、相关的、结构化的并且是安全的。而安全护栏Guardrails则是确保模型在“消化”这些原材料并“生产”答案的整个过程中不会跑偏、不会越界、不会产生有害内容的一系列防护措施。最近业界的热词比如“Agent四个阶段”提示词工程、上下文工程、驾驭工程、循环工程也把上下文工程提到了一个非常核心的位置。它不再是RAG检索增强生成里一个简单的“检索-拼接”步骤而是一个需要系统性设计和持续优化的工程领域。本文将结合LangChain的实践深入探讨如何做好上下文工程并构建有效的安全护栏让你的AI应用不仅聪明而且可靠、可控。2. 上下文工程详解超越简单的文本拼接上下文工程的目标很明确为模型提供最优的信息输入以最大化输出质量。这绝不仅仅是把检索到的文本片段用\n\n连接起来那么简单。它涉及检索、处理、编排和注入的完整链条。2.1 检索阶段找到对的“原料”检索是上下文工程的源头。如果检索不到相关文档后面的一切都是空中楼阁。在LangChain中检索器Retriever的选择和配置是关键。向量检索的局限性大多数人会直接使用Chroma、Pinecone等向量数据库的相似度搜索。这很好但它本质上是“语义相似度”搜索。当用户查询“苹果公司最新财报”时它可能会返回一篇关于“苹果水果种植技术”的文档因为两者都高频出现“苹果”这个词。纯粹的语义检索无法理解“苹果”在这里是一个品牌实体。混合检索策略成熟的上下文工程需要混合检索。LangChain提供了EnsembleRetriever允许你将向量检索和关键词检索如BM25的结果结合起来。from langchain.retrievers import EnsembleRetriever from langchain_community.retrievers import BM25Retriever from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings # 假设已有文档库 documents [...] # 1. 创建向量检索器 vectorstore Chroma.from_documents(documents, OpenAIEmbeddings()) vector_retriever vectorstore.as_retriever(search_kwargs{k: 5}) # 2. 创建关键词检索器 bm25_retriever BM25Retriever.from_documents(documents) bm25_retriever.k 5 # 3. 集成检索器 ensemble_retriever EnsembleRetriever( retrievers[vector_retriever, bm25_retriever], weights[0.7, 0.3] # 可以调整权重 )这样既能捕捉语义关联又能保证关键词的精确匹配。在实际项目中我通常会根据测试集上的表现来调整权重。对于专业术语多的领域如法律、医疗关键词检索的权重可以适当调高。元数据过滤这是提升检索相关性的利器。在存储文档时就为其添加元数据如文档类型、创建日期、部门、主题等。检索时可以让用户通过自然语言指定过滤器或者在前端提供筛选组件将过滤条件传递给检索器。# 检索时添加元数据过滤 retriever vectorstore.as_retriever( search_kwargs{ “k”: 5, “filter”: {“doc_type”: “财报”, “year”: {“$gte”: 2023}} # 只检索2023年以后的财报类型文档 } )这个简单的技巧能极大减少无关上下文的混入是提升RAG答案准确率最有效的手段之一。2.2 后处理阶段精炼与重排检索到一批文档后直接全部塞给模型是低效的因为模型有上下文窗口Token数限制且无关信息会形成干扰。我们需要对候选文档进行后处理。去重Deduplication不同检索器可能返回高度重复的文档。需要基于内容哈希或嵌入向量相似度进行去重。LangChain的ContextualCompressionRetriever可以结合一些压缩器来实现但更简单的做法是在获取文档列表后进行一轮基于文本片段的简单去重。相关性重排Re-ranking检索器返回的文档是按相似度分数排序的但这个分数不一定代表“对回答当前问题最有帮助”的顺序。我们可以使用一个专门的、更强大的重排模型Cross-Encoder来重新评估和排序。虽然会引入额外延迟但对答案质量提升显著。from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import CrossEncoderReranker from langchain_community.cross_encoders import HuggingFaceCrossEncoder # 使用一个轻量级的Cross-Encoder模型如BAAI/bge-reranker-base cross_encoder HuggingFaceCrossEncoder(model_name“BAAI/bge-reranker-base”) compressor CrossEncoderReranker(modelcross_encoder, top_n3) # 只保留重排后的前3个 compression_retriever ContextualCompressionRetriever( base_compressorcompressor, base_retrieverensemble_retriever )经过重排排在最前面的文档其包含答案的可能性最高。在实际部署中可以考虑对重排步骤进行缓存以减少对重排模型的频繁调用。关键信息提取有时我们不需要整段文档只需要其中的一个表格、几个关键数字或一句话。可以先用一个LLM调用对检索到的文档进行总结或提取再将提取出的精华部分作为上下文。这属于更高级的“递归检索”或“提取式”上下文构建在LangChain中可以通过MultiQueryRetriever或自定义的Chain来实现其核心思想是让模型自己决定需要哪些信息。2.3 上下文构造与提示模板编排这是将处理好的文档“喂”给模型前的最后一步也是最体现“工程”二字的地方。结构化上下文不要只是把文档片段堆在一起。给每个片段添加清晰的标识符。文档[1] 标题2024年Q1财报摘要 来源投资者关系网站 内容...营收同比增长15%... 文档[2] 标题产品发布会纪要 来源内部Wiki 内容...CEO强调了AI战略... 问题公司最新的增长动力是什么这种结构帮助模型建立引用关系当它说“根据文档[1]”时你和用户都能清晰追溯。在提示词中要明确指示模型使用这些引用。长度管理与Token计算你必须时刻清楚你的上下文消耗了多少Token。使用tiktoken或transformers的tokenizer进行计算。一个实用的策略是设置一个最大上下文Token预算比如模型限制的80%然后按优先级如重排分数依次添加文档直到预算用完。LangChain的splitter和context_chain通常有max_tokens参数但自己实现一个预算管理器会更灵活。位置与顺序模型对提示词开头和结尾的信息更敏感。把最重要的上下文如重排得分最高的文档放在靠近用户问题的地方。同时系统指令System Message和用户历史对话的放置也很有讲究。通常的推荐顺序是系统指令 历史对话越近的越靠后 检索到的上下文 当前用户问题。注意上下文窗口不是“填满就好”。过多的、冗余的上下文会稀释关键信息导致模型性能下降这种现象被称为“中间丢失”Lost in the Middle。所以追求的是“精准”而非“量大”。3. 构建多层次的安全护栏不只是内容过滤安全护栏是确保AI应用在可控范围内运行的系统。很多人把它等同于一个内容过滤器检查输出是否有害。这远远不够。一个健全的护栏系统应该是多层次、贯穿流程的。3.1 输入护栏守住第一道门在用户输入到达核心逻辑之前就进行拦截和清洗。格式与完整性校验检查输入是否为空、是否超过长度限制、是否包含不可解码的字符。对于期望结构化输入如JSON的场景进行解析验证。def input_guard(user_input: str): if not user_input or len(user_input.strip()) 0: raise ValueError(“输入不能为空”) if len(user_input) 2000: raise ValueError(“输入过长请精简您的问题”) # 简单的注入攻击检测示例 if “script” in user_input.lower(): raise ValueError(“检测到非法输入”) return user_input.strip()话题安全域Topic Safeguard定义你的应用可以讨论和不可以讨论的话题范围。例如一个客服机器人不应讨论政治、宗教或生成医疗建议。可以在输入层使用一个轻量级的分类模型或关键词列表进行快速拦截。LangChain社区中有些Guardrails相关的集成但自己实现一个基于规则或小模型的分类器也很直接。用户身份与权限校验结合认证系统如JWT Token验证用户是否有权进行当前操作。例如普通员工和经理能访问的文档范围不同这需要在检索上下文之前就通过权限过滤来实现而不是在模型输出后再补救。这直接关联到上文提到的元数据过滤。3.2 上下文护栏确保“原料”安全即使检索到了相关文档文档内容本身也可能有问题。源可信度验证为不同来源的数据设定可信度等级。例如来自官方知识库的文档等级为A来自网络爬虫的未经验证的文章等级为C。在构造上下文时可以优先使用高等级来源或明确告知模型某些来源的可靠性较低。这需要你在数据摄入Ingestion阶段就打上标签。内容实时过滤在将文档片段加入上下文前对其进行敏感信息检测。例如自动检测并脱敏上下文中的个人身份信息PII如邮箱、电话、身份证号。可以使用专门的PII检测库或服务。from presidio_analyzer import AnalyzerEngine from presidio_anonymizer import AnonymizerEngine analyzer AnalyzerEngine() anonymizer AnonymizerEngine() def anonymize_text(text: str): results analyzer.analyze(texttext, language‘en’) anonymized anonymizer.anonymize(texttext, analyzer_resultsresults) return anonymized.text将脱敏后的上下文提供给模型可以极大降低隐私泄露风险。模型基于脱敏数据生成的回答在返回给用户前可以根据需要再决定是否还原部分信息。3.3 模型调用护栏控制生成过程这是传统意义上最受关注的护栏层直接约束模型的输出。输出格式约束通过严格的提示词工程和解析器Parser强制模型以特定格式如JSON、XML输出。LangChain的PydanticOutputParser或StructuredOutputParser是这方面的利器。它们不仅能获取结构化数据其严格的格式要求本身也是一种护栏能减少模型“胡言乱语”的概率。from langchain.output_parsers import PydanticOutputParser from pydantic import BaseModel, Field class Answer(BaseModel): answer: str Field(description“对问题的直接回答”) confidence: float Field(description“回答的置信度0到1之间”) sources: list[str] Field(description“引用的文档ID列表”) parser PydanticOutputParser(pydantic_objectAnswer) prompt PromptTemplate( template“请根据以下上下文回答问题。{format_instructions}\n上下文{context}\n问题{question}\n”, input_variables[“context”, “question”], partial_variables{“format_instructions”: parser.get_format_instructions()} )如果模型输出不符合Pydantic模型定义解析会失败你可以选择重试或返回一个安全兜底回复。内容安全策略调用大模型服务如OpenAI、Azure OpenAI时通常它们会提供内置的内容安全API。务必开启并配置这些策略。你可以设置对不同类别仇恨、自残、性、暴力等的过滤严格等级。不要完全依赖它们但它们是重要的一环。在LangChain调用这些模型时可以通过model_kwargs传递这些安全参数。自研分类器后处理对于关键应用可以在模型生成答案后再用一个专门训练的小型、高效的文本分类模型对输出进行二次安全检查。这个分类器可以针对你的业务场景定制检测更细粒度的风险比如是否包含了未公开的内部数据、是否做出了超出其权限的承诺等。3.4 输出护栏与最终交付在将答案返回给用户前做最后一道检查。事实一致性检查Factual Consistency Check对于RAG应用一个常见问题是模型“幻觉”Hallucination即答案与提供的上下文不符。可以设计一个简单的验证链用模型从答案中提取关键主张Claim然后判断每个主张是否能在上下文中找到支持。LangChain的QAEvalChain可以用于此目的虽然它本身也依赖模型可能不准但作为一个预警机制是有效的。最终格式与安全转义确保返回给前端的数据是安全的。如果答案是HTML格式要进行转义以防止XSS攻击。如果是直接嵌入网页的文本也要注意特殊字符的处理。4. Token管理贯穿始终的成本与性能命脉无论是上下文工程还是安全护栏都无法绕开“Token”这个核心概念。它直接关联着成本、延迟和效果。Token计算是基础你必须对你使用的模型如GPT-4、Claude、本地LLM的Token化方式有基本了解。使用正确的tokenizer来计算提示词和上下文的长度。一个常见的坑是英文和中文的Token消耗差异巨大一个中文字符可能被算作多个Token。在规划上下文窗口时必须用实际文本进行测试估算。上下文窗口的“水位线”管理不要试图把上下文窗口用到100%。需要为模型的思考和生成预留空间。一个经验法则是所有输入系统指令对话历史检索上下文问题的总Token数不超过模型限制的75%-80%。同时要设计一个优雅的“上下文窗口溢出”处理策略是丢弃最老的对话历史还是总结之前的对话或是提示用户开始一个新会话长上下文模型的诱惑与陷阱现在很多模型支持128K甚至更长的上下文。这并不意味着你可以把所有文档都塞进去。前面提到的“中间丢失”效应在长上下文中可能更明显。而且长上下文会显著增加计算成本通常按输入Token收费和生成延迟。长上下文的最佳用途是容纳少量但必须完整的长文档如一份完整的合同而不是海量的短片段。对于海量知识库检索精炼仍然是更经济有效的方案。针对Token限制的工程优化摘要Summarization对于长的对话历史定期用模型生成一个摘要然后用摘要替代原始历史可以大幅节省Token。分层检索先检索到文档ID如果文档本身很长不是整个文档放入上下文而是用一个更小的模型或提取式方法从该文档中提取出与问题最相关的几个段落。压缩Compression使用特定的提示词让模型对检索到的上下文进行压缩保留核心信息去除冗余。LangChain的LLMChainExtractor是一个思路但要注意压缩过程本身也会消耗Token和增加延迟。5. 实战构建一个带安全护栏的RAG问答系统让我们把以上所有概念串联起来设计一个简单的、但具备基本上下文工程和安全护栏的问答系统流程。系统架构用户输入处理校验输入长度、清洗恶意字符。话题安全检查若涉及禁止领域直接返回预设回复。智能检索与上下文构建使用EnsembleRetriever向量关键词进行初步检索获取Top-10候选文档。使用CrossEncoderReranker对Top-10进行重排选出Top-3。对Top-3文档进行PII信息脱敏处理。为每个文档片段添加结构化标识[Doc ID]。计算当前对话历史Token数采用“滑动窗口”或“摘要”策略管理历史。将处理后的文档按重排分数插入提示词模板的上下文部分确保总输入Token在预算内。提示词组装与模型调用组装系统指令定义角色、输出格式、安全要求。组装处理后的对话历史和上下文。使用PydanticOutputParser定义严格的输出JSON格式。调用大模型API并启用其内置的内容安全过滤。输出后处理与验证解析模型输出若解析失败触发重试或返回兜底答案。使用一个简单的一致性检查链验证答案中的核心主张是否得到上下文支持。对最终答案文本进行必要的安全转义。返回与日志将答案、引用来源返回给用户。记录本次交互的完整链路日志输入、检索到的文档ID、模型输入/输出、Token用量用于后续分析和优化。一个关键的避坑点在这个流程中安全检查和过滤可能发生在多个环节输入、上下文、输出可能会误伤正常请求。例如一个关于“如何应对网络暴力”的合法咨询可能因为包含“暴力”关键词而在输入层被拦截。因此你的安全规则需要足够精细并且要有“误判申诉”或“人工审核”的通道。更好的做法是输入层只做最粗暴的、明确的恶意攻击拦截如SQL注入模式更细粒度的内容安全交给专门的内容安全模型在输出层处理。6. 持续迭代与评估没有一劳永逸的护栏上下文工程和安全护栏不是一次性配置就能完成的。它们需要持续的监控、评估和迭代。建立评估体系上下文相关性评估人工或通过模型评估检索到的文档与问题的真实相关性。答案质量评估评估答案的准确性、有用性、是否基于上下文。安全事件记录记录所有被护栏拦截的请求分析误报和漏报。基于反馈的优化如果发现模型经常从某个低质量文档中产生幻觉考虑将该文档从知识库中移除或降权。如果某种类型的用户问题总是触发误判的安全规则调整规则的阈值或逻辑。监控Token消耗和延迟优化检索策略或上下文压缩方法以提升性能。护栏本身的维护对抗性攻击在不断发展。今天有效的关键词过滤明天可能被绕过。需要定期更新你的敏感词列表、重新训练你的安全分类模型并关注大模型提供商最新的安全能力更新。构建一个健壮的LangChain应用上下文工程决定了它的“智商上限”而安全护栏决定了它的“行为底线”。这两者都需要像开发核心功能一样投入精心的设计和持续的维护。忽略它们你的应用可能在演示时很酷但在真实世界中会变得脆弱、不可靠甚至危险。从今天开始把你的提示词工程思维升级到上下文与安全工程的系统思维是迈向生产级AI应用的必经之路。