上个月评审一个自动化项目,有个同事在会上提了个问题,我印象很深:Agent这套方案,落到代码里不就是一个大号的if-else吗?拆单、查数据、发消息,这些操作硬编码早就全都能干,为什么要绕一圈套概念?当时现场好几个人点头。说实话,有这种直觉非常正常,因为Agent的一次执行过程,从外部看确实是"调函数、拼参数、返回结果",每一步都和普通代码没差别。但"每一步都一样"这个事实,恰恰掩盖了两者真正的分水岭——代码里的分支路径,到底是开发者在编码期写死的,还是系统在运行期根据目标自己选的。这篇文章我就围绕这个分水岭展开,把Agent的核心价值、适用边界、落地步骤和排查经验一次讲透。
1. 硬编码不是不够快,是没法覆盖"还没想到的分支"
1.1 硬编码的优势是真的,上限也是真的
先别急着否定硬编码。对确定的流程,硬编码是碾压级的存在:行为稳、响应快、好测试、成本低。一个订单状态机,从"已创建"到"已支付"到"已发货",每条迁移规则写死,输入确定、路径确定、输出确定,线上跑十年不出岔子。这种系统吃的是"确定性"的红利,任何引入随机性的方案都是在给自己添乱。
但硬编码有一个隐含前提:所有可能出现的情况,都得在开发阶段被提前枚举出来。换句话说,代码里每个if后面的条件,是开发者用过去的经验替未来可能遇到的情况提前买的保险。问题就出在这里——规则能买到的保险,永远只有一少部分。
1.2 规则数量一上来,维护成本会失控
拿一个常见的工单分类场景举例。某平台每天收到几千条售后工单,想自动把工单分给对应部门,顺便识别紧急程度。硬编码的做法很简单:写一堆关键词规则,出现"退款"就分给财务,出现"发货慢"就分给物流。刚开始只有20条规则,效果不错。但跑三个月你会发现问题:
- 用户不会按词说话。"退款"和"钱没到账"是一回事,"发货慢"和"东西还在路上"也是一回事,关键词规则要不断叠加同义词。
- 组合情况爆炸。工单可能同时包含退款、质量、物流三个问题,优先级怎么算?按关键词命中顺序?那换一种表述顺序,结果就变了。
- 长尾永远补不完。每年都有新促销、新活动、新话术,每出一个新case就要加一条规则。
- 规则之间还会打架。新增的"生鲜优先处理"可能覆盖掉之前"普通退款48小时响应"的逻辑,改A坏B是常态。
规则从20条涨到200条的时候,这套系统基本上就没人敢动了。维护的人每天都在猜"这条规则当初为什么这么写""那两条规则到底谁覆盖谁"。我见过不止一个项目,硬编码规则系统的最终归宿是"推倒重写"或者是"线上跑着但没人敢碰"。
这就是硬编码的真正上限:它不是一个性能问题,而是一个覆盖性问题。你永远没法用有限的规则,去覆盖一个无限变化、充满模糊语义的真实世界。
提示:判断你的业务是否逼近这个上限,可以看一个指标——平均每个规则/每个分支的改动频率。如果每周都要为线上反馈新增分支,说明规则体系已经开始追不上真实需求的增长速度了。
2. 把"决策权"从编码期搬到运行期,才是Agent的本质
2.1 "每一步都是函数调用"和"自己决定调哪个函数"是两件事
很多人拿"Agent执行过程中调用的工具,硬编码也能调用"来质疑Agent的价值。这句话不算错,但它偷换了概念:把"能力的原子"和"能力的组合方式"混为一谈了。
硬编码的本质是:开发者预先为所有可能的路径铺好轨道,列车只能沿着轨道走。走错路、绕路、遇到轨道断了临时改道,都不在系统能力范围内。Agent的本质则是:系统只知道"目的地"(目标),至于怎么到达,由它自己在运行时根据环境反馈实时规划。铁路变成了自驾——同样是从A到B,堵车了换高架、高架封了走地面,驾驶员手里的方向盘,就是决策权。
这套逻辑落到代码上,就是Agent里最核心的感知-决策-行动循环:
- 感知:读取当前的输入和环境状态,包括用户消息、工具返回结果、外部数据源。
- 决策:基于目标,决定"下一步要调用哪个工具、用什么参数,还是直接输出回答"。
- 行动:执行工具调用,把结果作为新的感知输入,回到步骤1。
硬编码也有"根据输入决定输出"的过程,但那是在代码里预先写好的判定树。Agent的决策则发生在运行时——每一次决策都是基于当前上下文现场"计算"出来的,而不是从预先穷举的路径表里查出来的。这才是区别的根源。
2.2 一个例子看懂决策权的差异
假设有这样一个需求:"帮我查一下华南区上个月的销售数据,和去年同期做个对比,然后给业务负责人写封摘要邮件。"
硬编码完全可以实现。前提是你要提前做一堆假设:查哪张表、按哪个维度聚合、和哪个时间段对比、邮件发给谁、用什么模板。写好了,下次需求变成"查华东区这季度的退货率,分析原因,抄送供应链负责人",原来的脚本立刻失效——因为没有"退货率"这个分支,没有"抄送供应链"这个动作。你得再写一个脚本。
Agent实现同样的事,只需要给它三样东西:
- 一套工具清单:订单查询工具、对比计算工具、邮件发送工具。每个工具写明"能干什么、接收什么参数"。
- 一段目标描述:也就是系统提示词,告诉它"你是数据分析助理,目标是把用户的需求拆解成可执行的工具调用序列"。
- 一个用户请求:就是那句"查华南区上个月……"。
Agent自己会理解"华南区"是工具参数,"上个月"是时间范围,"和去年同期对比"是调用完查询工具之后的处理步骤,"写封摘要邮件"是最后的输出动作。整个过程没有一个分支是开发者提前写死的,Agent是在运行时现算出来的。
这才是Agent对硬编码真正的替代:它不是一个"更大的if-else",而是把"写规则/写分支"这个工作本身自动化了。你不再用手写每条业务规则,而是用一套机制让系统在运行时自己生成规则。我把这个区别概括成一句话:硬编码是穷举式编程,Agent是意图式编程。
2.3 澄清一个误区:Agent不等于"有个大模型"
需要说明的是,Agent并不必然等于大模型应用。严格定义下,任何"能感知环境、自主决策、执行动作"的软件都可以叫Agent,自动驾驶就是典型例子。只不过最近大家讨论的Agent,大多数是在大模型之上构建的——大模型负责"决策"这个环节,工具调用负责"行动"。
把这两层分开理解很重要。决策层负责"下一步干什么",行动层负责"真正去干什么"。大模型是决策层目前最高效的实现方式,但不是唯一方式。你可以用决策树做决策层,甚至用一张规则表,只要它能在运行时根据输入动态选择路径,它就具备了Agent的雏形。理解了这个分层,你就知道"Agent离不离开大模型就废了"这种说法站不住脚——真正有价值的,是你是否把决策权交给了运行时。
3. 什么时候真的该上Agent:一张自检清单和两个反面典型
3.1 一张清单判断你的场景是否需要Agent
我不赞成"所有系统都要上Agent"这种泡沫话术。相反,我建议团队在立项前先拿这张清单自检,五个问题里如果中三个以上,Agent方案才值得认真考虑:
- 任务目标是开放的吗?用户可以用无限种说法提出需求,而不是固定填一张表单。
- 完成路径是多样的吗?同样的目标,有的路径要调外部接口,有的路径要查内部库,有的路径需要多步串联。
- 环境状态是动态的吗?外部依赖的返回结果不可预测,中间环节可能失败,需要根据现场反应调整下一步。
- 决策依据只在运行时出现吗?系统要做的判断,依赖的信息在开发阶段拿不到,必须等到执行时才能获得。
- 失败可以重试兜底吗?任务允许试错,失败后可以通过换一种策略再次尝试,而不是必须一步到位保证100%。
如果五个问题全是"否",硬编码、状态机或者RPA流程编排就足够,用Agent纯粹是增加复杂度和成本。如果答案集中在"是",那就说明你已经进入了一个"规则写不过来"的区域,再往下加if-else只会把系统拖入维护深渊。
3.2 反面典型:两个不适合Agent的场景
第一个是高频核心链路。比如支付下单流程,每秒几百笔请求,每笔都必须稳定在几十毫秒内返回,出错一步可能就是资损事故。这种链路追求的是极致的确定性和可测试性。你当然可以用Agent把这些步骤串起来,但Agent的决策延迟、不可穷尽的输出范围、偶发的"灵光一现",在这里全是灾难。正确做法是:核心链路继续用传统代码,把Agent放在外围,处理那些"需要人来做判断但不想派人来做"的边缘任务。
第二个是"为了概念而概念"的演示项目。我见过某团队为了跟上热度,把一个简单的告警通知功能硬包成了Agent:一个固定规则判断CPU超阈值,然后发一条固定格式的钉钉消息。本来一个定时任务加一个if就搞定的事,他们上了大模型、写了Agent循环、配了工具调用,结果延迟从毫秒级变成秒级,还时不时抽风。上线两个月后又悄悄改回了原方案。这类反面典型的共同特征,就是业务本身没有开放决策需求,是强行找场景,不是场景需要它。
注意:Agent的引入应该遵循"非必要不上"的原则。它不是银弹,它是一把专门解决"规则写不完"问题的工具,拿它去干"规则本来就很清楚"的活,只会适得其反。
4. 从零搭建一个最小可用Agent:工单处理实战
4.1 场景设定与总体结构
为了让这套思路落地,我带大家完整走一遍一个常见的Agent场景:智能工单处理。目标很简单——系统收到用户用自然语言提交的售后工单后,自动完成三件事:判断工单类型、提取关键信息、执行对应操作(查订单/发通知/转人工)。
硬编码的做法我们已经分析过了,关键词规则堆到200条也补不完长尾。Agent方案的思路完全不同:不预设关键词规则,只给Agent一篮子工具和目标描述,让它自己决定怎么处理每张工单。
4.2 工具定义:Agent能力的边界
Agent能干什么,完全取决于工具清单给它暴露了什么。这一步是Agent方案里性价比最高的投入,工具的描述质量直接决定决策质量。
TOOLS = { "search_order": { "description": "根据订单号或用户ID查询订单详情,包括金额、商品、状态、物流信息。参数:order_id 或 user_id。", "handler": lambda **kwargs: query_order(**kwargs), "required_params": ["order_id"] }, "check_refund_policy": { "description": "查询商品是否支持七天无理由退货以及退货条件。参数:product_id。", "handler": lambda **kwargs: query_refund_policy(**kwargs), "required_params": ["product_id"] }, "send_notice": { "description": "给指定用户发送通知消息。参数:user_id, message。", "handler": lambda **kwargs: push_message(**kwargs), "required_params": ["user_id", "message"] }, "create_human_ticket": { "description": "将工单转交给人工客服处理,在无法自动解决时使用。参数:ticket_id, reason。", "handler": lambda **kwargs: escalate(**kwargs), "required_params": ["ticket_id"] } }这里有个经验:工具description写得好不好,比选了哪个大模型更重要。模型是靠description来理解"什么情况该用哪个工具"的,描述里至少要包含三要素——工具能做什么、参数是什么、什么时候适合使用。一份模糊的description,经常会让模型在"查订单"和"查售后政策"之间选错。
4.3 Agent主循环:串起感知-决策-行动
核心循环代码非常薄,理解它你就理解了市面上绝大多数Agent框架的底层逻辑:
import json def agent_loop(user_input, tools, max_rounds=6): messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_input}, ] for _ in range(max_rounds): response = llm.chat(messages) messages.append({"role": "assistant", "content": response}) action = parse_action(response) # 解析模型输出:是调工具还是直接回答 if action is None: return response # 没有工具调用,说明Agent决定直接回答用户 result = tools[action["name"]]["handler"](**action["args"]) # 把工具结果作为新的上下文喂回去,进入下一轮决策 messages.append({"role": "tool", "content": json.dumps(result, ensure_ascii=False)}) raise TimeoutError("Agent超过最大轮数,未收敛")流程不复杂:把用户工单塞进对话上下文,让模型输出第一个决策——要么生成JSON形式的工具调用请求,要么直接给出最终回答。如果是工具调用,就执行对应的handler,把返回结果追加到消息列表里,再让模型基于最新的上下文做下一个决策。如此循环,直到模型认为信息足够、可以给出最终答复。
4.4 系统提示词:把"目标"翻译给Agent听
主循环搭好了,Agent能不能干活,剩下的就看System Prompt。我总结了一份经过多个项目验证的提示词骨架,拿去就能用:
你是一个售后工单处理助手。你的工作流程是: 1. 先理解用户诉求,判断工单类型(退款/换货/物流/维修/其他)。 2. 需要查信息时,优先调用search_order和check_refund_policy获取事实依据。 3. 信息不足时允许追问用户,但最多追问一次。 4. 能自动处理的情况,直接回复处理结果,并调用send_notice通知用户。 5. 遇到以下情况必须调用create_human_ticket转人工:金额超过500元、用户情绪激烈、连续处理失败、政策不明确。 6. 严禁编造订单号、退款金额等信息,所有回复必须基于工具返回的真实数据。这条提示词只做三件事:交代角色、给工作流顺序、划禁区。不要试图在提示词里枚举所有情况——你枚举得越细,就越回到了硬编码的老路上。禁区要写,是为了安全兜底;流程要写,是为了让Agent有章可循;至于那些"具体怎么判断"的细节,交给模型在运行时去发挥。
4.5 关键参数怎么定
三个参数是上线前必须反复调试的:
- max_rounds(最大轮数)。我一般从6开始调。太小,复杂任务没跑完就被截断;太大,遇到失控的Agent会烧掉大量token。6轮不够就说明工具拆得太大,应该把复杂工具拆成更细的原子操作,而不是一味放宽轮数。
- temperature(随机性)。决策类任务建议0到0.2之间。Agent的强项是稳定执行,不是创意发散,温度太高会导致同样的工单两次处理结果不一样,这在业务上不可接受。
- 输出格式约束。工具调用的返回必须用严格JSON解析,不要用自然语言穿插着表达。一旦让模型用自由格式输出动作,解析环节就会变成一场灾难。
实操心得:我踩过最深的一个坑是"让Agent自由决定要不要追问用户"。跑测试时发现它会对着一张订单号都没给的工单追问三次,用户体验极差。后来加了一条硬限制"最多追问一次,仍缺信息就转人工",问题立刻消失。Remember:Agent的自由度要靠约束来校准,不是给得越多越好。
5. 上线Agent之后的真实坑与排查实录
5.1 Agent循环失控:死循环与无效自旋
跑得多了你会遇到一种情况:Agent反复调用同一个工具,拿回来的结果一模一样,然后继续调,走到max_rounds上限被硬掐断。这就是典型的无效自旋。
排查思路不是去改max_rounds,而是回去看工具返回的内容。很多情况下是工具结果没有给模型提供"新信息",比如查询接口只返回"无数据"三个字,模型无法据此判断下一步,只能原地打转。解决方法是让工具结果更丰富——返回"无数据,可能原因包括:订单号不存在、订单未支付、查询时间超出范围",给模型继续推理的抓手。另一个实用技巧是增加重复动作检测:记录最近三轮的工具调用签名,如果完全一致,直接终止循环并转人工,比任何prompt都管用。
5.2 幻觉和乱调工具:Agent最令人头疼的部分
模型"编造"订单号、编造退款金额、或者干脆跳过查库直接告诉用户"您的订单已退款成功",这类问题在Agent落地时几乎一定会遇到。根因不在模型本身,而在于你的Agent架构没有做好事实约束。
我常用的三招,按优先级排序:
- 输出必须基于工具结果。在System Prompt里写死:所有涉及订单、金额、状态的回复,必须引用最近一次工具返回中的数据,无引用即无发言权。
- 落地结果校验。Agent生成回复后,用一段独立代码校验关键字段。比如回复里出现"退款成功"字样,就去确认退款接口确实被调用了。校验逻辑用硬编码写,不用模型判断——机器能做的事,不要让模型做。
- 不确定性兜底。当Agent自己都拿不准的时候,强制它转向"信息不足、请您补充"或者"转人工处理",而不是让它在模糊的上下文里硬编一个答案。
5.3 成本、延迟与可观测性
Agent方案不是免费的。每轮循环都是一次模型调用,一个复杂工单跑5轮,token消耗轻松翻几倍,响应延迟从毫秒级到几秒也是常事。控制成本的关键在于任务拆分:简单工单走一个极简Prompt版本,复杂工单才启用完整循环。我见过性价比最高的做法,是用一个轻量分类模型先判断工单复杂度,简单的直接走固定流程,只有长尾复杂工单才送进Agent。成本立省一半以上。
可观测性则是另一个容易被低估的问题。硬编码的日志就是记录"走了哪个分支",Agent的决策过程如果不记录,出了问题你连它是怎么走到这一步的都查不到。上线前必须把完整轨迹记录下来:每轮决策、调用了哪个工具、传了什么参数、工具返回了什么、最终为什么这么答复。排查幻觉问题的时候,这些轨迹就是你的第一现场。
5.4 安全兜底与人机协作
现阶段我坚决反对让Agent完全无人值守。正确的姿势是"Agent做初步处理,人工做最后把关"——高置信度的简单工单自动处理,低置信度的转人工,人工从转发按钮、拒绝自动处理模式里保留绝对控制权。这套兜底设计不仅能防安全事故,还能持续给Agent提供人工反馈样本,用来迭代Prompt和工具描述。等你在灰度数据里确认准确率稳定达标了,再逐步放开自动处理范围。
| 常见问题 | 表现形式 | 我的处理方案 |
|---|---|---|
| 无效自旋 | 反复调用同一工具,结果不变 | 加重复动作检测,三轮一致直接转人工 |
| 幻觉事实 | 编造订单号、金额、状态 | 回复必须基于工具结果,落地结果用代码校验 |
| 规则冲突 | 新需求导致旧逻辑失效 | 不在Prompt里堆规则,改用工具边界约束 |
| 成本失控 | token消耗过高、延迟变大 | 轻量分类前置,复杂任务才进完整Agent |
| 决策不可解释 | 出了错不知道为何如此决策 | 全程轨迹记录,每轮决策留痕 |
6. 写在最后的一点个人体会
做了几年Agent相关项目,我自己的一个很明显的感觉:Agent能不能成,不取决于模型有多聪明,也不取决于框架有多花哨,而是取决于你敢不敢把自己手里那套"写好的规则"放掉。硬编码是在编码期替系统做决定,Agent是在运行期让系统自己做决定。后者要求的不是写代码的能力,而是设计约束的能力——什么时候放开自由度,什么时候加限制,出错了怎么兜底,全都要靠实际跑出来的经验积累。如果让我给一句最实在的建议,就是先别管概念怎么吵,找一个"规则已经维护不动"的业务角落,用最小Agent方案顶上,跑一个月对比数据,你会对这个问题有远比看文章深刻得多的答案。