一文讲透确定性Harness

Agent 圈流行一句话:「模型是商品,harness 才是护城河」。凡是「高风险 + 复杂分支 + 结论必须可解释」的决策场景,都会碰到同一面墙:模型是概率性的,结论必须是确定性的。本文不讲怎么把模型调得更聪明,讲的是一个更普遍的问题:高风险决策场景下,如何让一个agent概率性的输出,约束为确定性结论,达到可交付的标准?

我们的答案是「确定性 Harness」。它做了四件事:把策略文档拆成阶段化的代码流水线;用 FlowTracer 把每一步的输入、输出、分支都留档;用 IsFinal 提前终止,能下结论就停;用统一网关加缓存,稳住外部依赖、幂等内部响应。LLM 在这套东西里不是主角,它是个可以随时插拔的判断单元。

以工单审核业务为例验证这套框架。这条链路涵盖九个阶段、几十个条件分支、六步顺序校验,背后是策略文档、十几个原子接口、用户提交的多类材料,只要有一单判错就是客诉,典型的「结论必须负责」场景。

工单审核结果直接决定用户、商家权益与合规认定。所以这个场景里,Agent 真正的门槛不是「模型能不能答对」,而是「它答完之后,谁来为这个答案负责」。模型能生成答案,却无法为答案背书;一个敢上线的 Agent,必须让每一个结论都能被验证、被追溯、被追责。这才是模型之外那层「执行纪律」存在的意义。

整体架构图

一文讲透确定性Harness

01 背景与痛点:「接个 LLM 就够」是错觉

1.1 业务规模

一文讲透确定性Harness

1.2 痛点传递链

过去一个工单从进来到出结论,绕不开这条「人肉流水线」:

一文讲透确定性Harness

业务预期vs工程现实

一文讲透确定性Harness

一句话痛点:「接个 LLM 就够」是个错觉。模型回答要抵达用户屏幕前,还挡着一条主链路和一堆能力分支,真正的麻烦都在这些工程细节里。

三个问题随之而来:

痛点 表现
黑盒不可解释 审单人员、用户、监管问「为什么驳回」,纯 LLM 答不上来
循环失控 工具调用链长、字段错配,模型一「死循环」就卡住整条流水线
证据链断裂 接口、模型、人工补充混在一起,事后复盘找不到「这一单凭什么这么判」

02 三大挑战:为什么「通用方案」撑不住高风险决策

问题不在模型聪不聪明,而是高风险场景里有三个绕不开的坎,通用方案一个都接不住。

2.1 黑盒不可解释

审单结论直接影响用户权益、合规认定、监管检查。结论必须能讲清楚为什么。纯 LLM 输出一句「我建议驳回」没用,得能追溯到具体规则、具体证据、具体数据点。

2.2循环失控

LLM Agent 容易「循环调用」:一个判断没得到想要的结果,它会换个说法再调一次、换个参数再试一遍,上下文越滚越长。一单审完要串起十几个原子接口、多份材料,每次重试都是一次实打实的接口开销,循环调用多了,工单结论出得慢。

2.3证据链断裂

模型调用、工具返回、人工补充、缓存命中混在一起,没有统一记录。一旦「为什么这么判」被质疑,找不到一处能回放完整判断轨迹的地方。高风险场景里,这是最不能接受的。

这三个坎叠在一起,「接个 LLM、套个 prompt、直接上线」这条路在高风险审单场景里走不通。

2.4问题本质:确定性与概率性的矛盾

为什么这三个坎,用更聪明的模型、更精致的 prompt 都绕不过去?因为它们背后是同一个根子问题——LLM 本质上是概率性的,而工单审核要的是确定性。

模型给的是「最可能的回答」,不是「必然正确的回答」。同一句话换种问法,答案可能不一样;同一个工单重跑一遍,结论可能漂移。放在写文案、做摘要里,这种漂移无伤大雅,甚至被称作「创造力」;但放在审单里,结论一旦漂移,就意味着同一条规则下,A 看到的结论是「驳回」、B 看到的是「通过」——这不可接受。

