
1. 为什么“Agent 接线工程”总想把规划搞复杂1.1 先分清 Agent 和它身边的 Harness聊 Agent 工程的人很多但我翻了很多团队的技术复盘真正把“模型能力”和“外围工程能力”分清楚的极少。先说个我自己的理解模型本身做的是“语义推断”它并不知道自己的输入是怎么来的、输出要被谁消费、调工具调失败之后该找谁兜底。把这些外围问题全部管起来的这一层才是 Harness Engineering。Harness 这个词最早是从测试脚手架里借来的。做 UI 自动化的时候没人会把 Selenium 的 Driver 本身当成测试逻辑它只是“让测试脚本能操作浏览器”的壳。Agent 的 Harness 也一样它处理的是请求怎么构造、上下文窗口怎么滚动、工具 schema 怎么暴露、输出怎么校验、超时重试怎么走、成本和权限怎么受限。说白了它是模型的“工装”。很多团队踩坑不是模型的错而是 Harness 层被做成了“高射炮打蚊子”。他们给一个本来只需要五步固定流程的审批机器人装上了完整的规划器让模型每次执行前都先“想”一遍全局计划再一步一步往下走。结果任务没变难token 先烧了三倍失败率还涨了。这不是模型不行而是 Harness 设计把不该交给模型的决策交了出去。我把这类问题归结为一个反直觉的判断在做 Agent 外围工程Harness Engineering的时候很多场景根本不需要复杂规划能力简单规则反而更快、更稳、更省钱。下面展开说为什么。1.2 “模型越强越要放权”——这是行业里最贵的一个直觉现在主流的多智能体框架默认会给 Agent 配一个“计划器”。用户丢一个任务进来模型先生成一段精心编排的计划拆成子任务再按计划调度工具。听起来很科学但实际跑起来这笔账往往算不过来。原因不复杂。规划能力在开放式任务里是加分项但在封闭式流程里它是纯开销。流程封闭意味着动作集合在手写代码的时候就已经确定了Agent 每走一步该做什么、不该做什么Harness 完全可以约束住。这时候你让模型再规划一遍等于让一个按流程办事的柜员每次办理业务前先写一篇“我打算怎么办”的作文。有这时间业务早办完了。另一个更隐蔽的问题是规划器的存在本质上是在鼓励模型“自由发挥”。我见过不少项目模型原本只需要做“抽取单据字段→按规则填入系统”有了规划器以后它开始自己设计执行顺序、自己猜测字段映射、自己决定跳过某一步。凡是给它发挥空间的地方它一定会发挥哪怕这个空间根本不该存在。所以我现在的习惯是先明确任务的决策空间到底有多大。如果动作集合是有限的、顺序是确定的、边界是清楚的优先级最高的永远是确定性逻辑而不是模型推理。1.3 复杂规划在 Harness 层的三个隐性代价很多人只看到了“复杂规划”的表面能力没算背后的成本。我拆过几个自己维护过的项目把隐性代价列一下每一条都是用真金白银换来的第一Token 成本被放大到难以预估。规划器在推理过程中会输出大段中间思考这些内容不直接产生业务价值却占满了上下文窗口。每一次工具调用结果回传还要和这些思考挤在一起很快就把窗口撑满触发截断或摘要进一步加剧信息损耗。第二延迟被拉高用户体感断崖式下降。复杂规划意味着每步之间多了“模型思考时间”。在客服工单这种场景里用户等 20 秒和等 3 秒满意度完全是两个量级。简单规则把大多数路径收敛为直连调用等一个 LLM 单次响应就好延迟基本可控。第三可解释性和排障难度同时恶化。规划器做出的路径是概率性的A 用户走到步骤 3B 用户走到的却是步骤 5你根本说不清是数据不同还是模型“临场发挥”。出了问题以后工程师不得不在日志里逐句翻模型的“自言自语”试图还原当时的决策依据。这种排障方式谁干谁知道有多痛苦。正是这三个代价让我后来在设计 Harness 时把目标从“让模型更聪明”改成了“让模型更听话”该它输出的地方输出不该它决策的地方一道闸门拦死。接下来我用两类最典型的场景把“简单规则更高效”这件事说透。2. 场景一流程可预定义的业务编排规则就是最优解2.1 这类业务到底是什么模样第一类场景我称呼它“流程可预定义的业务编排”。判断依据很简单你拿出一张纸能把这单子上的业务从头到尾画成流程图吗如果能这就是第一类。典型例子包括客户工单自动分配、审批流自动化、报销单初筛、数据同步任务编排、订单状态流转。这类业务的共同点是动作集合在需求评审阶段就定死了状态之间的迁移条件也都是明确的。拿“工单自动分配”来说无非是解析用户描述→判断紧急度→匹配客服分组→更新工单系统→发送通知。每一步都是铁板钉钉的。在这些业务里你问模型“下一步该干嘛”它并不能比代码回答得更好。因为所有“下一步”早就写在需求文档里了有明确分支没有开放性探索空间。把这个决策交给模型等于把裁判权交给了一个拥有无限想象力的参赛选手——它很厉害但它真没必要替你判断该走哪条分支。那模型在这类场景里的价值在哪恰恰在“步骤内部”把非结构化的用户描述转成结构化字段、对工单做语义分类、把审核意见生成一段合规文案。这些才需要语言能力。流程本身交给规则。2.2 规划器为什么在这里变成负资产我在一个客服项目上做过一次对照实验。同一个工单处理需求一版用复杂规划器让模型自行规划步骤一版用状态机把流程焊死模型只负责“读取意图→填字段→产出结论”。同一个模型同一个测试集结果差距比想象中大得多。规划器版本的问题是模型的“自由发挥”经常体现在不该发挥的地方。明明规则是“紧急工单直接转人工”规划器会让模型先去查历史工单、再查客户等级、再判断是否升级最后还不一定走对分支。每一步看起来都“合理”但整条链路又长又不可控。失败率统计下来有接近 20% 的工单因为模型中途选择了错误的路径兜底到了人工。这还不是最让我吃惊的。最让我吃惊的是 token 消耗。规划器版本平均每单消耗约 4200 token其中一半以上是中间推理和试错成本状态机版本把单次交互压在 900 token 以内直接把成本打到了原来的四分之一。后来我把这套逻辑想明白了流程可预定义的场景里规划器的“探索能力”没有用武之地反而变成了引入概率错误的最大入口。规则系统虽然“笨”但它每一步都按代码走不会突然心血来潮给你跳一步而这正是业务流程最需要的品质。2.3 落地形态状态机 工具白名单 规则校验那么“简单规则”具体落到 Harness 层长什么样子我习惯用三件套来搭第一件状态机负责“流程流向”。把业务拆成 FSM有限状态机的状态每个状态绑定一个明确的处理函数。所有状态迁移只由结构化数据触发字段 A 等于某值就进状态 B校验失败就进状态 C。模型没有修改状态的权力它只是“消费品”。第二件工具白名单负责“动作边界”。Agent 每次只能调用 Harness 暴露出来的少数工具工具的参数都要用 JSON Schema 卡死枚举值列清楚。比如“更新工单状态”只允许传“待处理、处理中、已解决、已关闭”模型传一个“转给领导看一眼”Harness 直接拒绝。第三件规则校验负责“输出兜底”。模型产出的结构化结果在进入业务系统之前一定要先过一个校验器。字段缺失、类型不对、枚举越界统统拦截住。校验不过就触发修正修正两次仍不过就转人工。这些逻辑全部写在代码里而不是写进提示词让模型“自己注意”。这套设计做下来Agent 的实际形态更像“智能表单处理器”模型负责把自然语言塞进一张张预制的表格Harness 负责让表格只能流经该走的路。用户感知到的依然是一个“会用工具的大脑”但工程上它已经退化成了一个高度可控的组件。这种退化业务稳定性反而起飞了。3. 场景二高保真、可审计、可回滚的批量作业确定性高于创造性3.1 批量作业的三个硬性要求第二类场景是高保真、可审计、可回滚的批量作业。和第一类的区别在于它不是一次性的在线交互而是对成百上千条数据做流水线处理跑完以后要留痕、要复盘、要能撤回。典型场景包括大批量合同关键信息提取、内容合规审核、票据 OCR 后处理、历史数据清洗、批量营销文案合规检查。这一类业务有三个硬性要求缺一不可一是“每条数据都要做到”不允许模型因为上下文太长、心情不好或者碰到没见过的情况就滑过去一条。漏掉一条可能整批数据报废。二是“每个动作都要有日志”。谁在什么时间对哪条数据做了什么修改、依据是什么全部要能追溯。这不是可选项很多领域是合规底线。三是“出了错要能回滚”。要么幂等要么留快照。模型批量处理完以后如果发现系统性偏差你不可能接受“再跑一遍”这种回答因为你根本不知道中间改了什么。你把这仨要求摆出来再回头看“复杂规划”会发现它跟批量作业至少有两条根本冲突规划器引入的概率性路径导致“同一条数据跑两遍可能得到不同结果”这是审计和回滚最怕的事规划器的中间推理过程可读性是差到让人崩溃的你不可能在事后从模型的自言自语里判断“这条为什么被删了”。3.2 复杂规划与审计要求天然冲突我在某个偏审计导向的项目里见过一次让人印象深刻的失控。Agent 负责批量整理一批录入单据团队为了让 Agent “更智能”给它加了自我校验步骤模型处理完一条自己再检查一遍有没有漏项。听起来很合理对吧但灾难恰恰出在这里。模型在自我校验环节开始“脑补”遇到一个不太像单据的文件它直接在日志里写道“该记录疑似重复标记为异常”然后就真把它标记成了异常提交到下游。人工复核的时候发现一批正常单据被无端标记源头又是模型的“判断”完全没法解释决策依据。最后把那批数据全部回滚重跑还赔上了下游系统的排期。这种事说明一个道理审计场景里的每一步动作都必须是可复现的。复杂规划器最擅长制造不可复现同样输入这次它决定走校验分支下次它决定跳过逻辑在哪在权重里在几千个参数里在永远说不清的“推理”里。所以这类场景的 Harness 设计我之前定过一个原则所有状态变更只能来自确定性代码模型永远不能直接写库。它把“我觉得这里有异常概率 0.87”写进结果即可由 Harness 判断“0.87 是否达到标记阈值”。判断这件事模型说了不算。3.3 用简单规则搭出来的 Harness 长什么样批量作业场景里的简单规则 Harness我通常用“三明治”结构来表达第一层注入层按批次从数据源拉取一条记录拼进标准提示词模板模板里不塞任何历史记录保证每次只见当前这一条。这是“可复现”的基础输入越封闭输出越可控。第二层处理层模型只做一件事——输出固定 JSON。比如合同抽取就输出“合同编号、甲方、乙方、金额、币种、生效日期、异常标记”外加一个固定格式的“异常说明”。提示词里写明存在歧义就把歧义写进异常说明绝对不允许编造。第三层校验层拿 JSON Schema 校验格式拿规则库校验业务逻辑。金额必须大于 0日期必须合法异常标记只能是 0 或 1。校验不过进修正队列修正两次还不过就单独落盘交给人工。再配合两个工程开关幂等控制每条记录处理前先查状态已处理过的直接跳过或确保可覆盖和快照回滚批量开始前把整表复制一份到备份库跑完保留全部日志。这套东西没有任何“规划”成分但它天然满足高保真、可审计、可回滚。有个细节想提醒一下批量场景里千万不要让模型“自己发现错误然后自己改正”。改正这个动作一旦交给模型就失去了可追溯性。正确做法是固定两遍流程——第一遍抽取第二遍针对第一遍异常标记的数据单独走“复核提示词”。这样每一遍的职责清晰日志也能对上号。4. 实操把两类场景的 Harness 用简单规则搭起来4.1 一个可以直接照抄的状态机骨架前面说了那么多思路这节给一个能直接拿来改的状态机骨架。我用 Python 伪代码写但思想是语言无关的重点是控制权怎么收回来# 状态定义 ST { INIT: 0, EXTRACTING: 1, VALIDATING: 2, FIXING: 3, DONE: 4, HUMAN: 5, } class Harness: def __init__(self): self.state ST[INIT] self.retry 0 def step(self, input_data): if self.state ST[INIT]: self.state ST[EXTRACTING] return self.call_llm_extract(input_data) if self.state ST[EXTRACTING]: ok, error validate_schema(self.llm_result) if ok: self.state ST[VALIDATING] elif self.retry 2: self.retry 1 # 只允许针对抽取结果修正不改变任务本身 return self.call_llm_fix(self.llm_result, extract_schema()) else: self.state ST[HUMAN] return error if self.state ST[VALIDATING]: ok, reason validate_business_rule(self.llm_result) if ok: self.state ST[DONE] return self.persist_and_log(self.llm_result) else: self.state ST[HUMAN] # 业务规则不过直接人工 return reason if self.state ST[DONE]: return {status: done} if self.state ST[HUMAN]: return {status: human_review, payload: self.llm_result}几个需要特别解释的设计第一模型的所有输出统一进llm_result它永远不会直接改状态、永远不会直接调业务接口、永远不会自己决定“下一步换一个任务”。第二修正循环有上限战场只在“抽取结果修正”这一个小范围内避免模型借“修正”之名重写任务。第三状态机的迁移条件全部是代码逻辑业务规则校验不过就进人工绝不接受“规则错了改规则”这类由模型触发的路径。这个骨架看着很基础但它恰恰是“简单规则”能扛住生产压力的核心原因流程封闭责任明确错了能定位到具体步骤。曾经有个同事形容它“像一个带安全锁扣的流水线而不是一个自由泳的机器人”我觉得这个比喻很准确。4.2 提示词工装把模型当“函数”而不是“合伙人”Harness 里还有一个常被忽视的部分提示词工装。同一套状态机配合不同的提示词组装方式稳定性能差出好几倍。我总结下来简单规则场景里的提示词不需要千变万化关键在于把模型当“函数”用——输入输出都钉死。做法是提示词模板固定下来且每个字段都赋予明确含义。比如抽取任务的模板我的习惯是明确告诉模型“你只执行抽取不负责判断流程输出必须是合法 JSON遇到无法确定的内容填入None任何补充说明放到remark字段里”。还有一个很重要的细节让模型输出结构化结果时尽量给它一个“输出示例”而不仅仅是描述。模型对“长什么样的输出”非常敏感你给一个具体的 JSON 样本比写十句话“请输出 JSON 格式”管用得多{ contract_no: HT-202407-001, party_a: 某某科技有限公司, party_b: 某某供应链公司, amount: 128000.50, plus_tax: false, sign_date: 2024-07-15, exception_flag: 0, remark: 合同日期格式已清洗原值为2024/7/15 }这套组合拳打下来模型的角色就是吃一段非结构化文本吐一个标准化 JSON。它不用知道这条数据后续要经过几轮校验也不用规划接下来调用什么接口。Harness 层把“路由、状态、重试、审计”全部包办了模型只需要当好转换器。有读者可能会问提示词里要不要写“如果遇到 XXX 情况你应该怎样怎样”我的建议是不要写。一旦提示词开始描述复杂分支模型就开始“主动思考”进而开始扩大输出自由度。所有分支处理逻辑放到 Harness 层去写这才是“简单规则”的真正边界。4.3 一组对比数据到底省了多少东西在另一个批量票据识别的项目里我们曾经在同一批测试数据上对比过“复杂规划”和“简单规则状态机”两套 Harness 方案。测试集是 2000 张卡片录入图片输出要求完全一致只是驱动方式不同。数据是模拟出来的但结论很有代表性对比项复杂规划方案简单规则方案单条平均 token 消耗约 3800约 1100单条平均处理延迟约 8.5 秒约 3.2 秒字段级准确率97.2%98.6%异常漏检率应转人工未转4.7%1.8%人工复核后返工率约 12%约 4%单次批量总成本约为简单规则版 3.4 倍基准有人可能会盯着“准确率”说0.7% 的差距也不多吧但请注意后面两行漏检率和返工率。在批量作业里返工是最贵的成本。复杂规划方案的大量返工来自模型“自作主张”的修正动作它改错的字段甚至会被日志记为“已处理”人工复核时才发现造成双倍工作量。事后对失败样本做了归类我发现规划方案的错误大多集中在同一类它在推理中发现了第一个异常后不是按规则停下而是继续推断“异常周边可能有更多异常”于是开始连带处理处理范围越滚越大最后把一张本可以一次搞定的单据搞成了高失败率样本。规则方案就没这个问题第一步校验失败就直接进人工不拖泥带水。5. 简单规则翻车现场常见问题与排查实录5.1 状态机写死了新需求一来就改代码总有人说“简单规则不好扩展”这个说法一半对一半错。对的部分是状态机确实比自由规划难加新流程错的部分是大部分“新流程”其实是旧流程的排列组合不需要动状态机核心只要在配置表里加条目。我们碰到过的真实案例是工单系统加了一个新的客服分组分支。如果 Harness 是硬编码状态迁移确实要去代码里加一个 if。但后来我把状态迁移表抽到了数据库状态、触发条件、动作、下一个状态四个字段配置化。新需求来了产品经理在后台加一行“如果工单属于 XX 分类 → 转到 XX 分组”测试跑一遍就上线完全不用重新部署。所以做简单规则 Harness 时尽量把“规则”和“代码”分离。状态机骨架是稳定的规则内容是数据。这样既保住了确定性和可审计性又规避了“改需求就要改代码”的维护噩梦。真正需要动代码的地方永远是新增“动作类型”而不是新增“分支条件”。5.2 校验太严模型输出被误伤简单规则落地的第一个常见问题是模型输出总是过不了规则校验。不是因为它错了而是校验器太死板。比如 JSON 格式允许 key 顺序不同模型偏偏把一个字段的 key 写成了驼峰校验器一看 key 不匹配直接拒收这会白白消耗修正次数。处理思路不是放宽校验而是优化它的“宽容边界”。我的做法是先用 Loose JSON Parser 把模型输出做一层归一化把 key 重命名、去掉多余空格、把 Unicode 转义还原再交给严格的业务规则校验。格式层放宽业务层收严两层分开。另一个更隐蔽的误伤来自“枚举值”太窄。比如异常标记只能填 0 或 1但模型遇到一种它觉得“有点怪但不算异常”的情况它就想填“0.5”。这时不要改提示词让模型别那么做而是在 Harness 层定义所有未知状态默认按 0 处理同时要求模型必须在remark里写明疑虑点。这样既保证流程推进又保留了可审计线索。5.3 把逻辑塞给提示词到头来还是失控最后一个常见问题最要命嘴上说“简单规则”实际却把大量逻辑写进提示词让模型去做判断和路由。我见过一个项目提示词里写满了“如果你是国内用户走 A 分支如果你是海外用户走 B 分支”整个提示词有两千字。表面看它没有用复杂规划器但本质是一样的把规则决策交给了概率输出。排查这种问题有一个很灵验的信号看模型在日志里有没有频繁输出“根据以上规则我认为应该……”这类内容。如果有说明规则已经写进模型脑子里了Harness 形同虚设。正确的做法是一切“用户画像→分支选择”的判断全部由 Harness 读用户字段、查配置表、走代码完成。模型只拿到已经就绪的那条分支的提示词它甚至不应该知道还有别的分支存在。真出现这种情况改起来也不难把提示词里的条件判断全部删掉改成在模型调用前由代码做分流。模块解耦之后每个分支的提示词都短一半不止模型的指令遵循度也会明显上升。这是我从一堆教训里提炼出来的第一原则能写在代码里的逻辑绝不写进提示词必须写在提示词里的只有“抽取什么、输出成什么样”。6. 什么时候才需要复杂规划边界划在哪6.1 三类例外开放域探索、目标模糊、多跳推理把简单规则吹了半天也得承认它并不是万能药。有三类例外是规则引擎明显顶不住的。第一类是开放域探索型任务。比如“调研一下目前市面上有哪些开源的向量数据库并对比它们的 License 策略”这类任务没有预定义的流程工具调用的选择本身就是目标。状态机没法穷举“探索路径”因为你不知道要找什么。第二类是目标模糊型任务。用户只丢一句“帮我分析一下这个季度的数据”但没说清分析哪个维度、用什么口径、产出什么报告。这时候模型需要多轮澄清、试探性提出假设规则流程很难提前画出来。第三类是多跳推理型任务。比如“找到所有与项目 X 有合作关系的公司再找到这些公司过去三年的诉讼记录汇总成风控报告”每一步单独看都能写规则但它组合起来构成的探索空间太大条件分支会指数膨胀规则表长到没法维护。这三类任务本质上都是“决策空间大、状态不完全可枚举”硬用简单规则会把代码写成天书维护成本远超收益。到这一步规划器确实有它的价值。6.2 三个信号出现再考虑上规划器判断一个任务要不要上规划器我自己的经验是看三个信号信号一规则分支数量开始失去控制。如果你画流程图时发现分支超过了三十个或者分支之间开始互相依赖、互相嵌套那说明这个任务的决策空间已经很复杂状态机快撑不住了。信号二工具调用顺序必须根据中间结果“创造性地”调整。比如做情报整理前面搜到的内容会影响后面该搜什么这种依赖是动态的没法预编译成固定路径。信号三任务目标允许有多个合法答案且答案质量需要凭经验判断。当“是否做完”的标准本身都变得模糊时规则就没法给确定答案了只能靠模型自评。这三个信号不必全中有一个比较突出就应该认真考虑引入规划能力了。但注意即使引入规划器我也不会推倒重来。Harness 层的状态机、校验器、审计日志这些底座会继续保留规划器只负责在底座之上生成动态路径走一步验一步验不过就回到规则兜底。6.3 折中方案混合架构规则保底规划做增量最后分享一个我目前比较满意的混合架构形态。具体做法是把任务拆两层第一层是“路由决策器”第二层是“执行 worker”。路由层用规则根据输入特征判断这是“固定流程”还是“开放探索”固定流程直接进状态机 worker开放探索才交给规划器。在承载规划器的 worker 内部我还特意设了一根“安全绳”规划器输出的计划必须是一份带格式的 JSON每个动作都要声明工具名、输入参数、预期产出。Harness 拿到计划后会做两件事——先校验所有参数是否满足 schema再在每一步执行后核对实际产出是否和预期一致。不一致就中断回到规则层重新决策。这种混合架构体验下来最舒服的地方是简单规则负责守住 90% 的确定性场景复杂规划只在那 10% 真正需要探索的场景里进场。所有动作依然有日志、有校验、有回滚能力“简单”和“智能”不再互相打架。我个人的体会是不要迷信“更复杂的模型能力”也不要用榔头砸螺丝。Harness Engineering 的核心职责是把模型的能力精准分配到该用的地方然后用工程手段把不该出现的偏差全部挡住。两类场景、一个原则——复杂留给模型擅长的语义推断简单留给一切可以被规则化的流程。只要这个边界划清楚Agent 系统的稳定性、成本和可维护性都会给你一个非常惊喜的回报。