想象一下,你家楼下新开了一家“阿模小厨”。主厨阿模脑子里装着一万本菜谱,上知天文下知地理,客人点什么都能接得住。但这家店有个江湖传说:阿模出菜全凭心情。高兴时鱼香肉丝根根分明、分量十足;不高兴时主料少一半、盐多放三勺,甚至把盘子倒扣着端出来。如果你是店主,会直接把这种菜端到客人面前吗?大概率不会。你会在厨房和餐桌之间,加一道“出餐质检台”:墙上贴着标准菜单卡,桌上摆着测量秤和温度计,台边还挂着过敏源检测仪。每道菜出锅,先在质检台验一轮——摆盘合不合规格、分量在不在范围、有没有违禁配料——合格的端出去,不合格的退回后厨重做,连续做不出来就换一道安全菜。这套“出餐质检制度”,就是大模型应用开发中的“输出验证与约束层”;而围绕大模型搭建的整套质检、退改、备援系统,今天被称作 Harness Engineering。本文就从这张“质检台”出发,讲清楚它为什么出现、怎么工作、如何落地。

岗位关联

对 AI 应用开发者而言,掌握输出验证与约束层,意味着你的大模型应用能从“偶尔出错”升级为“稳定可控”。这是把 Demo 变成生产力工具的必修课,也是 AI 工程化能力的分水岭。

目录
  1. 可视化演示
  2. 范式演进
  3. 核心概念
  4. 结构化输出
  5. 验证机制
  6. 安全护栏
  7. 容错重试
  8. 工程实践
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示LLM输出验证管道的四阶段流程、四种验证维度对比与三层分层防御体系,支持模式切换查看不同验证机制。

本节配有交互动效(进入视口后自动加载,约 3KB)。

验证管道 四阶段:解析验证约束决策 放行/修正/拒绝三路输出 P(valid)=P(parse)*P(val)*P(con) 数据流经管道逐层过滤

范式演进

店刚开张时,老板觉得做菜最难。后来才发现,真正难的是让阿模稳定发挥。最初老板只写“菜单卡”(提示词):要求主料多少克、口味分几个档。阿模大部分时候配合,但一忙就忘,一忘就乱。老板渐渐意识到,问题不在“菜谱写得好不好”,而在“阿模毕竟是个人,有状态、有脾气”。

在 AI 应用工程化早期,开发者也抱着同样的幻想:提示词写得好,模型就能稳定输出。但大模型是概率系统,同样的 Prompt 两次输出可能天差地别。于是行业走过三个阶段:裸调用阶段、提示词与上下文工程阶段,再到今天的 Harness Engineering 阶段。Harness 本意是马具、挽具,引申为大模型外围的整套工程装置:输入梳理、上下文注入、工具调用、输出校验、安全护栏、重试降级……

Harness Engineering 的关键思想是“分层解耦”:把“跟模型说话的能力”(提示词)、“给模型喂的背景”(上下文工程)、“限制模型不乱来”(约束与验证)拆成独立模块,各管各的。这样一来,即使模型底座更换,质检台依然可用;即使提示词重写,安全规则也不会丢。演进的动因也很直接:业务对可靠性、安全性、成本可控性的要求越来越高,靠“求厨师自觉”已经无法满足。

所以范式演进的本质,不是把菜谱写得更厚,而是承认厨师会飘,然后把“质检台”制度化地建起来。Harness 工程,就是给大模型配上整套鞍具——而输出验证与约束层,正是这套鞍具中最靠近“客人”的那道闸门。

Q=f(P, C, G)\displaystyle Q = f(P,\, C,\, G)
应用输出质量 Q 由三要素共同决定:提示词 P、上下文 C、约束护栏 G。过去我们只写 P,Harness 时代还要专门经营 C 和 G。
  1. Q应用最终交付给用户的输出质量,比如回答是否准确、格式是否合规、内容是否安全。它是顾客实际吃到的菜。
    Q∈[0,1]Q \in [0, 1]
  2. P(Prompt)提示词,与模型对话的“剧本”或“菜单卡”,描述希望模型做什么。写得再好也只是软约束。
    P=system prompt+user queryP = \text{system prompt} + \text{user query}
  3. C(Context)上下文工程,给模型补充的外部背景与知识,比如 RAG 检索回来的资料,相当于给厨师准备的备菜台。
    C=retrieved chunks+tool resultsC = \text{retrieved chunks} + \text{tool results}
  4. G(Guardrails)约束与护栏,包括输出验证、Schema 约束、内容安全过滤等,是质检台的实体装置。
    G=validation+constraint+safetyG = \text{validation} + \text{constraint} + \text{safety}