所以真正的问题不是模型不够聪明,而是「概率性的判断」和「确定性的结论」之间,隔着一道先天的鸿沟。Harness 做的所有事,本质上都是在为这道鸿沟搭桥:用编排固定路径,用留痕固定证据,用终止固定边界,用网关固定依赖。桥搭稳了,模型这个「概率性的部件」,才能被安全地装进「确定性的机器」里。

03 背景:服务重构为什么难

3.1 一句话哲学

模型是商品,harness 才是护城河。

LLM 是可替换的判断单元;真正让高风险决策能上线、能查、能复盘的,是它外面那套执行纪律,我们叫它 Harness。

3.2 Harness 的四个关键设计

一文讲透确定性Harness

Harness 关键设计总览

一文讲透确定性Harness

这四件事不是并列的四个技巧,而是一条环环相扣的闭环:编排回答「该走哪条路」,留痕回答「走完怎么证明」,终止回答「走到哪一步就够了」,网关回答「路上的外部依赖怎么稳住」。少了留痕,编排跑得再顺,出了错也无从查起;少了终止,编排就成了「明知结果还要跑完全程」;少了网关,前面的编排、留痕、终止再完善,也会被一个不稳定的上游接口反复打断。四个设计缺一个,闭环就断了。

3.3 层级职责矩阵

一文讲透确定性Harness

04 关键技术拆解

4.1 阶段化编排:9 个阶段

🎯 挑战:策略文档几十个分支,深层 if/else 会晦涩冗长。

💡 解决方案:拆成独立「阶段函数」,主流程用 goto 标签组织复杂分支,主干始终是一眼能看穿的线性结构。

具体做法是:每个阶段都是一个签名统一的函数,只做一件事、只写自己负责的结果字段;主流程用 goto 标签把「条件命中 → 规则匹配 → 信息提取 → 状态判定 → 材料核验 → 打标结单」这条主线串成一条可读路径。新增一个判断点,就是新增一个函数加一段编排,不动既有分支。

核心代码:阶段函数 + goto 标签编排

func (a *Agent) Main(ctx context.Context) (*Result, error) {
    a.Tracer = NewFlowTracer()
    
    // 阶段一
    st := a.Tracer.BeginStage("阶段一:条件命中判断")
    StageOne(ctx, &a.Req, &a.Rsp)
    st.EndStage()
    
    if a.Rsp.HitConditionA {
        // 阶段二:规则匹配
        StageTwo(ctx, &a.Req, &a.Rsp)
        if !a.Rsp.RuleMatched {
            goto TAG  // 未命中 → 直接打标结单
        }
        // 阶段三~六:信息提取 → 状态判定 → 分流...
    } else {
        goto VERIFY  // 走核验分支
    }

VERIFY:
    // 材料核验阶段
    StageVerify(ctx, &a.Req, &a.Rsp)

TAG:
    // 所有分支最终汇聚:打标/备注/结单
    StageFinalTag(ctx, &a.Req, &a.Rsp)
    a.Rsp.DebugTrace = a.Tracer.GetStages()
    return &a.Rsp, nil
}

关键设计:每个 StageXxx 是签名统一的阶段函数 (ctx, req, rsp) error,只写自己负责的字段;goto 标签让主干保持线性——分支再多也不会出现深层嵌套。

阶段化编排

一文讲透确定性Harness

一文讲透确定性Harness

📊 效果:新增阶段只改一处,review 一眼能定位;策略文档改动只需重写对应阶段函数,主流程骨架保持稳定。

4.2 全链路留痕:FlowTracer

🎯 挑战:阶段一多,每一步「为什么这么判」就没人记得住了。

💡 解决方案:每个阶段用 BeginStage 开档,SetInput / SetBranch / SetOutput / SetError 记数据,EndStage 收尾;调试开关默认关闭,需要时一键打开,零开销。

