Agent开发避坑指南:大模型不是唯一决策者,分层判断更稳定

发布时间:2026/10/27 23:56:27
Agent开发避坑指南:大模型不是唯一决策者,分层判断更稳定

1. 为什么“判断全塞给大模型”是个陷阱

1.1 一个真实项目的翻车现场

去年我接手一个内部工单自动分流的 Agent 项目,需求听起来特别简单:用户提交一段问题描述,Agent 判断它属于“网络故障”“账号权限”“硬件报修”还是“其他”,然后路由到对应处理人。团队第一反应就是——这不就是大模型的强项吗?把分类规则写进 System Prompt,把用户输入丢进去,让模型直接输出类别标签,一天就能上线。

结果上线第一周就出事了。一个用户写“我电脑连不上公司内网,但手机热点能上”,模型把它分到了“网络故障”,这没错;但另一个用户写“我账号登不上,提示密码错误,但我确认密码是对的”,模型居然分到了“硬件报修”,理由是“可能键盘坏了”。更离谱的是,有用户只写了“急!”,模型随机给了一个类别,因为 Prompt 里没规定“信息不足时怎么办”。

这三个坑我踩完才明白一件事:Agent 的判断链路里,大模型不该是唯一的决策者,甚至不该是主要决策者。大模型擅长的是理解语义、生成文本、处理模糊信息,但它不擅长稳定地执行确定性规则、不擅长在信息不足时主动追问、更不擅长保证输出格式永远一致。把所有这些判断都塞给大模型,等于让一个文科生去做会计对账——他能做,但你会提心吊胆。

1.2 大模型在 Agent 判断中的真实定位

先把概念理清楚。Agent 是什么?简单说就是一个能感知输入、做出判断、执行动作、拿到反馈后继续循环的系统。它的核心不是“大模型”,而是“判断-行动”的闭环。大模型在这个闭环里扮演的是“语义理解器”和“自然语言生成器”,而不是“规则引擎”和“状态机”。

我后来复盘,把 Agent 的判断分成了三层:

判断层级典型任务适合谁做原因
确定性规则判断字段校验、格式检查、阈值比较代码/规则引擎稳定、可测试、零成本
语义理解判断意图识别、情感分析、模糊匹配大模型需要泛化能力
状态与流程判断当前步骤、下一步动作、重试策略状态机/编排层需要精确控制

把这三层混在一起全丢给大模型,就是典型的“用锤子看什么都是钉子”。我见过太多 Agent 项目,Prompt 写了三千字,里面既有“如果用户说 A 就做 B”的规则,又有“请理解用户情绪”的语义任务,还有“如果上一步失败就重试”的流程控制。这种 Prompt 维护起来是灾难,调试起来是玄学。

1.3 判断分层的核心原则

我的经验是:能用代码判断的,绝不交给大模型;能用小模型判断的,绝不交给大模型;只有真正需要语义理解和生成的地方,才让大模型上场。

这条原则背后有三个理由。第一是成本,大模型每次调用都是钱,一个规则判断用代码是零成本,用大模型就是按 token 计费。第二是延迟,代码判断是微秒级,大模型是秒级,Agent 如果每步都等大模型,用户体验会很差。第三是可靠性,代码判断的结果是确定的,大模型是概率性的,同一个输入可能给出不同输出,这在生产环境里是致命的。

注意:这不是说大模型不重要,而是说大模型要用在刀刃上。一个设计良好的 Agent,大模型调用次数应该比很多人想象的少得多。

2. 三个坑的详细拆解与修复方案

2.1 第一个坑:把格式校验交给大模型

问题现象:用户输入里要求包含“工单编号”,格式是TK-加 6 位数字。我在 Prompt 里写“请检查用户是否提供了工单编号,格式为 TK-XXXXXX”。结果模型有时候说“用户提供了”,实际上用户写的是“TK123456”少了横杠;有时候说“没提供”,但用户明明写了“TK-123456”只是前面多了空格。

根因分析:大模型做的是语义理解,不是字符串匹配。它看到“TK123456”会觉得“这看起来像个工单号”,但不会严格按正则去校验。而且 Prompt 里的规则描述越长,模型越容易忽略细节。

修复方案:把格式校验从 Prompt 里拿出来,用代码做。

import re def validate_ticket_id(text): pattern = r'\bTK-\d{6}\b' match = re.search(pattern, text) if match: return {"valid": True, "ticket_id": match.group()} return {"valid": False, "reason": "未找到符合 TK-XXXXXX 格式的工单编号"}

然后在 Agent 流程里,先跑这个校验函数,只有校验通过才把文本送给大模型做后续的意图理解。如果校验不通过,直接返回追问话术,连大模型都不用调。