核心概念

客人等菜时,最着急也最容易出问题的就是“出菜那一刻”。阿模把菜端到窗口,老板伸手一接,菜盘斜了、酱汁洒了、少了主料——一切毛病都在出锅这最后一米暴露。输出验证与约束层,就是站在“厨房与餐桌之间”的那道窗口。

所谓输出验证,是指在大模型生成文本之后,用程序化手段检查输出是否符合预期;所谓约束层,则是提前或在生成过程中给模型划出的“行为边界”。两者合在一起,构成应用架构里“最后一道关”:无论上游提示词写得如何、上下文检索得多准,只要模型输出没过这道关,一律不许进入下游业务。

我们用一个验证器函数 V 来描述这道关:给定模型输出 o,V(o) 要么放行(1)、要么拦截(0)。约束层在横向上分成几类:结构约束(必须是合法 JSON、字段齐全)、值域约束(枚举、范围、正则)、安全约束(不越狱、不涉敏)、业务约束(满足内部规则)。验证器可以是一个纯函数,也可以由多个校验器复合而成;甚至可以把 LLM 本体当作验证器来用,也就是 LLM-as-judge——让一个模型来评另一个模型输出得好不好。

回到店铺里,质检窗口存在的意义就是让老板不必“盲收”阿模的每一道菜:看到对勾再递出去,看到红叉就退回去。对大模型应用开发者来说,这个“窗口”同样是系统的安全底线。一个没有输出验证的 LLM 应用,就像一家没有质检台的餐馆,出不出事,全看厨师心情。