实现上,追踪器是一个「阶段记录」数组。每个阶段 BeginStage 开一条记录,里面带着阶段名、开始时间、输入、接口原始返回、命中的分支、输出、错误;EndStage 收尾时把这一阶段的耗时也算好。整个数组最后随结果一起返回,方便逐阶段回放。

FlowTracer 留痕记录

一文讲透确定性Harness

核心代码:FlowTracer 数据结构与零开销设计

// debugEnabled 通过环境变量控制,关闭时 NewFlowTracer 返回 nil
var debugEnabled = os.Getenv("AGENT_DEBUG") != "0"

// StageTrace 单个阶段的追踪记录
type StageTrace struct {
    StageName  string                 `json:"stage_name"`
    StartTime  time.Time              `json:"start_time"`
    CostMs     int64                  `json:"cost_ms"`
    Inputs     map[string]interface{} `json:"inputs"`
    RawAPIData map[string]interface{} `json:"raw_api_data"`   // 接口原始返回
    BranchHit  string                 `json:"branch_hit"`     // 命中的分支
    Outputs    map[string]interface{} `json:"outputs"`        // 写入 result 的字段
    Error      string                 `json:"error,omitempty"`
}

// NewFlowTracer debug 关闭时返回 nil,所有方法空转,零开销
func NewFlowTracer() *FlowTracer {
    if !debugEnabled {
        return nil
    }
    return &FlowTracer{Stages: make([]*StageTrace, 0)}
}

// BeginStage / EndStage — tracer 为 nil 时安全降级
func (t *FlowTracer) BeginStage(name string) *StageTrace {
    if t == nil { return nil }
    st := &StageTrace{StageName: name, StartTime: time.Now(), /*...*/}
    t.Stages = append(t.Stages, st)
    return st
}
func (st *StageTrace) EndStage() {
    if st == nil { return }
    st.CostMs = time.Since(st.StartTime).Milliseconds()
}

关键设计:nil 安全降级——debug 关闭时 tracer 为 nil,所有 SetInput/SetOutput/SetBranch 方法第一行都是 if st == nil { return },线上零开销;需要排查时改一个环境变量即可全量开启。

一文讲透确定性Harness

📊 效果:每一单的完整判断轨迹都进 DebugTrace 字段,回放时一帧不差。线上默认关着不亏性能,需要排查时打开就行。

4.3 提前终止:IsFinal 短路

🎯 挑战:6 步顺序校验里,前面已经能下结论的工单,没必要把后面步骤全跑一遍。

💡 解决方案:每个 handler 设置 IsFinal=true,主循环立即跳出,结论能早一步出就早一步出。

实现上,整个校验流程是一个声明式的 handler 列表,主循环按顺序执行:单个 handler 失败只记日志、继续走下一个;一旦某个 handler 判定「这一单已经有最终结论」,就置 IsFinal=true,主循环 break。新增一个校验节点,就是数组里加一项,主循环不用动。

提前终止机制

一文讲透确定性Harness

核心代码:声明式 handler 列表 + IsFinal 短路

// 主循环:顺序执行,失败不阻断,IsFinal 短路
for i, handler := range a.buildWorkFlow() {
    if err := handler(ctx, &a.Req, &a.Rsp); err != nil {
        log.Errorf("handler[%d] failed (continue): %v", i, err)
        continue  // 单步异常不阻断
    }
    if a.Rsp.IsFinal {
        break  // 能下结论就停,省掉后续步骤
    }
}

// buildWorkFlow 声明式工作流 — 新增节点 = 数组加一项
func (a *Agent) buildWorkFlow() []FlowHandler {
    return []FlowHandler{
        CheckBasicInfo,       // 基本信息校验
        CheckConditionMatch,  // 条件匹配
        CheckMaterialA,       // 材料A核验
        CheckMaterialB,       // 材料B核验
        CheckMaterialC,       // 材料C核验
        CheckFinalVerify,     // 最终校验
    }
}