效果:格式校验准确率从 87% 提升到 100%,而且这部分调用成本降为零。更重要的是,用户不会再收到“请提供工单编号”的追问,而他明明已经提供了——这种体验伤害比分类错误还大。

2.2 第二个坑:让大模型决定“信息不足时怎么办”

问题现象:用户只写了“急!”,模型有时候追问“请问您遇到什么问题”,有时候直接分类到“其他”,有时候甚至编一个类别。行为完全不可预测。

根因分析:我在 Prompt 里没有明确定义“信息不足”的判定标准和对应动作。大模型面对模糊输入时,会根据自己的“理解”随机选择一种处理方式。这不是模型的问题,是我的设计问题。

修复方案:把“信息充足度判断”做成一个独立的、可配置的环节。我设计了一个简单的评分卡:

判断维度权重判定方式
文本长度20%少于 10 个字符直接判为不足
关键实体50%是否包含设备、系统、错误码等实体
意图明确度30%是否包含动作动词和问题描述

这个评分卡用代码实现,不涉及大模型。只有总分超过阈值,才进入大模型分类环节;否则直接走追问流程。

def assess_info_sufficiency(text): score = 0 if len(text) >= 10: score += 20 entities = extract_entities(text) # 简单的关键词匹配 if entities: score += 50 if has_action_verb(text): score += 30 return score # 阈值设为 60,低于则追问

效果:模糊输入的处理变得完全可预测。用户写“急!”一定会收到追问,而不是随机分类。追问话术也可以根据缺失的维度动态生成,比如“请补充您遇到问题的设备或系统名称”。

2.3 第三个坑:把流程控制写进 Prompt

问题现象:Agent 需要先确认用户身份,再判断问题类别,再决定是否转人工。我把这个流程写进了 Prompt:“如果用户未提供身份信息,请先询问;如果提供了,请判断问题类别;如果类别是硬件报修且用户情绪激动,请转人工。”结果模型经常跳步,或者把多个步骤混在一起输出。

根因分析:大模型没有“状态”概念。它每次调用都是独立的,不知道“上一步做了什么”“下一步该做什么”。把流程控制写进 Prompt,等于让模型在单次生成里模拟一个多步状态机,这超出了它的能力边界。

修复方案:把流程控制抽出来,用一个显式的状态机管理。大模型只在每个状态内部做它擅长的事——理解语义、生成话术。

class AgentState: def __init__(self): self.state = "awaiting_identity" self.context = {} def transition(self, user_input): if self.state == "awaiting_identity": identity = extract_identity(user_input) # 代码提取 if identity: self.context["identity"] = identity self.state = "classifying" return {"action": "classify", "input": user_input} else: return {"action": "ask_identity"} elif self.state == "classifying": category = llm_classify(user_input) # 大模型分类 self.context["category"] = category if category == "硬件报修" and is_urgent(user_input): self.state = "escalating" return {"action": "escalate"} self.state = "resolved" return {"action": "route", "category": category}

效果:流程不再跳步,每个状态的行为完全确定。大模型只负责“分类”这一个语义任务,输出稳定可控。

3. 重构后的 Agent 判断架构

3.1 三层判断架构的设计

踩完这三个坑,我把 Agent 的判断链路重构成了三层:

第一层:前置校验层(纯代码)。负责格式校验、字段提取、信息充足度评分。这一层不调用任何模型,纯规则和正则。它的输出是一个结构化的“输入质量报告”,告诉后续环节:输入是否合格、缺什么、有什么。

第二层:语义理解层(大模型)。只做一件事:在输入合格的前提下,做意图分类和实体抽取。Prompt 极简,只包含分类体系和少量示例,不包含任何流程规则和格式要求。输出强制 JSON 格式,用 function calling 或 JSON mode 保证结构。

第三层:流程编排层(状态机)。根据前两层的输出,决定下一步动作:追问、路由、转人工、结束。这一层是纯代码,状态转移条件明确可测。

这三层的调用顺序是固定的:先校验,再理解,再编排。任何一层不通过,都不会进入下一层。这样既省成本,又保证稳定性。

3.2 大模型调用的最小化策略

重构后,大模型调用次数从“每轮对话至少一次”降到了“只有输入合格且需要分类时才调用”。我统计了一下,原来 100 次用户交互要调 100 次大模型,现在只要 40 次左右。省下来的 60 次都是格式错误、信息不足、重复提交的情况,这些用代码处理又快又准。

具体策略包括:

  • 缓存:相同或相似的输入,如果之前分类过,直接复用结果。用简单的文本哈希或向量相似度做缓存键。
  • 批处理:如果多个工单同时进来,可以合并成一个批次调用大模型,减少调用次数。
  • 降级:大模型调用失败或超时,自动降级到规则匹配,保证 Agent 不卡死。

