以适当的谨慎对待大型语言模型(LLM)生成的输出,对于构建安全应用必不可少。LLM根据从大量数据集中学习到的模式生成文本,但它们缺乏对安全影响、上下文界限或其生成内容对下游系统潜在影响的固有理解。原始的LLM输出绝不应被盲目信任或直接传递给敏感功能或执行环境。安全的输出处理和解析构成了抵御LLM应用特有多种漏洞的主要防御层。未经检查的LLM输出的风险未能正确处理和解析LLM输出可能使您的应用面临重大风险:间接提示注入: LLM生成的输出可能包含恶意指令,旨在操纵处理链中后续的另一个LLM或系统。例如,一个总结用户反馈的LLM可能会无意中将用户尝试注入提示的内容包含在总结中,这可能影响后续的分析LLM。数据泄露: 尽管模型经过训练以避免泄露特定的训练数据实例,但如果提示得当,或者敏感数据在生成过程中无意中被包含在上下文窗口中,它们有时会生成包含敏感模式、占位符甚至重构片段的文本。不安全的代码执行: 如果您的应用期望从LLM获取代码(例如SQL、Python、JavaScript)并直接执行,输出中恶意或仅仅是不正确的代码可能导致严重后果,例如数据损坏、未经授权的访问或远程代码执行(RCE)。拒绝服务(DoS): LLM可能生成过长、计算成本高昂(例如,复杂的正则表达式)或深度嵌套的结构化数据(如JSON),这些数据可能使解析器、数据库或其他下游组件不堪重负,导致服务不可用。逻辑缺陷和可利用的格式: 输出可能在语法上正确,但在逻辑上存在缺陷,从而可利用后续的处理步骤。例如,如果解析过于宽松,生成带有意外字段或数据类型的JSON可能会绕过业务逻辑检查。格式错误的输出也可能在错误处理不足的情况下直接导致下游解析器崩溃。安全输出解析和处理的策略实现输出处理涉及多层防御,通常结合使用LangChain的解析组件和自定义验证逻辑。利用结构化输出解析器尽可能引导LLM生成可预测的结构化格式输出(如JSON或YAML),并使用LangChain为这些格式设计的OutputParser实现。这比解析自由格式文本安全得多。PydanticOutputParser: 这通常是复杂数据结构的首选。定义一个Pydantic模型来表示预期的输出模式。解析器将尝试将LLM的输出字符串解析为此模型的一个实例,自动验证Pydantic模型中定义的数据类型、必需字段和约束。from langchain_core.pydantic_v1 import BaseModel, Field from langchain_core.output_parsers import PydanticOutputParser from langchain_openai import ChatOpenAI from langchain_core.prompts import PromptTemplate # 定义您所需的数据结构。 class AnalysisResult(BaseModel): sentiment: str = Field(description="文本的情感(积极、消极、中性)") key_topics: list[str] = Field(description="讨论的主要主题列表") confidence_score: float = Field(description="置信度分数(0.0到1.0)") # 设置解析器 parser = PydanticOutputParser(pydantic_object=AnalysisResult) # 定义带有格式指令的提示 prompt_template = """ 分析以下文本: {user_text} {format_instructions} """ prompt = PromptTemplate( template=prompt_template, input_variables=["user_text"], partial_variables={"format_instructions": parser.get_format_instructions()}, ) # 示例用法(假设'llm'是一个已初始化的LLM) # llm = ChatOpenAI(model="gpt-4o", temperature=0) # chain = prompt | llm | parser # try: # result: AnalysisResult = chain.invoke({"user_text": "LangChain is great for building LLM apps!"}) # print(f"Sentiment: {result.sentiment}, Confidence: {result.confidence_score}") # except Exception as e: # print(f"Output parsing failed: {e}") SimpleJsonOutputParser / JsonOutputParser: 对于Pydantic可能过于复杂的简单JSON对象很有用,尽管PydanticOutputParser通常提供更强的验证。自定义解析器: 对于内置解析器不支持的独特格式或复杂验证逻辑,继承BaseOutputParser并实现自定义的parse逻辑(如第1章所述)。实施严格的验证和净化仅靠解析是不够的。始终验证解析后输出的内容和结构。模式强制: 严格使用Pydantic或类似的模式验证库。拒绝任何不符合预期结构、类型或值约束(例如,枚举值、数值范围)的输出。内容净化: 即使结构正确,内容也可能有害。HTML/脚本转义: 如果输出用于网页渲染,请务必转义HTML和JavaScript内容(例如,使用Python的html.escape)以防止跨站脚本(XSS)。黑名单/白名单过滤: 过滤掉已知的危险模式(例如,如果输出影响数据库查询,则过滤SQL注入关键字,如DROP TABLE、UNION SELECT),或只允许预期模式/值。这对于自由格式文本来说有难度,但对于结构化数据字段则更可行。控制字符移除: 清除可能在下游系统中引起问题的意外控制字符。长度和复杂度限制: 对输出字符串的大小或解析结构(例如,最大列表长度、JSON中的最大嵌套深度)的复杂度/深度施加合理限制,以减轻DoS风险。digraph G { rankdir=LR; node [shape=box, style=rounded, fontname="Arial", fontsize=10, margin=0.2]; edge [fontname="Arial", fontsize=9]; LLM [label="LLM\n生成", shape=cylinder, style=filled, fillcolor="#a5d8ff"]; RawOutput [label="原始文本输出", style=filled, fillcolor="#ffec99"]; Parser [label="结构化解析器\n(例如,PydanticOutputParser)", style=filled, fillcolor="#d0bfff"]; Validator [label="模式与内容\n验证", style=filled, fillcolor="#b2f2bb"]; Sanitizer [label="输出净化\n(例如,转义)", style=filled, fillcolor="#ffd8a8"]; SafeOutput [label="安全、已解析的输出", shape=ellipse, style=filled, fillcolor="#96f2d7"]; ErrorHandler [label="错误处理\n(日志、重试、回退)", style=filled, fillcolor="#ffc9c9"]; LLM -> RawOutput; RawOutput -> Parser [label=" 尝试解析 "]; Parser -> Validator [label=" 已解析数据 "]; Parser -> ErrorHandler [label=" 解析失败 "]; Validator -> Sanitizer [label=" 有效数据 "]; Validator -> ErrorHandler [label=" 无效数据 "]; Sanitizer -> SafeOutput [label=" 已净化数据 "]; Sanitizer -> ErrorHandler [label=" 净化失败 / 风险内容 "]; SafeOutput -> "Downstream System" [style=dashed]; ErrorHandler -> "Downstream System" [label=" 安全回退 / 错误 ", style=dashed]; subgraph cluster_processing { label = "安全输出处理流程"; style=filled; fillcolor="#e9ecef"; Parser; Validator; Sanitizer; ErrorHandler; SafeOutput; } }一个典型的LLM输出安全处理流程,包括解析、验证、净化和错误处理,然后才能在下游系统中使用。安全处理代码生成如果您的应用依赖于LLM生成可执行代码:绝不要对原始LLM输出使用eval()或其等效函数。使用沙盒: 在严格受控的隔离环境中执行生成的代码(例如,具有受限权限的专用Docker容器、WebAssembly运行时或专门的代码执行沙盒服务)。监控资源使用情况(CPU、内存、网络)以防止DoS。限制能力: 仅授予执行环境最低必需的权限。例如,如果生成SQL,尽可能使用只读数据库用户。静态分析: 对生成的代码执行静态分析,以便在执行前检测潜在的恶意模式,如果目标语言可行的话。使用支持函数/工具调用的模型现代LLM通常支持通过函数或工具调用功能生成结构化输出(如第2章所述)。提示模型使用具有定义模式的特定工具/函数,可以促使其生成符合该模式的输出(通常是JSON)。尽管并非万无一失(模型仍然可能产生幻觉或生成格式错误的参数),但这与仅在主提示文本中进行指令相比,显著提高了获取结构化、可解析输出的可靠性。LangChain集成了这些能力,使其更易于使用。实施错误处理解析和验证有时会失败。为此做好计划:捕获异常: 将解析和验证逻辑封装在try...except块中,以便优雅地处理错误,而不是导致程序崩溃。重试机制: 实施如LangChain的OutputFixingParser之类的策略,它会尝试将错误反馈给LLM以修正输出。谨慎使用重试,因为它们会增加延迟和成本。回退策略: 定义安全的回退行为:返回默认值、请求用户澄清,或返回特定的错误消息。避免回退到处理原始、未解析的输出。日志记录: 记录所有解析/验证失败,包括有问题的输出(如果包含敏感信息,可截断或净化)和错误详情。这对于监控和调试(与第5章相关联)非常重要。将LLM输出视为不可信输入是安全LLM应用开发的一项基本原则。通过结合结构化解析、严格验证、上下文感知的净化、安全执行实践(如适用)和健壮的错误处理,您可以显著减轻与不可预测或潜在恶意模型生成相关的风险。这种多层方法确保了集成到您的应用逻辑中的输出既可用又安全。