关键设计:continue 保证单步异常不炸全链路;IsFinal 保证能下结论就停——不让「已知结果还跑完全程」;新增校验节点只改声明数组,主循环零改动。

一文讲透确定性Harness

📊 效果:能下结论就下结论,不用把后面的材料识别全跑一遍;新增校验节点就是数组里加一项。

4.4 统一网关与缓存

🎯 挑战:十几个原子接口,签名各搞各的;同一工单重复查询会反复打接口。

💡 解决方案:所有原子接口走同一条 CallLxAtomAPIs(MD5 签名封装);结果按 TaskID 落 Redis 100h 缓存。

一文讲透确定性Harness

一文讲透确定性Harness

📊 效果:上游接口签名统一,业务层只关心路径和 body;重复请求直接命中缓存,天然幂等。

4.5 四件事的内在一致性

把四个设计拆开看,它们各解决一个问题;合起来看,它们遵循的是同一条原则——把「不可控」关进「可控」的笼子里。

阶段化编排,是把不可控的分支关进可控的流程;全链路留痕,是把不可控的黑盒关进可控的记录;提前终止,是把不可控的循环关进可控的边界;统一网关与缓存,是把不可控的外部依赖关进可控的接口。

四个「可控」,指向的是同一个词——确定性。这也是为什么这套骨架能沉淀成可复用的模式:它们不是审单的专属技巧,而是「如何让一个概率性系统变得确定」这个通用问题的四种解法。换个场景,分支还是那个分支、黑盒还是那个黑盒、循环还是那个循环、依赖还是那个依赖,解法不变。

05 实战案例

5.1 案例 1:多分支审单主线

🎯 场景:业务的工单审核需要经过多轮条件判断,逐层判定用户提交的材料是否满足条件,最终决定通过还是驳回。

Harness 表现

  1. 阶段一判断工单类型,留痕记录命中的类型和分支;
  2. 阶段三从材料文本里正则提取关键编号,记录原始文本和提取结果;
  3. 阶段五判断状态,分支到不同的比对路径,每条分支都进 DebugTrace;
  4. 满足条件则通过结单,否则进入材料核验。

价值体现:一条几十个分支的策略文档,被翻译成「阶段函数 + 标签路由」的确定性代码,每一单都有一份完整的判断轨迹。审核员面对用户质疑时,调出 DebugTrace 就能逐条回放:这一步看到什么数据、命中了哪条规则、最后为什么这么判。

5.2 案例 2:提前结单

🎯 场景:工单基本信息校验不通过,不该再走后续 5 步校验。

Harness 表现

  • 基础信息校验一步判定「信息不存在」,设置 IsFinal=true
  • 主循环立即 break,不再触发后续的材料识别;
  • 审核员 3 秒内拿到「驳回」结论,而不是等 5 个步骤全部跑完。

价值体现:提前终止把单工单耗时从「6 步全跑」压到「能下结论就停」,工单洪峰下尤其重要。

06 效果与方法论:从效率到模式

6.1 价值跃迁三重突破

一文讲透确定性Harness

价值跃迁三级阶梯

一文讲透确定性Harness

⚡ 量化提效:工单平均处理耗时从 2-3 分钟压缩至 20-40 秒。

📥 能力下沉:审核员从手动调接口加肉眼比对,变成看结论加调 DebugTrace 回放;开发从每个接口写样板代码,变成只写阶段业务判断。

📚 模式升级:「确定性 Harness」这套思路能从审单场景搬到任何「强监管 + 复杂分支」的决策场景,沉淀成可复用的资产。

6.2 方法论提炼

模型是商品,Harness 才是护城河:Model as Commodity, Harness as Moat

Agent 时代真正稀缺的是让 LLM 敢上线的工程纪律,模型本身反而会被快速拉平。

编排即流程,标签即路由:Orchestration as Flow, Labels as Routing

把策略文档拆成阶段函数加标签路由,复杂分支也能保持线性可读,新增节点成本很低。

