这两年圈子里冒出一个高频词:Harness。我一开始也以为这只是某个具体框架的名字,后来翻 Anthropic 的工程博客、研究 Claude Code 的架构设计、再看 Google AX 那类声明式调度方案,才意识到大家讲的其实是同一件事——Agent 的模型能力正在快速同质化,真正拉开差距的,是围绕模型外围那套"驾驭系统"。模型是发动机,Harness 才是整车底盘、变速箱和方向盘。这篇文章想跟你聊聊我理解的 Harness 到底解决什么问题,以及从长时任务到声明式调度,这条技术路线是怎么一步步成为 Agent 新护城河的。适合正在做 Agent 开发、被上下文丢失和任务失控折磨过的朋友参考。
1. 先搞清楚:Harness 到底是什么,凭什么成为护城河
1.1 模型能力同质化之后,拼的就是外围工程
说实话,这两年新模型一个接一个出来,从 GPT 系列到 Claude、DeepSeek,每次发版大家都觉得"天要变了"。但真正把同一批任务放到同量级的模型上跑,你会发现结果差距远没有发布会 PPT 上写的那么大。模型的能力边界正在趋同,就好比大家都拿到了同一颗发动机,你跑不过别人,问题往往不出在发动机上,而是出在供油系统、散热系统、悬挂调校这些外围工程上。
我在实际项目里观察到的现象也是这样。半年前我们组做 Agent 重构,把基座模型从 A 换到 B,代码几乎没改,整体效果浮动很小。但当我们把外围的流程编排、上下文压缩、工具调用规范翻新一遍之后,同样的模型、同样的任务,成功率从六成直接拉到九成。这件事让我彻底想明白了一句话:模型决定下限,Harness 决定上限。
1.2 Harness 不是框架,而是一整套驾驭系统
很多人把 Harness 和 Agent 框架混为一谈。LangChain、CrewAI 这类东西确实算 harness 的一种实现,但它们只是冰山一角。Claude Code 里那套 skill 机制、Anthropic 在长时任务里做的状态持久化、DeepSeek Harness 工具链里的插件调度,本质上都属于 harness 的范畴。
如果非要打个比方,Agent 是骑手,模型是马,Harness 是缰绳、马鞍、路线图和急救包。马决定了你能跑多快,但缰绳决定你不会跑偏,马鞍决定你能跑多久,路线图决定你去哪儿,急救包决定你摔了还能不能爬起来。这些加起来,才是一匹真正能上战场的战马。一个只有模型没有 harness 的 Agent,跟一匹只有速度没有缰绳的野马差不多——看着吓人,真跑起来分分钟脱轨。
1.3 一句话定义:约束、编排与上下文管理
我给自己总结了 Harness 的三层职责,方便团队里新同学快速建立概念:
- 约束层:告诉模型什么能做什么不能做。工具调用必须走白名单,参数必须有 schema 校验,输出必须符合格式。约束不是限制能力,而是防止模型在关键动作上随机发挥。
- 编排层:决定任务怎么拆、怎么派、怎么恢复。长任务怎么切分,子任务之间怎么传递中间结果,失败之后从哪个检查点重跑,这是编排层的事。
- 上下文管理层:在有限的 token 窗口里维护任务的"工作记忆"。历史对话怎么压缩,长期事实怎么外置,哪些信息必须保真,哪些可以模糊化,全靠这一层。
这三层加起来,才是完整的 Harness。理解了这三层,再看 Anthropic 的长时任务设计、Google AX 的声明式调度,会发现它们只是在不同层次上回答同一个问题:怎么让模型在真实业务里稳定、可控、持续地干活。
2. 从 Anthropic 长时任务设计看 Harness 的底层逻辑
2.1 长时任务为什么是 Agent 的照妖镜
普通的一次性问答,模型能力强一点弱一点,用户体验差别不大。但一旦任务拉长到半小时、几小时甚至跨天,所有没做 harness 的 Agent 都会集中暴露问题:上下文窗口被塞爆、中间某个环节出错导致全盘重来、任务跑着跑着 Agent 忘了自己最初的目标、工具调用产生的副作用没法回滚。
Anthropic 在讨论 agentic 系统设计时反复强调过一个观点:对话本身不应该是长时任务的核心载体,任务状态才是。我特别认同这个判断。把 Agent 当成一个"能连续对话的聊天机器人",和把 Agent 当成一个"需要被管理的后台进程",是完全不同的工程设计思路。前者把目标、进度、中间变量全塞在对话历史里,后者把状态显式地落在外部存储里,对话只是状态的一种投影。
我们做过一个真实案例:一个要跑二十分钟的数据清洗 Agent,早期版本把所有中间结果都存在上下文里,跑到第五分钟就开始丢信息,第十二分钟模型的注意力已经完全乱了,最终结果基本不可用。后来把中间结果全部落盘到任务目录,上下文里只保留摘要和指针,同样跑二十分钟,稳定性和准确率完全不是一个量级。
2.2 任务状态机:把临时会话变成可持续工程
要做好长时任务,第一步是转变思维。别再想着维护"一段超长对话",而是把它拆成"一组状态明确的步骤"。我常用的设计是画一张任务状态机,四种核心状态:待执行、执行中、已完成、失败待处理。每个状态在外部存储里都有一份记录,包括这一步的输入参数、输出产物、运行日志、依赖的检查点。
举个具体例子,一个生成行业调研报告的 Agent,我会把它拆成这样:
- 收集资料(工具:搜索/爬虫/文件读取)
- 数据清洗与去重(工具:数据处理脚本)
- 形成大纲(模型推理)
- 分章节撰写(模型推理 + 多轮自审)
- 汇总排版导出(工具:文档生成)
每个步骤都是一个独立状态。步骤 2 失败时,不需要从头搜索资料,直接从步骤 2 的检查点重跑。步骤 4 写某一章的时候发现原文引用缺失,可以回到步骤 1 补抓资料再回来继续,而不是整个任务重开。这套状态机的设计思路,就是 Anthropic 在长时任务文档里反复强调的"resumable by design"——不是靠运气不失败,而是让失败变成可恢复的常态。
2.3 检查点、心跳与幂等三件套
具体到技术实现,长时任务 harness 必须解决三件事:
检查点(Checkpoint)。每完成一个步骤,把本步骤的关键产物和上下文摘要持久化。检查点的粒度很讲究,太粗起不到恢复作用,太细又拖慢任务。我的经验是:以"工具调用边界"为粒度最合适,因为工具调用前后状态变化最剧烈,模型推理过程中的检查价值不大。
心跳(Heartbeat)。长时任务跑在后台,harness 要定期确认 Agent 还活着、还在有效工作。心跳机制本质上是一种 liveness probe,超过阈值没回应就判定任务异常,触发降级策略——可能是自动恢复,也可能是发告警让人介入。这个机制在跨夜跑批的任务里尤其重要,凌晨三点任务卡死,总不能等上班才发现。
幂等(Idempotency)。这可能是最容易被忽略但最要命的一点。工具调用失败后重试,最怕的不是失败本身,而是失败前已经产生了副作用。比如一个"创建订单"的工具,第一次调用其实已经创建成功了,但响应超时,Agent 误判失败然后重试,就产生了两个订单。解决办法是给每个任务步骤生成唯一的 request_id,写入外部存储,工具执行前先检查是否已经处理过这个请求。幂等是长时任务能安全重试的基石。
这三件事做扎实了,长时任务才真正具备工程意义上的可靠性。这比在提示词里写一百遍"请仔细思考"有用得多。
3. 声明式调度:Google AX 带来的范式转变
3.1 命令式编排为什么会烂尾
很多 Agent 项目的流程控制写的是代码逻辑:if什么条件就调用 A 工具,else就调用 B 工具,某一步返回什么值就决定下一步怎么走。这就是典型的命令式编排——每一步怎么走都在代码里写死,Agent 只是被动的执行器。
命令式编排的优点是直观,缺点是极其脆弱。业务逻辑一旦复杂起来,代码里的条件分支越来越多,跟意面没什么区别。更麻烦的是,命令式编排把"流程"和"执行"耦合死了,你没法在流程中间插入一个人工审批节点,没法让两个分支同时跑然后取先返回的结果,也没法在某一环节失败后优雅地把剩余流程换一种方式走完。你只能不断地加if、加状态位、加补丁,最后这套代码就成了没人敢动的定时炸弹。
3.2 声明式的核心:描述目标,而不是描述路径
Google AX 那类声明式调度方案,换了一条完全不同的思路:你不再告诉 Agent 每一步具体怎么走,而是告诉它"最终要什么结果、中间有什么约束、哪些步骤之间是什么关系",由调度器来决定最优执行路径。
我画过一张对比图,左边是命令式:
1. 读取订单列表 2. 调用客服系统查询异常原因 3. 根据原因分类 4. 方案A → 退款;方案B → 补发 5. 通知用户右边是声明式:
task: 处理待办异常订单 goal: 每个订单给出处理结果并通知用户 constraints: - 单笔退款金额超过1000元必须人工审批 - 补发操作需要在库存充足时执行 dependencies: - 查询异常原因 依赖于 读取订单列表 - 生成处理方案 依赖于 查询异常原因 - 通知用户 依赖于 生成处理方案两种描述方式最大的区别是:声明式方案里,调度器可以在"查询异常原因"这一步连续失败三次之后,选择调用备用的客服系统接口,而不是直接让整个任务报废。它也可以在"库存不足导致补发失败"时,跳过补发、直接走退款分支,因为约束清单里本来就定义了这两个方案的优先级关系。这些动态决策能力,在命令式代码里几乎是没法优雅实现的。
3.3 调度器替你做的那几件脏活
声明式调度的价值,是把下面这些脏活从业务代码里剥离出来,交给调度器统一处理:
- 并发控制:多个子任务之间如果没有依赖关系,调度器可以并行执行,而不是傻乎乎地串行跑。比如"查询订单详情"和"查询用户历史"本来就是独立的信息获取,完全可以同时发起。
- 超时与重试:声明式描述里会附带每个步骤的预期耗时和重试策略,调度器按策略执行,超时自动降级。
- 资源管理:给每个任务分配 token 预算、工具调用配额,超过预算挂起等待人审,而不是无限烧钱。
- 可观测性:因为流程是声明式的,调度器天然记录了"当前到哪一步、上一步用了多久、失败原因是什么",你甚至不需要额外打日志,就能画出一张完整的任务执行拓扑图。
我在一个客户项目里把一套命令式 Agent 流程改造成声明式调度后,代码量减少了大概六成,同时任务成功率提升了近二十个百分点。原因并不玄学——以前为了容错写的各种try-catch和状态机逻辑,现在大部分都被调度器承担了,业务代码只需要关心每个步骤做什么,不用再关心怎么兜底。这就是声明式范式最实在的红利。
4. Harness 工程实战:从 Claude Code 到 DeepSeek Harness 的启示
4.1 Skill 机制:可插拔的能力包
Claude Code 的 Skills 机制,以及 DeepSeek Harness 工具链里的插件体系,是我觉得值得所有 Agent 项目抄作业的设计。它的核心思想很简单:把某类任务需要用到的方法论、提示词片段、工具配置和验证规则打包成一个独立的 "skill 目录",Agent 在遇到相关任务时动态加载这个目录,而不是把所有知识全塞在系统提示词里。
举个例子,我们团队做了一个"数据分析师" skill,目录结构大概是这样的:
skills/data-analyst/ ├── SKILL.md # skill 的触发条件、适用场景、工作流程 ├── references/ │ ├── sql-guidelines.md │ └── chart-config.md └── scripts/ ├── validate_data.py └── make_charts.pySKILL.md 里写清楚"当你看到用户请求涉及数据清洗、聚合分析或可视化时,自动加载本技能,遵循以下步骤……"。Agent 平时不加载这些内容,不占 token;一旦遇到匹配任务,harness 把整个 skill 目录注入上下文。这套机制让同一个 Agent 可以按需切换能力边界,既保证了通用性,又保住了专业深度。
我强烈建议每个 Agent 项目都引入类似的 skill 机制。它带来的好处不只是 token 节省,更重要的是能力边界可管理。你可以给财务场景做一个 skill、给客服场景做一个 skill,每个 skill 内部可以指定专用的工具白名单和输出规范。Agent 的行为不再是模型自由发挥的结果,而是被 skill 约束的、可预期的产物。
4.2 上下文管理:token 窗口不够用是常态
上下文管理是 harness 工程里最磨人的部分。模型上下文窗口就那么大,任务一长必然溢出。我的处理思路是把上下文分成三层:
第一层是常驻上下文。系统提示词、核心工具定义、当前任务目标,这些必须每轮保留,是 Agent 的"工作台",占用要精打细算,能压缩就压缩。
第二层是短期工作记忆。最近几轮对话、尚未落盘的中间结果,放在上下文里。这里的核心技巧是滚动摘要——每当超过阈值,就把前面的对话压缩成一段结构化摘要,旧消息从上下文里移除。摘要一定要包含关键数据字段值,不能只给"用户前面提到了需求"这种模糊描述。
第三层是长期外部记忆。历史任务记录、知识库片段、用户偏好,放在向量库或外部文件里。只有 Agent 需要时,通过检索工具拉取。
三层之间有明确的升降级规则。比如某个信息在短期工作记忆里三个月没被用到,就降级到长期外部记忆;某个外部检索结果对当前任务至关重要,就提升到短期工作记忆。这一套策略本质上就是给 Agent 做"大脑管理系统",不是所有信息都值得占着工作记忆不放手。
4.3 并发模型:Agent 能扛多少并发,取决于 harness 而不是模型
很多人关心"AI Agent 怎么扛并发",我的答案可能会让人意外:模型的推理速度反而不是瓶颈,真正的瓶颈在 harness 和外部依赖上。
假设一个 Agent 任务平均要调 15 次模型 API、6 次外部工具调用。要支撑 100 个并发任务,意味着同一瞬间可能产生几百上千个外部请求。如果 harness 不做好连接池管理、接口限流和依赖隔离,Agent 还没因为模型响应慢崩掉,先被外部 API 的限流策略打崩了。我们有个血的教训:早期并发一上来,下游 ERP 系统直接把我们的服务 IP 拉黑了,就因为 harness 没做并发控制,10 个 Agent 同时用同一个账号登录 ERP 抢会话。
正确的做法是给每个 Agent 任务分配独立的外部会话,或者至少做好请求级的身份隔离;同时所有外部调用统一走网关,由网关做令牌桶限流、超时熔断和重试排队。让模型 API 去并发,让网关去保护外部依赖,这才是 Agent 系统扛高并发的正解。这背后全是 harness 工程,跟模型本身没什么关系。
5. Harness 的进阶战场:RPA 落地与 Agent 安全
5.1 给 RPA 装上大脑:动态流程替代死脚本
传统 RPA 解决的是"重复劳动自动化"问题,本质上是把固定流程录制成脚本,一旦业务规则有变化,脚本就要返工。Agent 解决的是"不确定场景下的智能决策"问题,但单靠 Agent 又没法直接操作老旧的业务系统界面。两者结合,也就是热词里提到的 "harness + RPA 落地",是我今年看到的落地价值最高的方向之一。
具体做法是用 Agent 作为决策大脑,RPA 作为执行手脚,harness 作为两者之间的调度中枢。举个例子,某银行对公账户的开户审核流程:RPA 负责从多个系统里抓取企业工商信息、司法记录、征信报告;Agent 负责根据这些材料判断是否存在风险点;如果材料不完整,Agent 自动生成补件清单,RPA 去发通知;如果风险等级较高,Agent 标记转人工。整个流程不再是一条写死的脚本,而是一个"感知-决策-执行-反馈"的闭环。
在这个架构里,harness 的关键工作是协议转换。RPA 返回的是非结构化的界面文本和表格数据,Agent 需要的是结构化的 JSON;harness 要完成两者的转换、校验和标准化。同时,RPA 每一步的状态要同步给 Agent 做进度感知,Agent 的决策要翻译成 RPA 能执行的原子指令。没有这层 harness,两个系统完全是鸡同鸭讲。
5.2 安全边界:最小权限、沙箱与审计
Agent 能力越强,越要担心它滥用工具。让 Agent 自由调用 shell、访问数据库、操作生产系统,是很多团队踩过的最大的坑。Harness 在这里承担的是"安全边界"职责,我总结了三道防线:
第一道防线是最小权限。Agent 的所有工具调用都走一个代理层,代理层检查调用者身份、任务级别、当前阶段的权限范围。比如"数据读取"任务只能查 SELECT,不能执行 DROP TABLE;"文档生成"任务只能写临时目录,不能碰生产环境。权限不是在系统提示词里靠"提醒"实现的,而是在代理层强制拦截的。
第二道防线是沙箱隔离。Agent 执行代码类工具时,绝不能让它在宿主机上直接跑。要么用容器隔离,要么用子进程加 seccomp 规则,要么至少跑在独立的工作目录里。尤其是社区开源生态里那些 harness 插件和 skill 包,来源复杂,里面藏恶意代码的风险不可忽视。从网上下载的 skill 脚本,一定要人工 review 过后才允许执行。
第三道防线是审计日志。所有工具调用输入输出、Agent 的每个决策点、系统注入的每次 prompt,全部留痕。一是为了排查问题,二是为了追溯事故。我们甚至会把每轮调用的 token 消耗和工具副作用存成结构化日志,出了问题可以直接回放整个决策链。这套审计数据在合规审计和事故定责时都是硬通货。
5.3 失败注入与可观测性:护城河的护城河
护城河的意思是"别人难以复制"。"难以复制"的技术能力,核心不只是你会多少新功能,而是你知不知道系统会怎么死。我见过太多 Agent 项目演示时惊艳全场,一上生产就崩得七零八落。原因就是团队只写了"happy path"的代码,对失败路径一无所知。
我的建议是:尽早开始做故障注入测试。定期随机让 harness 里的某个工具接口返回异常、让某个外部依赖超时、让模型连续输出非法 JSON,观察 Agent 和调度器怎么反应。一个成熟的 harness,应该能在故障注入时做到:任务挂起而不是崩溃、错误信息分级告警、能自动切换备用路径、能优雅地等待人工介入。
可观测性同样是 harness 工程里不可省的一部分。我给 Agent 系统实现的可观测性包括四个维度:
- 轨迹追踪:一次任务从开始到结束调用了哪些工具、每个环节耗时多少、token 消耗趋势。
- 状态可视化:当前并发任务数、各状态机状态分布、失败率与重试率。
- 成本监控:按任务类型、按模型、按 skill 维度统计 token 费用。
- 质量指标:任务完成率、人工介入率、单任务平均迭代轮数。
没有这些数据,你所谓的护城河只是一座黑灯瞎火的城堡——别人进不来,你也看不见外面发生了什么。
6. 从零搭建一个最小可用的 Harness
6.1 核心模块清单
如果你要从零搭一套自己的 harness,不需要一开始就上个多宏大的平台。我建议先按下面的清单做最小实现,跑通后再逐步加复杂能力:
| 模块 | 职责 | 最小实现方案 |
|---|---|---|
| 任务状态存储 | 记录每个任务的当前状态、中间产物、检查点 | 一个 SQLite 表或一个目录加 JSON 文件 |
| 上下文管理器 | 滚动摘要、三层上下文升降级 | 一个摘要函数加一个向量检索服务 |
| 工具代理层 | 权限校验、白名单、参数校验、调用限流 | FastAPI 写一层中间代理 |
| 调度器 | 执行规划、重试、超时、并发控制 | 先做串行执行器,后续换成可并发的调度引擎 |
| 审计日志 | 全量记录调用链和决策点 | 结构化日志写文件或 ES |
| Skill 加载器 | 按任务类型动态注入技能包 | 文件目录扫描加一次性注入 |
这里最容易被低估的是任务状态存储。很多人觉得用一个 SQLite 就够,实际上任务一旦多起来,并发读写、状态迁移、历史归档全都要考虑。我的建议是第一步就设计好状态模型,至少包含 task_id、parent_task_id、status、inputs、outputs、error_info、checkpoint_path、created_at、updated_at 这些字段。宁可前期多建一列,也别后面迁移表结构。
6.2 我在落地时踩过的四个坑
第一个坑:把提示词当 harness。早期我以为只要把系统提示词写得好一点,Agent 就会听话。实际上提示词约束是软约束,模型随时会犯难、会遗忘、会自由发挥。现在我的原则是:凡是不能容忍随机性的逻辑,必须用代码硬约束,不能用提示词软约束。
第二个坑:重试机制没有做幂等。前面提过,工具调用超时后重试,如果没有 request_id 去重,极容易重复执行副作用操作。我们生产环境踩过最严重的一次事故,就是因为重试导致同一个营销短信发了三遍。从那以后,所有会对外产生副作用的工具调用,都必须支持幂等。
第三个坑:上下文摘要丢关键细节。滚动摘要开始时,我们把对话压缩得太狠,把一些关键的数字和数据范围摘要丢了。Agent 在后续任务里一本正经地用了错误的数据。这是最危险的一类 bug,因为它不报错、不失败,只是安静地给你一个错的结果。解决办法是在摘要模板里强制保留"关键数据值"字段,摘要后还要做一轮一致性校验。
第四个坑:一个 Agent 打天下。早期我们试图用一个通用 Agent 处理所有任务,结果就是什么都能聊、什么都不精。后来改成多 Agent 加 skill 路由:每个专业场景配独立的 skill 包,甚至独立的模型配置和工具白名单。这让每个子 Agent 的行为简单可控,也让整体系统的可调试性大幅提升。
6.3 现成组件怎么选
如果你不想完全从零开始,完全可以站在开源生态的肩膀上。我筛选了几个方向的现成组件:
- Agent 框架:可以看 LangChain、CrewAI,它们提供了比较完整的状态管理和工具调用抽象,适合快速验证原型。
- Claude Code / DeepSeek Harness 的插件体系:如果你用的模型是这个生态,直接研究它们的 skill 目录和插件加载机制,相当于有一个成熟的参考实现可以对着抄。
- 调度引擎:可以用 Temporal 或 Celery 这类任务队列来承载长时任务的调度、重试和检查点,不需要自己重写一套。
- 向量库:做长期记忆和知识检索,轻量用 SQLite 的全文检索也能顶一阵子,后续上量再考虑独立的向量数据库。
- 网关:外部 API 的统一入口,用 Nginx 或者一个简单的 API 网关服务都能实现限流和熔断。
我个人的倾向是前期尽量少引入重型依赖。Agent 开发的迭代速度非常快,你今天选的重框架,三个月后可能就是负担。先把核心 harness 的骨架用最朴素的技术搭起来,验证业务闭环,再考虑替换和升级。
7. 最后分享一点个人体会
做完好几个 Agent 项目之后,我越来越觉得,Harness 这个词之所以能成为这一阶段的焦点,是因为整个行业终于从"模型崇拜"走出来了。模型当然重要,但它已经不再是需要重点博弈的变量。真正考验一个团队工程能力的,是你能不能设计出一套系统,让模型稳定地在一个约束完备、状态清晰、失败可恢复的环境里持续工作。
我的个人体会是:与其天天研究怎么把提示词写得花里胡哨,不如花时间把状态存储做好、把工具调用代理做扎实、把失败路径全部演练一遍。这些东西刚开始看起来没有"写智能体"有快感,但它们才是项目能够走上生产环境的真正底气。护城河从来不是某个灵光一现的技巧,而是一整套日复一日被验证和打磨的工程纪律。
最后送各位一个小建议:从今天开始,给你的 Agent 写一份"失败预案"文档,列出它可能死掉的十种方式,然后挨个去验证、去修复。做完这一件事,你的 Agent 系统稳定性起码上一个台阶。