这段时间一直在折腾 Agent 项目,遇到一个特别现实的问题:模型输出经常“一本正经地胡说八道”。让 Agent 去查资料、调工具、写代码,它都干得挺欢,但结果质量完全没底。于是我给整套链路加了一个“判断器”,让它在 Agent 把结果交出去之前先做一轮质检。核心就用到了两个模型:轻量快速的 Laya 负责初筛,能扛复杂决策的 Jev 负责深度裁决。这篇文章就把我的设计思路、部署过程、选型经验摊开讲讲,适合正在做 Agent 开发、又对输出质量头疼的朋友,也适合刚接触智能体、想搞清楚判断器该不该加、怎么加的读者。
1. 项目整体设计与思路拆解
1.1 为什么 Agent 需要一道“判断器”
大部分 Agent 框架的默认工作流都是“输入 -> 规划 -> 调工具 -> 输出”,最多在最后加一层渲染。问题在于,这个流程里没有任何一个环节真正检查过“输出到底对不对”。我见过客服 Agent 把医保报销比例说错一个数量级,代码 Agent 生成了带 SQL 注入漏洞的查询语句,研究型 Agent 给用户推荐了一篇根本不存在的论文。这些错误不是偶发,而是大模型的“幻觉 + 过度自信”导致的必然结果。
判断器的本质就是一个质量闸门:在 Agent 把答案递给用户之前,用另一个模型或另一套规则,对当前回答做独立审查。它不负责重新生成内容,只负责回答两个问题:这个结果能不能用?如果不能用,要怎么改?这个思路在传统软件工程里很常见,对应的是 code review、测试用例、发布前的质量门禁,只是在 Agent 场景里,审查对象从“代码”变成了“自由文本和工具调用结果”,审查者从“人”变成了“模型”。
我自己踩过的一个教训是:一开始我把判断逻辑直接写进 Agent 的 system prompt,让主模型“自己监督自己”,结果基本无效。模型会为它刚生成的错误答案找借口,这是典型的自我偏差。后来我把判断器拆成独立模块,跟主模型完全分开,误杀率反而降下来了。所以判断器的第一个设计原则就是:裁判不能和运动员是同一个模型。
1.2 Laya 和 Jev 怎么分工:轻量初筛 + 深度裁决
我给这套判断器设计的是两段式架构,对应两个角色不同的模型。Laya 是偏轻量的模型,跑得快、token 消耗低、响应时间短,适合做第一步粗筛。Jev 是偏重推理能力的模型,可以用来做多步分析、方案对比、逻辑验证,适合对复杂结果做深层次裁决。
举个例子,用户问“帮我查一下上海到北京的机票”,Agent 调了订票工具,返回了一堆航班信息。这时候 Laya 只需要做非常机械的检查:有没有出发时间?有没有价格?有没有航班号?缺一项就打回重试。这类任务规则明确、判断维度单一,用轻量模型完全够。
但用户如果换个问法,“这三个航班里哪个性价比最高?”,Laya 就有点拿不准了。它不知道“性价比”在用户语境里更看重价格、时刻还是航司准点率,这时候就得把问题升级到 Jev。Jev 会结合价格、飞行时长、起降时间、行李额等多维度信息做对比分析,还输出推荐理由。
我把这种“轻模型挡掉绝大多数简单问题,重型模型只处理少数高难度判断”的模式,叫作“漏斗式裁决”。实测下来,大概 80% 的判断任务在 Laya 这一层就结束了,只有 20% 会真正走到 Jev,成本和延迟都控制得住。
1.3 为什么“轻 + 重”的组合比单模型更划算
有人可能会问,直接让 Jev 一口气把判断做到底不就行了吗?我先算一笔账:如果每次 Agent 调用都让一个重型模型做裁决,响应时间至少增加 3 到 5 秒,token 费用可能翻 4 倍以上,而其中大部分请求只是需要判断“格式是否齐全”这种简单事。等于拿杀鸡的牛刀去屠一条蚯蚓。
另一个角度是部署灵活性。Laya 这种轻量模型可以直接跑在本地的 Ollama 上,甚至能部署到 RK3588 这类边缘设备里,这样用户问的敏感信息可以完全留在内网,不出域。Jev 这种重型模型则可以选择走云端的 API 服务,按次付费,本地不需要准备大显存的机器。两者一混合,既保住了数据隐私,又获得了顶级推理能力。
还有一点容易被忽略:解耦。把判断器做成独立服务之后,判断规则可以单独迭代,不用跟着 Agent 主模型一起升级。我发现很多线上事故并不是主模型变蠢了,而是判断器规则过时了,比如质检标准还停留在“回答有没有链接”的阶段,用户已经需要“链接必须来自白名单域名”。判断器独立部署之后,这类问题可以快速修。
2. 核心机制、参数与调用流程
2.1 判断器的一次完整裁决流程
我把判断器设计成一个独立服务,提供统一的 HTTP 接口,Agent 编排器在主循环里调用它。一次完整的裁决流程分五步走。
第一步是采集判断材料。我不仅把 Agent 的最终回答发给判断器,还带上前面的工具调用记录、原始用户输入、检索到的参考文档,这些统称为 FactContext。没有上下文做依据,判断器就只能靠猜,效果跟瞎蒙差不多。
第二步是构造判断提示词。判断提示词按任务类型分成两个模板:Laya 用的初筛模板和 Jev 用的深度裁决模板。模板里会明确告诉模型“你是一名质检员,你只输出结构化结论,不要生成任何额外内容”。
第三步是调用 Laya 做初筛。Laya 会返回一个三档结论:PASS、REJECT 或 UNCERTAIN。PASS 直接放行,REJECT 则进入重写流程,UNCERTAIN 才会升级到 Jev。
第四步是 Jev 深度裁决。Jev 会拿到完整材料,输出更加精细的决策建议,包括问题定位、严重程度、修改建议,部分场景下还会直接给出重写的完整文本。
第五步是把结果返回给编排器。编排器根据判断结果决定下一步动作:放行、重试一次、换一个工具重新调用,或者直接终止并把失败原因展示给用户。整套流程走下来,用户看到的还是一个标准答案,但背后已经多了一层看不见的质检。
2.2 Laya 轻量预判的规则怎么写
Laya 的判断规则要尽可能机械、明确,不要给它太多“自由发挥”的空间。我这里的做法是让 Laya 严格输出一个标记词,后面跟一个简短原因,格式如下:
__PASS__ __REJECT__: missing departure_time __UNCERTAIN__: needs price comparison with multiple options之所以用这种粗粒度格式,是因为轻量模型做复杂 JSON 生成容易出错,但输出一个标记词基本不会翻车。Laya 的判断维度我会控制在四个以内:任务完成度、信息缺失项、引用真实性、安全风险。判断维度越多,轻量模型的准确率掉得越快。
为了让 Laya 的初筛更稳定,我会在提示词里附带 2 到 3 个 few-shot 示例,覆盖“通过”“拒绝”“不确定”三种情况,让模型照着格式模仿。判断器上线前,我还会拿一批历史 badcase 跑一遍,统计误杀率和漏判率。如果误杀率偏高,就在提示词里加一句“保持宽松,不确定请标记 UNCERTAIN,不要直接拒绝”;如果漏判偏高,就反过来收紧。
2.3 Jev 深度裁决的提示词怎么设计
Jev 的提示词核心是“先思路链、后结构化输出”。我让它把判断依据完整写在 reasoning 字段里,然后再输出 decision 和 suggestions。这样做的好处是,后续排查问题时,我能看到它是怎么得出结论的,而不是面对一个黑盒结果。深度裁决的指令模板大致长这样:
你是 Agent 输出质量的最终裁决者。 请基于 FactContext 中的事实材料,判断 Agent 的回答是否满足用户需求。 判断要点: 1. 是否完全回答了用户的核心问题; 2. 信息是否与 FactContext 一致,禁止使用外部记忆补充; 3. 对多个候选方案做比较时,必须给出排序理由; 4. 若存在安全风险、数据泄露、高风险操作,必须标记 severity=high。 输出 JSON: { "decision": "PASS" | "REJECT" | "REWRITE", "severity": "low" | "medium" | "high", "reasoning": "判断依据的完整思路链", "suggestions": ["排序后的修改建议"] }这里最关键的约束是第二条:所有判断必须基于 FactContext,不能调用模型自身知识去补充。不然 Jev 就会凭自己的“想象”去判断 Agent 的输出对不对,等于又造了一个幻觉源。我曾经遇到过 Jev 用外部知识纠正 Agent 的答案,结果纠正出一个新的错误,就是这个原因。
2.4 判断器关键参数怎么调
判断器不是把两个模型接个接口就完事,参数配置会影响整个链路的稳定性。我把自己调试下来比较稳的一套参数列出来。
| 参数 | Laya 初筛 | Jev 深度裁决 | 说明 |
|---|---|---|---|
| timeout | 3 秒 | 15 秒 | 超时后按 UNCERTAIN 处理,保证主链路不卡死 |
| max_tokens | 128 | 1024 | Jev 需要足够空间输出思路链 |
| temperature | 0 | 0.1 | 裁决任务绝不高温,保证可重复性 |
| 重试次数 | 1 | 2 | REJECT 后最多重写两次,仍失败则终止 |
| 并发限制 | 20 QPS | 5 QPS | 轻量模型扛流量,重型模型保质量 |
timeout 的设定特别重要,尤其是 Laya。以前我把 Laya 的超时设成 10 秒,结果一旦模型响应慢了,Agent 主链路就跟着一起卡,用户体验骤降。后来改成 3 秒超时,超时就直接标成 UNCERTAIN 升级给 Jev,反而更稳。Jev 的超时则需要宽裕一些,因为深度推理确实耗时,设太短会导致大量请求被误杀。
temperature 也是一个容易踩坑的地方。判断器本质上是“评价”任务,不是“创作”任务,建议全部压在 0 到 0.3 之间。我用 0.7 的 temperature 试过一次,同一个结果一会儿 PASS 一会儿 REJECT,完全无法复现,线上根本没法排查问题。
3. 部署实践与接入实操
3.1 部署形态怎么选
不同团队、不同预算、不同安全要求,部署方式完全不同。我把常见方案分成三类,你可以按实际情况对号入座。
| 方案 | Laya | Jev | 适合场景 | 成本参考 |
|---|---|---|---|---|
| 全本地 | Ollama 本地部署 | Ollama / vLLM 本地部署 | 数据敏感、离线环境、需要完全自控 | 一台 32G 内存工作站起步 |
| 混合部署 | Ollama 本地部署 | 云端 API | 大多数中小团队,兼顾隐私和推理能力 | 本地电费 + API 按次计费 |
| 全托管 | 云端 API | 云端 API | 快速验证、个人项目、不想碰运维 | 纯 API 费用,单价最高 |
我实际用的是混合部署:Laya 走本地 Ollama,Jev 走远端 API。理由也很朴实,Laya 是高频调用,放在本地延迟低、免费、数据不出内网;Jev 是低频调用,走 API 可以省去买显卡的钱。如果你的业务已经全部上云,两个都走 API 也没问题,只是要注意成本,Jev 这种重型模型每百万 token 的价格是轻量模型的数倍。
3.2 Laya 本地部署:Ollama 操作实录
本地部署我用的是 Ollama,因为它对硬件要求低、安装简单,还提供 OpenAI 兼容接口,可以无缝接入各种 Agent 框架。安装过程不复杂,Linux 下执行一条命令就能完成。
curl -fsSL https://ollama.com/install.sh | sh装好后拉取模型。这里我以拉取 Laya 对应的轻量模型为例,名字按你实际使用的模型来替换。我选的是参数量在 7B 到 8B 区间的量化版本,在 16G 内存的机器上跑得很流畅。
ollama pull laya-7b-q4 ollama run laya-7b-q4验证服务是否正常,可以直接用 curl 请求本地接口:
curl http://localhost:11434/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "laya-7b-q4", "messages": [{"role": "user", "content": "判断:这个回答是否包含航班价格?只输出 PASS 或 REJECT。"}], "temperature": 0 }'这个接口和 OpenAI 的格式完全一致,所以接入 Agent 框架时,只需要把 base_url 改成http://localhost:11434/v1,把 api_key 填成任意占位符,比如ollama,就能直接替换掉默认的大模型客户端。我接入 Dify 时就是这么干的,在模型供应商里添加一个 OpenAI-API-compatible 的配置,模型名填laya-7b-q4,其余参数保持默认即可。
3.3 Jev 的接入与密钥管理
Jev 走云端 API 的时候,第一步是申请访问凭证。不同服务商的流程略有区别,但大方向都是:注册账号、创建应用、获取 API Key、开通对应模型权限。拿到 Key 后,我建议放到环境变量里管理,不要写死在代码仓库中。
export JEV_API_KEY="your_key_here" export JEV_API_BASE="https://api.example.com/v1"Python 端的接入代码也不复杂,用 requests 库直接构造请求即可。核心逻辑是把 Agent 的执行轨迹和 FactContext 拼进消息体,然后要求 Jev 返回 JSON 结构。
import os import json import requests api_key = os.environ["JEV_API_KEY"] api_base = os.environ["JEV_API_BASE"] def call_jev(fact_context: str, agent_answer: str): prompt = f""" 你是最终裁决者,请基于以下事实材料判断 Agent 回答质量。 FactContext: {fact_context} Agent回答: {agent_answer} 只输出 JSON。 """ resp = requests.post( f"{api_base}/chat/completions", headers={"Authorization": f"Bearer {api_key}"}, json={ "model": "jev", "messages": [{"role": "user", "content": prompt}], "temperature": 0.1, "max_tokens": 1024, "response_format": {"type": "json_object"}, }, timeout=15, ) resp.raise_for_status() return json.loads(resp.json()["choices"][0]["message"]["content"])如果你用的是 Codex 这类 Agent 工具,想在里面直接用 Jev 做后端模型,操作也简单,Codex 支持配置模型提供方,把请求地址指到 Jev 的 API 即可。这里要注意:Codex 系列工具默认会把一部分代码上下文传给模型,如果你的 Jev 计费按 token 来算,成本会涨得比较快,建议在配置里限制上下文长度。
如果你的场景要求 Jev 也完全本地部署,可以用 vLLM 拉起一个 OpenAI 兼容服务。最小配置建议至少 48G 显存,低于这个规格跑重型模型会非常痛苦,推理速度慢到没法用。我个人认为,除非有硬性的数据合规要求,混合部署的性价比远高于全本地。
3.4 与 Agent 框架的集成:Dify 示例
这里以 Dify 为例说说怎么把判断器接到现有 Agent 工作流里。Dify 的工作流是可视化的节点编排,我通常会在 Agent 节点后面加一个“HTTP 请求”节点,指向判断器服务,然后把 Agent 的输出和工具调用记录填到请求体里。
更通用的做法是在 Agent 主循环的 while 中插入判断器调用。伪代码大概是这样的:
max_retries = 2 retries = 0 while retries <= max_retries: result = agent.run(user_query) verdict = judge.evaluate( user_query=user_query, agent_result=result, fact_context=agent.get_tool_trace() ) if verdict.decision == "PASS": return result retries += 1 agent.revise(verdict.suggestions) return "Agent 无法生成可信答案,已终止。"这里我建议把判断器的裁决结果落到日志里,包括 decision、severity、reasoning,方便事后回溯。线上问题排查时你会发现,这个日志是你最重要的证据链。没有它,用户投诉一个错误答案,你根本不知道问题出在主模型、工具调用还是判断器漏判。
还有一个扩展玩法:如果你在边缘设备上,比如 RK3588 上跑视觉判断任务,可以把 Laya 换成更小的视觉模型,比如 YOLOv8 家族的目标检测模型,对 Agent 依赖的图片输入做前置校验。我曾经做一个巡检机器人 Agent,它需要把摄像头拍到的画面和用户描述比对,如果画面里根本没有对应目标,后续的 Agent 动作就全是空中楼阁。这时候在端侧加一道视觉判断器,比在后端反复调整提示词管用得多。
4. 选型思路与避坑指南
4.1 判断器误判怎么办
误判是判断器上线后最常见的问题,分两种:误杀和漏判。
误杀率高,表现是大量正常回答被 REJECT,用户体验直线下降。我第一次上线时误杀率达到 47%,差点把功能下线。后来排查发现,问题出在提示词里“严格审查”四个字,轻量模型对这个词的理解过于严厉,几乎把所有不确定性都标成了 REJECT。解决办法是放宽 Laya 的初筛标准,把判断目标从“提供完整正确回答”改成“是否存在明确错误项”,只有发现硬伤才打回,拿不准一律走 UNCERTAIN。
漏判率高,表现是错误答案溜过了判断器。这种情况一般是阈值太松了。我采用的办法是在 Jev 层补一道强制复核:所有涉及代码生成、医疗建议、金融操作的 Agent 输出,必须由 Jev 走一遍深度裁决,不允许直接 PASS。判断器上线稳定后,建议建立 badcase 回归集,每次更新判断规则都跑一遍历史样例,避免旧问题复发。
4.2 接入判断器后延迟变高怎么优化
判断器和主模型是串行调用的,延迟叠加是必然的。优化方向有三个。
第一个方向是并行预判。如果 Agent 同时返回多个候选结果,我可以把它们丢给多个 Laya 实例并行判断,而不是逐个排队。Laya 支持高并发,我用 20 个并发实例同时处理 5 个候选结果,整体耗时几乎没增加。
第二个方向是分级启用。不是所有请求都值得走判断器。我先用规则做一层前置路由:简单闲聊、天气查询这类低风险请求直接跳过判断器;涉及代码、支付、医疗、法律这类高风险任务才进入完整判断链路。实测这个策略减少了约 60% 的判断器调用量。
第三个方向是模型压量化。Laya 使用 Q4 量化版本后,响应速度比 FP16 版本快接近一倍,质量损失在判断这种简单任务上可以忽略。这也是我推荐在本地部署轻量模型时优先选择量化版本的原因。
4.3 安全、成本与稳定性方面的几个坑
判断器输出的 JSON 经常因为截断而解析失败,这是我遇到最多的问题。解决办法是双保险:一方面在提示词里要求“只输出 JSON,不要任何解释”,另一方面在解析代码里做容错处理,比如提取第一个{到最后一个}之间的内容再解析,解析失败就按 REJECT 处理,保证不会因为格式问题卡死主链路。
成本控制也要提前想清楚。Jev 这类重型模型按 token 计费,如果你把整个工具调用日志全部塞进判断器上下文,一次裁决可能消耗几千个 token。我的做法是只提取关键字段传给判断器,比如用户原始问题、Agent 最终回答、工具返回的摘要,而不是把完整日志照搬。API Key 要用环境变量管理,并且给账号设置单日调用上限,防止某次异常循环导致费用爆炸。
还有一个容易被忽视的隐私问题:判断器接收的数据可能包含用户个人信息,如果走云端 API,这些数据就会经过第三方服务。对于金融、医疗等合规要求较高的场景,我更建议全本地部署,哪怕推理能力弱一点,也不能让用户数据出域。如果必须走云端,至少要确认服务商的数据处理协议和区域。
4.4 怎么选 Laya 和 Jev 的模型版本
很多人会问,判断器里的两个模型到底怎么选型号。我的经验是分场景看。
个人开发者或者快速验证阶段,Laya 选 7B 级别的量化模型就行,本地 Ollama 跑毫无压力,Jev 直接走云端 API,按量付费,不用考虑硬件成本。生产级团队,Laya 可以考虑提升到 13B 级别的模型,判断准确率会好一些,尤其处理中文复杂句式时差距明显;Jev 则看你对推理能力的要求,复杂横向对比、多步逻辑验证这类任务,尽量选当前商用闭源模型里推理能力靠前的那一档。
如果团队有较强的私有化部署需求,没有外部 API 可选,那么 Jev 的本地部署就要优先考虑硬件方案:首选 2 块 24G 显存的 GPU 跑量化版本,配合 vLLM 做推理加速;内存建议 64G 以上,毕竟要同时伺候主 Agent 和判断器两个模型。我见过不少团队只给 Jev 配了一台 32G 内存的机器,结果推理耗时飙到几十秒,最后不得不降低 Jev 的判断深度。硬件预算和判断复杂度必须一起规划,不能先选模型再找硬件。
最后说几句实际操作中的体会。加了判断器之后,Agent 的整体稳定性和可信度确实上了一个台阶,但这个过程并不轻松。我自己踩过最大的坑,是判断器上线第一周误杀率飙到 47%,差点把整个机制下掉。后来想明白一件事,判断器不应该一开始就当“拦截者”,更稳妥的做法是先让它当“观察者”,只记录不合格的答案而不阻断,运行几天积累足够多的线上样本后,再逐步把规则收紧为拦截模式。这套渐进上线的思路,比一开始就要精确判断靠谱得多。
还有一点我想强调:判断器提示词的迭代频率,远高于 Agent 主模型的迭代频率。因为线上用户的提问方式千奇百怪,规则永远追不上语料变化。建议把它当作一个持续维护的模块,每次发布新判断规则时,除了跑历史回归集,还要人工抽看 50 到 100 条新样本,确认没有出现新的误判模式。判断器不是加完就一劳永逸的东西,它是 Agent 投产后最值得长期投入的部分。