提示:缓存策略要注意失效机制。分类体系变了,缓存要清空;用户补充了信息,缓存要失效。我一般用“输入文本 + 分类体系版本号”作为缓存键。

3.3 判断逻辑的可测试性

这是重构后最大的收益:判断逻辑变得可测试了。以前 Prompt 里的规则没法写单元测试,只能靠人工试。现在前置校验层和流程编排层都是纯函数,可以写完整的测试用例。

def test_validate_ticket_id(): assert validate_ticket_id("TK-123456")["valid"] == True assert validate_ticket_id("TK123456")["valid"] == False assert validate_ticket_id("tk-123456")["valid"] == False # 大小写敏感 assert validate_ticket_id("单号 TK-123456 请处理")["valid"] == True def test_assess_info_sufficiency(): assert assess_info_sufficiency("急!") < 60 assert assess_info_sufficiency("我的电脑连不上内网,错误码 0x800") >= 60

语义理解层虽然不能完全测试,但可以用固定数据集做回归测试,确保 Prompt 改动不会导致分类准确率下降。我一般准备 200 条标注数据,每次改 Prompt 都跑一遍,准确率掉超过 2% 就回滚。

4. 实操中的常见问题与排查技巧

4.1 大模型分类不稳定怎么办

这是最常见的问题。同一个输入,今天分到 A,明天分到 B。排查思路:

第一,检查 Prompt 里有没有模糊表述。比如“如果用户情绪激动就转人工”,“激动”没有定义,模型每次理解可能不同。改成明确的规则,或者把情绪判断也做成独立环节。

第二,检查分类体系有没有重叠。比如“网络故障”和“硬件报修”都包含“连不上”这个场景,模型就会摇摆。解决办法是给每个类别写清晰的边界定义,并在 Prompt 里加“如果无法确定,输出‘其他’”。

第三,降低 temperature。分类任务不需要创造性,temperature 设成 0 或 0.1,输出会稳定很多。

第四,用 few-shot 示例。每个类别给 2-3 个典型例子,模型有了参照,输出会一致得多。

4.2 信息不足时追问话术怎么设计

追问不是简单说“请补充信息”,而是要根据缺失的维度精准追问。我的做法是:

缺失维度追问话术模板
设备/系统“请告诉我您遇到问题的是哪台设备或哪个系统?”
错误信息“有没有具体的错误提示或错误码?截图也可以。”
操作步骤“您是在做什么操作时遇到这个问题的?”
影响范围“这个问题影响了您一个人还是整个团队?”

这些模板放在代码里,根据评分卡的缺失项动态选择。不要交给大模型生成,因为大模型可能生成过于冗长或偏离的话术。

4.3 如何判断一个判断该不该交给大模型

我总结了一个简单的决策树:

  1. 这个判断有唯一正确答案吗?有,用代码。
  2. 这个判断需要理解自然语言的模糊含义吗?不需要,用代码。
  3. 这个判断的结果需要每次都一样吗?需要,用代码。
  4. 这个判断涉及多步状态吗?涉及,用状态机。
  5. 以上都不是,才考虑大模型。

按这个决策树过一遍,你会发现真正需要大模型的判断其实不多。大部分“判断”都是规则、校验、状态转移,这些用代码做又快又稳。

4.4 成本与延迟的实测数据

重构前后我做了对比测试,同一批 500 条工单:

指标重构前重构后
大模型调用次数500187
平均响应时间2.3s0.8s
分类准确率82%94%
格式错误率11%0%
月度成本约 450 元约 170 元

准确率提升的主要原因是格式校验和信息充足度判断不再干扰分类。响应时间下降是因为大量请求在代码层就处理完了,不用等大模型。成本下降是调用次数减少的直接结果。

5. 从“全塞给大模型”到“分层判断”的迁移路径

5.1 存量 Agent 的改造步骤

如果你已经有一个“全塞给大模型”的 Agent,不用推倒重来。按这个顺序改造:

第一步,把 Prompt 里的规则抽出来。逐条读 Prompt,凡是“如果……就……”的句式,都是规则,用代码实现。抽完后 Prompt 应该只剩语义理解任务。

第二步,加前置校验层。在调用大模型之前,插入格式校验和信息充足度判断。不通过的直接返回,不调模型。

第三步,加流程编排层。把多步流程从 Prompt 里拿出来,用状态机管理。大模型只负责单步的语义任务。

第四步,加缓存和降级。对分类结果做缓存,对大模型调用做超时降级。

第五步,写测试。给前置校验层和流程编排层写单元测试,给语义理解层写回归测试。

5.2 新项目的设计检查清单