留痕即可追溯:Tracer as Trust

每一单的判断轨迹都进 DebugTrace,环境变量零开销开关,让「敢上线」和「能查账」同时成立。

终止即效率:Final as Speed

能下结论就立刻下,不让循环失控;IsFinal 短路让单工单耗时不再依赖「全链路跑完」。

一些反直觉的取舍

这套工程框架里有几个决策,看起来「反常识」,恰恰是它们最值得说:

  • 单一阶段失败不阻断。工程的第一反应是「出错就该停」,但我们选择「记下来、继续跑」。因为审单场景里,一个接口超时不该让整单卡死——给出一个「带着伤口的结论」,好过「没有结论」。
  • 留痕默认关着。可解释是刚需,但我们让它默认零开销。因为「需要时能查」和「平时不拖累」必须同时成立,否则留痕自己就会变成新的性能包袱。
  • goto 而非「更优雅」的结构goto 在大众认知里是「坏味道」,但在分支密集的编排里,它反而是让主干保持线性的最直白手段。

这些取舍的价值,不在于它们本身多高明,而在于它们提醒我们:Agent 工程不是把软件工程的老规矩照搬过来,而是要回到「这个场景到底要什么」重新做判断。

07 演进规划与边界

7.1 演进路线

  • 挂载可插拔的判断单元:在 stage 函数里把确定性判断逐步换成 LLM 调用,harness 整体保持不变。这正好是 Harness 设计预留的空间。
  • 触发模式升级:从被动接收工单,升级为定时主动巡检、异常主动告警。
  • 闭环动作扩展:从「只读判定 + 打标」逐步扩展到「可控写入」(自动回填标签、生成答复话术),但始终走 L1/L2/L3 等级。

7.2 适用边界

不适合本方案的场景

  • 场景 1:纯生成式任务(文案、翻译、摘要),用 prompt 工程就够了,harness 反而是负担。
  • 场景 2:必须 100% 黑盒的探索性任务,本方案强调可解释可追溯,和黑盒诉求冲突。
  • 场景 3:单次调用、不需要循环和工具的轻量任务,没有 harness 发挥空间。

核心判断:业务「高风险 + 复杂分支 + 必须可解释」,选 Harness 优先;「低风险 + 灵活开放 + 追求吞吐」,直接用模型即可。

边界之外,还有一层更本质的判断:Harness 解决的是「结论要负责」的问题,而不是「结论要更快」的问题。如果你的场景根本不在乎结论可不可解释、可不可追溯,那 Harness 的每一层都是纯成本——留痕是多余的内存,编排是多余的代码,终止是多余的约束。

所以该不该上 Harness,本质是问一句:这个 Agent 的结论,需不需要对某个人、某条规则、某次检查负责?需要,就值得为「确定性」付出工程成本;不需要,就让它自由地快。

7.3 分级置信度

一文讲透确定性Harness

LLM 介入也按这个等级走:L1 让模型直接出结论;L2 模型给建议、人工拍板;L3 模型只能给 SOP,禁止自动执行。

08 结语

阶段化编排,把复杂分支翻译成可读的流程;全链路留痕,让每一次判断都有迹可循;提前终止,在能下结论时立刻收口;统一网关与缓存,把外部依赖关进笼子。

四件事,没有一件是为了让模型更聪明。它们补的是模型天生缺的那块——约束与规范化的工程。它不指向任何一段代码,只指向一个判断:让 Agent 敢对结论负责的,从来不是模型的智力,而是模型之外那层纪律。

-End-

原创作者|左昊

本文来源@腾讯云开发者。原文链接:https://mp.weixin.qq.com/s/rQYSuTF98-xGcdgGPuNHjQ

行业动态

我给 WorkBuddy 加了一个浏览器 Skill,让 Agent 真正替你“上网干活”

2026-9-1 11:57:49

行业动态

498 装“小龙虾”年入百万?先别信,我从工程角度为你拆解 OpenClaw

2026-3-5 8:28:41

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