谈 LLM 性能时,大家很容易先问一句:“这个模型每秒能吐多少 token?”但在真实应用里,这通常只是答案的一小部分。
一个模型可能生成得很快,却经常答错;也可能单次回答质量很高,但上下文太长、工具调用太多,导致一次任务的总耗时和总成本都不可接受。更复杂的是,模型现在经常不是单独工作,而是被放进一个包含提示词、检索、工具、重试、缓存和权限控制的系统里。
所以,LLM 性能最好分成两层来看:模型性能回答“模型本身有多快、多好”,系统性能回答“整个应用能否稳定、经济地把任务完成”。Harness 正是连接这两层的地方。
Harness 到底是什么
Harness 直译是“挽具”或“线束”,在软件工程里通常指包裹、驱动和观察某个组件的一层基础设施。它不是模型,也不只是一个测试脚本,而是一套让模型可以被重复运行、比较、约束和评估的机制。
在 LLM 场景中,Harness 常见有三种形态:
- 评测 Harness:准备数据集,运行模型,记录输出,再用规则、人工或另一个模型评分。
- Agent Harness:管理多轮对话、工具调用、状态、重试、审批、预算和终止条件。
- 生产 Harness:把模型接入真实服务,负责路由、缓存、限流、观测、故障转移和成本控制。
这三类 Harness 可以共用一套任务定义和日志格式。理想状态下,线上失败的一次真实任务,可以脱敏后直接回放到评测 Harness 中;评测发现的回归,也可以在灰度环境里验证。
什么是 Harness Engineering
如果说 prompt engineering 是把一句话说得更清楚,context engineering 是把相关信息放到模型面前,那么 Harness Engineering 更像是在设计一整个“可工作的环境”。它关心的不只是模型收到什么 prompt,还关心模型能看见哪些事实、可以调用哪些工具、每一步如何验证、失败后怎样恢复,以及什么条件才算真正完成。
这个词并不是某个唯一的开源框架或严格标准。它更像一种工程方法:把原本依赖人脑记忆、口头约定和人工检查的部分,逐步变成 Agent 能够读取的文档、可调用的工具、可执行的约束和可观察的反馈回路。OpenAI 在介绍 Codex 实践时把它概括为:人负责引导,Agent 负责执行;工程师的重点从直接写代码,转向设计环境、表达意图和建立反馈循环。OpenAI 的 Harness Engineering 文章 也展示了这一方法在真实软件项目中的具体做法。
从“让模型回答”到“让系统完成”
普通 LLM 应用的流程经常是:用户提问,模型回答,应用展示文本。Harness Engineering 面向的是更长的闭环:
人类目标 ↓任务契约与验收标准 ↓上下文、文件、日志和业务状态 ↓模型推理与工具调用 ↓沙箱或受控环境中的实际执行 ↓测试、观测、评审和结果校验 ↓完成、重试、修正或请求人工判断模型只是这个闭环中的推理组件。真正决定 Agent 能不能持续工作的,是周围的接口和反馈是否足够清晰。
Harness Engineering 的六个工程面
| 工程面 | 要解决的问题 | 常见实现 |
|---|---|---|
| 意图 | 用户到底想要什么,什么算完成 | 任务说明、验收标准、执行计划 |
| 上下文 | 模型应该知道哪些事实 | 仓库文档、结构化状态、检索和摘要 |
| 工具 | 模型能做哪些动作 | 类型化工具、schema、权限和幂等键 |
| 环境 | 动作在哪里安全执行 | 沙箱、临时 worktree、测试数据库 |
| 反馈 | 模型如何知道自己做对了 | 单元测试、类型检查、日志、截图和评测器 |
| 治理 | 如何避免失控和长期腐化 | 审批、限额、审计、CI、架构约束和清理任务 |
这六个面不是六个独立模块。比如,一个“修改网页”的任务如果没有可启动的本地环境,Agent 就无法确认页面是否真的修好;如果没有截图、浏览器状态或测试结果,它只能根据代码猜测;如果没有权限边界,它可能为了完成任务修改了不该修改的数据。
为什么长任务特别需要 Harness
让模型回答一个问题,和让 Agent 连续工作几个小时,是两种完全不同的工程问题。短任务主要考验模型能不能理解指令;长任务还会受到上下文窗口、环境状态、工具可靠性和中间产物质量的影响。
一个没有设计过 Harness 的长任务,通常会出现几类反复发生的失败:
| 失败模式 | 表现 | Harness 层面的修复 |
|---|---|---|
| 一次性做完 | 一开始就修改大量文件,做到一半上下文耗尽 | 把目标拆成可验收的单个功能,每轮只推进一个增量 |
| 过早宣布完成 | 看到页面能运行,就把剩余功能也标记为完成 | 用结构化功能清单和自动验收状态代替模型自报完成 |
| 上下文断档 | 新一轮 Agent 不知道上轮改了什么、为什么这么改 | 保留 git 提交、进度日志、决策记录和下一步任务 |
| 只修表面问题 | 测试失败后不断改代码,却没有定位根因 | 让错误输出包含原因、位置、复现命令和下一步建议 |
| 陷入无效循环 | 长时间重复运行同一条测试或反复尝试同一方案 | 设置时间、步数、token 和工具调用预算,并提供停止条件 |
这里有一个很重要的判断:如果同一种错误在不同任务中反复出现,就不要只要求模型“下次注意”。应该把经验沉淀为一个新的系统能力,例如 schema、lint、测试、初始化脚本、文档索引或权限规则。这样修复才会对后续所有任务生效。
Harness Engineering 的四个支柱
参考业界对长时间运行 Agent 的实践,可以把 Harness Engineering 进一步归纳成四个相互配合的支柱。
1. 上下文架构:不是塞更多,而是组织得更好
上下文工程解决“模型应该看到什么”,但上下文架构还要解决“这些信息如何被发现、更新和验证”。一个成熟的仓库通常会有以下几层信息:
- 入口地图:项目如何启动、目录如何划分、常用命令在哪里。
- 领域规则:业务概念、状态转换、数据约束和权限边界。
- 架构决策:为什么选择某种依赖方向、接口或数据模型。
- 任务状态:当前完成了什么、还剩什么、哪些问题已知但未解决。
- 可执行证据:测试命令、截图、日志、指标和回归样例。
入口文档不应该变成一份几百页的总手册。更好的做法是提供一张可导航的地图,让 Agent 只在需要时读取相关细节。对 Agent 来说,放在仓库外但无法被工具访问的知识,实际上等同于不存在。
2. Agent 专业化:按职责拆分,而不是盲目增加数量
单个 Agent 可以完成很多事情,但并不意味着让一个 Agent 同时负责规划、实现、测试、视觉检查和清理是最优方案。长任务可以按职责拆成几个角色:
| 角色 | 输入 | 输出 |
|---|---|---|
| Planner | 用户目标和约束 | 功能清单、验收标准、实施顺序 |
| Builder | 一个具体功能和现有代码 | 增量代码、测试和结构化变更说明 |
| Evaluator | 变更和验收标准 | 测试结果、质量问题、是否允许继续 |
| Maintainer | 失败样本和历史变更 | 重构、文档更新、lint 或回归用例 |
专业化的重点不是让多个模型互相聊天,而是让每个阶段都有明确输入和可检查输出。一个测试 Agent 如果没有独立的验收标准,只是“再帮忙看一遍代码”,通常不会比原来的 Agent 更可靠。
3. 持久化记忆:让每个新会话都能快速接班
长任务不能假设同一个上下文永远存在。一个实用的交接目录可以很简单:
project/├── AGENTS.md # 项目地图、边界和常用命令├── docs/│ ├── decisions/ # 架构决策和重要取舍│ └── plans/ # 可验收的任务计划├── .agent/│ ├── progress.md # 最近几轮做了什么│ ├── known-issues.md # 已知问题和复现方式│ └── feature-list.json # 功能状态和验收结果└── scripts/init.sh # 初始化开发环境这些文件不是为了让人类写报告,而是为了降低下一轮 Agent 的接班成本。每一轮结束时,至少应该留下三件事:已经验证的结果、没有解决的问题、下一轮最值得做的一个动作。
4. 结构化执行:把“做得差不多”变成状态机
自然语言里的“完成”非常模糊。对于需要调用工具、修改状态或产生副作用的任务,最好把过程显式建模成状态机:
planned → implementing → checking → passed │ │ └─ blocked ←──┘每个状态都要有进入条件和退出条件。例如,checking 不能只表示“模型说已经检查过”,而应该要求测试命令返回成功、结构化输出通过 schema、关键页面检查通过。只有验收证据齐全,任务才能进入 passed。
三个公开实践带来的启发
OpenAI:把代码库变成 Agent 能读懂的工作环境
OpenAI 分享的 Codex 实践强调了一个容易被忽略的事实:当 Agent 负责越来越多的代码时,仓库本身就成了 Harness 的一部分。目录结构、文档、schema、架构约束、可执行计划和观测工具,都应该尽量在 Agent 工作时可发现、可读取、可验证。
另一个关键做法是约束边界而不是规定每一行实现。例如,要求数据在边界完成解析,要求领域层遵守固定的依赖方向,再通过自定义 lint 和结构化测试机械执行。这样既能保持架构一致,又不必把实现细节全部写死在 prompt 里。
这类实践对普通团队的启发并不是“马上建设百万行代码的全自动工厂”,而是:先把项目中最常被口头提醒的规则变成 Agent 能读取、工具能检查的规则。
Anthropic:初始化 Agent 和增量 Agent 分工
Anthropic 针对跨多个上下文窗口的长任务,采用了“初始化 Agent + 编码 Agent”的方式。第一轮负责搭建环境、生成启动脚本、建立功能清单和初始提交;后续每一轮只选择一个功能增量推进,并在结束时写入进度和提交记录。
这个设计解决的不是模型不会写代码,而是模型每次重新开始时不知道项目当前处于什么状态。它把“接班”本身变成 Harness 的固定流程,降低了每一轮重新探索环境的浪费。
从单循环到规划—执行—评估
当任务包含主观质量判断或复杂的多阶段交付时,可以进一步使用 Planner、Generator 和 Evaluator 的循环:
用户目标 ↓Planner:拆成规格和验收标准 ↓Generator:实现一个可运行增量 ↓Evaluator:执行测试并检查结果 ├─ 不通过:返回具体反馈,进入下一轮 └─ 通过:记录证据,进入下一个功能但多 Agent 不是默认答案。每增加一个角色,就增加上下文交接、状态同步、延迟和成本。应先用最简单的单 Agent Harness 建立基线,只有当某个职责确实成为瓶颈时,才拆出专门角色。
Harness 的成熟度,可以这样判断
可以用四个阶段快速判断一个项目目前处于什么水平:
| 阶段 | 典型状态 | 主要问题 |
|---|---|---|
| L0:手工试用 | 直接聊天,凭感觉判断结果 | 无法重跑,也无法解释回归 |
| L1:可重复运行 | 固定 prompt、数据集和模型版本 | 能比较结果,但失败原因不清楚 |
| L2:可观察验证 | 记录轨迹、工具调用、耗时、成本和评分 | 能定位问题,但规则还依赖人工 |
| L3:反馈闭环 | 失败样本自动进入回归集,约束由 CI 和工具执行 | 系统会持续吸收经验,人工只处理高风险判断 |
| L4:规模化编排 | 支持隔离环境、并发任务、角色分工和自动治理 | 关注架构熵、权限、成本和长期维护 |
大多数团队不需要直接追求 L4。能从 L0 走到 L2,已经足以解决“换个 prompt 看起来好了一点”这类不可复现的问题;从 L2 到 L3,则是 Harness 真正产生复利的阶段。
一份可落地的 Harness 检查清单
开始一个 Agent 项目时,可以先逐项回答下面的问题:
- Agent 是否有一个明确、短小、可导航的入口文档?
- 任务是否有机器可读的完成标准,而不只是“效果好一点”?
- 每个工具是否有 schema、权限边界、错误类型和幂等策略?
- Agent 是否可以在隔离环境中启动应用、运行测试和读取日志?
- 任务是否有最大步数、token、时间、金额和重试预算?
- 每次运行是否记录模型、prompt、数据集、工具版本和完整轨迹?
- 失败时能否区分模型、上下文、工具、环境和验收规则的问题?
- 修复一个失败后,是否新增了测试、文档、lint 或其他保护机制?
- 是否定期清理重复代码、过时文档、失效配置和无主任务?
- 高风险动作是否需要人工审批,且能完整审计?
如果这些问题大部分都答不上来,继续更换模型往往不是最优先的工作。先补齐 Harness 的可见性、可执行性和可验证性,通常能比单纯升级模型更快地改善真实任务成功率。
Harness Engineering 和 Prompt Engineering 的区别
两者并不冲突,但解决的问题不同:
| 做法 | 主要改变 | 典型问题 |
|---|---|---|
| Prompt engineering | 语言表达和指令结构 | 模型有没有理解这次要求 |
| Context engineering | 传给模型的信息组织方式 | 模型有没有拿到正确背景 |
| Harness Engineering | 工具、环境、反馈和约束 | 模型能不能稳定完成整件事 |
如果 Agent 第一次算错了一个字段,调整 prompt 可能有帮助;如果它反复读错同一种数据结构,更好的做法是给边界加 schema 校验;如果它经常修改错文件,就应该改善仓库地图、工作区隔离和验证命令。Harness Engineering 的思路是:当同一类失败重复出现时,优先增加一个系统能力,让它以后更难重复发生。
这也是它和“多写几句系统提示词”的区别。文档告诉 Agent 应该怎么做,代码和 CI 则负责判断它有没有做到。能机械验证的规则,不应该只停留在自然语言里。
三个关键概念:可见、可执行、可验证
1. 可见:让 Agent 看得到真实系统
Agent 无法使用它在当前上下文中无法访问的知识。把关键设计决策放在聊天记录、个人笔记或某个人脑中,对 Agent 来说就等于不存在。
因此需要一个仓库内的知识系统:入口文档只负责提供地图,详细规则放在按主题组织的文档、架构说明、产品规格、执行计划和数据 schema 中。这样 Agent 可以渐进式读取内容,而不是每次都把一千页手册塞进上下文。
可见性也包括运行中的应用。让 Agent 能启动一个隔离的 worktree、读取日志和指标、查看页面截图、重现错误,它才有机会完成“发现问题—修改—验证”的完整闭环。OpenAI 对 Codex 的实践中,就把 UI、日志和应用指标接入 Agent 可访问的环境,并为每个 worktree 提供隔离的运行实例。
2. 可执行:把意图变成稳定接口
“请把这个功能做好”不是一个足够好的 Agent 任务。更可执行的任务至少要说明:目标、范围、约束、可用工具、禁止操作和验收方式。
工具接口也要像普通软件 API 一样设计。工具名称和描述要清楚,参数要有 schema,错误要可区分,重复调用要安全。比如 update_candidate 不应该只接收一段任意 JSON,而应该明确允许修改哪些字段、字段之间有什么关系、更新失败时返回什么错误。
可执行还意味着环境真的能被调用:测试命令不应该依赖某个人的本地配置,开发服务应该能在临时目录启动,数据库和第三方服务要有沙箱或 mock。否则任务契约写得再漂亮,也只能停在纸面上。
3. 可验证:让系统给出反馈
Agent 不会因为“看起来完成了”就真的完成。Harness 要在每个关键阶段提供反馈:格式是否有效、测试是否通过、页面是否正确、日志是否出现异常、权限是否越界、最终结果是否满足验收标准。
反馈应该尽量靠近失败点。与其等到任务结束才告诉模型“结果不对”,不如让工具立即返回字段校验错误,让测试命令返回具体失败用例,让浏览器检查器指出哪个元素没有出现。反馈越具体,下一轮修正越有效。
架构约束:约束边界,不规定每一行代码
Harness Engineering 并不意味着把每个实现细节都写成 prompt。更有效的方式是规定不可违反的 invariant:数据在边界解析,领域层不能越级依赖,敏感操作必须经过权限检查,所有关键流程都要有结构化日志。
在这些边界之内,Agent 可以自行选择具体实现。这样既能保持架构一致,也不会因为过度规定而限制模型解决问题的空间。边界最好由 linter、结构化测试、类型系统和 CI 机械执行,而不是依赖评审者每次手工提醒。
一个常见的分层例子是:Types → Config → Repository → Service → Runtime → UI。业务代码只能沿固定方向依赖,认证、连接器、遥测和 feature flag 等横切能力统一从 Providers 接入。这个设计的重点不是层名,而是让依赖方向可读、可测、可拒绝。
从一次失败中改进 Harness
可以用下面的循环处理失败:
记录失败轨迹 ↓判断失败属于模型、上下文、工具、环境还是验收规则 ↓为这类失败增加一个具体能力 ↓把能力写成文档、代码、测试或 CI 约束 ↓把原失败样本加入回归集 ↓重新运行并观察质量、延迟和成本例如,Agent 总是把“已取消订单”当作“已完成订单”:
- 只改 prompt:告诉它再仔细一点,通常只能缓解一次。
- 改进上下文:提供订单状态的枚举和状态转换图。
- 改进工具:让 API 返回类型化状态,并拒绝非法状态转换。
- 改进验证:增加一条回归用例,检查最终动作与订单状态是否匹配。
最后一种处理方式才真正提升了 Harness,因为它让下一次运行具备了新的保护能力。
为什么 Agent 越强,Harness 越重要
弱模型通常卡在不会做;强模型更多卡在做得太多、做得太快,或者在错误方向上持续推进。模型能力提升后,工具权限、上下文质量、架构边界和反馈速度的重要性都会增加。
如果一个 Agent 每小时可以完成几十个修改,人工逐行检查就会成为瓶颈。此时需要把常规判断迁移到自动化检查,把人类经验沉淀成规则,把 UI、日志、指标和测试结果变成 Agent 可读取的证据。人类仍然负责目标、优先级和高风险决策,但不必把时间花在重复检查每个低风险细节上。
OpenAI 的实践还提到,Agent 生成速度提高后,技术债会像垃圾一样持续堆积,因此需要定期扫描偏离、更新质量等级、提交小型重构,让“清理”成为系统的一部分,而不是等到项目失控后集中返工。这是 Harness Engineering 很容易被忽略的一面:它不仅负责让 Agent 做得更快,也负责让系统长期保持可理解。
如何从零开始做 Harness Engineering
不需要一开始就搭建复杂的多 Agent 平台,可以按下面的顺序推进:
- 选一个边界清楚的真实任务,写出成功和失败的例子。
- 固定任务输入、模型、prompt、工具版本和验收规则,建立可重跑的基线。
- 给 Agent 一个最小但完整的环境:清晰的仓库地图、必要工具、隔离工作区和一条验证命令。
- 记录每一步轨迹,区分模型错误、工具错误、环境错误和规则错误。
- 把重复失败转化成 schema、测试、lint、文档或权限约束。
- 每次优化都同时观察任务成功率、P95 延迟、单任务成本和人工介入率。
做到这一步,Harness 就不再是模型外面的一层胶水,而是一个会随着失败样本持续进化的工程系统。
LLM 性能不止一个数字
延迟:用户究竟等多久
最常见的延迟指标包括:
| 指标 | 含义 | 适合观察什么 |
|---|---|---|
| TTFT | Time to First Token,从请求开始到首个 token | 用户感觉“有没有开始响应” |
| 端到端延迟 | 从请求发出到完整回答结束 | 一次任务实际花了多久 |
| TPOT | Time Per Output Token,生成每个输出 token 的平均耗时 | 流式输出速度 |
| 工具等待时间 | 工具请求发出到返回的时间 | 数据库、搜索、浏览器等外部瓶颈 |
| P95/P99 | 95%/99% 请求低于的延迟 | 长尾是否严重 |
流式输出会改善 TTFT 和体感,但不会自动缩短端到端时间。如果模型需要先调用搜索、再调用代码执行、最后生成报告,用户看到首个 token 之后仍可能要等待很久。因此报告性能时,应该同时记录“首 token 时间”和“任务完成时间”。
吞吐:系统能同时服务多少任务
吞吐通常用 requests per second(RPS)或 tokens per second(TPS)表示,但 LLM 的吞吐会受到输入 token、输出 token、并发数、上下文长度和限流策略共同影响。
同一个模型在短问答和长文档总结中的 TPS 不能直接比较。更合理的测试方式是固定请求分布,例如:输入 2K / 输出 500 token、输入 20K / 输出 2K token,各自测试并发 1、10、50 和 100,并记录成功率、P50、P95、TTFT、总 token 和排队时间。
成本:按请求算,而不是只看单价
一次任务的近似成本可以写成:
总成本 = 输入 token × 输入单价 + 缓存写入 token × 缓存写入单价 + 缓存命中 token × 缓存单价 + 输出 token × 输出单价 + 工具调用成本 + 重试成本如果一个 Agent 平均需要 6 次模型调用、2 次搜索、1 次代码执行,那么单次调用价格很低,也不代表整个任务便宜。Harness 应该按 task_id 聚合成本,并把每一次模型调用、工具调用和重试关联起来。
质量:答案正确不等于任务成功
LLM 质量至少可以拆成几层:
- 格式正确:JSON、字段、类型和 schema 是否符合要求。
- 事实正确:答案是否有依据,是否引用了正确的资料。
- 任务正确:是否真的完成了用户目标,而不是只写出一段看起来不错的文字。
- 工具正确:是否选择了合适的工具,参数是否正确,是否理解工具返回值。
- 安全正确:是否遵守权限、隐私、越权和审批要求。
- 交互正确:是否在不确定时提问,在目标明确时继续推进。
一个只检查最终文本的 benchmark,通常无法发现工具参数错误、无效重试或已经产生的副作用。好的 Harness 要把中间轨迹也纳入评测。
为什么需要 Harness
没有 Harness 时,模型效果往往靠“我刚刚试了一下,感觉不错”来判断。这种判断无法回答几个关键问题:
- 换一个模型后,是真的变好了,还是只是碰巧答对了几道题?
- prompt 改了以后,质量提升是否牺牲了延迟和成本?
- 工具调用失败时,系统会重试、降级,还是直接编造结果?
- 任务在并发 1 时很快,在并发 50 时是否已经排队超时?
- 上下文变长后,模型是否仍然能找到关键事实?
- 某次版本升级带来的提升,能否在下周重跑并得到相近结果?
Harness 的价值,是把一次性的体验变成可重复的实验,把黑盒输出变成可追踪的任务轨迹。
一个实用的评测 Harness 应该记录什么
每次运行至少需要一个稳定的 task_id,并记录以下信息:
{ "task_id": "support-0042", "dataset_version": "2026-09-07", "model": "model-id", "prompt_version": "support-v12", "reasoning_effort": "medium", "input_tokens": 1830, "output_tokens": 412, "ttft_ms": 620, "latency_ms": 4830, "tool_calls": 2, "retries": 0, "status": "passed", "grader_scores": { "task_success": 1, "citation_accuracy": 1, "format_valid": 1 }}这里的重点不是字段名称,而是把模型版本、prompt 版本、数据集版本、运行参数和结果绑定在一起。否则出现回归时,往往只能看到“昨天 87 分,今天 82 分”,却不知道是模型变了、题目变了、工具变了,还是评分器变了。
Harness 的基本运行流程
一个最小可用的评测流程大致如下:
读取数据集 ↓创建隔离的任务上下文 ↓运行模型和工具调用 ↓记录每一步输入、输出、耗时和 token ↓检查 schema、规则和安全约束 ↓执行任务级评分 ↓汇总质量、延迟、成本和失败原因 ↓与基线版本比较运行器要有明确的终止条件,例如最大步数、最大 token、最大金额、最长时间和允许的工具集合。对于会修改外部系统的 Agent,评测环境还应该使用 mock、沙箱或只读账号,避免为了测试模型而发送真实邮件、删除数据或创建订单。
评测时最容易踩的坑
只看平均值
平均延迟可能很好看,但 P99 已经超时;平均分也可能掩盖某一类关键任务全部失败。至少要同时看均值、P50、P95,以及按任务类型拆分的结果。
只测短上下文
短输入上的表现不能代表长上下文能力。测试集应该覆盖常见长度、极端长度和“关键信息埋在中间”的情况,并检查模型是否引用了正确位置的内容。
评分器和被测模型目标不一致
用另一个 LLM 做评分很方便,但评分器可能偏好更长、更像自己的回答。评分标准应该尽可能结构化,结合确定性规则、程序校验、人工抽检和模型评分,并定期检查评分器的一致性。
把重试后的成功算成原始成功
如果第一次工具调用失败,第三次重试才成功,最终任务可以算成功,但不应该把过程当成没有发生过失败。建议同时报告首尝试成功率、最终成功率、平均重试次数和重试带来的额外成本。
把 benchmark 当成生产结论
公开 benchmark 适合做方向性比较,不能替代自己的业务数据。客服、招聘、代码审查、数据分析等任务的真实约束完全不同,最终还是要建立一套与业务目标对应的私有评测集。
如何把性能优化落到 Harness 上
性能优化不应该只盯着换模型,还可以从 Harness 下手:
- 减少无效上下文:只保留任务需要的历史和检索结果,必要时做摘要或 compaction。
- 缩短关键路径:能并行的工具调用不要串行等待;把不影响最终答案的工作放到后台。
- 使用缓存:稳定的系统提示、工具 schema 和公共资料适合做 prompt cache,但要记录命中率和缓存成本。
- 设置模型路由:简单分类、格式转换交给低成本模型,复杂推理再升级到高能力模型。
- 限制重试:只对明确的临时错误重试,给每个任务设置预算和退避策略。
- 控制输出长度:要求模型先给结构化结论,再按需展开,避免无意义的长篇解释。
- 保留失败样本:不要只保存成功案例。失败轨迹才是改 prompt、改工具和改流程最有价值的素材。
最后:性能是系统属性
LLM 性能不是模型排行榜上的一个分数,也不是接口返回里的一个 tokens-per-second。对用户来说,真正重要的是:任务多久完成、结果是否可信、失败时是否可控,以及这次完成需要付出多少成本。
模型决定能力上限,Harness 决定能力能否稳定释放。没有 Harness,模型升级只能靠感觉;有了 Harness,才可以把质量、延迟、成本和安全放在同一张表里比较,也才能知道一次优化到底改善了什么、牺牲了什么。
如果要从今天开始做,最小步骤并不复杂:先选一组真实任务,固定数据集和 prompt 版本,记录每次运行的完整轨迹,再同时看任务成功率、P95 延迟、单任务成本和失败类型。等这四个指标稳定下来,再谈更大的模型、更复杂的 Agent 或更高的并发,判断会可靠得多。