新项目从第一天就按分层架构设计,可以避免后面返工。启动前过一遍这个清单:

  • [ ] 所有格式校验是否都用代码实现,没有写进 Prompt?
  • [ ] 信息充足度判断是否有明确的评分标准,而不是靠模型“感觉”?
  • [ ] 流程控制是否用状态机管理,而不是写在 Prompt 里?
  • [ ] 大模型调用是否有缓存和降级策略?
  • [ ] 分类体系的边界是否清晰,有没有重叠类别?
  • [ ] 是否有回归测试数据集,每次改 Prompt 都跑一遍?
  • [ ] 大模型输出是否强制结构化(JSON mode 或 function calling)?
  • [ ] 是否有监控,能追踪每次判断的耗时、成本和准确率?

5.3 团队协作中的分工建议

分层架构还有一个好处:分工清晰。后端工程师负责前置校验层和流程编排层,算法工程师负责语义理解层的 Prompt 和模型选型,测试工程师负责各层的测试用例。大家各管一摊,不会互相阻塞。

以前 Prompt 是“公共资产”,谁都能改,改完没人知道影响是什么。现在 Prompt 只是语义理解层的配置,改动有回归测试兜底,风险可控。

6. 一些容易被忽略的细节

6.1 大模型输出的结构化约束

即使只让大模型做分类,也要强制结构化输出。我推荐用 JSON mode 或 function calling,而不是让模型自由生成文本再解析。自由生成的问题是你永远不知道它会多输出什么,解析失败率很高。

{ "category": "网络故障", "confidence": 0.92, "entities": ["内网", "连不上"], "reason": "用户描述网络连接问题" }

有了 confidence 字段,流程编排层可以做更细的判断:置信度低于 0.7 就转人工,高于 0.9 就自动路由,中间区间可以追问确认。

6.2 判断逻辑的版本管理

判断逻辑会随业务变化。分类体系可能增加新类别,追问话术可能调整,阈值可能优化。这些变化都要版本化,并且和缓存失效、回归测试联动。

我的做法是给判断逻辑打版本号,每次变更都记录变更内容、影响范围、测试结果。缓存键里带上版本号,版本一变缓存自动失效。回归测试数据集也按版本管理,确保新旧版本可比。

6.3 监控与告警

上线后要监控几个关键指标:大模型调用成功率、平均延迟、分类准确率(用抽样人工标注)、缓存命中率、降级触发次数。任何一个指标异常,都要能告警。

我遇到过缓存键设计错误导致缓存命中率 100%,所有新输入都返回旧结果。也遇到过降级策略太激进,大模型稍微慢一点就降级到规则匹配,准确率暴跌。这些都要靠监控发现。

6.4 安全边界

Agent 的判断链路里,有些判断涉及安全边界,比如“是否允许用户执行某个操作”“是否涉及敏感信息”。这类判断绝对不能交给大模型,必须用代码做硬性校验。大模型可以被 Prompt 注入绕过,代码不会。

我的原则是:涉及权限、金额、敏感数据的判断,一律用代码,且代码要经过安全评审。大模型只处理不涉及安全边界的语义任务。

7. 最后分享几个实操心得

第一个心得:先写代码,再写 Prompt。很多人习惯先写 Prompt 试效果,跑通了再补代码。我的建议反过来:先把所有能用代码做的判断做掉,剩下的才写 Prompt。这样 Prompt 会非常短,维护成本极低。

第二个心得:给大模型的任务越单一越好。一个 Prompt 只做一件事。分类就分类,抽取就抽取,不要混在一起。任务越单一,输出越稳定,测试越容易。

第三个心得:不要迷信大模型的能力边界。大模型很强,但它不是万能的。它在语义理解上很强,在确定性判断上很弱。把合适的任务交给合适的组件,才是 Agent 设计的核心。

第四个心得:判断逻辑的可观测性比准确率更重要。准确率 95% 但不知道那 5% 错在哪,不如准确率 90% 但每次错误都能追踪到原因。可观测性让你能持续优化,黑盒准确率只是数字。

第五个心得:分层不是目的,可维护才是。三层架构不是教条,如果你的场景确实简单,两层也行。关键是每一层职责清晰,改动一层不影响另一层。我见过为了分层而分层,结果过度设计,维护更麻烦。

这个项目做完,我最大的体会是:Agent 的“智能”不在于用了多大的模型,而在于判断链路设计得是否合理。大模型是链路里的一环,不是全部。把判断分层,让每一层做自己擅长的事,Agent 才能既聪明又稳定。后来我又用同样的思路做了几个 Agent 项目,包括客服工单、内容审核、数据清洗,效果都很好。这套分层判断的方法,我觉得是 Agent 开发里最值得先掌握的基本功。