V(o)  =  {1,通过0,拒绝\displaystyle V(o) \;=\; \begin{cases} 1, & \text{通过} \\ 0, & \text{拒绝} \end{cases}
整个约束层可以抽象成一个“二值门卫”:输出合格放行,不合格拦截。至于“合格”如何定义,由后面几章一层层展开。
  1. V验证器(Validator),可以是规则代码、Pydantic 模型、内容分类器或它们的组合。相当于质检员本人。
    V:O→{0,1}V: O \to \{0, 1\}
  2. o模型输出的原始文本或对象,比如一句回答、一段 JSON、一篇文章。相当于厨房端出来的菜。
    o∈Oo \in O
  3. 1放行,输出被交付给下游业务逻辑,可以正常展示或入库。
    V(o)=1⇒acceptV(o) = 1 \quad \Rightarrow \quad \text{accept}
  4. 0拦截,输出被丢弃、重试或降级处理,绝不直接放行。
    V(o)=0⇒rejectV(o) = 0 \quad \Rightarrow \quad \text{reject}

结构化输出

质检台墙上挂着一张“标准菜单卡”,白纸黑字写着:每道菜必须包含三样——菜名(2 到 20 个字)、口味(三选一)、价格(10 到 200 元)。阿模再怎么发挥,端出来的菜必须能对上这张卡。对不上?退回。

在 LLM 应用中,这张“标准菜单卡”叫做 Schema(数据模式)。最常见的是 JSON Schema 或 Pydantic 模型。开发者定义好 schema 后,在提示词里要求模型“只输出符合该 schema 的 JSON”,同时在后端用 schema 解析与校验模型输出。这样一来,下游代码拿到的就不再是“一段随机的文字”,而是“一个结构完全确定的数据对象”。

这里有个隐藏难点:模型输出的 JSON 可能是残缺的、类型错的、甚至根本不是合法 JSON。于是约束层引入两层处理:先解析(parse),后校验(validate/cast)。解析把字符串变成 JSON 对象;校验发现“价格写了『四十八块』这种非数字”,则按 schema 强转或报错。现代工具如 Pydantic v2 能在一步中完成解析、类型强转和约束校验,并抛出可读的错误信息。字段治理还包括:缺失必填字段直接不通过;多余字段默认忽略或触发警告。

菜单卡让阿模的“即兴发挥”有了边界,也让质检员的检查有了依据。没有 Schema,所谓“输出验证”就像没有菜单卡却要求“做得像样”——标准模糊,自然无从谈起。在企业级 AI 应用中,结构化输出往往是所有下游自动化的地基。

o′  =  Φ(Ψ(o))\displaystyle o' \;=\; \Phi\bigl(\Psi(o)\bigr)
原始输出 o 先经过解析函数 Ψ 变成结构化对象,再经过校验函数 Φ 按 Schema 修正与定型,最终得到可信的 o'。
  1. o大模型输出的原始字符串,可能是一段文本或一堆乱码字符。
    o=LLM raw outputo = \text{LLM raw output}
  2. Ψ(parsing)解析器,把 JSON 字符串转成字典或对象;解析失败则抛出解析错误。
    Ψ(o)→dict/object\Psi(o) \to \text{dict}/\text{object}
  3. Φ(validation/cast)校验与强转函数,按 Schema 检查字段是否存在、类型是否正确、值域是否合规。
    Φ(⋅)→typed fields\Phi(\cdot) \to \text{typed fields}
  4. o'通过校验后的安全结构化数据,可以直接交给下游业务。
    o′=clean structured datao' = \text{clean structured data}

验证机制

光有菜单卡还不够。质检员把菜端起来,先看摆盘是否标准(格式),再尝一口确认味道对不对(语义),最后还要查一眼订单备注:“客人不吃香菜”(业务上下文)。三重检查都过了,才盖合格章。

输出验证在工程上也是分层复合的:规则校验、语义校验、业务校验。三层各管一摊,组合成最终的验证器。

第一层,规则校验(Rule):最廉价、最确定。用正则、枚举、数值范围、必填字段等硬条件检查输出。例如口味必须是三选一,价格必须落在 10 到 200 元之间。第二层,语义校验(Semantic):当规则无法判断“对不对”时,用 Embedding 相似度比较输出与参考答案,或请一个轻量 LLM 当裁判(LLM-as-judge),判断输出是否答非所问。第三层,业务校验(Business):把业务逻辑编码进验证器,比如“订单金额等于各商品金额之和”、“退货日期不能晚于今天”。业务层往往是客户定制化最多的部分,也最能体现 Harness 的工程价值。

阿模的菜可以色香俱全(格式对)、味道在线(语义对),但只要订单写着“不要香菜”而他放了香菜(业务违例),照样退回。对 AI 应用来说,业务校验往往才是客户真正愿意付费的那一关——因为它是每个公司独有、别人拿不走的规则资产。

Vfinal(o)  =  Vrule(o)  ∧  Vsemantic(o)  ∧  Vbusiness(o)\displaystyle V_{\text{final}}(o) \;=\; V_{\text{rule}}(o) \;\wedge\; V_{\text{semantic}}(o) \;\wedge\; V_{\text{business}}(o)
最终通过,必须同时满足规则、语义、业务三层验证。一个不满足,整体为失败——质检员的每一关都不可跳过,一票否决。
  1. V_rule规则校验器,硬编码的语法与值域检查,毫秒级完成。
    Vrule(o)=regex+range+enumV_{\text{rule}}(o) = \text{regex} + \text{range} + \text{enum}
  2. V_semantic语义校验器,判断内容“在不在理”,可用向量相似度或小模型评分。
    Vsemantic(o)=similarity(o,reference)V_{\text{semantic}}(o) = \text{similarity}(o, \text{reference})
  3. V_business业务校验器,嵌入业务断言,保证数据可被下游逻辑直接消费。
    Vbusiness(o)=business rulesV_{\text{business}}(o) = \text{business rules}
  4. ∧(AND)逻辑与,必须全部为真才放行。对应质检员的“一票否决”。
    a∧b∧c={1,a=b=c=10,otherwisea \wedge b \wedge c = \begin{cases} 1, & a=b=c=1 \\ 0, & \text{otherwise} \end{cases}

安全护栏

质检台边上还有一台“违禁品检测仪”。客人对花生过敏是写在档案里的,阿模有时会忘记,但检测仪不会:只要测出花生,直接亮红灯,这道菜再好吃也出不了窗口。这就是安全护栏。

LLM 的输出安全比格式正确更致命。模型可能被恶意提示词诱导(越狱攻击)、可能生成仇恨言论或暴力内容、可能泄露用户隐私(PII)。于是约束层里专门辟出“安全护栏”子层,用独立的分类器做最后一米的拦截。

常见手段有三类:其一是内容安全分类器,对输出文本打分,分数超过阈值即拒绝放行,比如 OpenAI Moderation API;其二是越狱防御,识别“假装故事、角色扮演、前缀注入”等攻击模式,在输出侧再兜一道;其三是合规脱敏,用 PII 识别模型把手机号、身份证、邮箱等打码或拦截。碰到被拦截的内容,产品层通常返回固定话术,比如“抱歉,我不能回答这个问题”,避免向用户暴露安全机制细节。这里要诚实承认:没有任何方案能 100% 拦截一切风险,工程上追求的是“把安全事件控制在可接受概率内”,同时保留完整审计日志。

检测仪不会跟厨师商量“这道菜的花生其实特别香”,它只会执行“过敏原就不许出门”这条铁律。同样,安全护栏不该依赖模型的道德自觉——它必须是码在代码里的、不带商量的规则。尤其在企业合规场景下,这条铁律就是底线。

accept(o)  =  [ Sharm(o)<θ ]\displaystyle \text{accept}(o) \;=\; \bigl[\, S_{\text{harm}}(o) < \theta \,\bigr]
若安全分类器给输出 o 打出的“有害度”S_harm 小于阈值 θ,则放行;否则拦截。中括号表示“条件成立时为 1,否则为 0”。
  1. S_harm有害度评分函数,输出一个实数值,越大越危险。
    Sharm(o)∈[0,1]S_{\text{harm}}(o) \in [0, 1]
  2. θ(theta)安全阈值,由业务方设定,比如 0.8。阈值选取需要在“误杀”和“漏网”之间做取舍。
    θ=0.8\theta = 0.8
  3. accept(o)最终放行决定,1 放行、0 拦截。
    accept(o)∈{0,1}\text{accept}(o) \in \{0, 1\}
  4. 方括号(Iverson 括号)条件为真时为 1,否则为 0,是一种简洁的数学记号。
    [x<θ]={1,x<θ0,x≥θ[x < \theta] = \begin{cases} 1, & x < \theta \\ 0, & x \geq \theta \end{cases}

容错重试

质检台把菜退回后厨时,不会只扔一句“不行”,而是在退回单上写明原因:“香菜未去、分量不足、价格标签缺失”。阿模看了一眼退回单,恍然大悟,赶紧重做。这就是“带反馈的重试”。但老板也立了规矩:同一道菜最多退回三次,三次还不行,就给客人上“安全套餐”。

输出验证必然有失败率。工程上不能让失败直接变成事故,而应设计出一条“容错路径”:无论重试还是降级,都让系统在失败时优雅地继续工作。

重试机制的核心是把验证错误信息拼进下一次调用的提示词,让模型“看着错题重考”。这种“看一眼错题再写一遍”的做法能显著提升成功率,但也有代价:每一次重试都是一次新的模型调用,Token 成本和延迟成倍增加。因此必须设上限,业界常见 2 到 4 次。超过上限,激活降级策略(Fallback):返回一个默认值、使用最近一次成功的缓存、或走一段人工兜底流程。同时,每次失败都要上报监控,方便团队发现“哪口锅总出什么菜”——是哪类 Schema 总错、哪类攻击总打穿护栏。

“退回单”让重试不是盲目地再来一遍,而是带着错题信息修正;“三次上限”让厨师不会无限折腾后厨;“安全套餐”保证客人永远不会空手而回。容错重试,是质检台里最体现工程智慧的部分——它承认失败,同时把失败的成本和影响都管理起来。

Ps  =  1−(1−p) n\displaystyle P_s \;=\; 1 - (1-p)^{\,n}
若单次调用通过率为 p,则最多尝试 n 次后至少成功一次的概率。p=0.6、n=3 时成功率约为 93.6%,说明重试次数不必太多就能获得高可靠性。
  1. p单次生成通过验证的概率,例如 0.6。它是模型能力与约束层严格程度共同决定的结果。
    p=0.6p = 0.6
  2. 1 - p单次失败概率。
    1−p=0.41 - p = 0.4
  3. n最大尝试次数(含首次调用),例如 3。
    n=3n = 3
  4. (1-p)^nn 次全部失败的概率。注意指数让失败率快速缩小。
    (1−p)n=0.43=0.064(1-p)^n = 0.4^3 = 0.064
  5. 1 - (1-p)^nn 次中至少一次成功的概率,随 n 增大趋向 1,但边际收益递减。
    Ps=1−0.064=0.936P_s = 1 - 0.064 = 0.936

工程实践

把质检台从“一张桌子”升级成“一套制度”,要点有四:守门员的站位要固定(双守门)、工具要顺手(工具链)、成本要记账(成本与延迟)、每天的退改记录要可追溯(可观测性)。

落到真实工程中,输出验证与约束层不是孤立函数,而是要嵌入整个应用流水线。最流行的架构是“双守门”:输入侧一个 Guard 校验用户指令(防注入、防越狱),输出侧一个 Guard 校验模型生成(防有害、防低质、防格式错)。输出侧就是本文主角的战场。

工具链方面,Python 生态首选 Pydantic 加 JSON Schema;大型系统可上 NeMo Guardrails 或 Guardrails AI 这类框架;对结构化生成要求极致时,可用 Outlines 等在解码阶段直接限制生成路径。成本与延迟是必须正视的:加一层验证,就多一层计算。工程上的通用策略是“轻量优先、重型兜底”:先用正则和范围这类近似零成本的校验过滤绝大多数坏输出,只在疑似问题时才升级到 LLM-as-judge。可观测性同样关键:要记录每次校验的通过率、失败原因、重试次数和耗时,作为调参的决策依据——比如让 Schema 更宽松、让重试更精准。

一套能长期运转的质检制度,绝不能只靠质检员“手勤”。双守门、标准工具、成本台账、退改记录——这些合在一起,才让“阿模小厨”从一个人的手艺,变成了一个可复制、可审计、可优化的餐饮品牌。对 AI 应用团队来说,这就是把实验性 Demo 工程化、产品化的必经之路。

Ctotal  =  nattempts×Cllm  +  Cverify\displaystyle C_{\text{total}} \;=\; n_{\text{attempts}} \times C_{\text{llm}} \;+\; C_{\text{verify}}
总成本 = 尝试次数 × 单次模型调用成本 + 验证层自身成本。这个式子解释了为什么不能无限重试,也提醒我们验证层要尽量轻。
  1. C_total一次业务请求付出的总成本,包含令牌费、算力费和验证开销。
    Ctotal=full costC_{\text{total}} = \text{full cost}
  2. n_attempts尝试次数,每重试一次就多一次 LLM 调用,成本线性上涨。
    nattempts=1+retriesn_{\text{attempts}} = 1 + \text{retries}
  3. C_llm单次 LLM 调用的令牌成本与算力成本,与输入和输出长度正相关。
    Cllm=f(tokens)C_{\text{llm}} = f(\text{tokens})
  4. C_verify验证层本身的成本,包括校验代码执行、分类器调用、额外的裁判模型调用。
    Cverify=Crules+Cclassifier+CjudgeC_{\text{verify}} = C_{\text{rules}} + C_{\text{classifier}} + C_{\text{judge}}

AI 应用场景

金融理财助手的输出校验

阶段:输出验证与约束层(结构化输出 + 业务校验)

解决的问题:传统做法只靠 Prompt 约束,理财助手可能输出“风险等级”乱码、建议超出客户风险承受能力,轻则体验差,重则合规事故。引入约束层后,模型输出先过 Schema 强校验,再过业务断言,让“质检台”成为最后一道防线。

具体案例:某银行 App 的理财助手用 Pydantic 定义 RiskAdvice 模型,risk_level 限定为“保守/稳健/激进”三选一,advice 文本长度限制在 5 到 200 字。模型输出后自动解析校验;再加一条业务断言:若客户风险测评等级为保守,则建议中不允许出现“炒股票”等高风险字样。校验失败的输出携带错误信息重试一次,仍失败则返回模板“已为您联系客户经理”。

电商评论自动分类流水线

阶段:结构化输出 + 语义验证

解决的问题:评论分类标签不稳定,导致运营统计失真。约束层用 JSON Schema 固定输出结构,用语义校验防止“好评标签配差评正文”之类的自相矛盾。

具体案例:某电商后台每天数十万条评论交由 LLM 打标签,要求输出 JSON:sentiment 只能是 positive/neutral/negative,tags 必须是预设 50 个标签之一。校验层先做规则检查,再用 Embedding 相似度判断 summary 与 sentiment 是否一致,不一致则重试;重试失败自动进入人工审核队列,绝不把脏数据写进数据仓库。

UGC 社区的 AI 回复合规审核

阶段:安全护栏(双守门架构)

解决的问题:社区用 LLM 自动回复用户帖子,纯提示词过滤存在漏网之鱼,曾出现引战回复被投诉的公关事故。输出侧再加一道安全护栏,把风险内容拦截在发布之前,守住内容安全的最后一道闸门。

具体案例:某内容社区对 AI 生成的“楼中楼”回复做双守门:输入侧用 Moderation API 过滤用户帖子,输出侧再跑一次有害分类加 PII 脱敏。若有害评分达到阈值,或出现手机号、身份证等敏感模式,则把回复替换为“内容审核中,请稍候”。同时把命中样本回传,用于持续优化提示词和阈值。

Python 演示

python44 行
import jsonimport randomfrom pydantic import BaseModel, Field, ValidationError  class Dish(BaseModel):    # 标准菜单卡: 定义结构化输出约束    name: str = Field(..., min_length=2, max_length=20)    taste: str = Field(..., pattern="^(清淡|微辣|重辣)$")    price: float = Field(..., ge=10, le=200)  class MockLLM:    # 假模型: 模拟LLM的概率性输出, 偶尔不守规矩    bad_tastes = ["变态辣", "", "特浓香菜味"]     def generate(self, prompt):        r = random.random()        if r < 0.55:            return json.dumps({"name": "宫保鸡丁", "taste": "微辣", "price": 48})        if r < 0.80:            return json.dumps({"name": "宫保鸡丁", "taste": random.choice(self.bad_tastes), "price": random.choice([5, 999])})        if r < 0.95:            return "{name: 宫保鸡丁, taste: 微辣, price: 元}"        return "[]"  def retry_parse(prompt, max_retries=3):    llm = MockLLM()    for attempt in range(1, max_retries + 1):        raw = llm.generate(prompt)        try:            dish = Dish.parse_raw(raw)            print(f"[第{attempt}次校验] 通过 -> {dish}")            return dish        except (ValidationError, TypeError, json.JSONDecodeError) as exc:            print(f"[第{attempt}次校验] 失败: {exc}")    print("重试超限, 启用降级备援: 返回默认套餐")    return Dish(name="默认套餐", taste="清淡", price=20)  if __name__ == "__main__":    random.seed(42)    retry_parse("给我推荐一道川菜")
目的:用 Pydantic 实现“标准菜单卡”(Schema),模拟 LLM 的不稳定输出,并演示“解析校验 → 失败重试 → 超限降级”的完整链路。
  1. class Dish(BaseModel):标准菜单卡:声明字段、类型和约束,这就是约束层的“卡面”。
  2. taste: str = Field(..., pattern="^(清淡|微辣|重辣)$")用正则限定口味只能三选一,超出即校验失败。
  3. price: float = Field(..., ge=10, le=200)限定价格在 10 到 200 元之间,防止模型乱报价。
  4. r = random.random()模拟 LLM 的概率性输出:不同概率区间产生合规、越界、乱码等不同结果。
  5. dish = Dish.parse_raw(raw)一步完成 JSON 解析、类型强转与约束校验,这是质检台的核心动作。
  6. except (ValidationError, TypeError, json.JSONDecodeError) as exc:捕获三类不合格:校验失败、类型错误、非法 JSON。
  7. print(f"[第{attempt}次校验] 失败: {exc}")模拟“退回单”:把具体失败原因暴露出来,方便观察重试过程。
  8. return Dish(name="默认套餐", taste="清淡", price=20)降级备援分支:试了三次还不行,就上一道安全菜。

常见误区

误区模型输出格式不对?那我就在提示词里多写几句“请严格输出JSON”“别乱来”。很多人觉得只要提示词够好,模型就不会乱,约束层根本没必要。

正解提示词是“软约束”,模型是概率性系统,总会有失误率。输出验证与约束层是“硬约束”,通过代码校验、Schema 定型、失败重试把失误拦截在系统边界之外。只靠提示词,就像只靠厨师自觉,不建质检台——你永远不知道他哪天心情不好。

为何常见:个人使用大模型时,多写几个字效果立竿见影,容易把“运气好”误当成“提示词万能”。

误区校验失败就重试呗,把 max_retries 设成 99,多试几次总有一次成功。反正模型调用又不贵。

正解单次通过率 p 固定时,重试 n 次的成功率是 1-(1-p)^n。当 n 变大,成功率趋于上限但成本线性增加,边际收益急剧下降。必须设上限(2 到 4 次),并准备降级方案。无限重试就像让厨师把同一道菜做 99 遍,后厨早就瘫痪了。

为何常见:人脑直觉以为“再试一次”没有成本,忽略了 LLM API 的每次调用都在产生真实费用和延迟。

误区JSON Schema 不就是检查一下 JSON 合不合法嘛,字段值写什么它不管。业务规则还得我自己写。

正解现代验证层(Pydantic、JSON Schema)不仅能校验类型、缺失、多余字段,还能约束范围(ge/le)、枚举、正则匹配,并通过 @field_validator 自定义任意业务断言。校验器里可以直接实现“日期不晚于今天”“金额不超过余额”这类规则。

为何常见:初学者只看过 parse 的示例,把验证等同于“能转成 JSON 就行”,没有深入了解 Schema 的完整表达能力。

误区用户输入都过滤干净了,模型输出自然安全。大不了在 Prompt 里加一句“不要输出有害内容”。

正解输入安全不等于输出安全。模型可能被越狱诱导,也可能在看似正常的上下文里“合理”地输出有害内容。输出侧安全护栏必须独立存在,与输入侧构成双守门。这就好比客人进门要安检,但后厨端出来的菜也要过检测仪。

为何常见:人习惯“源头治理”的直觉,认为入口拦住了一切,出口就没事,忽略了 LLM 的不可预测性和攻击者的对抗性。

误区规则校验太“笨”,正则和枚举根本看不出语义错误。干脆每次输出都请一个 GPT-4 当裁判打分,贵一点但准。

正解LLM-as-judge 虽强,但每次调用都带来显著延迟和成本。工程实践是“轻量优先、重型兜底”:先用零成本的正则和范围过滤掉 80% 的坏输出,只对疑似问题升级到重验证器。大炮打蚊子,打中一只,弹药费却比蚊子贵一万倍。

为何常见:追求“最强技术”而忽略成本工程,容易被 Demo 效果迷惑,忘了生产环境要算账。

面试题

Q1. 输出验证与约束层在 LLM 应用中的主要作用是什么?

单选题 · easy

  • A.让用户的输入更加规范
  • B.在模型输出后校验与约束← 正确答案
  • C.提高模型训练与收敛速度
  • D.替代检索增强生成模块

答案:B。输出验证与约束层专门在大模型生成之后做“质检”:检查格式、值域、语义与安全,不合规就拦截。A 是输入侧的事,D 是上下文工程的事,C 更是无关——质检台只盯着出锅的菜,不参与炒菜也不参与种地。

Q2. 以下哪个工具最适合做结构化输出校验?

单选题 · easy

  • A.Pandas数据处理与分析工具
  • B.Matplotlib基础绘图与可视化
  • C.Pydantic结构化数据校验工具← 正确答案
  • D.NumPy科学计算与数值工具

答案:C。Pydantic 自带类型强转、字段约束和校验逻辑,正是“标准菜单卡”的完美载体。Pandas 侧重数据分析,Matplotlib 负责画图,NumPy 做数值计算——它们都不是为“约束模型输出”而生的工具。

Q3. 关于“约束层”与“提示词”的区别,下列说法正确的是?

单选题 · mid

  • A.约束层靠代码硬性强制,提示词靠模型自觉← 正确答案
  • B.提示词靠代码硬性强制,约束层靠模型自觉
  • C.两者没有本质区别只是叫法上不同
  • D.约束层主要用于加速模型训练流程

答案:A。提示词是“软约束”,模型可能遵守也可能不遵守;约束层是“硬约束”,用代码在解析、校验、拦截环节强制生效。C 混淆了两者本质,D 则完全偏离主题——约束层管的是推理期的输出,跟训练加速无关。

Q4. 在重试机制中,把校验错误信息传回模型再调用,主要作用是什么?

单选题 · mid

  • A.携带错误信息让模型自动修正← 正确答案
  • B.提高大模型API限流访问权限
  • C.增加模型训练的样本数量比例
  • D.自动降低模型温度的随机参数

答案:A。这叫“带反馈的重试”,相当于质检台在退回单上写明“香菜未去、分量不足”,厨师得以照着错题重做。B 是权限问题,C 是训练问题,D 是采样参数问题,都与“让模型看错题”无关。

Q5. 已知某 LLM 单次输出通过验证的概率为 0.6,最多重试 3 次(共尝试 4 次),总成功概率约为多少?

单选题 · hard

  • A.约84.0%
  • B.约87.0%
  • C.约90.2%
  • D.约97.4%← 正确答案

答案:D。总成功概率 = 1 -(单次失败率)^ 尝试次数 = 1 - 0.4^4 = 1 - 0.0256 = 97.4%。这说明即使单次通过率只有 60%,重试 3 次也能把成功率抬到 97% 以上,所以重试策略不必无限叠加就能获得高可靠性。

Q6. 关于“输入侧守门”与“输出侧守门”,下列说法正确的是?

单选题 · mid

  • A.输入侧安全则输出自动安全
  • B.只需输出侧守门输入不重要
  • C.双守门架构只在云端环境部署
  • D.输入输出双守门构成纵深防御← 正确答案

答案:D。双守门架构是“进门安检 + 出菜检测”的组合,构成纵深防御。A 忽略了模型可能被越狱或产生新风险,B 忽略了输入侧防注入的重要性,C 则是部署位置的误解——双守门与部署环境没有必然关系。

Q7. LLM 输出的 JSON 格式混乱,工程上第一步应该怎么做?

单选题 · easy

  • A.扩大模型训练数据集的规模
  • B.用Schema校验并把错因回传重试← 正确答案
  • C.立即放弃所有JSON结构化输出
  • D.只保留字符串类型不做任何转换

答案:B。正确做法是先定义 Schema,对输出做解析校验,失败时把错误信息回传给模型重试。A 是训练层面的事,一次推理错误不需要重训模型;C 因噎废食;D 放弃结构化只会让下游更难处理。

Q8. 输出验证与约束层常见的实现手段有哪些?

多选题 · hard

  • A.用JSON Schema强制结构字段← 正确答案
  • B.用Pydantic做类型转换与校验← 正确答案
  • C.用内容安全分类器判定危险← 正确答案
  • D.修改模型内部参数与权重数值

答案:ABC。JSON Schema 定义结构,Pydantic 提供解析和校验,内容安全分类器负责有害内容拦截,三者是约束层的三大支柱。D 修改模型参数属于模型训练或微调范畴,不属于推理期的输出验证手段。

Q9. 输出验证与约束层会带来哪些额外开销?

多选题 · mid

  • A.额外的令牌消耗与费用成本← 正确答案
  • B.增加输出的延迟与响应耗时← 正确答案
  • C.让模型训练过程变得更稳定
  • D.重试机制带来额外的调用耗时← 正确答案

答案:ABD。重试意味着多次调用,Token 费和延迟都会上升;验证器自身(如 LLM 裁判)也要消耗计算资源。C 说的是训练稳定性,与推理期的输出验证无关——质检台不关心厨师怎么学手艺,只关心这道菜能不能出。

Q10. 当输出反复失败超过重试上限时,降级备援(Fallback)方案通常包括哪些?

多选题 · extreme

  • A.使用固定模板返回默认兜底值← 正确答案
  • B.直接返回最近一次缓存的结果← 正确答案
  • C.把错误信息拼进提示词再重试← 正确答案
  • D.修改大模型的激活函数做修复

答案:ABC。固定模板、缓存结果、带错误信息重试都是常见的降级手段,它们确保“客人不会空手而归”。C 本质上是重试策略,也算一种兜底路径;D 修改激活函数属于模型底层改造,绝不是约束层的降级方案。